diff options
| author | James Mills <prologic@shortcircuit.net.au> | 2020-03-21 08:20:16 +1000 |
|---|---|---|
| committer | James Mills <prologic@shortcircuit.net.au> | 2020-03-21 08:20:16 +1000 |
| commit | d7a1eab5beba4241dab4402c5cd268432d7b46cd (patch) | |
| tree | 6383b4ebf511b58597d827dc5af00b71c72caf38 /vendor/github.com/dhowden | |
| parent | 13ffd7117180d1a3093ff291481caeb07a8b71de (diff) | |
Migrate to Go111Modules, Add Dockerfile and GoRelaser support
Diffstat (limited to 'vendor/github.com/dhowden')
| -rw-r--r-- | vendor/github.com/dhowden/tag/.editorconfig | 19 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/.travis.yml | 5 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/LICENSE | 23 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/README.md | 72 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/dsf.go | 110 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/flac.go | 89 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/id.go | 81 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/id3v1.go | 144 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/id3v2.go | 434 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/id3v2frames.go | 638 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/id3v2metadata.go | 141 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/internal/id3v1_test/NOTICE.txt | 3 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/mp4.go | 372 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/ogg.go | 119 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/sum.go | 219 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/tag.go | 147 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/util.go | 81 | ||||
| -rw-r--r-- | vendor/github.com/dhowden/tag/vorbis.go | 255 |
18 files changed, 0 insertions, 2952 deletions
diff --git a/vendor/github.com/dhowden/tag/.editorconfig b/vendor/github.com/dhowden/tag/.editorconfig deleted file mode 100644 index 57515d0..0000000 --- a/vendor/github.com/dhowden/tag/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - -[*.go] -indent_style = tab -indent_size = 3 - -[*.md] -trim_trailing_whitespace = false - -[*] -# We recommend you to keep these unchanged -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/vendor/github.com/dhowden/tag/.travis.yml b/vendor/github.com/dhowden/tag/.travis.yml deleted file mode 100644 index 3a81bf9..0000000 --- a/vendor/github.com/dhowden/tag/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go - -go: - - 1.7 - - tip
\ No newline at end of file diff --git a/vendor/github.com/dhowden/tag/LICENSE b/vendor/github.com/dhowden/tag/LICENSE deleted file mode 100644 index dfd760c..0000000 --- a/vendor/github.com/dhowden/tag/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright 2015, David Howden -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/dhowden/tag/README.md b/vendor/github.com/dhowden/tag/README.md deleted file mode 100644 index de81883..0000000 --- a/vendor/github.com/dhowden/tag/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# MP3/MP4/OGG/FLAC metadata parsing library -[](https://travis-ci.org/dhowden/tag) -[](https://godoc.org/github.com/dhowden/tag) - -This package provides MP3 (ID3v1,2.{2,3,4}) and MP4 (ACC, M4A, ALAC), OGG and FLAC metadata detection, parsing and artwork extraction. - -Detect and parse tag metadata from an `io.ReadSeeker` (i.e. an `*os.File`): - -```go -m, err := tag.ReadFrom(f) -if err != nil { - log.Fatal(err) -} -log.Print(m.Format()) // The detected format. -log.Print(m.Title()) // The title of the track (see Metadata interface for more details). -``` - -Parsed metadata is exported via a single interface (giving a consistent API for all supported metadata formats). - -```go -// Metadata is an interface which is used to describe metadata retrieved by this package. -type Metadata interface { - Format() Format - FileType() FileType - - Title() string - Album() string - Artist() string - AlbumArtist() string - Composer() string - Genre() string - Year() int - - Track() (int, int) // Number, Total - Disc() (int, int) // Number, Total - - Picture() *Picture // Artwork - Lyrics() string - Comment() string - - Raw() map[string]interface{} // NB: raw tag names are not consistent across formats. -} -``` - -## Audio Data Checksum (SHA1) - -This package also provides a metadata-invariant checksum for audio files: only the audio data is used to -construct the checksum. - -[http://godoc.org/github.com/dhowden/tag#Sum](http://godoc.org/github.com/dhowden/tag#Sum) - -## Tools - -There are simple command-line tools which demonstrate basic tag extraction and summing: - -```console -$ go get github.com/dhowden/tag/... -$ cd $GOPATH/bin -$ ./tag 11\ High\ Hopes.m4a -Metadata Format: MP4 -Title: High Hopes -Album: The Division Bell -Artist: Pink Floyd -Composer: Abbey Road Recording Studios/David Gilmour/Polly Samson -Year: 1994 -Track: 11 of 11 -Disc: 1 of 1 -Picture: Picture{Ext: jpeg, MIMEType: image/jpeg, Type: , Description: , Data.Size: 606109} - -$ ./sum 11\ High\ Hopes.m4a -2ae208c5f00a1f21f5fac9b7f6e0b8e52c06da29 -``` diff --git a/vendor/github.com/dhowden/tag/dsf.go b/vendor/github.com/dhowden/tag/dsf.go deleted file mode 100644 index d826a74..0000000 --- a/vendor/github.com/dhowden/tag/dsf.go +++ /dev/null @@ -1,110 +0,0 @@ -// 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() -} diff --git a/vendor/github.com/dhowden/tag/flac.go b/vendor/github.com/dhowden/tag/flac.go deleted file mode 100644 index c370467..0000000 --- a/vendor/github.com/dhowden/tag/flac.go +++ /dev/null @@ -1,89 +0,0 @@ -// 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" -) - -// blockType is a type which represents an enumeration of valid FLAC blocks -type blockType byte - -// FLAC block types. -const ( - // Stream Info Block 0 - // Padding Block 1 - // Application Block 2 - // Seektable Block 3 - // Cue Sheet Block 5 - vorbisCommentBlock blockType = 4 - pictureBlock blockType = 6 -) - -// ReadFLACTags reads FLAC metadata from the io.ReadSeeker, returning the resulting -// metadata in a Metadata implementation, or non-nil error if there was a problem. -func ReadFLACTags(r io.ReadSeeker) (Metadata, error) { - flac, err := readString(r, 4) - if err != nil { - return nil, err - } - if flac != "fLaC" { - return nil, errors.New("expected 'fLaC'") - } - - m := &metadataFLAC{ - newMetadataVorbis(), - } - - for { - last, err := m.readFLACMetadataBlock(r) - if err != nil { - return nil, err - } - - if last { - break - } - } - return m, nil -} - -type metadataFLAC struct { - *metadataVorbis -} - -func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) { - blockHeader, err := readBytes(r, 1) - if err != nil { - return - } - - if getBit(blockHeader[0], 7) { - blockHeader[0] ^= (1 << 7) - last = true - } - - blockLen, err := readInt(r, 3) - if err != nil { - return - } - - switch blockType(blockHeader[0]) { - case vorbisCommentBlock: - err = m.readVorbisComment(r) - - case pictureBlock: - err = m.readPictureBlock(r) - - default: - _, err = r.Seek(int64(blockLen), io.SeekCurrent) - } - return -} - -func (m *metadataFLAC) FileType() FileType { - return FLAC -} diff --git a/vendor/github.com/dhowden/tag/id.go b/vendor/github.com/dhowden/tag/id.go deleted file mode 100644 index 2410356..0000000 --- a/vendor/github.com/dhowden/tag/id.go +++ /dev/null @@ -1,81 +0,0 @@ -package tag - -import ( - "fmt" - "io" -) - -// Identify identifies the format and file type of the data in the ReadSeeker. -func Identify(r io.ReadSeeker) (format Format, fileType FileType, err error) { - b, err := readBytes(r, 11) - if err != nil { - return - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - err = fmt.Errorf("could not seek back to original position: %v", err) - return - } - - switch { - case string(b[0:4]) == "fLaC": - return VORBIS, FLAC, nil - - case string(b[0:4]) == "OggS": - return VORBIS, OGG, nil - - case string(b[4:8]) == "ftyp": - b = b[8:11] - fileType = UnknownFileType - switch string(b) { - case "M4A": - fileType = M4A - - case "M4B": - fileType = M4B - - case "M4P": - fileType = M4P - } - return MP4, fileType, nil - - case string(b[0:3]) == "ID3": - b := b[3:] - switch uint(b[0]) { - case 2: - format = ID3v2_2 - case 3: - format = ID3v2_3 - case 4: - format = ID3v2_4 - case 0, 1: - fallthrough - default: - err = fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0])) - return - } - return format, MP3, nil - } - - n, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return - } - - tag, err := readString(r, 3) - if err != nil { - return - } - - _, err = r.Seek(-n, io.SeekCurrent) - if err != nil { - return - } - - if tag != "TAG" { - err = ErrNoTagsFound - return - } - return ID3v1, MP3, nil -} diff --git a/vendor/github.com/dhowden/tag/id3v1.go b/vendor/github.com/dhowden/tag/id3v1.go deleted file mode 100644 index 0953f0b..0000000 --- a/vendor/github.com/dhowden/tag/id3v1.go +++ /dev/null @@ -1,144 +0,0 @@ -// 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" - "strconv" - "strings" -) - -// id3v1Genres is a list of genres as given in the ID3v1 specification. -var id3v1Genres = [...]string{ - "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", - "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", - "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", - "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", - "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", - "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", - "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", - "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", - "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", - "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", - "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", - "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", - "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", - "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", - "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", - "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall", -} - -// ErrNotID3v1 is an error which is returned when no ID3v1 header is found. -var ErrNotID3v1 = errors.New("invalid ID3v1 header") - -// ReadID3v1Tags reads ID3v1 tags from the io.ReadSeeker. Returns ErrNotID3v1 -// if there are no ID3v1 tags, otherwise non-nil error if there was a problem. -func ReadID3v1Tags(r io.ReadSeeker) (Metadata, error) { - _, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return nil, err - } - - if tag, err := readString(r, 3); err != nil { - return nil, err - } else if tag != "TAG" { - return nil, ErrNotID3v1 - } - - title, err := readString(r, 30) - if err != nil { - return nil, err - } - - artist, err := readString(r, 30) - if err != nil { - return nil, err - } - - album, err := readString(r, 30) - if err != nil { - return nil, err - } - - year, err := readString(r, 4) - if err != nil { - return nil, err - } - - commentBytes, err := readBytes(r, 30) - if err != nil { - return nil, err - } - - var comment string - var track int - if commentBytes[28] == 0 { - comment = trimString(string(commentBytes[:28])) - track = int(commentBytes[29]) - } else { - comment = trimString(string(commentBytes)) - } - - var genre string - genreID, err := readBytes(r, 1) - if err != nil { - return nil, err - } - if int(genreID[0]) < len(id3v1Genres) { - genre = id3v1Genres[int(genreID[0])] - } - - m := make(map[string]interface{}) - m["title"] = trimString(title) - m["artist"] = trimString(artist) - m["album"] = trimString(album) - m["year"] = trimString(year) - m["comment"] = trimString(comment) - m["track"] = track - m["genre"] = genre - - return metadataID3v1(m), nil -} - -func trimString(x string) string { - return strings.TrimSpace(strings.Trim(x, "\x00")) -} - -// metadataID3v1 is the implementation of Metadata used for ID3v1 tags. -type metadataID3v1 map[string]interface{} - -func (metadataID3v1) Format() Format { return ID3v1 } -func (metadataID3v1) FileType() FileType { return MP3 } -func (m metadataID3v1) Raw() map[string]interface{} { return m } - -func (m metadataID3v1) Title() string { return m["title"].(string) } -func (m metadataID3v1) Album() string { return m["album"].(string) } -func (m metadataID3v1) Artist() string { return m["artist"].(string) } -func (m metadataID3v1) Genre() string { return m["genre"].(string) } - -func (m metadataID3v1) Year() int { - y := m["year"].(string) - n, err := strconv.Atoi(y) - if err != nil { - return 0 - } - return n -} - -func (m metadataID3v1) Track() (int, int) { return m["track"].(int), 0 } - -func (m metadataID3v1) AlbumArtist() string { return "" } -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) } diff --git a/vendor/github.com/dhowden/tag/id3v2.go b/vendor/github.com/dhowden/tag/id3v2.go deleted file mode 100644 index 063e6cb..0000000 --- a/vendor/github.com/dhowden/tag/id3v2.go +++ /dev/null @@ -1,434 +0,0 @@ -// 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 ( - "fmt" - "io" - "regexp" - "strconv" - "strings" -) - -var id3v2Genres = [...]string{ - "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", - "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", - "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", - "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", - "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", - "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", - "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", - "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", - "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", - "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", - "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", - "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", - "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", - "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", - "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", - "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", - "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", - "Britpop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", - "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", - "Christian Rock ", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", - "Synthpop", -} - -// id3v2Header is a type which represents an ID3v2 tag header. -type id3v2Header struct { - Version Format - Unsynchronisation bool - ExtendedHeader bool - Experimental bool - Size int -} - -// readID3v2Header reads the ID3v2 header from the given io.Reader. -// offset it number of bytes of header that was read -func readID3v2Header(r io.Reader) (h *id3v2Header, offset int, err error) { - offset = 10 - b, err := readBytes(r, offset) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err) - } - - if string(b[0:3]) != "ID3" { - return nil, 0, fmt.Errorf("expected to read \"ID3\"") - } - - b = b[3:] - var vers Format - switch uint(b[0]) { - case 2: - vers = ID3v2_2 - case 3: - vers = ID3v2_3 - case 4: - vers = ID3v2_4 - case 0, 1: - fallthrough - default: - return nil, 0, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0])) - } - - // NB: We ignore b[1] (the revision) as we don't currently rely on it. - h = &id3v2Header{ - Version: vers, - Unsynchronisation: getBit(b[2], 7), - ExtendedHeader: getBit(b[2], 6), - Experimental: getBit(b[2], 5), - Size: get7BitChunkedInt(b[3:7]), - } - - if h.ExtendedHeader { - switch vers { - case ID3v2_3: - b, err := readBytes(r, 4) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 4 bytes (ID3v23 extended header len): %v", err) - } - // skip header, size is excluding len bytes - extendedHeaderSize := getInt(b) - _, err = readBytes(r, extendedHeaderSize) - if err != nil { - return nil, 0, fmt.Errorf("expected to read %d bytes (ID3v23 skip extended header): %v", extendedHeaderSize, err) - } - offset += extendedHeaderSize - case ID3v2_4: - b, err := readBytes(r, 4) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 4 bytes (ID3v24 extended header len): %v", err) - } - // skip header, size is synchsafe int including len bytes - extendedHeaderSize := get7BitChunkedInt(b) - 4 - _, err = readBytes(r, extendedHeaderSize) - if err != nil { - return nil, 0, fmt.Errorf("expected to read %d bytes (ID3v24 skip extended header): %v", extendedHeaderSize, err) - } - offset += extendedHeaderSize - default: - // nop, only 2.3 and 2.4 should have extended header - } - } - - return h, offset, nil -} - -// id3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame. -type id3v2FrameFlags struct { - // Message (ID3 2.3.0 and 2.4.0) - TagAlterPreservation bool - FileAlterPreservation bool - ReadOnly bool - - // Format (ID3 2.3.0 and 2.4.0) - Compression bool - Encryption bool - GroupIdentity bool - // ID3 2.4.0 only (see http://id3.org/id3v2.4.0-structure sec 4.1) - Unsynchronisation bool - DataLengthIndicator bool -} - -func readID3v23FrameFlags(r io.Reader) (*id3v2FrameFlags, error) { - b, err := readBytes(r, 2) - if err != nil { - return nil, err - } - - msg := b[0] - fmt := b[1] - - return &id3v2FrameFlags{ - TagAlterPreservation: getBit(msg, 7), - FileAlterPreservation: getBit(msg, 6), - ReadOnly: getBit(msg, 5), - Compression: getBit(fmt, 7), - Encryption: getBit(fmt, 6), - GroupIdentity: getBit(fmt, 5), - }, nil -} - -func readID3v24FrameFlags(r io.Reader) (*id3v2FrameFlags, error) { - b, err := readBytes(r, 2) - if err != nil { - return nil, err - } - - msg := b[0] - fmt := b[1] - - return &id3v2FrameFlags{ - TagAlterPreservation: getBit(msg, 6), - FileAlterPreservation: getBit(msg, 5), - ReadOnly: getBit(msg, 4), - GroupIdentity: getBit(fmt, 6), - Compression: getBit(fmt, 3), - Encryption: getBit(fmt, 2), - Unsynchronisation: getBit(fmt, 1), - DataLengthIndicator: getBit(fmt, 0), - }, nil - -} - -func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 3) - if err != nil { - return - } - size, err = readInt(r, 3) - if err != nil { - return - } - headerSize = 6 - return -} - -func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 4) - if err != nil { - return - } - size, err = readInt(r, 4) - if err != nil { - return - } - headerSize = 8 - return -} - -func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 4) - if err != nil { - return - } - size, err = read7BitChunkedInt(r, 4) - if err != nil { - return - } - headerSize = 8 - return -} - -// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header. -func readID3v2Frames(r io.Reader, offset int, h *id3v2Header) (map[string]interface{}, error) { - result := make(map[string]interface{}) - - for offset < h.Size { - var err error - var name string - var size, headerSize int - var flags *id3v2FrameFlags - - switch h.Version { - case ID3v2_2: - name, size, headerSize, err = readID3v2_2FrameHeader(r) - - case ID3v2_3: - name, size, headerSize, err = readID3v2_3FrameHeader(r) - if err != nil { - return nil, err - } - flags, err = readID3v23FrameFlags(r) - headerSize += 2 - - case ID3v2_4: - name, size, headerSize, err = readID3v2_4FrameHeader(r) - if err != nil { - return nil, err - } - flags, err = readID3v24FrameFlags(r) - headerSize += 2 - } - - if err != nil { - return nil, err - } - - // FIXME: Do we still need this? - // if size=0, we certainly are in a padding zone. ignore the rest of - // the tags - if size == 0 { - break - } - - offset += headerSize + size - - // Avoid corrupted padding (see http://id3.org/Compliance%20Issues). - if !validID3Frame(h.Version, name) && offset > h.Size { - break - } - - if flags != nil { - if flags.Compression { - _, err = read7BitChunkedInt(r, 4) // read 4 - if err != nil { - return nil, err - } - size -= 4 - } - - if flags.Encryption { - _, err = readBytes(r, 1) // read 1 byte of encryption method - if err != nil { - return nil, err - } - size -= 1 - } - } - - b, err := readBytes(r, size) - if err != nil { - return nil, err - } - - // There can be multiple tag with the same name. Append a number to the - // name if there is more than one. - rawName := name - if _, ok := result[rawName]; ok { - for i := 0; ok; i++ { - rawName = name + "_" + strconv.Itoa(i) - _, ok = result[rawName] - } - } - - switch { - case name == "TXXX" || name == "TXX": - t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name[0] == 'T': - txt, err := readTFrame(b) - if err != nil { - return nil, err - } - result[rawName] = txt - - case name == "UFID" || name == "UFI": - t, err := readUFID(b) - if err != nil { - return nil, err - } - result[rawName] = t - - case name == "WXXX" || name == "WXX": - t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name[0] == 'W': - txt, err := readWFrame(b) - if err != nil { - return nil, err - } - result[rawName] = txt - - case name == "COMM" || name == "COM" || name == "USLT" || name == "ULT": - t, err := readTextWithDescrFrame(b, true, true) // both lang and enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name == "APIC": - p, err := readAPICFrame(b) - if err != nil { - return nil, err - } - result[rawName] = p - - case name == "PIC": - p, err := readPICFrame(b) - if err != nil { - return nil, err - } - result[rawName] = p - - default: - result[rawName] = b - } - } - return result, nil -} - -type unsynchroniser struct { - io.Reader - ff bool -} - -// filter io.Reader which skip the Unsynchronisation bytes -func (r *unsynchroniser) Read(p []byte) (int, error) { - b := make([]byte, 1) - i := 0 - for i < len(p) { - if n, err := r.Reader.Read(b); err != nil || n == 0 { - return i, err - } - if r.ff && b[0] == 0x00 { - r.ff = false - continue - } - p[i] = b[0] - i++ - r.ff = (b[0] == 0xFF) - } - return i, nil -} - -// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning -// non-nil error on failure. -func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) { - h, offset, err := readID3v2Header(r) - if err != nil { - return nil, err - } - - var ur io.Reader = r - if h.Unsynchronisation { - ur = &unsynchroniser{Reader: r} - } - - f, err := readID3v2Frames(ur, offset, h) - if err != nil { - return nil, err - } - return metadataID3v2{header: h, frames: f}, nil -} - -var id3v2genreRe = regexp.MustCompile(`(.*[^(]|.* |^)\(([0-9]+)\) *(.*)$`) - -// id3v2genre parse a id3v2 genre tag and expand the numeric genres -func id3v2genre(genre string) string { - c := true - for c { - orig := genre - if match := id3v2genreRe.FindStringSubmatch(genre); len(match) > 0 { - if genreID, err := strconv.Atoi(match[2]); err == nil { - if genreID < len(id3v2Genres) { - genre = id3v2Genres[genreID] - if match[1] != "" { - genre = strings.TrimSpace(match[1]) + " " + genre - } - if match[3] != "" { - genre = genre + " " + match[3] - } - } - } - } - c = (orig != genre) - } - return strings.Replace(genre, "((", "(", -1) -} diff --git a/vendor/github.com/dhowden/tag/id3v2frames.go b/vendor/github.com/dhowden/tag/id3v2frames.go deleted file mode 100644 index a92afa1..0000000 --- a/vendor/github.com/dhowden/tag/id3v2frames.go +++ /dev/null @@ -1,638 +0,0 @@ -// 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 ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "strings" - "unicode/utf16" -) - -// DefaultUTF16WithBOMByteOrder is the byte order used when the "UTF16 with BOM" encoding -// is specified without a corresponding BOM in the data. -var DefaultUTF16WithBOMByteOrder binary.ByteOrder = binary.LittleEndian - -// ID3v2.2.0 frames (see http://id3.org/id3v2-00, sec 4). -var id3v22Frames = map[string]string{ - "BUF": "Recommended buffer size", - - "CNT": "Play counter", - "COM": "Comments", - "CRA": "Audio encryption", - "CRM": "Encrypted meta frame", - - "ETC": "Event timing codes", - "EQU": "Equalization", - - "GEO": "General encapsulated object", - - "IPL": "Involved people list", - - "LNK": "Linked information", - - "MCI": "Music CD Identifier", - "MLL": "MPEG location lookup table", - - "PIC": "Attached picture", - "POP": "Popularimeter", - - "REV": "Reverb", - "RVA": "Relative volume adjustment", - - "SLT": "Synchronized lyric/text", - "STC": "Synced tempo codes", - - "TAL": "Album/Movie/Show title", - "TBP": "BPM (Beats Per Minute)", - "TCM": "Composer", - "TCO": "Content type", - "TCR": "Copyright message", - "TDA": "Date", - "TDY": "Playlist delay", - "TEN": "Encoded by", - "TFT": "File type", - "TIM": "Time", - "TKE": "Initial key", - "TLA": "Language(s)", - "TLE": "Length", - "TMT": "Media type", - "TOA": "Original artist(s)/performer(s)", - "TOF": "Original filename", - "TOL": "Original Lyricist(s)/text writer(s)", - "TOR": "Original release year", - "TOT": "Original album/Movie/Show title", - "TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group", - "TP2": "Band/Orchestra/Accompaniment", - "TP3": "Conductor/Performer refinement", - "TP4": "Interpreted, remixed, or otherwise modified by", - "TPA": "Part of a set", - "TPB": "Publisher", - "TRC": "ISRC (International Standard Recording Code)", - "TRD": "Recording dates", - "TRK": "Track number/Position in set", - "TSI": "Size", - "TSS": "Software/hardware and settings used for encoding", - "TT1": "Content group description", - "TT2": "Title/Songname/Content description", - "TT3": "Subtitle/Description refinement", - "TXT": "Lyricist/text writer", - "TXX": "User defined text information frame", - "TYE": "Year", - - "UFI": "Unique file identifier", - "ULT": "Unsychronized lyric/text transcription", - - "WAF": "Official audio file webpage", - "WAR": "Official artist/performer webpage", - "WAS": "Official audio source webpage", - "WCM": "Commercial information", - "WCP": "Copyright/Legal information", - "WPB": "Publishers official webpage", - "WXX": "User defined URL link frame", -} - -// ID3v2.3.0 frames (see http://id3.org/id3v2.3.0#Declared_ID3v2_frames). -var id3v23Frames = map[string]string{ - "AENC": "Audio encryption]", - "APIC": "Attached picture", - "COMM": "Comments", - "COMR": "Commercial frame", - "ENCR": "Encryption method registration", - "EQUA": "Equalization", - "ETCO": "Event timing codes", - "GEOB": "General encapsulated object", - "GRID": "Group identification registration", - "IPLS": "Involved people list", - "LINK": "Linked information", - "MCDI": "Music CD identifier", - "MLLT": "MPEG location lookup table", - "OWNE": "Ownership frame", - "PRIV": "Private frame", - "PCNT": "Play counter", - "POPM": "Popularimeter", - "POSS": "Position synchronisation frame", - "RBUF": "Recommended buffer size", - "RVAD": "Relative volume adjustment", - "RVRB": "Reverb", - "SYLT": "Synchronized lyric/text", - "SYTC": "Synchronized tempo codes", - "TALB": "Album/Movie/Show title", - "TBPM": "BPM (beats per minute)", - "TCMP": "iTunes Compilation Flag", - "TCOM": "Composer", - "TCON": "Content type", - "TCOP": "Copyright message", - "TDAT": "Date", - "TDLY": "Playlist delay", - "TENC": "Encoded by", - "TEXT": "Lyricist/Text writer", - "TFLT": "File type", - "TIME": "Time", - "TIT1": "Content group description", - "TIT2": "Title/songname/content description", - "TIT3": "Subtitle/Description refinement", - "TKEY": "Initial key", - "TLAN": "Language(s)", - "TLEN": "Length", - "TMED": "Media type", - "TOAL": "Original album/movie/show title", - "TOFN": "Original filename", - "TOLY": "Original lyricist(s)/text writer(s)", - "TOPE": "Original artist(s)/performer(s)", - "TORY": "Original release year", - "TOWN": "File owner/licensee", - "TPE1": "Lead performer(s)/Soloist(s)", - "TPE2": "Band/orchestra/accompaniment", - "TPE3": "Conductor/performer refinement", - "TPE4": "Interpreted, remixed, or otherwise modified by", - "TPOS": "Part of a set", - "TPUB": "Publisher", - "TRCK": "Track number/Position in set", - "TRDA": "Recording dates", - "TRSN": "Internet radio station name", - "TRSO": "Internet radio station owner", - "TSIZ": "Size", - "TSO2": "iTunes uses this for Album Artist sort order", - "TSOC": "iTunes uses this for Composer sort order", - "TSRC": "ISRC (international standard recording code)", - "TSSE": "Software/Hardware and settings used for encoding", - "TYER": "Year", - "TXXX": "User defined text information frame", - "UFID": "Unique file identifier", - "USER": "Terms of use", - "USLT": "Unsychronized lyric/text transcription", - "WCOM": "Commercial information", - "WCOP": "Copyright/Legal information", - "WOAF": "Official audio file webpage", - "WOAR": "Official artist/performer webpage", - "WOAS": "Official audio source webpage", - "WORS": "Official internet radio station homepage", - "WPAY": "Payment", - "WPUB": "Publishers official webpage", - "WXXX": "User defined URL link frame", -} - -// ID3v2.4.0 frames (see http://id3.org/id3v2.4.0-frames, sec 4). -var id3v24Frames = map[string]string{ - "AENC": "Audio encryption", - "APIC": "Attached picture", - "ASPI": "Audio seek point index", - - "COMM": "Comments", - "COMR": "Commercial frame", - - "ENCR": "Encryption method registration", - "EQU2": "Equalisation (2)", - "ETCO": "Event timing codes", - - "GEOB": "General encapsulated object", - "GRID": "Group identification registration", - - "LINK": "Linked information", - - "MCDI": "Music CD identifier", - "MLLT": "MPEG location lookup table", - - "OWNE": "Ownership frame", - - "PRIV": "Private frame", - "PCNT": "Play counter", - "POPM": "Popularimeter", - "POSS": "Position synchronisation frame", - - "RBUF": "Recommended buffer size", - "RVA2": "Relative volume adjustment (2)", - "RVRB": "Reverb", - - "SEEK": "Seek frame", - "SIGN": "Signature frame", - "SYLT": "Synchronised lyric/text", - "SYTC": "Synchronised tempo codes", - - "TALB": "Album/Movie/Show title", - "TBPM": "BPM (beats per minute)", - "TCMP": "iTunes Compilation Flag", - "TCOM": "Composer", - "TCON": "Content type", - "TCOP": "Copyright message", - "TDEN": "Encoding time", - "TDLY": "Playlist delay", - "TDOR": "Original release time", - "TDRC": "Recording time", - "TDRL": "Release time", - "TDTG": "Tagging time", - "TENC": "Encoded by", - "TEXT": "Lyricist/Text writer", - "TFLT": "File type", - "TIPL": "Involved people list", - "TIT1": "Content group description", - "TIT2": "Title/songname/content description", - "TIT3": "Subtitle/Description refinement", - "TKEY": "Initial key", - "TLAN": "Language(s)", - "TLEN": "Length", - "TMCL": "Musician credits list", - "TMED": "Media type", - "TMOO": "Mood", - "TOAL": "Original album/movie/show title", - "TOFN": "Original filename", - "TOLY": "Original lyricist(s)/text writer(s)", - "TOPE": "Original artist(s)/performer(s)", - "TOWN": "File owner/licensee", - "TPE1": "Lead performer(s)/Soloist(s)", - "TPE2": "Band/orchestra/accompaniment", - "TPE3": "Conductor/performer refinement", - "TPE4": "Interpreted, remixed, or otherwise modified by", - "TPOS": "Part of a set", - "TPRO": "Produced notice", - "TPUB": "Publisher", - "TRCK": "Track number/Position in set", - "TRSN": "Internet radio station name", - "TRSO": "Internet radio station owner", - "TSO2": "iTunes uses this for Album Artist sort order", - "TSOA": "Album sort order", - "TSOC": "iTunes uses this for Composer sort order", - "TSOP": "Performer sort order", - "TSOT": "Title sort order", - "TSRC": "ISRC (international standard recording code)", - "TSSE": "Software/Hardware and settings used for encoding", - "TSST": "Set subtitle", - "TXXX": "User defined text information frame", - - "UFID": "Unique file identifier", - "USER": "Terms of use", - "USLT": "Unsynchronised lyric/text transcription", - - "WCOM": "Commercial information", - "WCOP": "Copyright/Legal information", - "WOAF": "Official audio file webpage", - "WOAR": "Official artist/performer webpage", - "WOAS": "Official audio source webpage", - "WORS": "Official Internet radio station homepage", - "WPAY": "Payment", - "WPUB": "Publishers official webpage", - "WXXX": "User defined URL link frame", -} - -// ID3 frames that are defined in the specs. -var id3Frames = map[Format]map[string]string{ - ID3v2_2: id3v22Frames, - ID3v2_3: id3v23Frames, - ID3v2_4: id3v24Frames, -} - -func validID3Frame(version Format, name string) bool { - names, ok := id3Frames[version] - if !ok { - return false - } - _, ok = names[name] - return ok -} - -func readWFrame(b []byte) (string, error) { - // Frame text is always encoded in ISO-8859-1 - b = append([]byte{0}, b...) - return readTFrame(b) -} - -func readTFrame(b []byte) (string, error) { - if len(b) == 0 { - return "", nil - } - - txt, err := decodeText(b[0], b[1:]) - if err != nil { - return "", err - } - return strings.Join(strings.Split(txt, string(singleZero)), ""), nil -} - -const ( - encodingISO8859 byte = 0 - encodingUTF16WithBOM byte = 1 - encodingUTF16 byte = 2 - encodingUTF8 byte = 3 -) - -func decodeText(enc byte, b []byte) (string, error) { - if len(b) == 0 { - return "", nil - } - - switch enc { - case encodingISO8859: // ISO-8859-1 - return decodeISO8859(b), nil - - case encodingUTF16WithBOM: // UTF-16 with byte order marker - if len(b) == 1 { - return "", nil - } - return decodeUTF16WithBOM(b) - - case encodingUTF16: // UTF-16 without byte order (assuming BigEndian) - if len(b) == 1 { - return "", nil - } - return decodeUTF16(b, binary.BigEndian) - - case encodingUTF8: // UTF-8 - return string(b), nil - - default: // Fallback to ISO-8859-1 - return decodeISO8859(b), nil - } -} - -var ( - singleZero = []byte{0} - doubleZero = []byte{0, 0} -) - -func dataSplit(b []byte, enc byte) [][]byte { - delim := singleZero - if enc == encodingUTF16 || enc == encodingUTF16WithBOM { - delim = doubleZero - } - - result := bytes.SplitN(b, delim, 2) - if len(result) != 2 { - return result - } - - if len(result[1]) == 0 { - return result - } - - if result[1][0] == 0 { - // there was a double (or triple) 0 and we cut too early - result[0] = append(result[0], result[1][0]) - result[1] = result[1][1:] - } - return result -} - -func decodeISO8859(b []byte) string { - r := make([]rune, len(b)) - for i, x := range b { - r[i] = rune(x) - } - return string(r) -} - -func decodeUTF16WithBOM(b []byte) (string, error) { - if len(b) < 2 { - return "", errors.New("invalid encoding: expected at least 2 bytes for UTF-16 byte order mark") - } - - var bo binary.ByteOrder - switch { - case b[0] == 0xFE && b[1] == 0xFF: - bo = binary.BigEndian - b = b[2:] - - case b[0] == 0xFF && b[1] == 0xFE: - bo = binary.LittleEndian - b = b[2:] - - default: - bo = DefaultUTF16WithBOMByteOrder - } - return decodeUTF16(b, bo) -} - -func decodeUTF16(b []byte, bo binary.ByteOrder) (string, error) { - if len(b)%2 != 0 { - return "", errors.New("invalid encoding: expected even number of bytes for UTF-16 encoded text") - } - s := make([]uint16, 0, len(b)/2) - for i := 0; i < len(b); i += 2 { - s = append(s, bo.Uint16(b[i:i+2])) - } - return string(utf16.Decode(s)), nil -} - -// Comm is a type used in COMM, UFID, TXXX, WXXX and USLT tag. -// It's a text with a description and a specified language -// For WXXX, TXXX and UFID, we don't set a Language -type Comm struct { - Language string - Description string - Text string -} - -// String returns a string representation of the underlying Comm instance. -func (t Comm) String() string { - if t.Language != "" { - return fmt.Sprintf("Text{Lang: '%v', Description: '%v', %v lines}", - t.Language, t.Description, strings.Count(t.Text, "\n")) - } - return fmt.Sprintf("Text{Description: '%v', %v}", t.Description, t.Text) -} - -// IDv2.{3,4} -// -- Header -// <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT"> -// <Header for 'Comment', ID: "COMM"> -// -- readTextWithDescrFrame(data, true, true) -// Text encoding $xx -// Language $xx xx xx -// Content descriptor <text string according to encoding> $00 (00) -// Lyrics/text <full text string according to encoding> -// -- Header -// <Header for 'User defined text information frame', ID: "TXXX"> -// <Header for 'User defined URL link frame', ID: "WXXX"> -// -- readTextWithDescrFrame(data, false, <isDataEncoded>) -// Text encoding $xx -// Description <text string according to encoding> $00 (00) -// Value <text string according to encoding> -func readTextWithDescrFrame(b []byte, hasLang bool, encoded bool) (*Comm, error) { - enc := b[0] - b = b[1:] - - c := &Comm{} - if hasLang { - c.Language = string(b[:3]) - b = b[3:] - } - - descTextSplit := dataSplit(b, enc) - if len(descTextSplit) < 1 { - return nil, fmt.Errorf("error decoding tag description text: invalid encoding") - } - - desc, err := decodeText(enc, descTextSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding tag description text: %v", err) - } - c.Description = desc - - if len(descTextSplit) == 1 { - return c, nil - } - - if !encoded { - enc = byte(0) - } - text, err := decodeText(enc, descTextSplit[1]) - if err != nil { - return nil, fmt.Errorf("error decoding tag text: %v", err) - } - c.Text = text - - return c, nil -} - -// UFID is composed of a provider (frequently a URL and a binary identifier) -// The identifier can be a text (Musicbrainz use texts, but not necessary) -type UFID struct { - Provider string - Identifier []byte -} - -func (u UFID) String() string { - return fmt.Sprintf("%v (%v)", u.Provider, string(u.Identifier)) -} - -func readUFID(b []byte) (*UFID, error) { - result := bytes.SplitN(b, singleZero, 2) - if len(result) != 2 { - return nil, errors.New("expected to split UFID data into 2 pieces") - } - - return &UFID{ - Provider: string(result[0]), - Identifier: result[1], - }, nil -} - -var pictureTypes = map[byte]string{ - 0x00: "Other", - 0x01: "32x32 pixels 'file icon' (PNG only)", - 0x02: "Other file icon", - 0x03: "Cover (front)", - 0x04: "Cover (back)", - 0x05: "Leaflet page", - 0x06: "Media (e.g. lable side of CD)", - 0x07: "Lead artist/lead performer/soloist", - 0x08: "Artist/performer", - 0x09: "Conductor", - 0x0A: "Band/Orchestra", - 0x0B: "Composer", - 0x0C: "Lyricist/text writer", - 0x0D: "Recording Location", - 0x0E: "During recording", - 0x0F: "During performance", - 0x10: "Movie/video screen capture", - 0x11: "A bright coloured fish", - 0x12: "Illustration", - 0x13: "Band/artist logotype", - 0x14: "Publisher/Studio logotype", -} - -// Picture is a type which represents an attached picture extracted from metadata. -type Picture struct { - Ext string // Extension of the picture file. - MIMEType string // MIMEType of the picture. - Type string // Type of the picture (see pictureTypes). - Description string // Description. - Data []byte // Raw picture data. -} - -// String returns a string representation of the underlying Picture instance. -func (p Picture) String() string { - return fmt.Sprintf("Picture{Ext: %v, MIMEType: %v, Type: %v, Description: %v, Data.Size: %v}", - p.Ext, p.MIMEType, p.Type, p.Description, len(p.Data)) -} - -// IDv2.2 -// -- Header -// Attached picture "PIC" -// Frame size $xx xx xx -// -- readPICFrame -// Text encoding $xx -// Image format $xx xx xx -// Picture type $xx -// Description <textstring> $00 (00) -// Picture data <binary data> -func readPICFrame(b []byte) (*Picture, error) { - enc := b[0] - ext := string(b[1:4]) - picType := b[4] - - descDataSplit := dataSplit(b[5:], enc) - if len(descDataSplit) != 2 { - return nil, errors.New("error decoding PIC description text: invalid encoding") - } - desc, err := decodeText(enc, descDataSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding PIC description text: %v", err) - } - - var mimeType string - switch ext { - case "jpeg", "jpg": - mimeType = "image/jpeg" - case "png": - mimeType = "image/png" - } - - return &Picture{ - Ext: ext, - MIMEType: mimeType, - Type: pictureTypes[picType], - Description: desc, - Data: descDataSplit[1], - }, nil -} - -// IDv2.{3,4} -// -- Header -// <Header for 'Attached picture', ID: "APIC"> -// -- readAPICFrame -// Text encoding $xx -// MIME type <text string> $00 -// Picture type $xx -// Description <text string according to encoding> $00 (00) -// Picture data <binary data> -func readAPICFrame(b []byte) (*Picture, error) { - enc := b[0] - mimeDataSplit := bytes.SplitN(b[1:], singleZero, 2) - mimeType := string(mimeDataSplit[0]) - - b = mimeDataSplit[1] - if len(b) < 1 { - return nil, fmt.Errorf("error decoding APIC mimetype") - } - picType := b[0] - - descDataSplit := dataSplit(b[1:], enc) - if len(descDataSplit) != 2 { - return nil, errors.New("error decoding APIC description text: invalid encoding") - } - desc, err := decodeText(enc, descDataSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding APIC description text: %v", err) - } - - var ext string - switch mimeType { - case "image/jpeg": - ext = "jpg" - case "image/png": - ext = "png" - } - - return &Picture{ - Ext: ext, - MIMEType: mimeType, - Type: pictureTypes[picType], - Description: desc, - Data: descDataSplit[1], - }, nil -} diff --git a/vendor/github.com/dhowden/tag/id3v2metadata.go b/vendor/github.com/dhowden/tag/id3v2metadata.go deleted file mode 100644 index 6185963..0000000 --- a/vendor/github.com/dhowden/tag/id3v2metadata.go +++ /dev/null @@ -1,141 +0,0 @@ -// 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 ( - "strconv" - "strings" -) - -type frameNames map[string][2]string - -func (f frameNames) Name(s string, fm Format) string { - l, ok := f[s] - if !ok { - return "" - } - - switch fm { - case ID3v2_2: - return l[0] - case ID3v2_3: - return l[1] - case ID3v2_4: - if s == "year" { - return "TDRC" - } - return l[1] - } - return "" -} - -var frames = frameNames(map[string][2]string{ - "title": [2]string{"TT2", "TIT2"}, - "artist": [2]string{"TP1", "TPE1"}, - "album": [2]string{"TAL", "TALB"}, - "album_artist": [2]string{"TP2", "TPE2"}, - "composer": [2]string{"TCM", "TCOM"}, - "year": [2]string{"TYE", "TYER"}, - "track": [2]string{"TRK", "TRCK"}, - "disc": [2]string{"TPA", "TPOS"}, - "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. -type metadataID3v2 struct { - header *id3v2Header - frames map[string]interface{} -} - -func (m metadataID3v2) getString(k string) string { - v, ok := m.frames[k] - if !ok { - return "" - } - return v.(string) -} - -func (m metadataID3v2) Format() Format { return m.header.Version } -func (m metadataID3v2) FileType() FileType { return MP3 } -func (m metadataID3v2) Raw() map[string]interface{} { return m.frames } - -func (m metadataID3v2) Title() string { - return m.getString(frames.Name("title", m.Format())) -} - -func (m metadataID3v2) Artist() string { - return m.getString(frames.Name("artist", m.Format())) -} - -func (m metadataID3v2) Album() string { - return m.getString(frames.Name("album", m.Format())) -} - -func (m metadataID3v2) AlbumArtist() string { - return m.getString(frames.Name("album_artist", m.Format())) -} - -func (m metadataID3v2) Composer() string { - return m.getString(frames.Name("composer", m.Format())) -} - -func (m metadataID3v2) Genre() string { - return id3v2genre(m.getString(frames.Name("genre", m.Format()))) -} - -func (m metadataID3v2) Year() int { - year, _ := strconv.Atoi(m.getString(frames.Name("year", m.Format()))) - return year -} - -func parseXofN(s string) (x, n int) { - xn := strings.Split(s, "/") - if len(xn) != 2 { - x, _ = strconv.Atoi(s) - return x, 0 - } - x, _ = strconv.Atoi(strings.TrimSpace(xn[0])) - n, _ = strconv.Atoi(strings.TrimSpace(xn[1])) - return x, n -} - -func (m metadataID3v2) Track() (int, int) { - return parseXofN(m.getString(frames.Name("track", m.Format()))) -} - -func (m metadataID3v2) Disc() (int, int) { - return parseXofN(m.getString(frames.Name("disc", m.Format()))) -} - -func (m metadataID3v2) Lyrics() string { - t, ok := m.frames[frames.Name("lyrics", m.Format())] - if !ok { - return "" - } - 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 { - return nil - } - return v.(*Picture) -} diff --git a/vendor/github.com/dhowden/tag/internal/id3v1_test/NOTICE.txt b/vendor/github.com/dhowden/tag/internal/id3v1_test/NOTICE.txt deleted file mode 100644 index f7e01d8..0000000 --- a/vendor/github.com/dhowden/tag/internal/id3v1_test/NOTICE.txt +++ /dev/null @@ -1,3 +0,0 @@ -All files in this directory are subject to the CC0 1.0 Universal (CC0 1.0) -Public Domain Dedication license. Its contents can be found at: -http://creativecommons.org/publicdomain/zero/1.0 diff --git a/vendor/github.com/dhowden/tag/mp4.go b/vendor/github.com/dhowden/tag/mp4.go deleted file mode 100644 index 4c3dd59..0000000 --- a/vendor/github.com/dhowden/tag/mp4.go +++ /dev/null @@ -1,372 +0,0 @@ -// 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 ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "strconv" - "strings" -) - -var atomTypes = map[int]string{ - 0: "implicit", // automatic based on atom name - 1: "text", - 13: "jpeg", - 14: "png", - 21: "uint8", -} - -// NB: atoms does not include "----", this is handled separately -var atoms = atomNames(map[string]string{ - "\xa9alb": "album", - "\xa9art": "artist", - "\xa9ART": "artist", - "aART": "album_artist", - "\xa9day": "year", - "\xa9nam": "title", - "\xa9gen": "genre", - "trkn": "track", - "\xa9wrt": "composer", - "\xa9too": "encoder", - "cprt": "copyright", - "covr": "picture", - "\xa9grp": "grouping", - "keyw": "keyword", - "\xa9lyr": "lyrics", - "\xa9cmt": "comment", - "tmpo": "tempo", - "cpil": "compilation", - "disk": "disc", -}) - -// Detect PNG image if "implicit" class is used -var pngHeader = []byte{137, 80, 78, 71, 13, 10, 26, 10} - -type atomNames map[string]string - -func (f atomNames) Name(n string) []string { - res := make([]string, 1) - for k, v := range f { - if v == n { - res = append(res, k) - } - } - return res -} - -// metadataMP4 is the implementation of Metadata for MP4 tag (atom) data. -type metadataMP4 struct { - fileType FileType - data map[string]interface{} -} - -// ReadAtoms reads MP4 metadata atoms from the io.ReadSeeker into a Metadata, returning -// non-nil error if there was a problem. -func ReadAtoms(r io.ReadSeeker) (Metadata, error) { - m := metadataMP4{ - data: make(map[string]interface{}), - fileType: UnknownFileType, - } - err := m.readAtoms(r) - return m, err -} - -func (m metadataMP4) readAtoms(r io.ReadSeeker) error { - for { - name, size, err := readAtomHeader(r) - if err != nil { - if err == io.EOF { - return nil - } - return err - } - - switch name { - case "meta": - // next_item_id (int32) - _, err := readBytes(r, 4) - if err != nil { - return err - } - fallthrough - - case "moov", "udta", "ilst": - return m.readAtoms(r) - } - - _, ok := atoms[name] - var data []string - if name == "----" { - name, data, err = readCustomAtom(r, size) - if err != nil { - return err - } - - if name != "----" { - ok = true - size = 0 // already read data - } - } - - if !ok { - _, err := r.Seek(int64(size-8), io.SeekCurrent) - if err != nil { - return err - } - continue - } - - err = m.readAtomData(r, name, size-8, data) - if err != nil { - return err - } - } -} - -func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32, processedData []string) error { - var b []byte - var err error - var contentType string - if len(processedData) > 0 { - b = []byte(strings.Join(processedData, ";")) // add delimiter if multiple data fields - contentType = "text" - } else { - // read the data - b, err = readBytes(r, int(size)) - if err != nil { - return err - } - if len(b) < 8 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, got %d", 8, len(b)) - } - - // "data" + size (4 bytes each) - b = b[8:] - - if len(b) < 3 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for class, got %d", 3, len(b)) - } - class := getInt(b[1:4]) - var ok bool - contentType, ok = atomTypes[class] - if !ok { - return fmt.Errorf("invalid content type: %v (%x) (%x)", class, b[1:4], b) - } - - // 4: atom version (1 byte) + atom flags (3 bytes) - // 4: NULL (usually locale indicator) - if len(b) < 8 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for atom version and flags, got %d", 8, len(b)) - } - b = b[8:] - } - - if name == "trkn" || name == "disk" { - if len(b) < 6 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for track and disk numbers, got %d", 6, len(b)) - } - - m.data[name] = int(b[3]) - m.data[name+"_count"] = int(b[5]) - return nil - } - - if contentType == "implicit" { - if name == "covr" { - if bytes.HasPrefix(b, pngHeader) { - contentType = "png" - } - // TODO(dhowden): Detect JPEG formats too (harder). - } - } - - var data interface{} - switch contentType { - case "implicit": - if _, ok := atoms[name]; ok { - return fmt.Errorf("unhandled implicit content type for required atom: %q", name) - } - return nil - - case "text": - data = string(b) - - case "uint8": - if len(b) < 1 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for integer tag data, got %d", 1, len(b)) - } - data = getInt(b[:1]) - - case "jpeg", "png": - data = &Picture{ - Ext: contentType, - MIMEType: "image/" + contentType, - Data: b, - } - } - m.data[name] = data - - return nil -} - -func readAtomHeader(r io.ReadSeeker) (name string, size uint32, err error) { - err = binary.Read(r, binary.BigEndian, &size) - if err != nil { - return - } - name, err = readString(r, 4) - return -} - -// Generic atom. -// Should have 3 sub atoms : mean, name and data. -// We check that mean is "com.apple.iTunes" and we use the subname as -// the name, and move to the data atom. -// Data atom could have multiple data values, each with a header. -// If anything goes wrong, we jump at the end of the "----" atom. -func readCustomAtom(r io.ReadSeeker, size uint32) (_ string, data []string, _ error) { - subNames := make(map[string]string) - - for size > 8 { - subName, subSize, err := readAtomHeader(r) - if err != nil { - return "", nil, err - } - - // Remove the size of the atom from the size counter - if size >= subSize { - size -= subSize - } else { - return "", nil, errors.New("--- invalid size") - } - - b, err := readBytes(r, int(subSize-8)) - if err != nil { - return "", nil, err - } - - if len(b) < 4 { - return "", nil, fmt.Errorf("invalid encoding: expected at least %d bytes, got %d", 4, len(b)) - } - switch subName { - case "mean", "name": - subNames[subName] = string(b[4:]) - case "data": - data = append(data, string(b[4:])) - } - } - - // there should remain only the header size - if size != 8 { - err := errors.New("---- atom out of bounds") - return "", nil, err - } - - if subNames["mean"] != "com.apple.iTunes" || subNames["name"] == "" || len(data) == 0 { - return "----", nil, nil - } - return subNames["name"], data, nil -} - -func (metadataMP4) Format() Format { return MP4 } -func (m metadataMP4) FileType() FileType { return m.fileType } - -func (m metadataMP4) Raw() map[string]interface{} { return m.data } - -func (m metadataMP4) getString(n []string) string { - for _, k := range n { - if x, ok := m.data[k]; ok { - return x.(string) - } - } - return "" -} - -func (m metadataMP4) getInt(n []string) int { - for _, k := range n { - if x, ok := m.data[k]; ok { - return x.(int) - } - } - return 0 -} - -func (m metadataMP4) Title() string { - return m.getString(atoms.Name("title")) -} - -func (m metadataMP4) Artist() string { - return m.getString(atoms.Name("artist")) -} - -func (m metadataMP4) Album() string { - return m.getString(atoms.Name("album")) -} - -func (m metadataMP4) AlbumArtist() string { - return m.getString(atoms.Name("album_artist")) -} - -func (m metadataMP4) Composer() string { - return m.getString(atoms.Name("composer")) -} - -func (m metadataMP4) Genre() string { - return m.getString(atoms.Name("genre")) -} - -func (m metadataMP4) Year() int { - date := m.getString(atoms.Name("year")) - if len(date) >= 4 { - year, _ := strconv.Atoi(date[:4]) - return year - } - return 0 -} - -func (m metadataMP4) Track() (int, int) { - x := m.getInt([]string{"trkn"}) - if n, ok := m.data["trkn_count"]; ok { - return x, n.(int) - } - return x, 0 -} - -func (m metadataMP4) Disc() (int, int) { - x := m.getInt([]string{"disk"}) - if n, ok := m.data["disk_count"]; ok { - return x, n.(int) - } - return x, 0 -} - -func (m metadataMP4) Lyrics() string { - t, ok := m.data["\xa9lyr"] - if !ok { - return "" - } - 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 { - return nil - } - p, _ := v.(*Picture) - return p -} diff --git a/vendor/github.com/dhowden/tag/ogg.go b/vendor/github.com/dhowden/tag/ogg.go deleted file mode 100644 index bc26d4a..0000000 --- a/vendor/github.com/dhowden/tag/ogg.go +++ /dev/null @@ -1,119 +0,0 @@ -// 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" -) - -const ( - idType int = 1 - commentType int = 3 -) - -// ReadOGGTags reads OGG metadata from the io.ReadSeeker, returning the resulting -// metadata in a Metadata implementation, or non-nil error if there was a problem. -// See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html -// and http://www.xiph.org/ogg/doc/framing.html for details. -func ReadOGGTags(r io.ReadSeeker) (Metadata, error) { - oggs, err := readString(r, 4) - if err != nil { - return nil, err - } - if oggs != "OggS" { - return nil, errors.New("expected 'OggS'") - } - - // Skip 22 bytes of Page header to read page_segments length byte at position 26 - // See http://www.xiph.org/ogg/doc/framing.html - _, err = r.Seek(22, io.SeekCurrent) - if err != nil { - return nil, err - } - - nS, err := readInt(r, 1) - if err != nil { - return nil, err - } - - // Seek and discard the segments - _, err = r.Seek(int64(nS), io.SeekCurrent) - if err != nil { - return nil, err - } - - // First packet type is identification, type 1 - t, err := readInt(r, 1) - if err != nil { - return nil, err - } - if t != idType { - return nil, errors.New("expected 'vorbis' identification type 1") - } - - // Seek and discard 29 bytes from common and identification header - // See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 - _, err = r.Seek(29, io.SeekCurrent) - if err != nil { - return nil, err - } - - // Beginning of a new page. Comment packet is on a separate page - // See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2 - oggs, err = readString(r, 4) - if err != nil { - return nil, err - } - if oggs != "OggS" { - return nil, errors.New("expected 'OggS'") - } - - // Skip page 2 header, same as line 30 - _, err = r.Seek(22, io.SeekCurrent) - if err != nil { - return nil, err - } - - nS, err = readInt(r, 1) - if err != nil { - return nil, err - } - - _, err = r.Seek(int64(nS), io.SeekCurrent) - if err != nil { - return nil, err - } - - // Packet type is comment, type 3 - t, err = readInt(r, 1) - if err != nil { - return nil, err - } - if t != commentType { - return nil, errors.New("expected 'vorbis' comment type 3") - } - - // Seek and discard 6 bytes from common header - _, err = r.Seek(6, io.SeekCurrent) - if err != nil { - return nil, err - } - - m := &metadataOGG{ - newMetadataVorbis(), - } - - err = m.readVorbisComment(r) - return m, err -} - -type metadataOGG struct { - *metadataVorbis -} - -func (m *metadataOGG) FileType() FileType { - return OGG -} diff --git a/vendor/github.com/dhowden/tag/sum.go b/vendor/github.com/dhowden/tag/sum.go deleted file mode 100644 index 97b27a7..0000000 --- a/vendor/github.com/dhowden/tag/sum.go +++ /dev/null @@ -1,219 +0,0 @@ -package tag - -import ( - "crypto/sha1" - "encoding/binary" - "errors" - "fmt" - "hash" - "io" -) - -// Sum creates a checksum of the audio file data provided by the io.ReadSeeker which is metadata -// (ID3, MP4) invariant. -func Sum(r io.ReadSeeker) (string, error) { - b, err := readBytes(r, 11) - if err != nil { - return "", err - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("could not seek back to original position: %v", err) - } - - switch { - case string(b[0:4]) == "fLaC": - return SumFLAC(r) - - case string(b[4:11]) == "ftypM4A": - return SumAtoms(r) - - case string(b[0:3]) == "ID3": - return SumID3v2(r) - } - - h, err := SumID3v1(r) - if err != nil { - if err == ErrNotID3v1 { - return SumAll(r) - } - return "", err - } - return h, nil -} - -// SumAll returns a checksum of the content from the reader (until EOF). -func SumAll(r io.ReadSeeker) (string, error) { - h := sha1.New() - _, err := io.Copy(h, r) - if err != nil { - return "", nil - } - return hashSum(h), nil -} - -// SumAtoms constructs a checksum of MP4 audio file data provided by the io.ReadSeeker which is -// metadata invariant. -func SumAtoms(r io.ReadSeeker) (string, error) { - for { - var size uint32 - err := binary.Read(r, binary.BigEndian, &size) - if err != nil { - if err == io.EOF { - return "", fmt.Errorf("reached EOF before audio data") - } - return "", err - } - - name, err := readString(r, 4) - if err != nil { - return "", err - } - - switch name { - case "meta": - // next_item_id (int32) - _, err := r.Seek(4, io.SeekCurrent) - if err != nil { - return "", err - } - fallthrough - - case "moov", "udta", "ilst": - continue - - case "mdat": // stop when we get to the data - h := sha1.New() - _, err := io.CopyN(h, r, int64(size-8)) - if err != nil { - return "", fmt.Errorf("error reading audio data: %v", err) - } - return hashSum(h), nil - } - - _, err = r.Seek(int64(size-8), io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("error reading '%v' tag: %v", name, err) - } - } -} - -func sizeToEndOffset(r io.ReadSeeker, offset int64) (int64, error) { - n, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return 0, fmt.Errorf("error seeking end offset (%d bytes): %v", offset, err) - } - - _, err = r.Seek(-n, io.SeekCurrent) - if err != nil { - return 0, fmt.Errorf("error seeking back to original position: %v", err) - } - return n, nil -} - -// SumID3v1 constructs a checksum of MP3 audio file data (assumed to have ID3v1 tags) provided -// by the io.ReadSeeker which is metadata invariant. -func SumID3v1(r io.ReadSeeker) (string, error) { - n, err := sizeToEndOffset(r, 128) - if err != nil { - return "", fmt.Errorf("error determining read size to ID3v1 header: %v", err) - } - - // TODO: improve this check??? - if n <= 0 { - return "", fmt.Errorf("file size must be greater than 128 bytes (ID3v1 header size) for MP3") - } - - h := sha1.New() - _, err = io.CopyN(h, r, n) - if err != nil { - return "", fmt.Errorf("error reading %v bytes: %v", n, err) - } - return hashSum(h), nil -} - -// SumID3v2 constructs a checksum of MP3 audio file data (assumed to have ID3v2 tags) provided by the -// io.ReadSeeker which is metadata invariant. -func SumID3v2(r io.ReadSeeker) (string, error) { - header, _, err := readID3v2Header(r) - if err != nil { - return "", fmt.Errorf("error reading ID3v2 header: %v", err) - } - - _, err = r.Seek(int64(header.Size), io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("error seeking to end of ID3V2 header: %v", err) - } - - n, err := sizeToEndOffset(r, 128) - if err != nil { - return "", fmt.Errorf("error determining read size to ID3v1 header: %v", err) - } - - // TODO: remove this check????? - if n < 0 { - return "", fmt.Errorf("file size must be greater than 128 bytes for MP3: %v bytes", n) - } - - h := sha1.New() - _, err = io.CopyN(h, r, n) - if err != nil { - return "", fmt.Errorf("error reading %v bytes: %v", n, err) - } - return hashSum(h), nil -} - -// SumFLAC costructs a checksum of the FLAC audio file data provided by the io.ReadSeeker (ignores -// metadata fields). -func SumFLAC(r io.ReadSeeker) (string, error) { - flac, err := readString(r, 4) - if err != nil { - return "", err - } - if flac != "fLaC" { - return "", errors.New("expected 'fLaC'") - } - - for { - last, err := skipFLACMetadataBlock(r) - if err != nil { - return "", err - } - - if last { - break - } - } - - h := sha1.New() - _, err = io.Copy(h, r) - if err != nil { - return "", fmt.Errorf("error reading data bytes from FLAC: %v", err) - } - return hashSum(h), nil -} - -func skipFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) { - blockHeader, err := readBytes(r, 1) - if err != nil { - return - } - - if getBit(blockHeader[0], 7) { - blockHeader[0] ^= (1 << 7) - last = true - } - - blockLen, err := readInt(r, 3) - if err != nil { - return - } - - _, err = r.Seek(int64(blockLen), io.SeekCurrent) - return -} - -func hashSum(h hash.Hash) string { - return fmt.Sprintf("%x", h.Sum([]byte{})) -} diff --git a/vendor/github.com/dhowden/tag/tag.go b/vendor/github.com/dhowden/tag/tag.go deleted file mode 100644 index 306f1d7..0000000 --- a/vendor/github.com/dhowden/tag/tag.go +++ /dev/null @@ -1,147 +0,0 @@ -// 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 provides MP3 (ID3: v1, 2.2, 2.3 and 2.4), MP4, FLAC and OGG metadata detection, -// parsing and artwork extraction. -// -// Detect and parse tag metadata from an io.ReadSeeker (i.e. an *os.File): -// m, err := tag.ReadFrom(f) -// if err != nil { -// log.Fatal(err) -// } -// log.Print(m.Format()) // The detected format. -// log.Print(m.Title()) // The title of the track (see Metadata interface for more details). -package tag - -import ( - "errors" - "fmt" - "io" -) - -// ErrNoTagsFound is the error returned by ReadFrom when the metadata format -// cannot be identified. -var ErrNoTagsFound = errors.New("no tags found") - -// ReadFrom detects and parses audio file metadata tags (currently supports ID3v1,2.{2,3,4}, MP4, FLAC/OGG). -// Returns non-nil error if the format of the given data could not be determined, or if there was a problem -// parsing the data. -func ReadFrom(r io.ReadSeeker) (Metadata, error) { - b, err := readBytes(r, 11) - if err != nil { - return nil, err - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - return nil, fmt.Errorf("could not seek back to original position: %v", err) - } - - switch { - case string(b[0:4]) == "fLaC": - return ReadFLACTags(r) - - case string(b[0:4]) == "OggS": - return ReadOGGTags(r) - - case string(b[4:8]) == "ftyp": - return ReadAtoms(r) - - case string(b[0:3]) == "ID3": - return ReadID3v2Tags(r) - - case string(b[0:4]) == "DSD ": - return ReadDSFTags(r) - } - - m, err := ReadID3v1Tags(r) - if err != nil { - if err == ErrNotID3v1 { - err = ErrNoTagsFound - } - return nil, err - } - return m, nil -} - -// Format is an enumeration of metadata types supported by this package. -type Format string - -// Supported tag formats. -const ( - UnknownFormat Format = "" // Unknown Format. - ID3v1 Format = "ID3v1" // ID3v1 tag format. - ID3v2_2 Format = "ID3v2.2" // ID3v2.2 tag format. - ID3v2_3 Format = "ID3v2.3" // ID3v2.3 tag format (most common). - ID3v2_4 Format = "ID3v2.4" // ID3v2.4 tag format. - MP4 Format = "MP4" // MP4 tag (atom) format (see http://www.ftyps.com/ for a full file type list) - VORBIS Format = "VORBIS" // Vorbis Comment tag format. -) - -// FileType is an enumeration of the audio file types supported by this package, in particular -// there are audio file types which share metadata formats, and this type is used to distinguish -// between them. -type FileType string - -// Supported file types. -const ( - UnknownFileType FileType = "" // Unknown FileType. - MP3 FileType = "MP3" // MP3 file - M4A FileType = "M4A" // M4A file Apple iTunes (ACC) Audio - M4B FileType = "M4B" // M4A file Apple iTunes (ACC) Audio Book - M4P FileType = "M4P" // M4A file Apple iTunes (ACC) AES Protected Audio - 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. -type Metadata interface { - // Format returns the metadata Format used to encode the data. - Format() Format - - // FileType returns the file type of the audio file. - FileType() FileType - - // Title returns the title of the track. - Title() string - - // Album returns the album name of the track. - Album() string - - // Artist returns the artist name of the track. - Artist() string - - // AlbumArtist returns the album artist name of the track. - AlbumArtist() string - - // Composer returns the composer of the track. - Composer() string - - // Year returns the year of the track. - Year() int - - // Genre returns the genre of the track. - Genre() string - - // Track returns the track number and total tracks, or zero values if unavailable. - Track() (int, int) - - // Disc returns the disc number and total discs, or zero values if unavailable. - Disc() (int, int) - - // Picture returns a picture, or nil if not available. - Picture() *Picture - - // 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{} -} diff --git a/vendor/github.com/dhowden/tag/util.go b/vendor/github.com/dhowden/tag/util.go deleted file mode 100644 index ff9c4f1..0000000 --- a/vendor/github.com/dhowden/tag/util.go +++ /dev/null @@ -1,81 +0,0 @@ -// 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 ( - "encoding/binary" - "io" -) - -func getBit(b byte, n uint) bool { - x := byte(1 << n) - return (b & x) == x -} - -func get7BitChunkedInt(b []byte) int { - var n int - for _, x := range b { - n = n << 7 - n |= int(x) - } - return n -} - -func getInt(b []byte) int { - var n int - for _, x := range b { - n = n << 8 - n |= int(x) - } - 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) - if err != nil { - return nil, err - } - return b, nil -} - -func readString(r io.Reader, n int) (string, error) { - b, err := readBytes(r, n) - if err != nil { - return "", err - } - return string(b), nil -} - -func readInt(r io.Reader, n int) (int, error) { - b, err := readBytes(r, n) - if err != nil { - return 0, err - } - return getInt(b), nil -} - -func read7BitChunkedInt(r io.Reader, n int) (int, error) { - b, err := readBytes(r, n) - if err != nil { - return 0, err - } - return get7BitChunkedInt(b), nil -} - -func readInt32LittleEndian(r io.Reader) (int, error) { - var n int32 - err := binary.Read(r, binary.LittleEndian, &n) - return int(n), err -} diff --git a/vendor/github.com/dhowden/tag/vorbis.go b/vendor/github.com/dhowden/tag/vorbis.go deleted file mode 100644 index 9f5ecb8..0000000 --- a/vendor/github.com/dhowden/tag/vorbis.go +++ /dev/null @@ -1,255 +0,0 @@ -// 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" - "fmt" - "io" - "strconv" - "strings" - "time" -) - -func newMetadataVorbis() *metadataVorbis { - return &metadataVorbis{ - c: make(map[string]string), - } -} - -type metadataVorbis struct { - c map[string]string // the vorbis comments - p *Picture -} - -func (m *metadataVorbis) readVorbisComment(r io.Reader) error { - vendorLen, err := readInt32LittleEndian(r) - if err != nil { - return err - } - - if vendorLen < 0 { - return fmt.Errorf("invalid encoding: expected positive length, got %d", vendorLen) - } - - vendor, err := readString(r, vendorLen) - if err != nil { - return err - } - m.c["vendor"] = vendor - - commentsLen, err := readInt32LittleEndian(r) - if err != nil { - return err - } - - for i := 0; i < commentsLen; i++ { - l, err := readInt32LittleEndian(r) - if err != nil { - return err - } - s, err := readString(r, l) - if err != nil { - return err - } - k, v, err := parseComment(s) - if err != nil { - return err - } - m.c[strings.ToLower(k)] = v - } - return nil -} - -func (m *metadataVorbis) readPictureBlock(r io.Reader) error { - b, err := readInt(r, 4) - if err != nil { - return err - } - pictureType, ok := pictureTypes[byte(b)] - if !ok { - return fmt.Errorf("invalid picture type: %v", b) - } - mimeLen, err := readInt(r, 4) - if err != nil { - return err - } - mime, err := readString(r, mimeLen) - if err != nil { - return err - } - - ext := "" - switch mime { - case "image/jpeg": - ext = "jpg" - case "image/png": - ext = "png" - case "image/gif": - ext = "gif" - } - - descLen, err := readInt(r, 4) - if err != nil { - return err - } - desc, err := readString(r, descLen) - if err != nil { - return err - } - - // We skip width <32>, height <32>, colorDepth <32>, coloresUsed <32> - _, err = readInt(r, 4) // width - if err != nil { - return err - } - _, err = readInt(r, 4) // height - if err != nil { - return err - } - _, err = readInt(r, 4) // color depth - if err != nil { - return err - } - _, err = readInt(r, 4) // colors used - if err != nil { - return err - } - - dataLen, err := readInt(r, 4) - if err != nil { - return err - } - data := make([]byte, dataLen) - _, err = io.ReadFull(r, data) - if err != nil { - return err - } - - m.p = &Picture{ - Ext: ext, - MIMEType: mime, - Type: pictureType, - Description: desc, - Data: data, - } - return nil -} - -func parseComment(c string) (k, v string, err error) { - kv := strings.SplitN(c, "=", 2) - if len(kv) != 2 { - err = errors.New("vorbis comment must contain '='") - return - } - k = kv[0] - v = kv[1] - return -} - -func (m *metadataVorbis) Format() Format { - return VORBIS -} - -func (m *metadataVorbis) Raw() map[string]interface{} { - raw := make(map[string]interface{}, len(m.c)) - for k, v := range m.c { - raw[k] = v - } - return raw -} - -func (m *metadataVorbis) Title() string { - return m.c["title"] -} - -func (m *metadataVorbis) Artist() string { - // PERFORMER - // The artist(s) who performed the work. In classical music this would be the - // conductor, orchestra, soloists. In an audio book it would be the actor who - // did the reading. In popular music this is typically the same as the ARTIST - // and is omitted. - if m.c["performer"] != "" { - return m.c["performer"] - } - return m.c["artist"] -} - -func (m *metadataVorbis) Album() string { - return m.c["album"] -} - -func (m *metadataVorbis) AlbumArtist() string { - // This field isn't actually included in the standard, though - // it is commonly assigned to albumartist. - return m.c["albumartist"] -} - -func (m *metadataVorbis) Composer() string { - // ARTIST - // The artist generally considered responsible for the work. In popular music - // this is usually the performing band or singer. For classical music it would - // be the composer. For an audio book it would be the author of the original text. - if m.c["composer"] != "" { - return m.c["composer"] - } - if m.c["performer"] == "" { - return "" - } - return m.c["artist"] -} - -func (m *metadataVorbis) Genre() string { - return m.c["genre"] -} - -func (m *metadataVorbis) Year() int { - var dateFormat string - - // The date need to follow the international standard https://en.wikipedia.org/wiki/ISO_8601 - // and obviously the VorbisComment standard https://wiki.xiph.org/VorbisComment#Date_and_time - switch len(m.c["date"]) { - case 0: - return 0 - case 4: - dateFormat = "2006" - case 7: - dateFormat = "2006-01" - case 10: - dateFormat = "2006-01-02" - } - - t, _ := time.Parse(dateFormat, m.c["date"]) - return t.Year() -} - -func (m *metadataVorbis) Track() (int, int) { - x, _ := strconv.Atoi(m.c["tracknumber"]) - // https://wiki.xiph.org/Field_names - n, _ := strconv.Atoi(m.c["tracktotal"]) - return x, n -} - -func (m *metadataVorbis) Disc() (int, int) { - // https://wiki.xiph.org/Field_names - x, _ := strconv.Atoi(m.c["discnumber"]) - n, _ := strconv.Atoi(m.c["disctotal"]) - return x, n -} - -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 -} |
