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 }