-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid reading the whole file into memory when updating ID3 tag of a file #106
Conversation
Currently, a lot of existing tests fail. However, this seems to be unrelated to the change. Somehow the tag size of the I have added a failing test that shows the root of the problem: @Test func parseTagSizeToBeModified() {
let mp3 = NSData(contentsOfFile: PathLoader().pathFor(name: "example-to-be-modified", fileType: "mp3"))!
#expect(ID3TagSizeParser().parse(data: mp3) < mp3.count)
} Maybe you have an idea why this file can't be read correctly. |
Hi @fabiankr and thanks for the contribution 🙏 I will try to have a look at your PR in the next days and let you know which is the problem. Let's see if we can merge this in the next weeks. |
Source/ID3TagEditor.swift
Outdated
let currentTag = try self.id3TagParser.parse(mp3: mp3) | ||
let mp3WithId3Tag = try mp3WithID3TagBuilder.build(mp3: mp3, newId3Tag: tag, currentId3Tag: currentTag) | ||
try mp3FileWriter.write(mp3: mp3WithId3Tag, path: newPath ?? path) | ||
let currentId3Tag = try read(from: path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the problem rely in this change. If you rollback to
let mp3 = try mp3FileReader.readFileFrom(path: path)
let currentId3Tag = try self.id3TagParser.parse(mp3: mp3)
Everything should be fine. I will investigate deeply after the merge if there are problems with the examples (because generally speaking other library are not so strict in the check as this one I did, and some of the mp3 where generated using them. So probably, when I created this example, they came as output from another library).
Can you verify that by reverting this part your implementation is still fine? This will be a BIG improvements for the library (thanks again for the contribution 🙏 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readFileFrom(path:)
reads the whole file into memory, so I can't just revert that line. However, I think the problem is that read(from:)
doesn't check for the ID3 tag presence first using isTagPresentIn(mp3:version:)
. So it tries to parse the tag size even though there's no ID3 tag at all. I'll fix it.
@fabiankr I added a couple of comments explaining the situation. Let me know if it is clear 🚀 |
As mentioned in the CR comments, probably that example come from another library (I mean I generated it using another library/tool, like "Mp3Tag" app or a java/c++ library), that are not as strict as ID3TagEditor on the checks to be done. Let's try apply the suggestion I made to see if everything works fine. |
So turns out the I fixed the reading function to perform the check and made some slight refactoring. Now all tests are green. 👌 |
Ok so I was probably out of my mind when I reviewed the changes 🤣 I reviewed the new changes and they seem fine, but there are still some errors on tvOS and linux (check the pipeline results). I think it is just a matter of conditionally change the code for tvOS (like you did for iOS) + removing the PS if you need to run ID3TagEditor on linux, there is a script in the |
Thanks for the links! Yeah I didn’t check the correct OS versions for the deprecations yet. I’ll fix it and also look at the Linux build. 👍 |
@chicio All tests are now green. 👍 |
Very good 🚀 I will merge and release it this evening or tomorrow evening. Thanks again for the contribution, I really appreciate it 🙏 |
@fabiankr version 5.2.0 published with your contribution. The GitHub actions pipelines added also you to the contributors 🚀 🚀 🚀 🚀 🚀 |
Currently, when updating the ID3 tag of an existing file the whole file is read into memory. This PR aims to avoid that in order to keep memory consumption low.
Description
Instead of reading the whole MP3 file as
Data
into memory use file handles to read only the current ID3 tag from the existing file (implemented in #99), then write the new tag and read the rest of the existing file (the audio data) in chunks of 64 KB.Motivation and Context
I'm using this library to update the ID3 tag of audio files when importing them into my app. While working on a share extension I noticed that the extension was killed by the system for using too much memory when saving the tag of a large MP3 file (greater than 60 MB). With this change that no longer happens.
Apps may use more memory than extensions, but even for apps this change is good because it will lower the memory pressure of the devices.
How Has This Been Tested?
Tested in my app with hundreds of MP3 files, from small (10 MB) to large (300 MB).
Types of changes
Checklist: