Skip to content
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

Cover Art Copy Worker #10902

Merged
merged 18 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
048beb3
cover art copy worker added.
fatihemreyildiz Sep 15, 2022
643272e
wcoverartmenu: coverartcopyworker added
fatihemreyildiz Sep 20, 2022
36e8696
safelywritablefile: prefix added for cacopy worker
fatihemreyildiz Sep 21, 2022
9a52cd3
safelywritablefile: boolean flag added for using "temp" as prefix or …
fatihemreyildiz Sep 22, 2022
690c645
safelywritablefile: condition & methods polished
fatihemreyildiz Sep 24, 2022
76b9dc8
safelywritablefile.h: unused m_UseTempFile deleted
fatihemreyildiz Sep 25, 2022
2e13c64
safelywritablefile: reverted to old constructor
fatihemreyildiz Sep 25, 2022
a333bcf
safelywritablefile: enum safetymodes added
fatihemreyildiz Sep 27, 2022
372741f
SafelyWritableFile: Enum names change to CamelCase
fatihemreyildiz Sep 27, 2022
775eed1
CoverWorker: OverwriteAnswer change to CamelCase
fatihemreyildiz Sep 27, 2022
c63be19
Safelywritablefile: Redundant break deleted
fatihemreyildiz Sep 27, 2022
d6102b0
wcoverartmenu: Disc Access moved to another thread
fatihemreyildiz Sep 29, 2022
f974a48
coverartworker: signal name changed & run protected
fatihemreyildiz Sep 30, 2022
e5558a2
coverartcopyworker: Error handling added.
fatihemreyildiz Sep 30, 2022
97196d8
coverartcopyworker: fail signal added.
fatihemreyildiz Sep 30, 2022
48977b3
coverartcopyworker: fileaccess.info() added
fatihemreyildiz Sep 30, 2022
388fe59
coverartcopyworker: wait() added in destructor
fatihemreyildiz Oct 1, 2022
a615bed
wcoverartmenu: m_isWorkerRunning initialized.
fatihemreyildiz Oct 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/library/dlgtrackinfo.cpp
src/library/dlgtrackinfo.ui
src/library/dlgtrackmetadataexport.cpp
src/library/export/coverartcopyworker.cpp
src/library/export/dlgtrackexport.ui
src/library/export/trackexportdlg.cpp
src/library/export/trackexportwizard.cpp
Expand Down Expand Up @@ -950,6 +951,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/util/duration.cpp
src/util/experiment.cpp
src/util/file.cpp
src/util/imagefiledata.cpp
src/util/fileaccess.cpp
src/util/fileinfo.cpp
src/util/filename.cpp
Expand Down
107 changes: 107 additions & 0 deletions src/library/export/coverartcopyworker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "library/export/coverartcopyworker.h"

#include <QDebug>
#include <QFileInfo>
#include <QMessageBox>

#include "util/fileaccess.h"
#include "util/imagefiledata.h"
#include "util/safelywritablefile.h"

