Giter Club home page Giter Club logo

chicio / id3tageditor Goto Github PK

View Code? Open in Web Editor NEW
225.0 10.0 47.0 25.63 MB

:musical_note::guitar:A Swift library to read and write ID3 Tag of any mp3 file. Supported ID3 tag version: 2.2, 2.3 and 2.4. Supported platform: iOS, macOS, tvOS, watchOS, Linux Ubuntu. :musical_note::guitar:

Home Page: https://id3tageditor.fabrizioduroni.it

License: MIT License

Swift 99.15% Objective-C 0.06% Ruby 0.24% Shell 0.55%
id3 id3v2 id3v2-tag id3-writer id3-reader id3-parser mp3 mp3tag mp3-tags swift

id3tageditor's Introduction

ID3TagEditor

Build iOS Build macOS Build watchOS Build tvOS Build Linux SwiftLint codebeat badge codecov CocoaPods swift package index platforms swift package index swift version GitHub license

A swift library to read and modify ID3 Tag of any mp3 file. Listed in the implementations section of the official ID3 standard website id3.org.

ID3TagEditor: A swift library to read and modify ID3 Tag of any mp3 file


Installation

There are four ways to install ID3TagEditor in your project:

  • manual installation
  • framework
  • cocoapods
  • Swift Package Manager (with support for linux platform)

Manual installation

To manually install ID3TagEditor simply drag and drop all the file contained in the Source folder inside your project (except for the info.plist file).

Framework

ID3TagEditor is also available as a framework. You can follow the standard procedure to install a custom Cocoa Touch framework. Drag the ID3TagEditor.xcodeproj inside your project and add it to the Embedded Binaries/Linked Frameworks and Libraries section of your project. See the demo project for a complete example of the setup of the framework.

CocoaPods

ID3TagEditor is also available as a pod on CocoaPods. Add the dependency to your Podfile (choose the release version you prefer):

target 'MyApp' do
	pod 'ID3TagEditor', '~> 4.0'
end

and then run pod install (or pod update).

Swift Package Manager

ID3TagEditor is also available as a Swift Package for the Swift Package Manager. Add it to your dependecies in your Package.swift file. After that you can build your project with the command swift build, and eventually run you project (if it is an executable target) with the command swift run. If you want you can also run tests using swift test.

// swift-tools-version:5.0

import PackageDescription

let package = Package(
    name: "Demo Ubuntu",
    dependencies: [
        .package(url: "https://github.com/chicio/ID3TagEditor.git", from: "4.0.0")
    ],
    targets: [
        .target(
            name: "Demo Ubuntu",
            dependencies: ["ID3TagEditor"]
        )
    ]
)

Usage

ID3TagEditor is compatible with the following platforms:

  • iOS
  • MacOS
  • Apple Watch
  • Apple TV
  • Linux (on distros where Swift is available)

ID3TagEditor gives you the ability to read and write ID3Tag to your mp3 files.

Read

To read the ID3 tag of an mp3 file you can choose between two API contained in the ID3TagEditor class:

  • public func read(from path: String) throws -> ID3Tag?
  • public func read(mp3: Data) throws -> ID3Tag?

After getting a ID3Tag from one of the read API above, you have two options to read the content:

  • if you're interested in reading just the content, you can create an instance of ID3TagContentReader by passing to it the ID3Tag received from the read API and then access the frames content by using its methods (see the doc to have a list of all the methods available).
do {
    if let id3Tag = try id3TagEditor.read(from: PathLoader().pathFor(name: "example", fileType: "mp3")) {
        let tagContentReader = ID3TagContentReader(id3Tag: id3Tag)
        print(tagContentReader.title() ?? "")
        print(tagContentReader.artist() ?? "")
        // ...read other stuff...
    }
} catch  {
    print(error)
}  
  • if you need full frame data, you can access to the frames property of ID3Tag and start to cast/analyze the various frames received. In this way you will have access to all the data contained in the frame, including its content and its features like the size and the ID3 frame identifier.
do {
    let id3TagEditor = ID3TagEditor()

    if let id3Tag = try id3TagEditor.read(from: "<valid path to the mp3 file>") {
        // ...use the tag...
        // For example to read the title, album and artist content you can do something similar
        print((id3Tag.frames[.title] as?  ID3FrameWithStringContent)?.content ?? "")
        print((id3Tag.frames[.artist] as? ID3FrameWithStringContent)?.content ?? "")
        print((id3Tag.frames[.album] as? ID3FrameWithStringContent)?.content ?? "")
    }
    
    if let id3Tag = try id3TagEditor.read(mp3: "<valid mp3 file passed as Data>") {
        // ...use the tag...
        // For example to read the title, album and artist content you can do something similar
        print((id3Tag.frames[.title] as?  ID3FrameWithStringContent)?.content ?? "")
        print((id3Tag.frames[.artist] as? ID3FrameWithStringContent)?.content ?? "")
        print((id3Tag.frames[.album] as? ID3FrameWithStringContent)?.content ?? "")
    }    
} catch {
    print(error)
}  

