I would like to read the Popularimeter frame from a file with id3-go.
This is how the frame looks like when printing with mutagen-inspect:
$ mutagen-inspect samples/with_popm.mp3 | grep POPM
POPM=traktor@native-instruments.de=0 255/255
I would like to read the value (255/255) from the file. As I could not find any documentation, my naive approach is:
popFrame := mp3File.Frame("POPM")
log.Println(popFrame.String())
But when I run this (on a file with and also without a popularimeter tag), I get segmentation faults:
$ ./id3-go-popm-example samples/with_popm.mp3
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]
goroutine 1 [running]:
main.main()
/home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2
$ ./id3-go-popm-example samples/without_popm.mp3
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]
goroutine 1 [running]:
main.main()
/home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2
I setup a sample repository containing two sample files (one with, one without popularimeter frame) here: https://github.com/ifischer/id3-go-popm-example
How to I get the Popularimeter value (255/255) with id3-go library?
You're getting a segmentation fault because mp3File.Frame("POPM")
is actually returning a nil
value. I've narrowed it down to the V23FrameTypeMap
in id3-go
which lacks the TRDC
frame. I'm really not familiar with the internals of ID3 but maybe you need to check if TRDC
is really in V2.3 and if it is, add the missing ids in V23FrameTypeMap
in id3-go
.
Since it would be too much of a hassle to learn ID3, I've managed to get the POPM
tag using another library https://github.com/dhowden/tag
f, _ := os.Open("with_popm.mp3")
m, _ := tag.ReadFrom(f)
fmt.Printf("%s
", m.Raw()["POPM"])
which outputs:
traktor@native-instruments.de
Edit: I mistook TDRC
as TRDC
, my mistake. Anyway, here is what you need to do to get the POPM
field using id3-go
Add the following in V23FrameTypeMap
in github.com/mikkyang/id3-go/v2/idv23.go
V23FrameTypeMap = map[string]FrameType{
//...
"TDRC": FrameType{id: "TDRC", description: "Recording Date", constructor: ParseTextFrame},
}
Then you can easily get the POPM
field e.g.
package main
import (
"fmt"
id3 "github.com/mikkyang/id3-go"
)
func main() {
f, _ := id3.Open("with_popm.mp3")
p := f.Frame("POPM")
fmt.Printf("%s
", p.Bytes())
}
Edit 2: I've added a way so that you can parse the POPM
field. You can then access the rating 255/255 from Popularimeter.Rating
:
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
id3 "github.com/mikkyang/id3-go"
)
func main() {
f, _ := id3.Open("with_popm.mp3")
popm := f.Frame("POPM")
popularimeter, _ := FromData(popm.Bytes())
fmt.Println(popularimeter)
}
type Rating uint8
func (r Rating) String() string {
if r == 0 {
return "unknown"
}
return fmt.Sprintf("%d/255", r)
}
type Popularimeter struct {
Email string
Rating Rating
Counter uint32
}
func FromData(data []byte) (*Popularimeter, error) {
tokens := bytes.SplitN(data, []byte{0x0}, 2)
// Pupolarimeter: <string>, null, 1byte rating, 4bytes counter
if len(tokens) != 2 || len(tokens[1]) != 5 {
return nil, errors.New("invalid Popularimeter")
}
return &Popularimeter{
Email: string(tokens[0]),
Rating: Rating(tokens[1][0]),
Counter: binary.BigEndian.Uint32(tokens[1][1:]),
}, nil
}