2015-03-19 13:21:53 +01:00
|
|
|
// Copyright 2015, David Howden
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2015-06-28 02:32:57 +02:00
|
|
|
// Package tag provides MP3 (ID3: v1, 2.2, 2.3 and 2.4), MP4, FLAC and OGG metadata detection,
|
|
|
|
// parsing and artwork extraction.
|
2015-06-28 03:25:07 +02:00
|
|
|
//
|
2015-06-28 02:32:57 +02:00
|
|
|
// Detect and parse tag metadata from an io.ReadSeeker (i.e. an *os.File):
|
2023-05-19 20:53:25 +02:00
|
|
|
//
|
|
|
|
// 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).
|
2015-03-19 13:21:53 +01:00
|
|
|
package tag
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2015-06-07 04:58:58 +02:00
|
|
|
"fmt"
|
2015-03-19 13:21:53 +01:00
|
|
|
"io"
|
2023-05-19 20:53:25 +02:00
|
|
|
"os"
|
|
|
|
"strings"
|
2015-03-19 13:21:53 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// ErrNoTagsFound is the error returned by ReadFrom when the metadata format
|
|
|
|
// cannot be identified.
|
|
|
|
var ErrNoTagsFound = errors.New("no tags found")
|
|
|
|
|
2023-05-19 20:53:25 +02:00
|
|
|
// Supported file types.
|
|
|
|
func AcceptedFileTypes() []FileType {
|
|
|
|
return []FileType{
|
|
|
|
FileType(strings.ToLower(string(MP3))),
|
|
|
|
FileType(strings.ToLower(string(M4A))),
|
|
|
|
FileType(strings.ToLower(string(M4B))),
|
|
|
|
FileType(strings.ToLower(string(M4P))),
|
|
|
|
FileType(strings.ToLower(string(ALAC))),
|
|
|
|
FileType(strings.ToLower(string(FLAC))),
|
|
|
|
FileType(strings.ToLower(string(OGG))),
|
|
|
|
FileType(strings.ToLower(string(DSF))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse metadata from the file at the given path
|
|
|
|
// for readonly operations
|
|
|
|
func Parse(path string) (Metadata, error) {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err == nil {
|
|
|
|
defer f.Close()
|
|
|
|
return ReadFrom(f)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-06-28 02:32:57 +02:00
|
|
|
// 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.
|
2015-04-14 16:06:32 +02:00
|
|
|
func ReadFrom(r io.ReadSeeker) (Metadata, error) {
|
2015-03-19 13:21:53 +01:00
|
|
|
b, err := readBytes(r, 11)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-04-02 03:06:41 +02:00
|
|
|
_, err = r.Seek(-11, io.SeekCurrent)
|
2015-06-07 04:58:58 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not seek back to original position: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-04-14 16:06:32 +02:00
|
|
|
switch {
|
2015-04-14 16:09:58 +02:00
|
|
|
case string(b[0:4]) == "fLaC":
|
|
|
|
return ReadFLACTags(r)
|
2015-04-14 16:06:32 +02:00
|
|
|
|
2015-05-19 21:56:05 +02:00
|
|
|
case string(b[0:4]) == "OggS":
|
|
|
|
return ReadOGGTags(r)
|
|
|
|
|
2015-12-28 21:34:48 +01:00
|
|
|
case string(b[4:8]) == "ftyp":
|
2015-04-14 16:06:32 +02:00
|
|
|
return ReadAtoms(r)
|
|
|
|
|
|
|
|
case string(b[0:3]) == "ID3":
|
|
|
|
return ReadID3v2Tags(r)
|
2018-11-04 23:57:29 +01:00
|
|
|
|
|
|
|
case string(b[0:4]) == "DSD ":
|
|
|
|
return ReadDSFTags(r)
|
2015-03-19 13:21:53 +01:00
|
|
|
}
|
|
|
|
|
2015-04-14 16:06:32 +02:00
|
|
|
m, err := ReadID3v1Tags(r)
|
2015-03-19 13:21:53 +01:00
|
|
|
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
|
|
|
|
|
2015-06-06 08:29:40 +02:00
|
|
|
// Supported tag formats.
|
2015-03-19 13:21:53 +01:00
|
|
|
const (
|
2015-07-02 15:07:17 +02:00
|
|
|
UnknownFormat Format = "" // Unknown Format.
|
2017-09-14 00:55:56 +02:00
|
|
|
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.
|
2015-05-24 02:44:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2015-06-06 08:29:40 +02:00
|
|
|
// Supported file types.
|
2015-05-24 02:44:45 +02:00
|
|
|
const (
|
2015-07-02 15:07:17 +02:00
|
|
|
UnknownFileType FileType = "" // Unknown FileType.
|
2017-09-14 00:55:56 +02:00
|
|
|
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
|
2018-11-04 23:57:29 +01:00
|
|
|
DSF FileType = "DSF" // DSF file DSD Sony format see https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
2015-03-19 13:21:53 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2015-05-24 02:44:45 +02:00
|
|
|
// FileType returns the file type of the audio file.
|
|
|
|
FileType() FileType
|
|
|
|
|
2015-03-19 13:21:53 +01:00
|
|
|
// 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
|
|
|
|
|
2015-05-24 14:25:04 +02:00
|
|
|
// Genre returns the genre of the track.
|
|
|
|
Genre() string
|
|
|
|
|
2015-03-19 13:21:53 +01:00
|
|
|
// 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)
|
|
|
|
|
2015-05-18 09:32:54 +02:00
|
|
|
// Picture returns a picture, or nil if not available.
|
2015-03-19 13:21:53 +01:00
|
|
|
Picture() *Picture
|
|
|
|
|
2015-05-18 09:32:54 +02:00
|
|
|
// Lyrics returns the lyrics, or an empty string if unavailable.
|
|
|
|
Lyrics() string
|
|
|
|
|
2018-11-04 21:56:00 +01:00
|
|
|
// Comment returns the comment, or an empty string if unavailable.
|
|
|
|
Comment() string
|
|
|
|
|
2015-03-19 13:21:53 +01:00
|
|
|
// 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{}
|
|
|
|
}
|