void CoverArtCopyWorker::run() {
auto coverArtFileInfo = mixxx::FileInfo(m_selectedCoverArtFilePath);
auto selectedCoverFileAccess = mixxx::FileAccess(coverArtFileInfo);
if (!selectedCoverFileAccess.isReadable()) {
if (Sandbox::askForAccess(&coverArtFileInfo)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will issue a message box from this thread. I am not an apply guy. Is this necessary at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't notice that the askForAccess populates message box. That will cause a SEG fault according to my experience with this worker. So I just check if it is readable, if it is not readable emitting fail signal would work IMHO. What do you think?

selectedCoverFileAccess = mixxx::FileAccess(coverArtFileInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need also check for failure in the second path. if this code remains.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So double check isReadable for both of the paths would work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the isReadable() == true state at least. I have no clue about the implications on macOS.

} else {
return;
}
}

ImageFileData imageFileData = ImageFileData::fromFilePath(m_selectedCoverArtFilePath);
if (imageFileData.isNull()) {
// TODO(rryan): feedback
return;
}

m_coverInfo.type = CoverInfo::FILE;
m_coverInfo.source = CoverInfo::USER_SELECTED;
m_coverInfo.coverLocation = m_selectedCoverArtFilePath;
m_coverInfo.setImage(imageFileData);

if (QFileInfo(m_oldCoverArtFilePath).canonicalPath() ==
QFileInfo(m_selectedCoverArtFilePath).canonicalPath()) {
qDebug() << "Track and selected cover art are in the same path:"
<< QFileInfo(m_selectedCoverArtFilePath).canonicalPath()
<< "Cover art updated without copying";
emit coverArtUpdated(m_coverInfo);
return;
daschuer marked this conversation as resolved.
Show resolved Hide resolved
}

copyFile(m_selectedCoverArtFilePath, m_oldCoverArtFilePath);
}

void CoverArtCopyWorker::copyFile(
const QString& m_selectedCoverArtFilePath,
const QString& m_oldCoverArtFilePath) {
QFileInfo coverArtPathFileInfo(m_oldCoverArtFilePath);
ImageFileData imageFileData = ImageFileData::fromFilePath(m_selectedCoverArtFilePath);

if (coverArtPathFileInfo.exists()) {
switch (makeOverwriteRequest(m_oldCoverArtFilePath)) {
case OverwriteAnswer::Cancel:
return;
case OverwriteAnswer::Overwrite:
break;
}

mixxx::SafelyWritableFile safelyWritableFile(m_oldCoverArtFilePath,
mixxx::SafelyWritableFile::SafetyMode::Replace);

DEBUG_ASSERT(!safelyWritableFile.fileName().isEmpty());
if (imageFileData.saveFile(safelyWritableFile.fileName())) {
qDebug() << "Cover art"
<< m_oldCoverArtFilePath
<< "copied successfully";
fatihemreyildiz marked this conversation as resolved.
Show resolved Hide resolved
safelyWritableFile.commit();
} else {
qWarning() << "Error while copying the cover art to" << safelyWritableFile.fileName();
}
} else {
if (imageFileData.saveFile(m_oldCoverArtFilePath)) {
qDebug() << "Cover art"
<< m_oldCoverArtFilePath
<< "copied successfully";
} else {
qWarning() << "Error while copying the cover art to" << m_oldCoverArtFilePath;
fatihemreyildiz marked this conversation as resolved.
Show resolved Hide resolved
}
}
emit coverArtUpdated(m_coverInfo);
quit();
fatihemreyildiz marked this conversation as resolved.
Show resolved Hide resolved
}

CoverArtCopyWorker::OverwriteAnswer CoverArtCopyWorker::makeOverwriteRequest(
const QString& filename) {
QScopedPointer<std::promise<OverwriteAnswer>> mode_promise(
new std::promise<OverwriteAnswer>());
std::future<OverwriteAnswer> mode_future = mode_promise->get_future();
emit askOverwrite(filename, mode_promise.data());

mode_future.wait();

if (!mode_future.valid()) {
qWarning() << "CoverArtCopyWorker::askOverwrite invalid answer from future";
return OverwriteAnswer::Cancel;
}

OverwriteAnswer answer = mode_future.get();
switch (answer) {
case OverwriteAnswer::Cancel:
qDebug() << "Cover art overwrite declined";
break;
default:;
}

return answer;
}
50 changes: 50 additions & 0 deletions src/library/export/coverartcopyworker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

#include <QObject>
#include <QScopedPointer>
#include <QString>
#include <QThread>
#include <future>

#include "library/coverart.h"
#include "library/coverartutils.h"
#include "util/fileinfo.h"
#include "util/imagefiledata.h"

// This is a QThread class for copying the cover art.

class CoverArtCopyWorker : public QThread {
Q_OBJECT
public:
enum class OverwriteAnswer {
Overwrite,
Cancel = -1,
};

CoverArtCopyWorker(const QString& selectedCoverArtFilePath,
const QString& oldCoverArtFilePath)
: m_selectedCoverArtFilePath(selectedCoverArtFilePath),
m_oldCoverArtFilePath(oldCoverArtFilePath) {
qRegisterMetaType<CoverInfoRelative>("CoverInfoRelative");
}

~CoverArtCopyWorker() override = default;

signals:
void askOverwrite(const QString& filename,
std::promise<CoverArtCopyWorker::OverwriteAnswer>* promise);
void coverArtUpdated(const CoverInfoRelative& coverInfo);

protected:
void run() override;

private:
void copyFile(const QString& m_selectedCoverArtFilePath,
const QString& m_oldCoverArtFilePath);

OverwriteAnswer makeOverwriteRequest(const QString& filename);
fatihemreyildiz marked this conversation as resolved.
Show resolved Hide resolved

CoverInfoRelative m_coverInfo;
const QString m_selectedCoverArtFilePath;
const QString m_oldCoverArtFilePath;
};
6 changes: 2 additions & 4 deletions src/sources/metadatasourcetaglib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ namespace {

Logger kLogger("MetadataSourceTagLib");

// TODO(uklotzde): Add a configurable option in the user settings
const bool kExportTrackMetadataIntoTemporaryFile = true;
daschuer marked this conversation as resolved.
Show resolved Hide resolved

// Workaround for missing functionality in TagLib 1.11.x that
// doesn't support to read text chunks from AIFF files.
// See also:
Expand Down Expand Up @@ -613,7 +610,8 @@ MetadataSourceTagLib::exportTrackMetadata(
<< "into file" << m_fileName
<< "with type" << m_fileType;

SafelyWritableFile safelyWritableFile(m_fileName, kExportTrackMetadataIntoTemporaryFile);
SafelyWritableFile safelyWritableFile(m_fileName,
SafelyWritableFile::SafetyMode::Edit);
if (!safelyWritableFile.isReady()) {
kLogger.warning()
<< "Unable to export track metadata into file"
Expand Down
32 changes: 28 additions & 4 deletions src/util/safelywritablefile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ const Logger kLogger("SafelyWritableFile");
// Appended to the original file name of the temporary file used for writing
const QString kSafelyWritableTempFileSuffix = QStringLiteral("_temp");

// SafelyWritableFile is also used to write cover arts.
// Cover art worker is suffix sensetive.
// In order to have a successful writing we need a correct suffix.
// So instead of suffix, we need a temp file prefix.
// There is a bug related with the image format reported
// See: https://bugreports.qt.io/browse/QTBUG-89022
const QString kSafelyWritableTempFilePrefix = QStringLiteral("temp_");

// Appended to the original file name for renaming and before deleting this
// file. Should not be longer than kSafelyWritableTempFileSuffix to avoid
// potential failures caused by exceeded path length.
Expand All @@ -31,7 +39,8 @@ const int kWindowsSharingViolationSleepBeforeNextRetryMillis = 100;

} // namespace

SafelyWritableFile::SafelyWritableFile(QString origFileName, bool useTemporaryFile) {
SafelyWritableFile::SafelyWritableFile(QString origFileName,
SafelyWritableFile::SafetyMode safetyMode) {
// Both file names remain uninitialized until all prerequisite operations
// in the constructor have been completed successfully. Otherwise failure
// to create the temporary file will not be handled correctly!
Expand All @@ -46,7 +55,8 @@ SafelyWritableFile::SafelyWritableFile(QString origFileName, bool useTemporaryFi
// Abort constructor
return;
}
if (useTemporaryFile) {
switch (safetyMode) {
case SafetyMode::Edit: {
QString tempFileName = origFileName + kSafelyWritableTempFileSuffix;
QFile origFile(origFileName);
if (!origFile.copy(tempFileName)) {
fatihemreyildiz marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -80,10 +90,24 @@ SafelyWritableFile::SafelyWritableFile(QString origFileName, bool useTemporaryFi
// Successfully cloned original into temporary file for writing - finish initialization
m_origFileName = std::move(origFileName);
m_tempFileName = std::move(tempFileName);
} else {
// Directly write into original file - finish initialization
daschuer marked this conversation as resolved.
Show resolved Hide resolved
break;
}
case SafetyMode::Replace: {
QString origFilePath = QFileInfo(origFileName).canonicalPath();
QString origFileCompleteBasename = QFileInfo(origFileName).completeBaseName();
QString origFileSuffix = QFileInfo(origFileName).suffix();
QString tempFileCompleteBasename = kSafelyWritableTempFilePrefix +
origFileCompleteBasename + '.' + origFileSuffix;
QString tempFileName = origFilePath + '/' + tempFileCompleteBasename;
m_origFileName = std::move(origFileName);
m_tempFileName = std::move(tempFileName);
break;
}
case SafetyMode::Direct: {
// Directly write into original file - finish initialization
DEBUG_ASSERT(m_tempFileName.isNull());
m_origFileName = std::move(origFileName);
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/util/safelywritablefile.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ namespace mixxx {

class SafelyWritableFile final {
public:
SafelyWritableFile(QString origFileName, bool useTemporaryFile);
enum class SafetyMode {
// Bypass
Direct,
// Create a temp file with suffix
Edit,
// Use temp file name with prefix
Replace,
};
SafelyWritableFile(QString origFileName,
SafelyWritableFile::SafetyMode safetyMode);
~SafelyWritableFile();

const QString& fileName() const;
Expand All @@ -34,7 +43,6 @@ class SafelyWritableFile final {
void cancel();

private:
bool m_UseTemporaryFile;
QString m_origFileName;
QString m_tempFileName;
};
Expand Down
Loading