github.com/boki/go-xmp@v1.0.1/models/id3/model.go (about)

     1  // Copyright (c) 2017-2018 Alexander Eichhorn
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // ID3 v2.2, v2.3 & v2.4
    16  // http://id3.org/
    17  
    18  // TODO:
    19  // - almost all TextUnmarshalers are unimplemented due to missing samples
    20  // - embedded XMP may be carried inside a PRIV frame with XMP owner identifier
    21  // - multi-language text fields (requires samples)
    22  // - multi-lang comments using CommentArray type (requires samples)
    23  
    24  // FIXME: Version 2.4 of the specification prescribes that all text fields
    25  // (the fields that start with a T, except for TXXX) can contain multiple
    26  // values separated by a null character. The null character varies by
    27  // character encoding.
    28  
    29  // Package id3 implements the ID3v2.4 metadata standard for audio files.
    30  package id3
    31  
    32  import (
    33  	"fmt"
    34  	"strings"
    35  	"time"
    36  
    37  	"trimmer.io/go-xmp/models/dc"
    38  	"trimmer.io/go-xmp/models/xmp_base"
    39  	"trimmer.io/go-xmp/models/xmp_dm"
    40  	"trimmer.io/go-xmp/models/xmp_rights"
    41  	"trimmer.io/go-xmp/xmp"
    42  )
    43  
    44  var (
    45  	NsID3 = xmp.NewNamespace("id3", "http://id3.org/ns/2.4/", NewModel)
    46  )
    47  
    48  func init() {
    49  	xmp.Register(NsID3, xmp.MusicMetadata)
    50  }
    51  
    52  func NewModel(name string) xmp.Model {
    53  	if name == "id3" {
    54  		return &ID3{}
    55  	}
    56  	return nil
    57  }
    58  
    59  func MakeModel(d *xmp.Document) (*ID3, error) {
    60  	m, err := d.MakeModel(NsID3)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	x, _ := m.(*ID3)
    65  	return x, nil
    66  }
    67  
    68  func FindModel(d *xmp.Document) *ID3 {
    69  	if m := d.FindModel(NsID3); m != nil {
    70  		return m.(*ID3)
    71  	}
    72  	return nil
    73  }
    74  
    75  func (m *ID3) Namespaces() xmp.NamespaceList {
    76  	return xmp.NamespaceList{NsID3}
    77  }
    78  
    79  func (m *ID3) Can(nsName string) bool {
    80  	return nsName == NsID3.GetName()
    81  }
    82  
    83  func (x *ID3) SyncModel(d *xmp.Document) error {
    84  	return nil
    85  }
    86  
    87  func (x *ID3) SyncFromXMP(d *xmp.Document) error {
    88  	if m := dc.FindModel(d); m != nil {
    89  		if len(m.Title) > 0 {
    90  			x.TitleDescription = m.Title.Default()
    91  		}
    92  		if len(m.Rights) > 0 {
    93  			x.Copyright = m.Rights.Default()
    94  		}
    95  	}
    96  	if base := xmpbase.FindModel(d); base != nil {
    97  		if !base.CreateDate.IsZero() {
    98  			x.RecordingTime = base.CreateDate
    99  		}
   100  	}
   101  	if dm := xmpdm.FindModel(d); dm != nil {
   102  		if dm.Artist != "" {
   103  			x.LeadPerformer = dm.Artist
   104  		}
   105  		if dm.Album != "" {
   106  			x.AlbumTitle = dm.Album
   107  		}
   108  		if dm.LogComment != "" {
   109  			x.Comments = dm.LogComment
   110  		}
   111  		if dm.Genre != "" {
   112  			if err := x.ContentType.UnmarshalText([]byte(dm.Genre)); err != nil {
   113  				return err
   114  			}
   115  		}
   116  		if dm.TrackNumber > 0 {
   117  			x.TrackNumber = TrackNum{
   118  				Track: dm.TrackNumber,
   119  			}
   120  		}
   121  		if dm.PartOfCompilation.Value() {
   122  			x.IsCompilation = True
   123  		} else {
   124  			x.IsCompilation = False
   125  		}
   126  		if dm.Composer != "" {
   127  			x.Composer = dm.Composer
   128  		}
   129  		if dm.Engineer != "" {
   130  			x.ModifiedBy = dm.Engineer
   131  		}
   132  		if dm.DiscNumber > 0 {
   133  			x.PartOfASet = TrackNum{
   134  				Track: dm.DiscNumber,
   135  			}
   136  		}
   137  		if dm.Lyrics != "" {
   138  			x.UnsynchronizedLyrics = strings.Split(dm.Lyrics, "\n")
   139  		}
   140  	}
   141  	if rights := xmprights.FindModel(d); rights != nil {
   142  		if rights.WebStatement != "" {
   143  			x.CopyrightInformation = xmp.Url(rights.WebStatement)
   144  		}
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (x ID3) SyncToXMP(d *xmp.Document) error {
   151  	// pick default language for multi-lang XMP properties
   152  	lang := x.Language
   153  
   154  	// DC gets title (TIT2) and copyright (TCOP)
   155  	if x.TitleDescription != "" || x.Copyright != "" {
   156  		m, err := dc.MakeModel(d)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		if x.TitleDescription != "" && len(m.Title) == 0 {
   161  			m.Title.AddDefault(lang, x.TitleDescription)
   162  		}
   163  		if x.Copyright != "" && len(m.Rights) == 0 {
   164  			m.Rights.AddDefault(lang, x.Copyright)
   165  		}
   166  	}
   167  
   168  	// XMP base gets creation date
   169  	if x.RecordingTime.IsZero() && !x.Date_v23.IsZero() && !x.Time_v23.IsZero() {
   170  		_, mm, dd := x.Date_v23.Value().Date()
   171  		h, m, s := x.Time_v23.Value().Clock()
   172  		x.RecordingTime = xmp.Date(time.Date(x.Year_v23, mm, dd, h, m, s, 0, nil))
   173  	}
   174  	if !x.RecordingTime.IsZero() {
   175  		base, err := xmpbase.MakeModel(d)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		base.CreateDate = x.RecordingTime
   180  	}
   181  
   182  	// XmpDM gets always created
   183  	dm, err := xmpdm.MakeModel(d)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	if x.Comments != "" {
   188  		dm.LogComment = x.Comments
   189  	}
   190  	if x.AlbumTitle != "" {
   191  		dm.Album = x.AlbumTitle
   192  	}
   193  	dm.PartOfCompilation = xmp.Bool(x.IsCompilation.Value())
   194  	if x.Composer != "" {
   195  		dm.Composer = x.Composer
   196  	}
   197  	if len(x.ContentType) > 0 {
   198  		dm.Genre = x.ContentType.String()
   199  	}
   200  	if x.LeadPerformer != "" {
   201  		dm.Artist = x.LeadPerformer
   202  	}
   203  	if x.ModifiedBy != "" {
   204  		dm.Engineer = x.ModifiedBy
   205  	}
   206  	if !x.PartOfASet.IsZero() {
   207  		dm.DiscNumber = x.PartOfASet.Track
   208  	}
   209  	if !x.TrackNumber.IsZero() {
   210  		dm.TrackNumber = x.TrackNumber.Track
   211  	}
   212  	if len(x.UnsynchronizedLyrics) > 0 {
   213  		dm.Lyrics = strings.Join(x.UnsynchronizedLyrics, "\n")
   214  	}
   215  	// XmpRights gets copyright
   216  	if x.CopyrightInformation != "" {
   217  		rights, err := xmprights.MakeModel(d)
   218  		if err != nil {
   219  			return err
   220  		}
   221  		rights.WebStatement = string(x.CopyrightInformation)
   222  	}
   223  	return nil
   224  }
   225  
   226  type ID3 struct {
   227  	AudioEncryption                      *AudioEncryption        `id3:"AENC"        xmp:"id3:audioEncryption"`                      // AENC Audio encryption
   228  	AttachedPicture                      AttachedPictureArray    `id3:"APIC"        xmp:"id3:attachedPicture"`                      // APIC Attached picture
   229  	AudioSeekPointIndex                  *AudioSeekIndex         `id3:"ASPI,v2.4+"  xmp:"id3:audioSeekPointIndex,v2.4+"`            // ASPI Audio seek point index
   230  	Comments                             string                  `id3:"COMM"        xmp:"id3:comments"`                             // COMM Comments
   231  	Commercial                           *Commercial             `id3:"COMR"        xmp:"id3:commercial"`                           // COMR Commercial frame
   232  	Encryption                           EncryptionMethodArray   `id3:"ENCR"        xmp:"id3:encryption"`                           // ENCR Encryption method registration
   233  	Equalization_v23                     string                  `id3:"EQUA,v2.3-"  xmp:"id3:equalization_v23,v2.3-"`               // EQUA Equalization
   234  	Equalization                         EqualizationList        `id3:"EQU2,v2.4+"  xmp:"id3:equalization,v2.4+"`                   // EQU2 Equalisation (2)
   235  	EventTimingCodes                     MarkerList              `id3:"ETCO"        xmp:"id3:eventTimingCodes"`                     // ETCO Event timing codes
   236  	GeneralEncapsulatedObject            EncapsulatedObjectArray `id3:"GEOB"        xmp:"id3:generalEncapsulatedObject"`            // GEOB General encapsulated object
   237  	GroupIdentifier                      GroupArray              `id3:"GRID"        xmp:"id3:groupIdentifier"`                      // GRID Group identification registration
   238  	InvolvedPeopleList_v23               string                  `id3:"IPLS,v2.3-"  xmp:"id3:involvedPeopleList_v23,v2.3-"`         // IPLS Involved people list
   239  	Link                                 LinkArray               `id3:"LINK"        xmp:"id3:link"`                                 // LINK Linked information
   240  	MusicCDIdentifier                    []byte                  `id3:"MCDI"        xmp:"id3:musicCDIdentifier"`                    // MCDI Music CD identifier
   241  	MPEGLocationLookupTable              []byte                  `id3:"MLLT"        xmp:"id3:mpegLocationLookupTable"`              // MLLT MPEG location lookup table
   242  	Ownership                            *Owner                  `id3:"OWNE"        xmp:"id3:ownership"`                            // OWNE Ownership frame
   243  	Private                              PrivateDataArray        `id3:"PRIV"        xmp:"id3:private"`                              // PRIV Private frame
   244  	PlayCounter                          int64                   `id3:"PCNT"        xmp:"id3:playCounter"`                          // PCNT Play counter
   245  	Popularimeter                        PopularimeterArray      `id3:"POPM"        xmp:"id3:popularimeter"`                        // POPM Popularimeter
   246  	PositionSynchronization              *PositionSync           `id3:"POSS"        xmp:"id3:positionSynchronization"`              // POSS Position synchronisation frame
   247  	RecommendedBufferSize                *BufferSize             `id3:"RBUF"        xmp:"id3:recommendedBufferSize"`                // RBUF Recommended buffer size
   248  	RelativeVolumeAdjustment_v23         string                  `id3:"RVAD,v2.3-"  xmp:"id3:relativeVolumeAdjustment_v23,v2.3-"`   // RVAD Relative volume adjustment
   249  	RelativeVolumeAdjustment             VolumeAdjustArray       `id3:"RVA2,v2.4+"  xmp:"id3:relativeVolumeAdjustment,v2.4+"`       // RVA2 Relative volume adjustment (2)
   250  	Reverb                               *Reverb                 `id3:"RVRB"        xmp:"id3:reverb"`                               // RVRB Reverb
   251  	Seek                                 int64                   `id3:"SEEK,v2.4+"  xmp:"id3:seek,v2.4+"`                           // SEEK Seek frame
   252  	Signature                            SignatureArray          `id3:"SIGN,v2.4+"  xmp:"id3:signature,v2.4+"`                      // SIGN Signature frame
   253  	SynchronizedLyrics                   TimedLyricsArray        `id3:"SYLT"        xmp:"id3:synchronizedLyrics"`                   // SYLT Synchronized lyric/text
   254  	SynchronizedTempoCodes               []byte                  `id3:"SYTC"        xmp:"id3:synchronizedTempoCodes"`               // SYTC Synchronized tempo codes
   255  	AlbumTitle                           string                  `id3:"TALB"        xmp:"id3:albumTitle"`                           // TALB Album/Movie/Show title
   256  	BeatsPerMinute                       int                     `id3:"TBPM"        xmp:"id3:beatsPerMinute"`                       // TBPM BPM (beats per minute)
   257  	Composer                             string                  `id3:"TCOM"        xmp:"id3:composer"`                             // TCOM Composer
   258  	ContentType                          Genre                   `id3:"TCON"        xmp:"id3:contentType"`                          // TCON Content type
   259  	Copyright                            string                  `id3:"TCOP"        xmp:"id3:copyright"`                            // TCOP Copyright message
   260  	Date_v23                             Date23                  `id3:"TDAT,v2.3-"  xmp:"id3:date_v23,v2.3-"`                       // TDAT Date
   261  	EncodingTime                         xmp.Date                `id3:"TDEN,v2.4+"  xmp:"id3:encodingTime,v2.4+"`                   // TDEN Encoding time
   262  	PlaylistDelay                        int64                   `id3:"TDLY"        xmp:"id3:playlistDelay"`                        // TDLY Playlist delay
   263  	OriginalReleaseTime                  xmp.Date                `id3:"TDOR,v2.4+"  xmp:"id3:originalReleaseTime,v2.4+"`            // TDOR Original release time
   264  	RecordingTime                        xmp.Date                `id3:"TDRC,v2.4+"  xmp:"id3:recordingTime,v2.4+"`                  // TDRC Recording time
   265  	ReleaseTime                          xmp.Date                `id3:"TDRL,v2.4+"  xmp:"id3:releaseTime,v2.4+"`                    // TDRL Release time
   266  	TaggingTime                          xmp.Date                `id3:"TDTG,v2.4+"  xmp:"id3:taggingTime,v2.4+"`                    // TDTG Tagging time
   267  	EncodedBy                            string                  `id3:"TENC"        xmp:"id3:encodedBy"`                            // TENC Encoded by
   268  	Lyricist                             string                  `id3:"TEXT"        xmp:"id3:lyricist"`                             // TEXT Lyricist/Text writer
   269  	FileType                             string                  `id3:"TFLT"        xmp:"id3:fileType"`                             // TFLT File type
   270  	Time_v23                             Time23                  `id3:"TIME,v2.3-"  xmp:"id3:time_v23,v2.3-"`                       // TIME Time
   271  	InvolvedPeopleList                   string                  `id3:"TIPL,v2.4+"  xmp:"id3:involvedPeopleList,v2.4+"`             // TIPL Involved people list
   272  	ContentGroupDescription              string                  `id3:"TIT1"        xmp:"id3:contentGroupDescription"`              // TIT1 Content group description
   273  	TitleDescription                     string                  `id3:"TIT2"        xmp:"id3:titleDescription"`                     // TIT2 Title/songname/content description
   274  	SubTitle                             string                  `id3:"TIT3"        xmp:"id3:subTitle"`                             // TIT3 Subtitle/Description refinement
   275  	InitialKey                           string                  `id3:"TKEY"        xmp:"id3:initialKey"`                           // TKEY Initial key
   276  	Language                             string                  `id3:"TLAN"        xmp:"id3:language"`                             // TLAN Language(s)
   277  	Length                               string                  `id3:"TLEN"        xmp:"id3:length"`                               // TLEN Length
   278  	MusicianCreditsList                  string                  `id3:"TMCL,v2.4+"  xmp:"id3:musicianCreditsList,v2.4+"`            // TMCL Musician credits list
   279  	MediaType                            string                  `id3:"TMED"        xmp:"id3:mediaType"`                            // TMED Media type
   280  	Mood                                 string                  `id3:"TMOO,v2.4+"  xmp:"id3:mood,v2.4+"`                           // TMOO Mood
   281  	OriginalAlbumTitle                   string                  `id3:"TOAL"        xmp:"id3:originalAlbumTitle"`                   // TOAL Original album/movie/show title
   282  	OriginalFilename                     string                  `id3:"TOFN"        xmp:"id3:originalFilename"`                     // TOFN Original filename
   283  	OriginalLyricist                     string                  `id3:"TOLY"        xmp:"id3:originalLyricist"`                     // TOLY Original lyricist(s)/text writer(s)
   284  	OriginalArtist                       string                  `id3:"TOPE"        xmp:"id3:originalArtist"`                       // TOPE Original artist(s)/performer(s)
   285  	OriginalReleaseYear_v23              int                     `id3:"TORY,v2.3-"  xmp:"id3:originalReleaseYear_v23,v2.3-"`        // TORY Original release year
   286  	FileOwner                            string                  `id3:"TOWN"        xmp:"id3:fileOwner"`                            // TOWN File owner/licensee
   287  	LeadPerformer                        string                  `id3:"TPE1"        xmp:"id3:leadPerformer"`                        // TPE1 Lead performer(s)/Soloist(s)
   288  	Band                                 string                  `id3:"TPE2"        xmp:"id3:band"`                                 // TPE2 Band/orchestra/accompaniment
   289  	Conductor                            string                  `id3:"TPE3"        xmp:"id3:conductor"`                            // TPE3 Conductor/performer refinement
   290  	ModifiedBy                           string                  `id3:"TPE4"        xmp:"id3:modifiedBy"`                           // TPE4 Interpreted, remixed, or otherwise modified by
   291  	PartOfASet                           TrackNum                `id3:"TPOS"        xmp:"id3:partOfASet"`                           // TPOS Part of a set
   292  	ProducedNotice                       string                  `id3:"TPRO,v2.4+"  xmp:"id3:producedNotice,v2.4+"`                 // TPRO Produced notice
   293  	Publisher                            string                  `id3:"TPUB"        xmp:"id3:publisher"`                            // TPUB Publisher
   294  	TrackNumber                          TrackNum                `id3:"TRCK"        xmp:"id3:trackNumber"`                          // TRCK Track number/Position in set
   295  	RecordingDates                       string                  `id3:"TRDA"        xmp:"id3:recordingDates"`                       // TRDA Recording dates
   296  	InternetRadioStationName             string                  `id3:"TRSN"        xmp:"id3:internetRadioStationName"`             // TRSN Internet radio station name
   297  	InternetRadioStationOwner            string                  `id3:"TRSO"        xmp:"id3:internetRadioStationOwner"`            // TRSO Internet radio station owner
   298  	Size_v23                             string                  `id3:"TSIZ,v2.3-"  xmp:"id3:size_v23,v2.3-"`                       // TSIZ Size
   299  	AlbumSortOrder                       string                  `id3:"TSOA,v2.4+"  xmp:"id3:albumSortOrder,v2.4+"`                 // TSOA Album sort order
   300  	PerformerSortOrder                   string                  `id3:"TSOP,v2.4+"  xmp:"id3:performerSortOrder,v2.4+"`             // TSOP Performer sort order
   301  	TitleSortOrder                       string                  `id3:"TSOT,v2.4+"  xmp:"id3:titleSortOrder,v2.4+"`                 // TSOT Title sort order
   302  	InternationalStandardRecordingCode   string                  `id3:"TSRC"        xmp:"id3:internationalStandardRecordingCode"`   // TSRC ISRC (international standard recording code)
   303  	EncodedWith                          string                  `id3:"TSSE"        xmp:"id3:encodedWith"`                          // TSSE Software/Hardware and settings used for encoding
   304  	SetSubtitle                          string                  `id3:"TSST,v2.4+"  xmp:"id3:setSubtitle,v2.4+"`                    // TSST Set subtitle
   305  	Year_v23                             int                     `id3:"TYER,v2.3-"  xmp:"id3:year_v23,v2.3-"`                       // TYER Year
   306  	UserText                             string                  `id3:"TXXX"        xmp:"id3:userText"`                             // TXXX User defined text information frame
   307  	UniqueFileIdentifier                 xmp.Uri                 `id3:"UFID"        xmp:"id3:uniqueFileIdentifier"`                 // UFID Unique file identifier
   308  	TermsOfUse                           xmp.StringList          `id3:"USER"        xmp:"id3:termsOfUse"`                           // USER Terms of use
   309  	UnsynchronizedLyrics                 xmp.StringList          `id3:"USLT"        xmp:"id3:unsynchronizedLyrics"`                 // USLT Unsynchronized lyric/text transcription
   310  	CommercialInformation                xmp.Url                 `id3:"WCOM"        xmp:"id3:commercialInformation"`                // WCOM Commercial information
   311  	CopyrightInformation                 xmp.Url                 `id3:"WCOP"        xmp:"id3:copyrightInformation"`                 // WCOP Copyright/Legal information
   312  	OfficialAudioFileWebpage             xmp.Url                 `id3:"WOAF"        xmp:"id3:officialAudioFileWebpage"`             // WOAF Official audio file webpage
   313  	OfficialArtistWebpage                xmp.Url                 `id3:"WOAR"        xmp:"id3:officialArtistWebpage"`                // WOAR Official artist/performer webpage
   314  	OfficialAudioSourceWebpage           xmp.Url                 `id3:"WOAS"        xmp:"id3:officialAudioSourceWebpage"`           // WOAS Official audio source webpage
   315  	OfficialInternetRadioStationHomepage xmp.Url                 `id3:"WORS"        xmp:"id3:officialInternetRadioStationHomepage"` // WORS Official Internet radio station homepage
   316  	Payment                              xmp.Url                 `id3:"WPAY"        xmp:"id3:payment"`                              // WPAY Payment
   317  	OfficialPublisherWebpage             xmp.Url                 `id3:"WPUB"        xmp:"id3:officialPublisherWebpage"`             // WPUB Publishers official webpage
   318  	UserURL                              xmp.Url                 `id3:"WXXX"        xmp:"id3:userURL"`                              // WXXX User defined URL link frame
   319  	AccessibilityText                    string                  `id3:"ATXT"        xmp:"id3:accessibilityText"`                    // accessibility addendum http://id3.org/id3v2-accessibility-1.0
   320  	Chapters                             ChapterList             `id3:"CHAP"        xmp:"id3:chapters"`                             // chapters addedum http://id3.org/id3v2-chapters-1.0
   321  	TableOfContents                      TocEntryList            `id3:"CTOC"        xmp:"id3:tableOfContents"`                      // chapters addedum http://id3.org/id3v2-chapters-1.0
   322  	AlbumArtistSortOrder                 string                  `id3:"TSO2"        xmp:"id3:albumArtistSortOrder"`                 // special iTunes tags
   323  	ComposerSortOrder                    string                  `id3:"TSOC"        xmp:"id3:composerSortOrder"`
   324  	IsCompilation                        Bool                    `id3:"TCMP"        xmp:"id3:isCompilation"`
   325  	ITunesU                              Bool                    `id3:"ITNU"        xmp:"id3:iTunesU"`
   326  	IsPodcast                            Bool                    `id3:"PCST"        xmp:"id3:isPodcast"`
   327  	PodcastDescription                   string                  `id3:"TDES"        xmp:"id3:podcastDescription"`
   328  	PodcastID                            string                  `id3:"TGID"        xmp:"id3:podcastID"`
   329  	PodcastURL                           string                  `id3:"WFED"        xmp:"id3:podcastURL"`
   330  	PodcastKeywords                      string                  `id3:"TKWD"        xmp:"id3:podcastKeywords"`
   331  	PodcastCategory                      string                  `id3:"TCAT"        xmp:"id3:podcastCategory"`
   332  	Extension                            xmp.TagList             `id3:",any"        xmp:"id3:extension"`
   333  }
   334  
   335  var V11_TO_V24_TAGS map[string]string = map[string]string{
   336  	"0x0003": "TIT2", // title
   337  	"0x0021": "TPE1", // artist
   338  	"0x003f": "TALB", // album
   339  	"0x005d": "TYER", // year
   340  	"0x0061": "COMM", // comment
   341  	"0x007d": "TRCK", // track
   342  	"0x007f": "TCON", // genre
   343  }
   344  
   345  var V22_TO_V24_TAGS map[string]string = map[string]string{
   346  	"BUF": "RBUF", //   Recommended buffer size
   347  	"CNT": "PCNT", //   Play counter
   348  	"COM": "COMM", //   Comments
   349  	"CRA": "AENC", //   Audio encryption
   350  	"CRM": "ENCR", //   Encrypted meta frame
   351  	"ETC": "ETCO", //   Event timing codes
   352  	"EQU": "EQU2", //   Equalization
   353  	"GEO": "GEOB", //   General encapsulated object
   354  	"IPL": "TIPL", //   Involved people list
   355  	"LNK": "LINK", //   Linked information
   356  	"MCI": "MCDI", //   Music CD Identifier
   357  	"MLL": "MLLT", //   MPEG location lookup table
   358  	"PIC": "APIC", //   Attached picture
   359  	"POP": "POPM", //   Popularimeter
   360  	"REV": "RVRB", //   Reverb
   361  	"RVA": "RVA2", //   Relative volume adjustment
   362  	"SLT": "SYLT", //   Synchronized lyric/text
   363  	"STC": "SYTC", //   Synced tempo codes
   364  	"TAL": "TALB", //   Album/Movie/Show title
   365  	"TBP": "TBPM", //   BPM (Beats Per Minute)
   366  	"TCM": "TCOM", //   Composer
   367  	"TCO": "TMED", //   Content type
   368  	"TCR": "TCOP", //   Copyright message
   369  	"TDA": "TDRC", // # Date, arguably this could also be release date TDRL
   370  	"TDY": "TDLY", //   Playlist delay
   371  	"TEN": "TENC", //   Encoded by
   372  	"TFT": "TFLT", //   File type
   373  	"TIM": "TDRC", // # Time, arguably this could also be release date TDRL
   374  	"TKE": "TKEY", //   Initial key
   375  	"TLA": "TLAN", //   Language(s)
   376  	"TLE": "TLEN", //   Length
   377  	"TMT": "TMED", //   Media type
   378  	"TOA": "TOPE", //   Original artist(s)/performer(s)
   379  	"TOF": "TOFN", //   Original filename
   380  	"TOL": "TOLY", //   Original Lyricist(s)/text writer(s)
   381  	"TOR": "TORY", //   Original release year (v2.3-)
   382  	"TOT": "TOAL", //   Original album/Movie/Show title
   383  	"TP1": "TPE1", //   Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
   384  	"TP2": "TPE2", //   Band/Orchestra/Accompaniment
   385  	"TP3": "TPE3", //   Conductor/Performer refinement
   386  	"TP4": "TPE4", //   Interpreted, remixed, or otherwise modified by
   387  	"TPA": "TPOS", //   Part of a set
   388  	"TPB": "TPUB", //   Publisher
   389  	"TRC": "TSRC", //   ISRC (International Standard Recording Code)
   390  	"TRD": "TDRC", //   Recording dates
   391  	"TRK": "TRCK", //   Track number/Position in set
   392  	"TSI": "TSIZ", //   Size (v2.3-)
   393  	"TSS": "TSSE", //   Software/hardware and settings used for encoding
   394  	"TT1": "TIT1", //   Content group description
   395  	"TT2": "TIT2", //   Title/Songname/Content description
   396  	"TT3": "TIT3", //   Subtitle/Description refinement
   397  	"TXT": "TEXT", //   Lyricist/text writer
   398  	"TXX": "TXXX", //   User defined text information frame
   399  	"TYE": "TYER", //   Year (v2.3-)
   400  	"UFI": "UFID", //   Unique file identifier
   401  	"ULT": "USLT", //   Unsychronized lyric/text transcription
   402  	"WAF": "WOAF", //   Official audio file webpage
   403  	"WAR": "WOAR", //   Official artist/performer webpage
   404  	"WAS": "WOAS", //   Official audio source webpage
   405  	"WCM": "WCOM", //   Commercial information
   406  	"WCP": "WCOP", //   Copyright/Legal information
   407  	"WPB": "WPUB", //   Publishers official webpage
   408  	"WXX": "WXXX", //   User defined URL link frame
   409  	"TCP": "TCMP", //   iTunes compilation
   410  	"TST": "TSOT", //   iTunes title sort order
   411  	"TS2": "TSO2", //   iTunes album_artist_sort
   412  	"TSA": "TSOA", //   iTunes album_sort
   413  	"TSP": "TSOP", //   iTunes artist_sort
   414  	"TSC": "TSOC", //   iTunes composer_sort
   415  }
   416  
   417  // tag can be a four letter v2.3/v2.4 code or a 3 letter v2.2 code
   418  func mapTagToV24(tag string) (string, error) {
   419  	if len(tag) > 4 {
   420  		if t, ok := V11_TO_V24_TAGS[tag]; !ok {
   421  			return tag, fmt.Errorf("id3: unknown v1 tag '%s'", tag)
   422  		} else {
   423  			tag = t
   424  		}
   425  	} else if len(tag) < 4 {
   426  		if t, ok := V22_TO_V24_TAGS[tag]; !ok {
   427  			return tag, fmt.Errorf("id3: unknown v2.2 tag '%s'", tag)
   428  		} else {
   429  			tag = t
   430  		}
   431  	}
   432  	return tag, nil
   433  }
   434  
   435  func (x *ID3) CanTag(tag string) bool {
   436  	t, err := mapTagToV24(tag)
   437  	if err != nil {
   438  		return false
   439  	}
   440  	_, err = xmp.GetNativeField(x, t)
   441  	return err == nil
   442  }
   443  
   444  func (x *ID3) GetTag(tag string) (string, error) {
   445  	t, err := mapTagToV24(tag)
   446  	if err != nil {
   447  		return "", err
   448  	}
   449  	if v, err := xmp.GetNativeField(x, t); err != nil {
   450  		return "", fmt.Errorf("%s: %v", NsID3.GetName(), err)
   451  	} else {
   452  		return v, nil
   453  	}
   454  }
   455  
   456  func (x *ID3) SetTag(tag, value string) error {
   457  	t, err := mapTagToV24(tag)
   458  	if err != nil {
   459  		return err
   460  	}
   461  	if err := xmp.SetNativeField(x, t, value); err != nil {
   462  		return fmt.Errorf("%s: %v", NsID3.GetName(), err)
   463  	}
   464  	return nil
   465  }
   466  
   467  // Returns tag value as string for the selected language. Falls back to
   468  // default language when lang is empty.
   469  func (x *ID3) GetLocaleTag(lang string, tag string) (string, error) {
   470  	t, err := mapTagToV24(tag)
   471  	if err != nil {
   472  		return "", err
   473  	}
   474  	if val, err := xmp.GetLocaleField(x, lang, t); err != nil {
   475  		return "", fmt.Errorf("%s: %v", NsID3.GetName(), err)
   476  	} else {
   477  		return val, nil
   478  	}
   479  }
   480  
   481  func (x *ID3) SetLocaleTag(lang string, tag, value string) error {
   482  	if err := xmp.SetLocaleField(x, lang, tag, value); err != nil {
   483  		return fmt.Errorf("%s: %v", NsID3.GetName(), err)
   484  	}
   485  	return nil
   486  }
   487  
   488  // Lists all non-empty tags.
   489  func (x *ID3) ListTags() (xmp.TagList, error) {
   490  	if l, err := xmp.ListNativeFields(x); err != nil {
   491  		return nil, fmt.Errorf("%s: %v", NsID3.GetName(), err)
   492  	} else {
   493  		return l, nil
   494  	}
   495  }