forked from mikkyang/id3-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathid3.go
130 lines (109 loc) · 2.47 KB
/
id3.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2013 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package id3
import (
"errors"
"os"
"github.com/hasty/id3-go/v1"
"github.com/hasty/id3-go/v2"
)
const (
LatestVersion = 3
)
// Tagger represents the metadata of a tag
type Tagger interface {
Title() string
Artist() string
Album() string
Year() string
Genre() string
Comments() []string
SetTitle(string)
SetArtist(string)
SetAlbum(string)
SetYear(string)
SetGenre(string)
AllFrames() []v2.Framer
Frames(string) []v2.Framer
Frame(string) v2.Framer
DeleteFrames(string) []v2.Framer
AddFrames(...v2.Framer)
Bytes() []byte
Dirty() bool
Padding() uint
Size() int
Version() string
}
// File represents the tagged file
type File struct {
Tagger
originalSize int
file *os.File
}
var ErrInvalidFile = errors.New("invalid file")
// Reads a tagged file
func Read(name string) (*File, error) {
fi, err := os.OpenFile(name, os.O_RDONLY, 0666)
if err != nil {
return nil, err
}
file := &File{file: fi}
if v2Tag := v2.ParseTag(fi); v2Tag != nil {
file.Tagger = v2Tag
file.originalSize = v2Tag.Size()
} else if v1Tag := v1.ParseTag(fi); v1Tag != nil {
file.Tagger = v1Tag
} else {
return nil, ErrInvalidFile
}
return file, nil
}
// Opens a new tagged file
func Open(name string) (*File, error) {
fi, err := os.OpenFile(name, os.O_RDWR, 0666)
if err != nil {
return nil, err
}
file := &File{file: fi}
if v2Tag := v2.ParseTag(fi); v2Tag != nil {
file.Tagger = v2Tag
file.originalSize = v2Tag.Size()
} else if v1Tag := v1.ParseTag(fi); v1Tag != nil {
file.Tagger = v1Tag
} else {
// Add a new tag if none exists
file.Tagger = v2.NewTag(LatestVersion)
}
return file, nil
}
// Saves any edits to the tagged file
func (f *File) Close() error {
defer f.file.Close()
if !f.Dirty() {
return nil
}
switch f.Tagger.(type) {
case (*v1.Tag):
if _, err := f.file.Seek(-v1.TagSize, os.SEEK_END); err != nil {
return err
}
case (*v2.Tag):
if f.Size() > f.originalSize {
start := int64(f.originalSize + v2.HeaderSize)
offset := int64(f.Tagger.Size() - f.originalSize)
if err := shiftBytesBack(f.file, start, offset); err != nil {
return err
}
}
if _, err := f.file.Seek(0, os.SEEK_SET); err != nil {
return err
}
default:
return errors.New("Close: unknown tag version")
}
if _, err := f.file.Write(f.Tagger.Bytes()); err != nil {
return err
}
return nil
}