Write

To write a new ID3 tag into an mp3 file you can choose between two API contained in the ID3TagEditor class:

  • public func write(tag: ID3Tag, to path: String, andSaveTo newPath: String? = nil) throws
  • public func write(tag: ID3Tag, mp3: Data) throws -> Data

The only way to create a valid ID3Tag is by using of the tag builder available:

  • ID32v2TagBuilder, a builder useful to create ID3 tag version 2.2
  • ID32v3TagBuilder, a builder useful to create ID3 tag version 2.3
  • ID32v4TagBuilder, a builder useful to create ID3 tag version 2.4

You can't create an instance of ID3Tag without one of the builders above. Below you can find a sample code that will write an ID3Tag version 3 with all the frames supported by ID3TagEditor to an mp3 file.

do {
    let id3Tag = ID32v3TagBuilder()
        .title(frame: ID3FrameWithStringContent(content: "title V3"))
        .album(frame: ID3FrameWithStringContent(content: "album V3"))
        .albumArtist(frame: ID3FrameWithStringContent(content: "album artist V3"))
        .artist(frame: ID3FrameWithStringContent(content: "artist V3"))
        .composer(frame: ID3FrameWithStringContent(content: "composer V3"))
        .conductor(frame: ID3FrameWithStringContent(content: "conductor V3"))
        .contentGrouping(frame: ID3FrameWithStringContent(content: "ContentGrouping V3"))
        .copyright(frame: ID3FrameWithStringContent(content: "Copyright V3"))
        .encodedBy(frame: ID3FrameWithStringContent(content: "EncodedBy V3"))
        .encoderSettings(frame: ID3FrameWithStringContent(content: "EncoderSettings V3"))
        .fileOwner(frame: ID3FrameWithStringContent(content: "FileOwner V3"))
        .lyricist(frame: ID3FrameWithStringContent(content: "Lyricist V3"))
        .mixArtist(frame: ID3FrameWithStringContent(content: "MixArtist V3"))
        .publisher(frame: ID3FrameWithStringContent(content: "Publisher V3"))
        .subtitle(frame: ID3FrameWithStringContent(content: "Subtitle V3"))
        .beatsPerMinute(frame: ID3FrameWithIntegerContent(value: 50))
        .originalFilename(frame: ID3FrameWithStringContent(content: "filenameV3.mp3"))
        .lengthInMilliseconds(frame: ID3FrameWithIntegerContent(value: 9000))
        .sizeInBytes(frame: ID3FrameWithIntegerContent(value: 1500))
        .genre(frame: ID3FrameGenre(genre: .metal, description: "Metalcore"))
        .discPosition(frame: ID3FramePartOfTotal(part: 1, total: 3))
        .trackPosition(frame: ID3FramePartOfTotal(part: 2, total: 9))
        .recordingDayMonth(frame: ID3FrameRecordingDayMonth(day: 5, month: 8))
        .recordingYear(frame: ID3FrameWithIntegerContent(year: 2020))
        .recordingHourMinute(frame: ID3FrameRecordingHourMinute(hour: 15, minute: 39))
        .attachedPicture(pictureType: .frontCover, frame: ID3FrameAttachedPicture(picture: <picture as Data object>, type: .frontCover, format: .jpeg))
        .attachedPicture(pictureType: .backCover, frame: ID3FrameAttachedPicture(picture: <picture as Data object>, type: .backCover, format: .jpeg))
        .unsynchronisedLyrics(language: .ita, frame: ID3FrameWithLocalizedContent(language: ID3FrameContentLanguage.ita, contentDescription: "CD", content: "v3 ita unsync lyrics"))
        .unsynchronisedLyrics(language: .eng, frame: ID3FrameWithLocalizedContent(language: ID3FrameContentLanguage.eng, contentDescription: "CD", content: "v3 eng unsync lyrics"))
        .iTunesGrouping(frame: ID3FrameWithStringContent(content: "ItunesGrouping V3"))
        .iTunesMovementName(frame: ID3FrameWithStringContent(content: "MovementName V3"))
        .iTunesMovementIndex(frame: ID3FrameWithIntegerContent(value: 6))
        .iTunesMovementCount(frame: ID3FrameWithIntegerContent(value: 13))
        .iTunesPodcastCategory(frame: ID3FrameWithStringContent(content: "PodcastCategory V3"))
        .iTunesPodcastDescription(frame: ID3FrameWithStringContent(content: "PodcastDescription V3"))
        .iTunesPodcastID(frame: ID3FrameWithStringContent(content: "PodcastID V3"))
        .iTunesPodcastKeywords(frame: ID3FrameWithStringContent(content: "PodcastKeywords V3"))
        .comment(language: .ita, frame: ID3FrameWithLocalizedContent(language: ID3FrameContentLanguage.ita, contentDescription: "CD", content: "v2 ita comment"))
        .comment(language: .eng, frame: ID3FrameWithLocalizedContent(language: ID3FrameContentLanguage.eng, contentDescription: "CD", content: "v2 eng comment"))
        .build()
    
    try id3TagEditor.write(tag: id3Tag, to: "<valid path to the mp3 file that will be overwritten>")
    try id3TagEditor.write(tag: id3Tag, 
                           to: "<valid path to the mp3 file>",
                           andSaveTo: "<new path where you want to save the mp3>")
    let newMp3: Data = try id3TagEditor.write(tag: id3Tag, mp3: <valid mp3 file passed as Data>)                          
} catch {
    print(error)
}    

