Merge branch 'master' of https://github.com/jooola/tag into enhance_testing

This commit is contained in:
jo 2018-11-10 16:09:04 +01:00
commit b00b4fe75f
13 changed files with 202 additions and 7 deletions

View File

@ -36,6 +36,7 @@ type Metadata interface {
Picture() *Picture // Artwork
Lyrics() string
Comment() string
Raw() map[string]interface{} // NB: raw tag names are not consistent across formats.
}

View File

@ -95,4 +95,5 @@ func printMetadata(m tag.Metadata) {
fmt.Printf(" Picture: %v\n", m.Picture())
fmt.Printf(" Lyrics: %v\n", m.Lyrics())
fmt.Printf(" Comment: %v\n", m.Comment())
}

110
dsf.go Normal file
View File

@ -0,0 +1,110 @@
// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tag
import (
"errors"
"io"
)
// ReadDSFTags reads DSF metadata from the io.ReadSeeker, returning the resulting
// metadata in a Metadata implementation, or non-nil error if there was a problem.
// samples: http://www.2l.no/hires/index.html
func ReadDSFTags(r io.ReadSeeker) (Metadata, error) {
dsd, err := readString(r, 4)
if err != nil {
return nil, err
}
if dsd != "DSD " {
return nil, errors.New("expected 'DSD '")
}
_, err = r.Seek(int64(16), io.SeekCurrent)
if err != nil {
return nil, err
}
n4, err := readBytes(r, 8)
if err != nil {
return nil, err
}
id3Pointer := getIntLittleEndian(n4)
_, err = r.Seek(int64(id3Pointer), io.SeekStart)
if err != nil {
return nil, err
}
id3, err := ReadID3v2Tags(r)
if err != nil {
return nil, err
}
return metadataDSF{id3}, nil
}
type metadataDSF struct {
id3 Metadata
}
func (m metadataDSF) Format() Format {
return m.id3.Format()
}
func (m metadataDSF) FileType() FileType {
return DSF
}
func (m metadataDSF) Title() string {
return m.id3.Title()
}
func (m metadataDSF) Album() string {
return m.id3.Album()
}
func (m metadataDSF) Artist() string {
return m.id3.Artist()
}
func (m metadataDSF) AlbumArtist() string {
return m.id3.AlbumArtist()
}
func (m metadataDSF) Composer() string {
return m.id3.Composer()
}
func (m metadataDSF) Year() int {
return m.id3.Year()
}
func (m metadataDSF) Genre() string {
return m.id3.Genre()
}
func (m metadataDSF) Track() (int, int) {
return m.id3.Track()
}
func (m metadataDSF) Disc() (int, int) {
return m.id3.Disc()
}
func (m metadataDSF) Picture() *Picture {
return m.id3.Picture()
}
func (m metadataDSF) Lyrics() string {
return m.id3.Lyrics()
}
func (m metadataDSF) Comment() string {
return m.id3.Comment()
}
func (m metadataDSF) Raw() map[string]interface{} {
return m.id3.Raw()
}

View File

@ -141,3 +141,4 @@ func (m metadataID3v1) Composer() string { return "" }
func (metadataID3v1) Disc() (int, int) { return 0, 0 }
func (m metadataID3v1) Picture() *Picture { return nil }
func (m metadataID3v1) Lyrics() string { return "" }
func (m metadataID3v1) Comment() string { return m["comment"].(string) }

View File

