NCrusher74 / SwiftTaggerID3

A Swift library for reading and editing ID3 tags
Apache License 2.0
5 stars 2 forks source link

If you find this library helpful, please consider donating

SwiftTaggerID3

SwiftTaggerID3 is a Swift library for reading and writing ID3 tags in MP3 audio files.

Usage

Reading ID3 frames from a file

let mp3Url = URL(fileURLWithPath: "/path/to/file.mp3")
let mp3File = try Mp3File(location: mp3Url)
var tag = try mp3File.tag()

print(tag.album)
print(tag.artist)
print(tag.trackNumber)

Writing ID3 frames to a file

To add new frames to a file, or edit existing frames, read the tag as demonstrated above, and add/edit whatever frames you wish:

tag.album = "New Album Title"
tag.artist = "New Artist"
tag.trackNumber.disc = 1
tag.trackNumber.totalDiscs = 2

let outputUrl = URL(fileURLWithPath: "/path/to/new.mp3")
try mp3File.write(
    tagVersion: .v2_4,
    using: tag,
    writingTo: outputUrl)

If you wish to overwrite all metadata on the file and replace it with only your newly-created frames, initialize tag to an empty Tag() instance instead of reading from an mp3 file.

var tag = Tag()

tag.album = "Completely New Album Title"

To wipe the data from a particular frame, set it equal to nil:

var tag = try mp3File.tag()

tag.album = nil
tag.artist = nil
tag.trackNumber = nil

For some frames (such as Comment or userDefinedText), multiple versions of the frame are permitted in a tag (according to the ID3 spec.) In these cases, you can locate the specific frame using it's descriptionString as a subscript, or (for frames that also require an ISO-639-2 language code) the language code and the descriptionString:

print(tag["UserText"]) // "User Defined Text Content"
print(tag[userDefinedUrl: "UserURL"]) // "http://userdefined.url"
print(tag[comment: "Comment", .eng]) // "Comment Content"
print(tag[lyrics: "Lyrics", .eng]) // "Lyrics Content"

Writing to these frames works the same way. NOTE The Comment and UnsynchronizedLyrics frames permit the use of newline characters.

tag["UserText"] = "User Defined Text Content"
tag[userDefinedUrl: "UserURL"] = "http://userdefined.url"
tag[comment: "Comment", .eng] = """Comment Content"""
tag[lyrics: "Lyrics", .eng] = """Lyrics Content"""

To overwrite an existing frame with a subscript accessor, just use the same descriptionString and language (if applicable). To remove an existing frame of this type, access it using its removal function:

tag["UserText"] = nil
tag[userDefinedUrl: "UserURL"] = nil
tag[comment: "Comment", .eng] = nil
tag[lyrics: "Lyrics", .eng] = nil

Involved People and Musician Credit List frames

Data in the InvolvedPeopleList and MusicianCreditsList frames is available as a dictionary, [role: [person]] where role is whatever part or function is being performed, and [person] is an array of the people performing it.

To return the entire dictionary:

print(tag.musicianCreditsList)
print(tag.involvedPeopleList)

To access information for a particular role:

print(tag.involvedPeopleList?[.producer]) // ["Producer Name", "Coproducer Name"]
print(tag.musicianCreditsList?[.singer]) // ["Singer Name"]

To clear all the values from the InvolvedPeopleList or MusicianCreditsList frames:

tag.clearInvolvedPeopleList()
tag.clearMusicianCreditsList()

To clear the [person] array for a specific role:

tag.clearInvolvedPeopleForRole(role: .director)
tag.clearMusicianCreditsForRole(role: .guitarist)

Chapter Frames

To retrieve a list of all the chapters in the file, use the chapterList property.

print(tag.chapterList)

This will return an array of ( startTime, chapterTitle) pairs, where the startTime is in milliseconds.

To access the data of a specific chapter, use its index in the chapterList array:

print(tag.chapterList[0].startTime) // 0
print(tag.chapterList[0].title) // "Chapter 01"

To add a chapter, use the addChapter(at startTime: Int, title: String) function. The startTime must be in milliseconds. If a chapter exists at the specified startTime, it will be overwritten. Otherwise, new chapters will be added to any existing chapters:

tag.addChapter(at: 1000, title: "Chapter 02") // start time in milliseconds

To remove a single chapter frame from the tag:

tag.removeChapter(at: startTime)

To wipe all chapters frames from the tag:

tag.removechapterList()

Other Frames

You can export the images from the AttachedPicture frames using their optional descriptionString as a subscript, but honestly it'd be just as easy to get them using AVFoundation:

let outputURL = URL(fileURLWithPath: "/destination/path/for/image.jpg")
let coverImageData = tag[attachedPicture: "SampleCover"]
try coverImageData?.write(to: outputURL)

Unknown or unhandled frames are assigned a UUID that may be used in a similar fashion to a descriptionString.

Here's a complete list of the frames handled by SwiftTaggerID3:

For ID3 version 2.4 only:

For ID3 versions 2.3 and 2.4 only:

For ID3 versions 2.2 and 2.3 only:

A note on ID3 specification compliance SwiftTaggerID3 tries to stick pretty close to the requirements of the documented specs, but there are a few places where it deviates, either because the spec is silly, or compliance would be more cumbersome to achieve can be justified by the author's needs, or compliance would make the usage of SwiftTaggerID3 too convoluted. These deviations are:

Known Issues SwiftTaggerID3 will not convert one version tag to another version. When altering tags on a file that already has a tag, the output tag will be generated in the same version as the input tag.

If you wish to add these missing features or tags, while keeping the usage user-friendly, the author will welcome pull requests.