Supported frames

Below you can find the list of the official ID3 frames supported by ID3TagEditor:

  • .title
  • .album
  • .albumArtist
  • .artist
  • .composer
  • .conductor
  • .contentGrouping
  • .copyright
  • .encodedBy
  • .encoderSettings
  • .fileOwner, available only for ID3 v2.3/v2.4
  • .lyricist
  • .mixArtist
  • .publisher
  • .subtitle
  • .beatsPerMinute
  • .originalFilename
  • .lengthInMilliseconds
  • .sizeInBytes, available only for ID3 v2.2/v2.3
  • .genre
  • .discPosition
  • .trackPosition
  • .recordingDayMonth, available only for ID3 v2.2/v2.3
  • .recordingYear, available only for ID3 v2.2/v2.3
  • .recordingHourMinute, available only for ID3 v2.2/v2.3
  • .recordingDateTime, available only for ID3 v2.4
  • .attachedPicture(_ pictureType: ID3PictureType), with support for multiple frames in the same tag distinguished by ID3PictureType
  • .unsynchronizedLyrics(_ language: ID3FrameContentLanguage), with support for multiple frames in the same tag distinguished by ID3FrameContentLanguage
  • .comment(_ language: ID3FrameContentLanguage), with support for multiple frames in the same tag distinguished by ID3FrameContentLanguage

In addition, ID3TagEditor supports the following iTunes unofficial frames:

  • .iTunesGrouping, available only for ID3 v2.3/v2.4
  • .iTunesMovementName, available only for ID3 v2.3/v2.4
  • .iTunesMovementIndex, available only for ID3 v2.3/v2.4
  • .iTunesMovementCount, available only for ID3 v2.3/v2.4
  • .iTunesPodcastCategory, available only for ID3 v2.3/v2.4
  • .iTunesPodcastDescription, available only for ID3 v2.3/v2.4
  • .iTunesPodcastID, available only for ID3 v2.3/v2.4
  • .iTunesPodcastKeyword, available only for ID3 v2.3/v2.4

All frames are encoded/formatted following the specification:

  • text frames (frames with identifier starting with a capital T) uses UTF-16 to encode text
  • frames with ad hoc encoding/formatting are supported (for example recordingDateTime must always be a ISO88591 string)
  • frames with localized content (e.g. .unsynchronizedLyrics) support all the languages identifier contained in the ISO-639-2 (see ID3FrameContentLanguage for the complete list of supported languages).
    Refer to the id3 specification for additional details.

Documentation

You can find the complete API documentation on fabrizioduroni.it.


Examples

In the following screenshots you can find examples of the data extracted/updated. In the demo project you will find an example for each supported target. You can also find more usage example in the read/write acceptance test.


Contributors

bonfa
bonfa
chicio
chicio
martinjbaker
martinjbaker
joeljfischer
joeljfischer
BLeeEZ
BLeeEZ
NCrusher74
NCrusher74
Scytalion
Scytalion
aquaflamingo
aquaflamingo
Shabinder
Shabinder
lordzsolt
lordzsolt
jverkoey
jverkoey
github-actions[bot]
github-actions[bot]

id3tageditor's People

Contributors

aquaflamingo avatar bleeez avatar chicio avatar github-actions[bot] avatar joeljfischer avatar jverkoey avatar lordzsolt avatar martinjbaker avatar ncrusher74 avatar scytalion avatar shabinder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

id3tageditor's Issues

"Build input file cannot be found"

I'm not sure I should report this as a bug, as it's entirely possibly it's my fault.

