diff --git a/v2/encoding.go b/v2/encoding.go index 6c89e89..de9d07c 100644 --- a/v2/encoding.go +++ b/v2/encoding.go @@ -121,6 +121,18 @@ func decodeText(src []byte, from Encoding) string { return string(result) } +// decodeMulti decodes multi valued src from "from" encoding to UTF-8. +func decodeMulti(src []byte, from Encoding) []string { + src = bytes.TrimSuffix(src, from.TerminationBytes) // See https://github.com/bogem/id3v2/issues/41 + splitted := bytes.Split(src, from.TerminationBytes) + + var res []string + for _, s := range splitted { + res = append(res, decodeText(s, from)) + } + return res +} + // encodeWriteText encodes src from UTF-8 to "to" encoding and writes to bw. func encodeWriteText(bw *bufWriter, src string, to Encoding) error { if to.Equals(EncodingUTF8) { diff --git a/v2/parse_test.go b/v2/parse_test.go index 5ca7785..0463163 100644 --- a/v2/parse_test.go +++ b/v2/parse_test.go @@ -34,6 +34,45 @@ func TestParse(t *testing.T) { testUnknownFrames(t, tag) } +// TestParse compares parsed frames with expected frames. +func TestMultiParse(t *testing.T) { + tag, err := Open(multiMp3Path, parseOpts) + if tag == nil || err != nil { + t.Error("Error while opening mp3 file:", err) + } + defer tag.Close() + + testMultiTextFrames(t, tag) + testMultiTXXXFrames(t, tag) +} + +func testMultiTextFrames(t *testing.T, tag *Tag) { + if err := compareTwoStrings(tag.Artist(), "artist1"); err != nil { + t.Error(err) + } + artistFrames := tag.GetFrames(tag.CommonID("Artist")) + if len(artistFrames) != 1 { + t.Fatalf("Expected artist frames: %v, got %v", 1, len(artistFrames)) + } + + artistFrame, ok := artistFrames[0].(TextFrame) + if !ok { + t.Fatal("Couldn't assert artist frame") + } + + if len(artistFrame.Multi) != 2 { + t.Fatalf("Expected artist values: %v, got %v", 2, len(artistFrame.Multi)) + } + + if err := compareTwoStrings(artistFrame.Multi[0], "artist1"); err != nil { + t.Error(err) + } + + if err := compareTwoStrings(artistFrame.Multi[1], "artist2"); err != nil { + t.Error(err) + } +} + func testTextFrames(t *testing.T, tag *Tag) { if err := compareTwoStrings(tag.Artist(), "Artist"); err != nil { t.Error(err) @@ -173,6 +212,27 @@ func testTXXXFrames(t *testing.T, tag *Tag) { } } +func testMultiTXXXFrames(t *testing.T, tag *Tag) { + txxxFrames := tag.GetFrames(tag.CommonID("User defined text information frame")) + if len(txxxFrames) != 1 { + t.Fatalf("Expected TXXX frames: %v, got %v", 1, len(txxxFrames)) + } + + var parsedUserDefinedTextFrame UserDefinedTextFrame + for _, f := range txxxFrames { + txxx, ok := f.(UserDefinedTextFrame) + if !ok { + t.Fatal("Couldn't assert TXXX frame") + } + parsedUserDefinedTextFrame = txxx + fmt.Printf("%s\n", txxx.Description) + } + + if err := compareTXXXFrames(parsedUserDefinedTextFrame, multiUDTF); err != nil { + t.Error(err) + } +} + func compareTXXXFrames(actual, expected UserDefinedTextFrame) error { if err := compareTwoBytes(actual.Encoding.Key, expected.Encoding.Key); err != nil { return err diff --git a/v2/tag_test.go b/v2/tag_test.go index 218317c..1f94893 100644 --- a/v2/tag_test.go +++ b/v2/tag_test.go @@ -18,6 +18,7 @@ import ( const ( mp3Path = "testdata/test.mp3" + multiMp3Path = "testdata/test_multi.mp3" frontCoverPath = "testdata/front_cover.jpg" backCoverPath = "testdata/back_cover.jpg" @@ -62,6 +63,16 @@ var ( Value: "fbd94fb6-2a74-42d0-acbc-81caf8b84984", } + multiUDTF = UserDefinedTextFrame{ + Encoding: EncodingUTF8, + Description: "multi", + Value: "val1", + Multi: []string{ + "val1", + "val2", + }, + } + musicBrainzUF = UFIDFrame{ OwnerIdentifier: "https://musicbrainz.org", Identifier: []byte("fbd94fb6-2a74-42d0-acbc-81caf8b84984"), diff --git a/v2/testdata/test.mp3 b/v2/testdata/test.mp3 index 1b5d0e3..30d4f39 100644 Binary files a/v2/testdata/test.mp3 and b/v2/testdata/test.mp3 differ diff --git a/v2/testdata/test_multi.mp3 b/v2/testdata/test_multi.mp3 new file mode 100644 index 0000000..0600f65 Binary files /dev/null and b/v2/testdata/test_multi.mp3 differ diff --git a/v2/text_frame.go b/v2/text_frame.go index 504f6a4..2b21aee 100644 --- a/v2/text_frame.go +++ b/v2/text_frame.go @@ -11,6 +11,7 @@ import "io" type TextFrame struct { Encoding Encoding Text string + Multi []string } func (tf TextFrame) Size() int { @@ -45,9 +46,16 @@ func parseTextFrame(br *bufReader) (Framer, error) { return nil, err } + values := decodeMulti(buf.Bytes(), encoding) + var first string + if len(values) > 0 { + first = values[0] + } + tf := TextFrame{ Encoding: encoding, - Text: decodeText(buf.Bytes(), encoding), + Text: first, + Multi: values, } return tf, nil diff --git a/v2/user_defined_text_frame.go b/v2/user_defined_text_frame.go index a08ca6b..4239102 100644 --- a/v2/user_defined_text_frame.go +++ b/v2/user_defined_text_frame.go @@ -8,6 +8,7 @@ type UserDefinedTextFrame struct { Encoding Encoding Description string Value string + Multi []string } func (udtf UserDefinedTextFrame) Size() int { @@ -42,10 +43,17 @@ func parseUserDefinedTextFrame(br *bufReader, version byte) (Framer, error) { return nil, err } + values := decodeMulti(value.Bytes(), encoding) + var first string + if len(values) > 0 { + first = values[0] + } + udtf := UserDefinedTextFrame{ Encoding: encoding, Description: decodeText(description, encoding), - Value: decodeText(value.Bytes(), encoding), + Value: first, + Multi: values, } return udtf, nil