@ -43,6 +43,7 @@ var frames = frameNames(map[string][2]string{
"genre": [2]string{"TCO", "TCON"},
"picture": [2]string{"PIC", "APIC"},
"lyrics": [2]string{"", "USLT"},
"comment": [2]string{"COM", "COMM"},
})
// metadataID3v2 is the implementation of Metadata used for ID3v2 tags.
@ -119,6 +120,18 @@ func (m metadataID3v2) Lyrics() string {
return t.(*Comm).Text
}
func (m metadataID3v2) Comment() string {
t, ok := m.frames[frames.Name("comment", m.Format())]
if !ok {
return ""
}
// id3v23 has Text, id3v24 has Description
if t.(*Comm).Description == "" {
return trimString(t.(*Comm).Text)
}
return trimString(t.(*Comm).Description)
}
func (m metadataID3v2) Picture() *Picture {
v, ok := m.frames[frames.Name("picture", m.Format())]
if !ok {

8
mp4.go
View File

@ -344,6 +344,14 @@ func (m metadataMP4) Lyrics() string {
return t.(string)
}
func (m metadataMP4) Comment() string {
t, ok := m.data["\xa9cmt"]
if !ok {
return ""
}
return t.(string)
}
func (m metadataMP4) Picture() *Picture {
v, ok := m.data["covr"]
if !ok {

7
tag.go
View File

@ -50,6 +50,9 @@ func ReadFrom(r io.ReadSeeker) (Metadata, error) {
case string(b[0:3]) == "ID3":
return ReadID3v2Tags(r)
case string(b[0:4]) == "DSD ":
return ReadDSFTags(r)
}
m, err := ReadID3v1Tags(r)
@ -91,6 +94,7 @@ const (
ALAC FileType = "ALAC" // Apple Lossless file FIXME: actually detect this
FLAC FileType = "FLAC" // FLAC file
OGG FileType = "OGG" // OGG file
DSF FileType = "DSF" // DSF file DSD Sony format see https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
)
// Metadata is an interface which is used to describe metadata retrieved by this package.
@ -134,6 +138,9 @@ type Metadata interface {
// Lyrics returns the lyrics, or an empty string if unavailable.
Lyrics() string
// Comment returns the comment, or an empty string if unavailable.
Comment() string
// Raw returns the raw mapping of retrieved tag names and associated values.
// NB: tag/atom names are not standardised between formats.
Raw() map[string]interface{}

View File

@ -39,15 +39,17 @@ var fullMetadata = testMetadata{
Track: 3,
TrackTotal: 6,
Year: 2000,
Comment: "Test Comment",
}
var mp3id3v11Metadata = testMetadata{
Album: "Test Album",
Artist: "Test Artist",
Genre: "Jazz",
Lyrics: "",
Title: "Test Title",
Track: 3,
Year: 2000,
Album: "Test Album",
Artist: "Test Artist",
Genre: "Jazz",
Lyrics: "",
Title: "Test Title",
Track: 3,
Year: 2000,
Comment: "Test Comment",
}
type testData struct {
@ -116,6 +118,7 @@ func compareMetadata(t *testing.T, m Metadata, tt testData) {
testValue(t, tt.Lyrics, m.Lyrics())
testValue(t, tt.Title, m.Title())
testValue(t, tt.Year, m.Year())
testValue(t, tt.Comment, m.Comment())
disc, discTotal := m.Disc()
testValue(t, tt.Disc, disc)

BIN
testdata/with_tags/sample.dsf vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@ -32,6 +32,15 @@ func getInt(b []byte) int {
return n
}
func getIntLittleEndian(b []byte) int {
var n int
for i := len(b) - 1; i >= 0; i-- {
n = n << 8
n |= int(b[i])
}
return n
}
func readBytes(r io.Reader, n int) ([]byte, error) {
b := make([]byte, n)
_, err := io.ReadFull(r, b)

View File

@ -79,3 +79,38 @@ func TestGetInt(t *testing.T) {
}
}
}
func TestGetIntLittleEndian(t *testing.T) {
tests := []struct {
input []byte
output int
}{
{
[]byte{},
0,
},
{
[]byte{0x01},
1,
},
{
[]byte{0xF1, 0xF2},
0xF2F1,
},
{
[]byte{0xF1, 0xF2, 0xF3},
0xF3F2F1,
},
{
[]byte{0xF1, 0xF2, 0xF3, 0xF4},
0xF4F3F2F1,
},
}
for ii, tt := range tests {
got := getIntLittleEndian(tt.input)
if got != tt.output {
t.Errorf("[%d] getInt(%v) = %v, expected %v", ii, tt.input, got, tt.output)
}
}
}

View File

@ -243,6 +243,13 @@ func (m *metadataVorbis) Lyrics() string {
return m.c["lyrics"]
}
func (m *metadataVorbis) Comment() string {
if m.c["comment"] != "" {
return m.c["comment"]
}
return m.c["description"]
}
func (m *metadataVorbis) Picture() *Picture {
return m.p
}