For a personal project that involves library management for audiobooks, I've added a bunch of tags (mostly tags that don't generally get used much) with the intent of re-purposing them for audiobook-specific metadata. And I figured while I was at it, I'd add an assortment of other tags just as my way of contributing.

However, I've only been coding for about six months, so I was rather surprised when, aside from a few typo checks, all my tests passed on the first go. Turns out, I had opened the package, rather than the project, when adding my files, and was I ever surprised when I went to open the project a few days later and none of my files were there and it was full of errors whereas it had built beautifully before.

Did I mention I've only been doing this for six months? ... yeah.

Anyway, I started adding in all my new files, but I'm getting an error about FrameType.swift. Specifically:

error: Build input file cannot be found: '/Users/nolainecrusher/Documents/GitHub/ID3TagEditor/FrameType.swift' (in target 'ID3TagEditor iOS' from project 'ID3TagEditor')

As it has always been, FrameType.swift is right there, inside the .../ID3TagEditor/Frame/ directory. But if I'm reading that error correctly, it looks as though the iOS version wants FrameType.swift to be in .../ID3TagEditor/ rather than .../ID3TagEditor/Frame.

Did I screw up, or did I find a bug?

Extract ID3Tag description creation from ID3Tag class

Extract ID3Tag description creation from ID3Tag class

Describe the solution you'd like
At the moment the debugDescription property value creation is inside the ID3Tag class. Extract the creation of this string in an ad-hoc collaborator.

Additional context
See ID3Tag class.

Improve read api

Is your feature request related to a problem? Please describe.
Improve read api in order to let users of the library to be able to read data directly without any cast/search on the array of frames.

Describe the solution you'd like
A new public class that is an api that wraps all the cast/search needed on the frames in order to extract data. Should be version independent.

Chapter support

Does the library support enumerating chapters and their offsets in the mp3 file?

ID3FrameRecordingYear is out of scope

Describe the bug
It may not be a bug, but mistake of readme.md. .recordingYear(frame: ID3FrameRecordingYear(year: 2020)) is wrong.

To Reproduce
.recordingYear(frame: ID3FrameRecordingYear(year: 2020)) can't compile in Swift.
It should be written as .recordingYear(frame: ID3FrameWithIntegerContent(value: 2020))

Expected behavior
Compile should be finished without problem.

Screenshots
None

Device (please complete the following information):

  • Model: MacBook Pro 2018
  • OS: macOS12.3.1

Additional context
None

ID3 v2.4 Does AttachedPicture(.FrontCover) have this error?

Describe the bug
In ID3 v2.3, after adding an image, when reading the ID3Tag again, an error does not occur and it operates normally, whereas in v2.4 an error occurs.

To Reproduce
Steps to reproduce the behavior:

var test = tagModel.tag.reduce(into: [FrameName:ID3Frame]()) {
    $0[$1.title] = ID3FrameWithStringContent(content: $1.text)
}
if let data = image.jpegData(compressionQuality: 1.0) {
    test[.AttachedPicture(.FrontCover)] = ID3FrameAttachedPicture(picture: data, type: .FrontCover, format: .Jpeg)
}
do {
    try ID3TagEditor().write(tag: ID3Tag(version: .version3, frames: test), to: parentModel.deviceFilePath.path, andSaveTo:
FileManager.default.urls(for: .documentDirectory, in: .allDomainsMask)[0].appendingPathComponent("a.mp3").path)
    try ID3TagEditor().write(tag: ID3Tag(version: .version4, frames: test), to: parentModel.deviceFilePath.path, andSaveTo:
FileManager.default.urls(for: .documentDirectory, in: .allDomainsMask)[0].appendingPathComponent("b.mp3").path)
}catch {
    print(error)
}

After saving the a.mp3 file and b.mp3 above,
If you did ID3 Tag Editor().read(from: "a.mp3") there is no problem, but
If ID3 Tag Editor().read(from: "b.mp3") is done, an error occurs.

Expected behavior
The exception should be caught at the library level, and nil should be returned if an error occurs.

Screenshots
스크린샷 2020-10-08 오후 12 32 29
스크린샷 2020-10-08 오후 12 32 47
스크린샷 2020-10-08 오후 12 32 56
스크린샷 2020-10-08 오후 12 33 05

Device (please complete the following information):

  • Model: [Iphone 11 Pro]
  • OS: [iOS 14.0]
  • Emulator

** additional Info **

  • test Image : Here is the below image.

스크린샷 2020-10-08 오후 12 32 56

I am not good English, so I used Google translate. >,&

Improve demos macOS content

Is your feature request related to a problem? Please describe.
Improved demos macOS to test as much possible features of ID3TagEditor.

