Reorganised Vorbis Comment parsing into own type
- Added FileType to Metadata to distinguish between FLAC and OGG - TODO: fix ALAC files being identified as AAC
This commit is contained in:
@@ -6,11 +6,8 @@ package tag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BlockType is a type which represents an enumeration of valid FLAC blocks
|
||||
@@ -43,7 +40,7 @@ func ReadFLACTags(r io.ReadSeeker) (Metadata, error) {
|
||||
}
|
||||
|
||||
m := &metadataFLAC{
|
||||
c: make(map[string]string),
|
||||
newMetadataVorbis(),
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -60,8 +57,7 @@ func ReadFLACTags(r io.ReadSeeker) (Metadata, error) {
|
||||
}
|
||||
|
||||
type metadataFLAC struct {
|
||||
c map[string]string // the vorbis comments
|
||||
p *Picture
|
||||
*metadataVorbis
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) {
|
||||
@@ -93,205 +89,6 @@ func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err er
|
||||
return
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) readVorbisComment(r io.Reader) error {
|
||||
vendorLen, err := readInt32LittleEndian(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 *metadataFLAC) 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 *metadataFLAC) Format() Format {
|
||||
func (m *metadataFLAC) FileType() FileType {
|
||||
return FLAC
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) 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 *metadataFLAC) Title() string {
|
||||
return m.c["title"]
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) 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 *metadataFLAC) Album() string {
|
||||
return m.c["album"]
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) AlbumArtist() string {
|
||||
// This field isn't included in the standard.
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) 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 *metadataFLAC) Genre() string {
|
||||
return m.c["genre"]
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) Year() int {
|
||||
// FIXME: try to parse the date in m.c["date"] to extract this
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) 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 *metadataFLAC) 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 *metadataFLAC) Lyrics() string {
|
||||
return m.c["lyrics"]
|
||||
}
|
||||
|
||||
func (m *metadataFLAC) Picture() *Picture {
|
||||
return m.p
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user