Skip to content

Commit

Permalink
Add id3v2.Options
Browse files Browse the repository at this point in the history
BREAKS AN API!!! Please read the README.md or make and issue, if you
have any questions.
  • Loading branch information
n10v committed May 1, 2017
1 parent f74418d commit 0735adc
Show file tree
Hide file tree
Showing 19 changed files with 364 additions and 129 deletions.
52 changes: 45 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ It can't:
* Do unsyncronization
* Work with extended header, flags, padding, footer

*id3v2 is still in beta. Until version 1.0 the API may be changed.*
**id3v2 is still in beta. Until version 1.0 the API may be changed.**

If you have **issues with encoding** or you don't know, how to **set
encoding to frame**, please write new issue!
Expand All @@ -53,21 +53,23 @@ import (
)

func main() {
// Open file and find tag in it
tag, err := id3v2.Open("file.mp3")
// Open file and parse tag in it.
tag, err := id3v2.Open("file.mp3", id3v2.Options{Parse: true})
if err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}
defer tag.Close()

// Read tags
// Read frames.
fmt.Println(tag.Artist())
fmt.Println(tag.Title())

// Set tags
// Set simple text frames.
tag.SetArtist("New artist")
tag.SetTitle("New title")


// Set comment frame.
comment := id3v2.CommentFrame{
Encoding: id3v2.ENUTF8,
Language: "eng",
Expand All @@ -76,14 +78,50 @@ func main() {
}
tag.AddCommentFrame(comment)

// Write it to file
// Write it to "file.mp3".
if err = tag.Save(); err != nil {
log.Fatal("Error while saving a tag: ", err)
}
}
```

## Read multiple frames:
```go
pictures := tag.GetFrames(tag.CommonID("Attached picture"))
if pictures != nil {
for _, f := range pictures {
pic, ok := f.(id3v2.PictureFrame)
if !ok {
log.Fatal("Couldn't assert picture frame")
}

// Do something with picture frame.
// For example, print the description:
fmt.Println(pic.Description)
}
}
```

## Options:
```go
// Options influence on processing the tag.
type Options struct {
// Parse defines, if tag will be parsed.
Parse bool

// ParseFrames defines, that frames do you only want to parse. For example,
// `ParseFrames: []string{"Artist", "Title"}` will only parse artist
// and title frames. You can specify IDs ("TPE1", "TIT2") as well as
// descriptions ("Artist", "Title"). If ParseFrame is blank or nil,
// id3v2 will parse all frames in tag. It works only if Parse is true.
//
// It's very useful for performance, so for example
// if you want to get only some text frames,
// id3v2 will not parse huge picture or unknown frames.
ParseFrames []string
}
```

## Documentation

You can find it here: https://godoc.org/github.com/bogem/id3v2
https://godoc.org/github.com/bogem/id3v2
6 changes: 3 additions & 3 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func BenchmarkParseAllFrames(b *testing.B) {
b.ResetTimer()

for n := 0; n < b.N; n++ {
tag, err := Open(mp3Name)
tag, err := Open(mp3Name, defaultOpts)
if tag == nil || err != nil {
b.Fatal("Error while opening mp3 file:", err)
}
Expand All @@ -35,7 +35,7 @@ func BenchmarkParseAndWriteCommonCase(b *testing.B) {
// We use b.N+1, because in first iteration we just resetting tag
// and setting common frames. Also timer will be resetted.
for n := 0; n < b.N+1; n++ {
tag, err := Open(mp3Name)
tag, err := Open(mp3Name, defaultOpts)
if tag == nil || err != nil {
b.Fatal("Error while opening mp3 file:", err)
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func BenchmarkParseAndWriteManyFrames(b *testing.B) {
// We use b.N+1, because in first iteration we just resetting tag
// and setting many frames. Also timer will be resetted.
for n := 0; n < b.N+1; n++ {
tag, err := Open(mp3Name)
tag, err := Open(mp3Name, defaultOpts)
if tag == nil || err != nil {
b.Fatal("Error while opening mp3 file:", err)
}
Expand Down
2 changes: 2 additions & 0 deletions bwpool/bwpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ var bwPool = sync.Pool{
New: func() interface{} { return bufio.NewWriter(nil) },
}

// Get returns *bufio.Writer with specified w.
func Get(w io.Writer) *bufio.Writer {
bw := bwPool.Get().(*bufio.Writer)
bw.Reset(w)
return bw
}

// Put puts bw back to pool.
func Put(bw *bufio.Writer) {
bwPool.Put(bw)
}
26 changes: 26 additions & 0 deletions comment_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,32 @@ import (
// The information about how to add comment frame to tag you can
// see in the docs to tag.AddCommentFrame function.
//
// Example of reading comment frames:
//
// comments := tag.GetFrames(tag.CommonID("Comments"))
// if comments != nil {
// for _, f := range comments {
// comment, ok := f.(id3v2.CommentFrame)
// if !ok {
// log.Fatal("Couldn't assert comment frame")
// }
//
// // Do something with comment frame.
// // For example, print the text:
// fmt.Println(comment.Text)
// }
// }
//
// Example of adding comment frames to tag:
//
// comment := id3v2.CommentFrame{
// Encoding: id3v2.ENUTF8,
// Language: "eng",
// Desciption: "My opinion",
// Text: "Very good song",
// }
// tag.AddCommentFrame(comment)
//
// You must choose a three-letter language code from
// ISO 639-2 code list: https://www.loc.gov/standards/iso639-2/php/code_list.php
type CommentFrame struct {
Expand Down
10 changes: 10 additions & 0 deletions common_ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ var (
"Year": "TYER",
"User defined text information frame": "TXXX",
"Unsynchronised lyrics/text transcription": "USLT",

// Just for convenience
"Artist": "TPE1",
"Title": "TIT2",
"Genre": "TCON",
}

V24CommonIDs = map[string]string{
Expand Down Expand Up @@ -111,6 +116,11 @@ var (
"Recording dates": "TDRC",
"Size": "",
"Year": "TDRC",

// Just for convenience
"Artist": "TPE1",
"Title": "TIT2",
"Genre": "TCON",
}
)

Expand Down
24 changes: 13 additions & 11 deletions id3v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@
// )
//
// func main() {
// // Open file and find tag in it
// tag, err := id3v2.Open("file.mp3")
// // Open file and parse tag in it.
// tag, err := id3v2.Open("file.mp3", id3v2.Options{Parse: true})
// if err != nil {
// log.Fatal("Error while opening mp3 file: ", err)
// }
// defer tag.Close()
//
// // Read tags
// // Read frames.
// fmt.Println(tag.Artist())
// fmt.Println(tag.Title())
//
// // Set tags
// // Set simple text frames.
// tag.SetArtist("Artist")
// tag.SetTitle("Title")
//
// // Set comment frame.
// comment := id3v2.CommentFrame{
// Encoding: id3v2.ENUTF8,
// Language: "eng",
Expand All @@ -39,7 +40,7 @@
// }
// tag.AddCommentFrame(comment)
//
// // Write it to file
// // Write tag to "file.mp3".
// if err = tag.Save(); err != nil {
// log.Fatal("Error while saving a tag: ", err)
// }
Expand Down Expand Up @@ -106,17 +107,18 @@ var (
Encodings = []util.Encoding{ENISO, ENUTF16, ENUTF16BE, ENUTF8}
)

// Open opens file with name and finds tag in it.
func Open(name string) (*Tag, error) {
// Open opens file with name and passes it to OpenFile.
func Open(name string, opts Options) (*Tag, error) {
file, err := os.Open(name)
if err != nil {
return nil, err
}

return ParseFile(file)
return OpenFile(file, opts)
}

// ParseFile parses opened file and finds tag in it.
func ParseFile(file *os.File) (*Tag, error) {
return parseTag(file)
// OpenFile parses opened file and finds tag in it considering opts.
// If there is no tag in file, OpenFile will create new one with version ID3v2.4.
func OpenFile(file *os.File, opts Options) (*Tag, error) {
return parseTag(file, opts)
}
8 changes: 6 additions & 2 deletions lrpool/lrpool.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Copyright 2017 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

Expand All @@ -16,11 +16,15 @@ var lrPool = sync.Pool{
New: func() interface{} { return new(io.LimitedReader) },
}

func Get() *io.LimitedReader {
// Get returns *io.LimitedReader with specified rd and n from pool.
func Get(rd io.Reader, n int64) *io.LimitedReader {
r := lrPool.Get().(*io.LimitedReader)
r.R = rd
r.N = n
return r
}

// Put puts r back to pool.
func Put(r *io.LimitedReader) {
r.N = 0
r.R = nil
Expand Down
22 changes: 22 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2017 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

// Options influence on processing the tag.
type Options struct {
// Parse defines, if tag will be parsed.
Parse bool

// ParseFrames defines, that frames do you only want to parse. For example,
// `ParseFrames: []string{"Artist", "Title"}` will only parse artist
// and title frames. You can specify IDs ("TPE1", "TIT2") as well as
// descriptions ("Artist", "Title"). If ParseFrame is blank or nil,
// id3v2 will parse all frames in tag. It works only if Parse is true.
//
// It's very useful for performance, so for example
// if you want to get only some text frames,
// id3v2 will not parse huge picture or unknown frames.
ParseFrames []string
}
Loading

0 comments on commit 0735adc

Please sign in to comment.