Describe the solution you'd like
Add a much more complex UI to the macOS demo in order to show/edit all the ID3Tag frame supported by ID3TagEditor.

Additional context
See Demo macOS xcodeproj.

Add SwiftLint

  • https://github.com/realm/SwiftLint
  • modify README.MD to explain in a contribution section that you have to install it to before starting contributing
  • modify CONTRIBUTING.MD to explain in a contribution section that you have to install it to before starting contributing

Can't Read Cover

Hello!
If I use an example file, then it reads her cover, but when I upload mine, it gives nil

What's the problem, everything else reads
Снимок экрана 2022-12-02 в 14 40 51

Снимок экрана 2022-12-02 в 14 41 06

Chapter support

Looking through the code and docs, as far as I can tell there's no support for mp3 chapters and table of contents. Is there any particular reason for this? As a podcaster and developer of a podcasting-related app I would find this really useful. I might try to add support, but I thought I'd ask first if I'm trying something completely silly :-) …

exceeds data length error on some mp3 files with version4

I get an error -[Foundation.__NSSwiftData subdataWithRange:]: range {115888, 24847503} exceeds data length 11634651
on trying to read mp3 songs with version4.

I've uploaded the mp3 file below.

I use ID3TagEditor 4.1.3

Steps to reproduce the behavior:

  1. Go to Example App macOS version
  2. Add file "The Black Eyed Peas - I Gotta Feeling" to the project
  3. Change "pathFor(name:)" in ViewController file, line 20 to "The Black Eyed Peas - I Gotta Feeling"
  4. Run the app

The Black Eyed Peas - I Gotta Feeling.mp3.zip

Add User defined text information frame

Add support for the TXXX comment frame.

Describe the solution you'd like
Add support for the TXXX comment frame. This frame has the same structure of USTL and COMM unsynchronised lyrics.
This issue should be done after #46 is completed.
I need to:

  • use the same approach used for USTL and COMM frame to add the TXXX frame

See chain of creation and parsing in ID3TagEditor
https://id3.org/d3v2.3.0 see section "4.2.2. User defined text information frame"

Restructure project folders

Change folder structure.

Describe the solution you'd like
Remove deep level of folder nesting in order to have a better navigation in the project.
There should be just one level of folders. If it is not possible we can have an additional second level folder.

Crashes on partial files

Describe the bug
When reading partial downloads, I've identified several places where the library crashes. Mostly when making .subdata with NSMakeRange and not checking if the range is valid.

To Reproduce
Steps to reproduce the behavior:

  1. Try reading e.g. first 5kB of a mp3 file (partial download)

Expected behavior
Not crashing

Device (please complete the following information):

  • Model: iPad mini, simulator

Additional context
I've identified the following crash points:
ID3TagParser: let frame = mp3.subdata(with: NSMakeRange(position, frameSize))
ID3FrameContentParser: let frameIdentifierData = [UInt8](frame.subdata(in: Range(0...frameIdentifierSize - 1)))
ID3FrameContentSizeParser: mp3.getBytes(&frameSize, range: NSMakeRange(frameSizePosition, 4))

Read/Write lyrics

Hi

I am reading the metadata of a file
AVAsset has lyrics
After saving the metadata
let id3Tag = ID32v4TagBuilder()
.lyricist(frame: ID3FrameWithStringContent(content: asset.lyrics ?? ""))
.build()

try id3TagEditor.write(
tag: id3tag,
to: videoURL.path,
andSaveTo: destinationURL2?.path)

The following happens
Lyrics are removed from AVAsset and written to the metadata under the id3/TEXT key

How can I leave lyrics in AVAsset?

Split Frames by version/Validate Frame Creation

Find a way to split frame so that you cannot use frame of version 2.4 on tags of version 2.2 and viceversa. At the moment it is possible to try to create a frame in this way but then ID3TagEditor crashes.

Discuss/think about a new architecture of the ID3Tag/ID3Frame classes to manage this new feature.

Crashes on wrong frame size

Describe the bug
Crashes when trying to read some mp3 files

To Reproduce
Steps to reproduce the behavior:

  1. Download http://cdndl.zaycev.net/807218/6982189/matrang_-_meduza_%28zaycev.net%29.mp3
  2. Read tags from downloaded file

Device (please complete the following information):

  • Model: iPhone7 , iPhone X (emulator)
  • OS: iOs 12.1.1, iOs 11.0.1

