Fix warnings and improve style in makeMp3File()
This commit is contained in:
parent
ae65ad0408
commit
30553c947c
|
@ -1478,14 +1478,30 @@ void MediaFileInfo::invalidated()
|
||||||
void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &progress)
|
void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &progress)
|
||||||
{
|
{
|
||||||
static const string context("making MP3/FLAC file");
|
static const string context("making MP3/FLAC file");
|
||||||
// there's no need to rewrite the complete file if there are no ID3v2 tags present or to be written
|
|
||||||
|
// don't rewrite the complete file if there are no ID3v2/FLAC tags present or to be written
|
||||||
if (!isForcingRewrite() && m_id3v2Tags.empty() && m_actualId3v2TagOffsets.empty() && m_saveFilePath.empty()
|
if (!isForcingRewrite() && m_id3v2Tags.empty() && m_actualId3v2TagOffsets.empty() && m_saveFilePath.empty()
|
||||||
&& m_containerFormat != ContainerFormat::Flac) {
|
&& m_containerFormat != ContainerFormat::Flac) {
|
||||||
if (m_actualExistingId3v1Tag) {
|
// alter ID3v1 tag
|
||||||
// there is currently an ID3v1 tag at the end of the file
|
if (!m_id3v1Tag) {
|
||||||
if (m_id3v1Tag) {
|
// remove ID3v1 tag
|
||||||
// the file shall still have an ID3v1 tag
|
if (!m_actualExistingId3v1Tag) {
|
||||||
progress.updateStep("Updating ID3v1 tag ...");
|
diag.emplace_back(DiagLevel::Information, "Nothing to be changed.", context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progress.updateStep("Removing ID3v1 tag ...");
|
||||||
|
stream().close();
|
||||||
|
if (truncate(path().data(), static_cast<std::streamoff>(size() - 128)) == 0) {
|
||||||
|
reportSizeChanged(size() - 128);
|
||||||
|
} else {
|
||||||
|
diag.emplace_back(DiagLevel::Critical, "Unable to truncate file to remove ID3v1 tag.", context);
|
||||||
|
throwIoFailure("Unable to truncate file to remove ID3v1 tag.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// add or update ID3v1 tag
|
||||||
|
if (m_actualExistingId3v1Tag) {
|
||||||
|
progress.updateStep("Updating existing ID3v1 tag ...");
|
||||||
// ensure the file is still open / not readonly
|
// ensure the file is still open / not readonly
|
||||||
open();
|
open();
|
||||||
stream().seekp(-128, ios_base::end);
|
stream().seekp(-128, ios_base::end);
|
||||||
|
@ -1495,21 +1511,7 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
|
||||||
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the currently existing ID3v1 tag shall be removed
|
progress.updateStep("Adding new ID3v1 tag ...");
|
||||||
progress.updateStep("Removing ID3v1 tag ...");
|
|
||||||
stream().close();
|
|
||||||
if (truncate(path().c_str(), size() - 128) == 0) {
|
|
||||||
reportSizeChanged(size() - 128);
|
|
||||||
} else {
|
|
||||||
diag.emplace_back(DiagLevel::Critical, "Unable to truncate file to remove ID3v1 tag.", context);
|
|
||||||
throwIoFailure("Unable to truncate file to remove ID3v1 tag.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// there is currently no ID3v1 tag at the end of the file
|
|
||||||
if (m_id3v1Tag) {
|
|
||||||
progress.updateStep("Adding ID3v1 tag ...");
|
|
||||||
// ensure the file is still open / not readonly
|
// ensure the file is still open / not readonly
|
||||||
open();
|
open();
|
||||||
stream().seekp(0, ios_base::end);
|
stream().seekp(0, ios_base::end);
|
||||||
|
@ -1518,229 +1520,232 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
|
||||||
} catch (const Failure &) {
|
} catch (const Failure &) {
|
||||||
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
diag.emplace_back(DiagLevel::Information, "Nothing to be changed.", context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID3v2 needs to be modified
|
||||||
|
FlacStream *const flacStream = (m_containerFormat == ContainerFormat::Flac ? static_cast<FlacStream *>(m_singleTrack.get()) : nullptr);
|
||||||
|
progress.updateStep(flacStream ? "Updating FLAC tags ..." : "Updating ID3v2 tags ...");
|
||||||
|
|
||||||
|
// prepare ID3v2 tags
|
||||||
|
vector<Id3v2TagMaker> makers;
|
||||||
|
makers.reserve(m_id3v2Tags.size());
|
||||||
|
uint32 tagsSize = 0;
|
||||||
|
for (auto &tag : m_id3v2Tags) {
|
||||||
|
try {
|
||||||
|
makers.emplace_back(tag->prepareMaking(diag));
|
||||||
|
tagsSize += makers.back().requiredSize();
|
||||||
|
} catch (const Failure &) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine stream offset and make track/format specific metadata
|
||||||
|
uint32 streamOffset; // where the actual stream starts
|
||||||
|
stringstream flacMetaData(ios_base::in | ios_base::out | ios_base::binary);
|
||||||
|
flacMetaData.exceptions(ios_base::badbit | ios_base::failbit);
|
||||||
|
uint32 startOfLastMetaDataBlock;
|
||||||
|
if (flacStream) {
|
||||||
|
// if it is a raw FLAC stream, make FLAC metadata
|
||||||
|
startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
|
||||||
|
tagsSize += flacMetaData.tellp();
|
||||||
|
streamOffset = flacStream->streamOffset();
|
||||||
} else {
|
} else {
|
||||||
// ID3v2 needs to be modified
|
// make no further metadata, just use the container offset as stream offset
|
||||||
progress.updateStep("Updating ID3v2 tags ...");
|
streamOffset = static_cast<uint32>(m_containerOffset);
|
||||||
|
}
|
||||||
|
|
||||||
// prepare ID3v2 tags
|
// check whether rewrite is required
|
||||||
vector<Id3v2TagMaker> makers;
|
bool rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
|
||||||
makers.reserve(m_id3v2Tags.size());
|
size_t padding = 0;
|
||||||
uint32 tagsSize = 0;
|
if (!rewriteRequired) {
|
||||||
for (auto &tag : m_id3v2Tags) {
|
// rewriting is not forced and new tag is not too big for available space
|
||||||
try {
|
// -> calculate new padding
|
||||||
makers.emplace_back(tag->prepareMaking(diag));
|
padding = streamOffset - tagsSize;
|
||||||
tagsSize += makers.back().requiredSize();
|
// -> check whether the new padding matches specifications
|
||||||
} catch (const Failure &) {
|
if (padding < minPadding() || padding > maxPadding()) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether it is a raw FLAC stream
|
|
||||||
FlacStream *flacStream = (m_containerFormat == ContainerFormat::Flac ? static_cast<FlacStream *>(m_singleTrack.get()) : nullptr);
|
|
||||||
uint32 streamOffset; // where the actual stream starts
|
|
||||||
stringstream flacMetaData(ios_base::in | ios_base::out | ios_base::binary);
|
|
||||||
flacMetaData.exceptions(ios_base::badbit | ios_base::failbit);
|
|
||||||
uint32 startOfLastMetaDataBlock;
|
|
||||||
|
|
||||||
if (flacStream) {
|
|
||||||
// if it is a raw FLAC stream, make FLAC metadata
|
|
||||||
startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
|
|
||||||
tagsSize += flacMetaData.tellp();
|
|
||||||
streamOffset = flacStream->streamOffset();
|
|
||||||
} else {
|
|
||||||
// make no further metadata, just use the container offset as stream offset
|
|
||||||
streamOffset = static_cast<uint32>(m_containerOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether rewrite is required
|
|
||||||
bool rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
|
|
||||||
uint32 padding = 0;
|
|
||||||
if (!rewriteRequired) {
|
|
||||||
// rewriting is not forced and new tag is not too big for available space
|
|
||||||
// -> calculate new padding
|
|
||||||
padding = streamOffset - tagsSize;
|
|
||||||
// -> check whether the new padding matches specifications
|
|
||||||
if (padding < minPadding() || padding > maxPadding()) {
|
|
||||||
rewriteRequired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (makers.empty() && !flacStream) {
|
|
||||||
// an ID3v2 tag is not written and it is not a FLAC stream
|
|
||||||
// -> can't include padding
|
|
||||||
if (padding) {
|
|
||||||
// but padding would be present -> need to rewrite
|
|
||||||
padding = 0; // can't write the preferred padding despite rewriting
|
|
||||||
rewriteRequired = true;
|
|
||||||
}
|
|
||||||
} else if (rewriteRequired) {
|
|
||||||
// rewriting is forced or new ID3v2 tag is too big for available space
|
|
||||||
// -> use preferred padding when rewriting anyways
|
|
||||||
padding = preferredPadding();
|
|
||||||
} else if (makers.empty() && flacStream && padding && padding < 4) {
|
|
||||||
// no ID3v2 tag -> must include padding in FLAC stream
|
|
||||||
// but padding of 1, 2, and 3 byte isn't possible -> need to rewrite
|
|
||||||
padding = preferredPadding();
|
|
||||||
rewriteRequired = true;
|
rewriteRequired = true;
|
||||||
}
|
}
|
||||||
if (rewriteRequired && flacStream && makers.empty() && padding) {
|
}
|
||||||
// the first 4 byte of FLAC padding actually don't count because these
|
if (makers.empty() && !flacStream) {
|
||||||
// can not be used for additional meta data
|
// an ID3v2 tag is not written and it is not a FLAC stream
|
||||||
padding += 4;
|
// -> can't include padding
|
||||||
|
if (padding) {
|
||||||
|
// but padding would be present -> need to rewrite
|
||||||
|
padding = 0; // can't write the preferred padding despite rewriting
|
||||||
|
rewriteRequired = true;
|
||||||
}
|
}
|
||||||
progress.updateStep(rewriteRequired ? "Preparing streams for rewriting ..." : "Preparing streams for updating ...");
|
} else if (rewriteRequired) {
|
||||||
|
// rewriting is forced or new ID3v2 tag is too big for available space
|
||||||
|
// -> use preferred padding when rewriting anyways
|
||||||
|
padding = preferredPadding();
|
||||||
|
} else if (makers.empty() && flacStream && padding && padding < 4) {
|
||||||
|
// no ID3v2 tag -> must include padding in FLAC stream
|
||||||
|
// but padding of 1, 2, and 3 byte isn't possible -> need to rewrite
|
||||||
|
padding = preferredPadding();
|
||||||
|
rewriteRequired = true;
|
||||||
|
}
|
||||||
|
if (rewriteRequired && flacStream && makers.empty() && padding) {
|
||||||
|
// the first 4 byte of FLAC padding actually don't count because these
|
||||||
|
// can not be used for additional meta data
|
||||||
|
padding += 4;
|
||||||
|
}
|
||||||
|
progress.updateStep(rewriteRequired ? "Preparing streams for rewriting ..." : "Preparing streams for updating ...");
|
||||||
|
|
||||||
// setup stream(s) for writing
|
// setup stream(s) for writing
|
||||||
// -> define variables needed to handle output stream and backup stream (required when rewriting the file)
|
// -> define variables needed to handle output stream and backup stream (required when rewriting the file)
|
||||||
string backupPath;
|
string backupPath;
|
||||||
NativeFileStream &outputStream = stream();
|
NativeFileStream &outputStream = stream();
|
||||||
NativeFileStream backupStream; // create a stream to open the backup/original file for the case rewriting the file is required
|
NativeFileStream backupStream; // create a stream to open the backup/original file for the case rewriting the file is required
|
||||||
|
|
||||||
if (rewriteRequired) {
|
if (rewriteRequired) {
|
||||||
if (m_saveFilePath.empty()) {
|
if (m_saveFilePath.empty()) {
|
||||||
// move current file to temp dir and reopen it as backupStream, recreate original file
|
// move current file to temp dir and reopen it as backupStream, recreate original file
|
||||||
try {
|
try {
|
||||||
BackupHelper::createBackupFile(path(), backupPath, outputStream, backupStream);
|
BackupHelper::createBackupFile(path(), backupPath, outputStream, backupStream);
|
||||||
// recreate original file, define buffer variables
|
// recreate original file, define buffer variables
|
||||||
outputStream.open(path(), ios_base::out | ios_base::binary | ios_base::trunc);
|
outputStream.open(path(), ios_base::out | ios_base::binary | ios_base::trunc);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
const char *what = catchIoFailure();
|
const char *const what = catchIoFailure();
|
||||||
diag.emplace_back(DiagLevel::Critical, "Creation of temporary file (to rewrite the original file) failed.", context);
|
diag.emplace_back(DiagLevel::Critical, "Creation of temporary file (to rewrite the original file) failed.", context);
|
||||||
throwIoFailure(what);
|
throwIoFailure(what);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// open the current file as backupStream and create a new outputStream at the specified "save file path"
|
|
||||||
try {
|
|
||||||
close();
|
|
||||||
backupStream.exceptions(ios_base::badbit | ios_base::failbit);
|
|
||||||
backupStream.open(path(), ios_base::in | ios_base::binary);
|
|
||||||
outputStream.open(m_saveFilePath, ios_base::out | ios_base::binary | ios_base::trunc);
|
|
||||||
} catch (...) {
|
|
||||||
const char *what = catchIoFailure();
|
|
||||||
diag.emplace_back(DiagLevel::Critical, "Opening streams to write output file failed.", context);
|
|
||||||
throwIoFailure(what);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
} else { // !rewriteRequired
|
// open the current file as backupStream and create a new outputStream at the specified "save file path"
|
||||||
// reopen original file to ensure it is opened for writing
|
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
outputStream.open(path(), ios_base::in | ios_base::out | ios_base::binary);
|
backupStream.exceptions(ios_base::badbit | ios_base::failbit);
|
||||||
|
backupStream.open(path(), ios_base::in | ios_base::binary);
|
||||||
|
outputStream.open(m_saveFilePath, ios_base::out | ios_base::binary | ios_base::trunc);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
const char *what = catchIoFailure();
|
const char *const what = catchIoFailure();
|
||||||
diag.emplace_back(DiagLevel::Critical, "Opening the file with write permissions failed.", context);
|
diag.emplace_back(DiagLevel::Critical, "Opening streams to write output file failed.", context);
|
||||||
throwIoFailure(what);
|
throwIoFailure(what);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start actual writing
|
} else { // !rewriteRequired
|
||||||
|
// reopen original file to ensure it is opened for writing
|
||||||
try {
|
try {
|
||||||
if (!makers.empty()) {
|
close();
|
||||||
// write ID3v2 tags
|
outputStream.open(path(), ios_base::in | ios_base::out | ios_base::binary);
|
||||||
progress.updateStep("Writing ID3v2 tag ...");
|
|
||||||
for (auto i = makers.begin(), end = makers.end() - 1; i != end; ++i) {
|
|
||||||
i->make(outputStream, 0, diag);
|
|
||||||
}
|
|
||||||
// include padding into the last ID3v2 tag
|
|
||||||
makers.back().make(outputStream, (flacStream && padding && padding < 4) ? 0 : padding, diag);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flacStream) {
|
|
||||||
if (padding && startOfLastMetaDataBlock) {
|
|
||||||
// if appending padding, ensure the last flag of the last "METADATA_BLOCK_HEADER" is not set
|
|
||||||
flacMetaData.seekg(startOfLastMetaDataBlock);
|
|
||||||
flacMetaData.seekp(startOfLastMetaDataBlock);
|
|
||||||
flacMetaData.put(static_cast<byte>(flacMetaData.peek()) & (0x80u - 1));
|
|
||||||
flacMetaData.seekg(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write FLAC metadata
|
|
||||||
outputStream << flacMetaData.rdbuf();
|
|
||||||
|
|
||||||
// write padding
|
|
||||||
if (padding) {
|
|
||||||
flacStream->makePadding(outputStream, padding, true, diag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (makers.empty() && !flacStream) {
|
|
||||||
// just write padding (however, padding should be set to 0 in this case?)
|
|
||||||
for (; padding; --padding) {
|
|
||||||
outputStream.put(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy / skip actual stream data
|
|
||||||
// -> determine media data size
|
|
||||||
uint64 mediaDataSize = size() - streamOffset;
|
|
||||||
if (m_actualExistingId3v1Tag) {
|
|
||||||
mediaDataSize -= 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rewriteRequired) {
|
|
||||||
// copy data from original file
|
|
||||||
switch (m_containerFormat) {
|
|
||||||
case ContainerFormat::MpegAudioFrames:
|
|
||||||
progress.updateStep("Writing MPEG audio frames ...");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
progress.updateStep("Writing frames ...");
|
|
||||||
}
|
|
||||||
backupStream.seekg(streamOffset);
|
|
||||||
CopyHelper<0x4000> copyHelper;
|
|
||||||
copyHelper.callbackCopy(backupStream, stream(), mediaDataSize, bind(&AbortableProgressFeedback::isAborted, ref(progress)),
|
|
||||||
bind(&AbortableProgressFeedback::updateStepPercentage, ref(progress), _1));
|
|
||||||
} else {
|
|
||||||
// just skip actual stream data
|
|
||||||
outputStream.seekp(mediaDataSize, ios_base::cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write ID3v1 tag
|
|
||||||
if (m_id3v1Tag) {
|
|
||||||
progress.updateStep("Writing ID3v1 tag ...");
|
|
||||||
try {
|
|
||||||
m_id3v1Tag->make(stream(), diag);
|
|
||||||
} catch (const Failure &) {
|
|
||||||
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle streams
|
|
||||||
if (rewriteRequired) {
|
|
||||||
// report new size
|
|
||||||
reportSizeChanged(outputStream.tellp());
|
|
||||||
// "save as path" is now the regular path
|
|
||||||
if (!saveFilePath().empty()) {
|
|
||||||
reportPathChanged(saveFilePath());
|
|
||||||
m_saveFilePath.clear();
|
|
||||||
}
|
|
||||||
// stream is useless for further usage because it is write-only
|
|
||||||
outputStream.close();
|
|
||||||
} else {
|
|
||||||
const auto newSize = static_cast<uint64>(outputStream.tellp());
|
|
||||||
if (newSize < size()) {
|
|
||||||
// file is smaller after the modification -> truncate
|
|
||||||
// -> close stream before truncating
|
|
||||||
outputStream.close();
|
|
||||||
// -> truncate file
|
|
||||||
if (truncate(path().c_str(), newSize) == 0) {
|
|
||||||
reportSizeChanged(newSize);
|
|
||||||
} else {
|
|
||||||
diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// file is longer after the modification -> just report new size
|
|
||||||
reportSizeChanged(newSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
BackupHelper::handleFailureAfterFileModified(*this, backupPath, outputStream, backupStream, diag, context);
|
const char *const what = catchIoFailure();
|
||||||
|
diag.emplace_back(DiagLevel::Critical, "Opening the file with write permissions failed.", context);
|
||||||
|
throwIoFailure(what);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start actual writing
|
||||||
|
try {
|
||||||
|
// ensure we can cast padding safely to uint32
|
||||||
|
if (padding > numeric_limits<uint32>::max()) {
|
||||||
|
padding = numeric_limits<uint32>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!makers.empty()) {
|
||||||
|
// write ID3v2 tags
|
||||||
|
progress.updateStep("Writing ID3v2 tag ...");
|
||||||
|
for (auto i = makers.begin(), end = makers.end() - 1; i != end; ++i) {
|
||||||
|
i->make(outputStream, 0, diag);
|
||||||
|
}
|
||||||
|
// include padding into the last ID3v2 tag
|
||||||
|
makers.back().make(outputStream, (flacStream && padding && padding < 4) ? 0 : static_cast<uint32>(padding), diag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flacStream) {
|
||||||
|
if (padding && startOfLastMetaDataBlock) {
|
||||||
|
// if appending padding, ensure the last flag of the last "METADATA_BLOCK_HEADER" is not set
|
||||||
|
flacMetaData.seekg(startOfLastMetaDataBlock);
|
||||||
|
flacMetaData.seekp(startOfLastMetaDataBlock);
|
||||||
|
flacMetaData.put(static_cast<byte>(flacMetaData.peek()) & (0x80u - 1));
|
||||||
|
flacMetaData.seekg(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write FLAC metadata
|
||||||
|
outputStream << flacMetaData.rdbuf();
|
||||||
|
|
||||||
|
// write padding
|
||||||
|
if (padding) {
|
||||||
|
flacStream->makePadding(outputStream, static_cast<uint32>(padding), true, diag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (makers.empty() && !flacStream) {
|
||||||
|
// just write padding (however, padding should be set to 0 in this case?)
|
||||||
|
for (; padding; --padding) {
|
||||||
|
outputStream.put(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy / skip actual stream data
|
||||||
|
// -> determine media data size
|
||||||
|
uint64 mediaDataSize = size() - streamOffset;
|
||||||
|
if (m_actualExistingId3v1Tag) {
|
||||||
|
mediaDataSize -= 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rewriteRequired) {
|
||||||
|
// copy data from original file
|
||||||
|
switch (m_containerFormat) {
|
||||||
|
case ContainerFormat::MpegAudioFrames:
|
||||||
|
progress.updateStep("Writing MPEG audio frames ...");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
progress.updateStep("Writing frames ...");
|
||||||
|
}
|
||||||
|
backupStream.seekg(streamOffset);
|
||||||
|
CopyHelper<0x4000> copyHelper;
|
||||||
|
copyHelper.callbackCopy(backupStream, stream(), mediaDataSize, bind(&AbortableProgressFeedback::isAborted, ref(progress)),
|
||||||
|
bind(&AbortableProgressFeedback::updateStepPercentage, ref(progress), _1));
|
||||||
|
} else {
|
||||||
|
// just skip actual stream data
|
||||||
|
outputStream.seekp(static_cast<std::streamoff>(mediaDataSize), ios_base::cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write ID3v1 tag
|
||||||
|
if (m_id3v1Tag) {
|
||||||
|
progress.updateStep("Writing ID3v1 tag ...");
|
||||||
|
try {
|
||||||
|
m_id3v1Tag->make(stream(), diag);
|
||||||
|
} catch (const Failure &) {
|
||||||
|
diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle streams
|
||||||
|
if (rewriteRequired) {
|
||||||
|
// report new size
|
||||||
|
reportSizeChanged(static_cast<uint64>(outputStream.tellp()));
|
||||||
|
// "save as path" is now the regular path
|
||||||
|
if (!saveFilePath().empty()) {
|
||||||
|
reportPathChanged(saveFilePath());
|
||||||
|
m_saveFilePath.clear();
|
||||||
|
}
|
||||||
|
// stream is useless for further usage because it is write-only
|
||||||
|
outputStream.close();
|
||||||
|
} else {
|
||||||
|
const auto newSize = static_cast<uint64>(outputStream.tellp());
|
||||||
|
if (newSize < size()) {
|
||||||
|
// file is smaller after the modification -> truncate
|
||||||
|
// -> close stream before truncating
|
||||||
|
outputStream.close();
|
||||||
|
// -> truncate file
|
||||||
|
if (truncate(path().c_str(), static_cast<std::streamoff>(newSize)) == 0) {
|
||||||
|
reportSizeChanged(newSize);
|
||||||
|
} else {
|
||||||
|
diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// file is longer after the modification -> just report new size
|
||||||
|
reportSizeChanged(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
BackupHelper::handleFailureAfterFileModified(*this, backupPath, outputStream, backupStream, diag, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace TagParser
|
} // namespace TagParser
|
||||||
|
|
Loading…
Reference in New Issue