diff --git a/Source/ID3TagEditor.swift b/Source/ID3TagEditor.swift index 674acc18..cb15f3b2 100644 --- a/Source/ID3TagEditor.swift +++ b/Source/ID3TagEditor.swift @@ -68,46 +68,9 @@ public class ID3TagEditor { ID3 tag). */ public func write(tag: ID3Tag, to path: String, andSaveTo newPath: String? = nil) throws { - guard let newPath else { - let mp3 = try mp3FileReader.readFileFrom(path: path) - 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: path) - return - } - - let currentTag = try read(from: path) - let newTag = try ID3TagCreatorFactory.make().create(id3Tag: tag) - try mp3FileWriter.write(mp3: newTag, path: newPath) - - // Create file handles - let readHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path)) - defer { - readHandle.closeFile() - } - - let writeHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: newPath)) - defer { - writeHandle.closeFile() - } - - // Seek over the tag of the existing file, then copy the rest in chunks - readHandle.seek(toFileOffset: 0) - writeHandle.seekToEndOfFile() - - if let currentTag { - let tagSizeWithHeader = UInt64(currentTag.properties.size) + UInt64(ID3TagConfiguration().headerSize()) - readHandle.seek(toFileOffset: tagSizeWithHeader) - } - - var isFinished = false - while !isFinished { - autoreleasepool { - let chunk = readHandle.readData(ofLength: 65536) // 64 KB - writeHandle.write(chunk) - isFinished = chunk.isEmpty - } - } + let newId3TagData = try mp3WithID3TagBuilder.build(mp3: Data(), newId3Tag: tag, currentId3Tag: nil) + let currentId3Tag = try read(from: path) + try mp3FileWriter.write(newId3TagData: newId3TagData, currentId3Tag: currentId3Tag, fromPath: path, toPath: newPath ?? path) } /** diff --git a/Source/Mp3/Mp3FileWriter.swift b/Source/Mp3/Mp3FileWriter.swift index 9e73e0d3..53bd334b 100644 --- a/Source/Mp3/Mp3FileWriter.swift +++ b/Source/Mp3/Mp3FileWriter.swift @@ -8,9 +8,59 @@ import Foundation class Mp3FileWriter { - func write(mp3: Data, path: String) throws { - try eventuallyCreateIntermediatesDirectoriesFor(path: path) - try mp3.write(to: URL(fileURLWithPath: path)) + func write(newId3TagData: Data, currentId3Tag: ID3Tag?, fromPath: String, toPath: String) throws { + // Create a temporary file for the new mp3 + let temporaryPath = { + if toPath != fromPath { + return toPath + } + + return FileManager.default.temporaryDirectory.appendingPathComponent("\(UUID().uuidString).mp3").path + }() + + defer { + if temporaryPath != toPath { + try? FileManager.default.removeItem(atPath: temporaryPath) + } + } + + try eventuallyCreateIntermediatesDirectoriesFor(path: temporaryPath) + try newId3TagData.write(to: URL(fileURLWithPath: temporaryPath)) + + // Create file handles + let readHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: fromPath)) + defer { + readHandle.closeFile() + } + + let writeHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: temporaryPath)) + defer { + writeHandle.closeFile() + } + + // Seek over the tag of the existing file, then copy the rest in chunks + writeHandle.seekToEndOfFile() + + if let validCurrentId3Tag = currentId3Tag { + let tagSizeWithHeader = UInt64(validCurrentId3Tag.properties.size) + UInt64(ID3TagConfiguration().headerSize()) + readHandle.seek(toFileOffset: tagSizeWithHeader) + } else { + readHandle.seek(toFileOffset: 0) + } + + var isFinished = false + while !isFinished { + autoreleasepool { + let chunk = readHandle.readData(ofLength: 65536) // 64 KB + writeHandle.write(chunk) + isFinished = chunk.isEmpty + } + } + + // Replace the file + if temporaryPath != toPath { + _ = try FileManager.default.replaceItemAt(URL(fileURLWithPath: toPath), withItemAt: URL(fileURLWithPath: temporaryPath)) + } } private func eventuallyCreateIntermediatesDirectoriesFor(path: String) throws { diff --git a/Source/Mp3/Mp3WithID3TagBuilder.swift b/Source/Mp3/Mp3WithID3TagBuilder.swift index e68f96fc..ddf9efad 100644 --- a/Source/Mp3/Mp3WithID3TagBuilder.swift +++ b/Source/Mp3/Mp3WithID3TagBuilder.swift @@ -22,7 +22,9 @@ class Mp3WithID3TagBuilder { tagSizeWithHeader = Int(validCurrentId3Tag.properties.size) + ID3TagConfiguration().headerSize() } var mp3WithTag = try id3TagCreator.create(id3Tag: newId3Tag) - mp3WithTag.append(mp3.subdata(in: tagSizeWithHeader..