Additional context
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSConcreteData subdataWithRange:]: range {113, 17764438} exceeds data length 8907127'
*** First throw call stack:
(
0 CoreFoundation 0x0000000104a331cb __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000102eeff41 objc_exception_throw + 48
2 CoreFoundation 0x0000000104aa7b95 +[NSException raise:format:] + 197
3 Foundation 0x000000010255f0f6 -[NSData(NSData) subdataWithRange:] + 252
4 ID3TagEditor 0x0000000102ba0ee6 $S12ID3TagEditor0aB6ParserC12getFrameFrom33_1D3E9BDC72A711DCC87CD9BADE0624C3LL3mp38position7version10Foundation4DataVSo6NSDataC_SiAA0A7VersionOtF + 230
5 ID3TagEditor 0x0000000102ba0d05 $S12ID3TagEditor0aB6ParserC14parseFramesFor33_1D3E9BDC72A711DCC87CD9BADE0624C3LL3mp303id3B0ySo6NSDataC_AA0aB0CtF + 373
6 ID3TagEditor 0x0000000102ba07b7 $S12ID3TagEditor0aB6ParserC5parse3mp3AA0aB0CSg10Foundation4DataV_tKF + 551
7 ID3TagEditor 0x0000000102b9edd3 $S12ID3TagEditorAAC4read4fromAA0aB0CSgSS_tKF + 163
...
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Memory Issue with large files - greater than roughly 250MB

Saving tags works flawlessly for files up to at least 217MB, but other files 280MB and larger crash the app with a message of "Terminated due to memory issue". XCode shows the app consuming over 1.2GB of memory while saving the tags, eventually exhausting the memory and crashing the app. Seems like a huge memory usage to file size ratio (4:1 or 5:1).

To Reproduce
Steps to reproduce the behavior:
1 - use the sample code from the "Usage" section of this page -> https://github.com/chicio/ID3TagEditor

2 - supply a large MP3 file (at least 300MB, ideally at least 500MB). Sample files can be found here ->
www.global-sets.com

For example, this one is 295MB ->
http://www.global-sets.com/carl-cox-live-ultra-music-festival-miami-united-states-resistance-31-mar-2019/

Expected behavior
Expect this to work for MP3 files of all sizes, up to at least 500MB

Device (please complete the following information):

  • Model: iPhone6S
  • OS: iOS 12.4.1

Support all text encoding declared in the ID3 standard

At the moment all the text frame support only UTF16 as text encoding. We should add the possibility to parse and create frame with one of preferred encoding supported by the ID3.

Describe the solution you'd like
All text frame should contains a property `textEcoding, that contains the parsed value for the current text frame or the value set by the library user during text frame creation.

Additional context
See id3.org for the complete list of text encoding supported.

"Involved People"/IPLS or similar tag

Is your feature request related to a problem? Please describe.
Right now, ID3TagEditor doesn't appear to allow multiple values for a single key. For example, TXXX or IPLS can only be used once in the commonNamesForIdentifiers

Describe the solution you'd like
I'm working on a audiobook library management suite, and I'd like to be able to tag each involved person (i.e. multiple co-authors, or an audiobook narrated by a cast rather than a single narrator) in such a way that it will be straightforward to parse the author or narrator field of the app to add new values to an existing database of authors or narrators.

I'd like the ability to assign multiple values to a single key, i.e IPLS: authorA, IPLS: authorB. This may also come in handy down the line when I work out how to add chapter metadata.

Describe alternatives you've considered
I've considered re-purposing as many ID3FrameWithStringContent eligible tags as possible that don't generally get used for audiobooks, such as "Conductor", "MixArtist" etc. It would be possible to cobble together something like that, but only if I set some of those fields aside for Author B, Author C, etc, and some aside for Narrator B, Narrator C, and so forth.

Remove ID3StringFrameCreator class hierarchy

Describe the solution you'd like
At the moment the string frames use ID3StringFrameCreator as base class to parse frames with string content. Remove this hierarchy in order to converge to a architecture structure that doesn't need the FrameType enum architecture.

Additional context
See for example ID3AlbumArtistFrameCreator

USLT Tag not being written to MP3 file

Describe the bug
I have a method that uses ffmpeg as a subprocess to trim silence from the beginning and end of an mp3 track, then it processes it a second time to normalize the audio to LUFS -23. Before modifying the file, it is preserved (copied) to a new location.

After running the trim process, I am updating the comment via ID3TagEditor to add "trimmed" to the comment field. I am doing the same thing after the track is normalized, adding "normalized (-23)."

It appears that when I read the id3 tags, then modify the comment tag, then write the id3 back to the file, the lyrics are not being written to the mp3 file.

To Reproduce
Steps to reproduce the behavior:
func writeID3Comment(comment:String, filePath: String) {
do {
let id3TagEditor = ID3TagEditor()
if let id3Tag = try id3TagEditor.read(from: filePath) {
id3Tag.frames[FrameName.comment(.eng)] = ID3FrameWithLocalizedContent(language: .eng, contentDescription: "", content: comment)
try id3TagEditor.write(tag: id3Tag, to: filePath)
}
} catch {
print("An error has occurred writing id3Tag comment frame with (comment) to mp3 file (filePath)")
}
}

Expected behavior
I would expect that any tags read with the id3TagEditor.read(from: filePath) would preserve all tags read and write them back out to the file, along with the modified tag(s).

Screenshots
If applicable, add screenshots to help explain your problem.

Device (please complete the following information):

  • Model: iMac (late 2015)
  • OS: macOS Big Sur 11.2.1
  • Xcode 12.4

Additional context
Am I not using the id3TagEditor or the id3Tag.frames methods correctly? Thanks for providing this API!

M4A files?

Hi, is it possible to add support for M4A files?

DiscPosition Tag disappears when attach cover art

Describe the bug
DiscPosition Tag works expected without attaching cover art, however, it was lost with attaching cover art.

To Reproduce
Steps to reproduce the behavior:

  1. Prepare any mp3 file.

  2. Prepare any jpg image.

  3. Run the following code.

     import Cocoa
     import ID3TagEditor
     
     class ViewController: NSViewController {
     
     	@IBAction func Button(_ sender: Any) {
     	
    
                      let coverArtPath = "/Users/roushi/Music/test.jpg"
             let coverArtImage = NSImage(contentsOfFile: coverArtPath)
             let coverArtData = coverArtImage?.tiffRepresentation
                     
             let mp3FilePath = "/Users/roushi/Music/test.mp3"
             
             do {
                     let id3Tag = ID3Tag(
                         version: .version3,
                         frames: [
                             .DiscPosition : ID3FramePartOfTotal(part: 1, total: 3),
                             .AttachedPicture(.FrontCover) : ID3FrameAttachedPicture(picture: coverArtData!, type: .FrontCover, format: .Jpeg),
                         ]
                     )
                     
                     let id3TagEditor = ID3TagEditor()
                     try id3TagEditor.write(tag: id3Tag, to: mp3FilePath)
                     
                 } catch {
                     
                     print(error)
                 }
     		 }
      }
    
  4. Click on 'the button'

  5. See the resulting mp3 file with any ID3Tag Editor. Of course you can choose Music.app. Then you may see discPosition was disappear. If you comment out .AttachedPicture line, you may see discPosition.

Screenshots
If applicable, add screenshots to help explain your problem.

Device (please complete the following information):

  • Model: MacBook Pro 2018 15inch
  • OS: macOS 10.15.7 Catalina

Additional context
Add any other context about the problem here.

test.zip

Existing ID3 attributes are cleared when using 'Id3TagEditor.write'

Describe the bug
I used the macOS demo app and when i try to write properties of an MP3 file using the following code
All the exiting ID3 attributes in the mp3 file are overwritten. Is there any way to keep the existing attributes?
`

     do { 
                 let id3TagAx = ID32v3TagBuilder()
            .title(frame: ID3FrameWithStringContent(content: "Love Come Down (CFLO Intro) Kam1"))
            .artist(frame: ID3FrameWithStringContent(content: "Evelyn Champagne King Kam1"))
            .albumArtist(frame: ID3FrameWithStringContent(content: "Evelyn Champagne King Kam1"))
            .album(frame: ID3FrameWithStringContent(content: "djcflo.com Kam1"))
            .beatsPerMinute(frame: ID3FrameWithIntegerContent(value: 9))
            .mixArtist(frame: ID3FrameWithStringContent(content: "Remix Guy Kam1"))
            .comment(language: ID3FrameContentLanguage(rawValue: "eng")!,
                     frame: ID3FrameWithLocalizedContent(language: ID3FrameContentLanguage.eng,
                                                         contentDescription: "Comment",
                                                         content: "Kam is here Kam1"))
            .build()
         try id3TagEditor.write(
            tag: id3TagAx,
            to: "/Users/kamyfc/Music/KamySongs/CFLO 12.19/Stevie Wonder - Signed Sealed Delivered (CFLO Edit).mp3"
        )
    } catch {
        print(error)
    }

`

To Reproduce
Steps to reproduce the behavior:

  1. Ensure you see many attributes in MP3 files.
  2. Write an update values of few attributes to an MP3 file using ID3TagEditor demo app
  3. Observe the attributes
  4. See that existing attributes are wiped away.

Expected behavior
Ideally existing attributes should not be cleared away. We need to update the values of the attributes that are edited and retain other attributes.

Screenshots
The data on right shows the ID3 data before editing using ID3TagEditor. The data on left shows after using the 'Id3TagEditor.write' method. As you can see how existing attributes on the right are cleared away.
https://pasteboard.co/JRda9gg.png

Device (please complete the following information):

  • Model: Mac Mini 2018
  • OS: macOS Catalina

Additional context
Add any other context about the problem here.

Add all missing PLAIN Text information frames

Add all the missing frames contained in 4.2. Text information frames of the ID3 standard that doesn't require any adapting/parsing of the content (plain text without any specific adapting)

Describe the solution you'd like
All the frame contained in the section quoted above have the same structure of the basic title/subtitle etc. frame. All this frame are implemented using ID3FrameWithStringContent.

  • add frame identifiers configuration
  • add parsing and creator classes for each frame

Additional context
Add any other context or screenshots about the feature request here.

Delete methods missing

Is your feature request related to a problem? Please describe.
I'm looking for an easy method to delete frames.

Describe the solution you'd like
I want to have the ability to delete a frame, or modify an existing frame. For example, some of my artwork, as mentioned in your blog post, are not set as "frontCover" but instead as "other." I would like the ability to either change the picture type, or an easy method to delete the frame after copying the attchedPicture data and then recreate the frame with the correct type.

Describe alternatives you've considered
I have stepped through my code and I do not understand how to do what I am trying to achieve.

Additional context
None.

Build Error

Describe the bug
Fails to build on Ubuntu 16.04 with Swift 4.2

To Reproduce
Steps to reproduce the behavior:

  1. Clone repo
  2. swift build
  3. ID3TagEditor: error: package has unsupported layout; found loose source files: /home/userx/Swift/ID3TagEditor/Source/ID3TagEditor.swift, /home/userx/Swift/ID3TagEditor/Source/ID3TagEditorError.swift, /home/userx/Swift/ID3TagEditor/Source/ID3Version.swift

Expected behavior
Successful build

Screenshots
If applicable, add screenshots to help explain your problem.

Device (please complete the following information):

  • Model: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]

Additional context
Add any other context about the problem here.

Read another frames

I will like can read the another frames, like the PRIV frames. How we can do it?

Thanks !

Best Regards

Add Comment Frame

Add support for the COMM comment frame.

Describe the solution you'd like
Add support for the COMM comment frame. This frame has the same structure of USTL unsynchronised lyrics. I need to:

  • extract a common Frame with content description and content frame class
    • it would be better a collaborator that contains the common properties
  • create USTL and COMM frame with this new base frame type
  • parse and create these frames using the above class

Additional context

Reading ID3 tags of large files

Is your feature request related to a problem? Please describe.

  • Because you're using Data(contentsOf: validPath) inside Mp3FileReader, the entire file gets loaded into memory

Describe the solution you'd like

  • According to documentation, InputStream is meant to be used in this case.
  • https://developer.apple.com/documentation/foundation/inputstream
  • Since the ID3 tags are at the start of the file, it's enough to only read Int(tagSize) + id3TagConfiguration.headerSize() worth
  • This would involve some amount of code change, to first peak into the file to get the tagSize, and then perform a second read to read the entire ID3 header

Additional context

  • I'm writing an Audiobook player for myself, and wanted to read the ID3 tags to show some additional information, when viewing the files / playing them.
  • Tested this with a 500MB audiobook, all 500 MB of it get loaded into RAM.
  • Looked at the tagSize + id3TagConfiguration.headerSize() values, it would be enough to read 588549 bytes, or ~5MB

In fact, if I modify the code, and hardcode this value, it behaves exactly as before:

    func readFrom(path: String) throws -> Data {
        let validPath = URL(fileURLWithPath: path)
        guard validPath.pathExtension.caseInsensitiveCompare("mp3") == ComparisonResult.orderedSame else {
            throw ID3TagEditorError.invalidFileFormat
        }

        guard let inputStream = InputStream(fileAtPath: path) else {
            throw ID3TagEditorError.invalidFileFormat
        }

        inputStream.open()

        var buffer = [UInt8](repeating: 0, count: 588549)
        let result = inputStream.read(&buffer, maxLength: 588549)

        return Data(buffer)
}

  • Have you ever thought about this problem?
  • Are you interested in a contribution that would change this?
    • It would involve some slight refactor to accommodate the 2 read events.

ID3TagEditor on Windows

Bring ID3TagEditor on windows

Describe the solution you'd like

  • test ID3TagEditor on Windows
  • setup CI to check that ID3TagEditor works on Windows (need to create a new GitHub Action?)
  • use Demo Ubuntu as testing demo (Rename it to Demo SwiftPM or something similar)

Additional context

Refactor ID3FrameCreatorsChainFactory

Refactor ID3FrameCreatorsChainFactory to avoid GPA codebeat score issues

Describe the solution you'd like
Remove the current creator chain implementation and substitute it with array + reduce.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.