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

Automatically update track metadata from file tags if outdated #4218

Merged
merged 15 commits into from
Oct 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/soundio/soundmanagerutil.cpp
src/sources/audiosource.cpp
src/sources/audiosourcestereoproxy.cpp
src/sources/metadatasource.cpp
src/sources/metadatasourcetaglib.cpp
src/sources/readaheadframebuffer.cpp
src/sources/soundsource.cpp
Expand Down
24 changes: 21 additions & 3 deletions src/library/dao/trackdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "library/dao/libraryhashdao.h"
#include "library/dao/playlistdao.h"
#include "library/dao/trackschema.h"
#include "library/library_prefs.h"
#include "library/queryutil.h"
#include "library/trackset/crate/cratestorage.h"
#include "moc_trackdao.cpp"
Expand Down Expand Up @@ -853,8 +854,10 @@ TrackPointer TrackDAO::addTracksAddFile(

// Initially (re-)import the metadata for the newly created track
// from the file.
SoundSourceProxy(pTrack).updateTrackFromSource();
if (!pTrack->isSourceSynchronized()) {
SoundSourceProxy(pTrack).updateTrackFromSource(
m_pConfig,
SoundSourceProxy::UpdateTrackFromSourceMode::Once);
if (!pTrack->checkSourceSynchronized()) {
qWarning() << "TrackDAO::addTracksAddFile:"
<< "Failed to parse track metadata from file"
<< pTrack->getLocation();
Expand Down Expand Up @@ -1491,7 +1494,22 @@ TrackPointer TrackDAO::getTrackById(TrackId trackId) const {
// file. This import might have never been completed successfully
// before, so just check and try for every track that has been
// freshly loaded from the database.
SoundSourceProxy(pTrack).updateTrackFromSource();
auto updateTrackFromSourceMode =
SoundSourceProxy::UpdateTrackFromSourceMode::Once;
if (m_pConfig &&
m_pConfig->getValueString(
mixxx::library::prefs::kSyncTrackMetadataConfigKey)
.toInt() == 1) {
// An implicit re-import and update is performed if the
// user has enabled export of file tags in the preferences.
// Either they want to keep their file tags synchronized or
// not, no exceptions!
updateTrackFromSourceMode =
SoundSourceProxy::UpdateTrackFromSourceMode::Newer;
}
SoundSourceProxy(pTrack).updateTrackFromSource(
m_pConfig,
updateTrackFromSourceMode);
if (kLogger.debugEnabled() && pTrack->isDirty()) {
kLogger.debug()
<< "Updated track metadata from file tags:"
Expand Down
6 changes: 4 additions & 2 deletions src/library/library_prefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ const ConfigKey mixxx::library::prefs::kSearchDebouncingTimeoutMillisConfigKey =
mixxx::library::prefs::kConfigGroup,
QStringLiteral("SearchDebouncingTimeoutMillis")};

const ConfigKey mixxx::library::prefs::kSyncTrackMetadataExportConfigKey =
// The "Export" suffix in the key is kept for backward compatibility
const ConfigKey mixxx::library::prefs::kSyncTrackMetadataConfigKey =
ConfigKey{
mixxx::library::prefs::kConfigGroup,
QStringLiteral("SyncTrackMetadataExport")};

const ConfigKey mixxx::library::prefs::kSeratoMetadataExportConfigKey =
// The naming is unchanged for backward compatibility
const ConfigKey mixxx::library::prefs::kSyncSeratoMetadataConfigKey =
ConfigKey{
mixxx::library::prefs::kConfigGroup,
QStringLiteral("SeratoMetadataExport")};
6 changes: 4 additions & 2 deletions src/library/library_prefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ extern const ConfigKey kEditMetadataSelectedClickConfigKey;

const bool kEditMetadataSelectedClickDefault = false;

extern const ConfigKey kSyncTrackMetadataExportConfigKey;
extern const ConfigKey kSyncTrackMetadataConfigKey;

extern const ConfigKey kSeratoMetadataExportConfigKey;
extern const ConfigKey kSyncSeratoMetadataConfigKey;

extern const ConfigKey kSyncSeratoMetadataConfigKey;

} // namespace prefs

Expand Down
2 changes: 1 addition & 1 deletion src/library/trackcollectionmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ void TrackCollectionManager::exportTrackMetadata(
(pTrack->isDirty() &&
m_pConfig &&
m_pConfig->getValueString(
mixxx::library::prefs::kSyncTrackMetadataExportConfigKey)
mixxx::library::prefs::kSyncTrackMetadataConfigKey)
.toInt() == 1)) {
switch (mode) {
case TrackMetadataExportMode::Immediate:
Expand Down
22 changes: 11 additions & 11 deletions src/preferences/dialog/dlgpreflibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ DlgPrefLibrary::DlgPrefLibrary(
QDesktopServices::openUrl(url);
});

connect(checkBox_SyncTrackMetadataExport,
connect(checkBox_SyncTrackMetadata,
&QCheckBox::toggled,
this,
&DlgPrefLibrary::slotSyncTrackMetadataExportToggled);
&DlgPrefLibrary::slotSyncTrackMetadataToggled);

// Initialize the controls after all slots have been connected
slotUpdate();
Expand Down Expand Up @@ -191,7 +191,7 @@ void DlgPrefLibrary::initializeDirList() {

void DlgPrefLibrary::slotResetToDefaults() {
checkBox_library_scan->setChecked(false);
checkBox_SyncTrackMetadataExport->setChecked(false);
checkBox_SyncTrackMetadata->setChecked(false);
checkBox_SeratoMetadataExport->setChecked(false);
checkBox_use_relative_path->setChecked(false);
checkBox_show_rhythmbox->setChecked(true);
Expand All @@ -211,10 +211,10 @@ void DlgPrefLibrary::slotUpdate() {
initializeDirList();
checkBox_library_scan->setChecked(m_pConfig->getValue(
ConfigKey("[Library]","RescanOnStartup"), false));
checkBox_SyncTrackMetadataExport->setChecked(
m_pConfig->getValue(kSyncTrackMetadataExportConfigKey, false));
checkBox_SyncTrackMetadata->setChecked(
m_pConfig->getValue(kSyncTrackMetadataConfigKey, false));
checkBox_SeratoMetadataExport->setChecked(
m_pConfig->getValue(kSeratoMetadataExportConfigKey, false));
m_pConfig->getValue(kSyncSeratoMetadataConfigKey, false));
checkBox_use_relative_path->setChecked(m_pConfig->getValue(
ConfigKey("[Library]","UseRelativePathOnExport"), false));
checkBox_show_rhythmbox->setChecked(m_pConfig->getValue(
Expand Down Expand Up @@ -379,10 +379,10 @@ void DlgPrefLibrary::slotApply() {
m_pConfig->set(ConfigKey("[Library]","RescanOnStartup"),
ConfigValue((int)checkBox_library_scan->isChecked()));
m_pConfig->set(
kSyncTrackMetadataExportConfigKey,
ConfigValue{checkBox_SyncTrackMetadataExport->isChecked()});
kSyncTrackMetadataConfigKey,
ConfigValue{checkBox_SyncTrackMetadata->isChecked()});
m_pConfig->set(
kSeratoMetadataExportConfigKey,
kSyncSeratoMetadataConfigKey,
ConfigValue{checkBox_SeratoMetadataExport->isChecked()});
m_pConfig->set(ConfigKey("[Library]","UseRelativePathOnExport"),
ConfigValue((int)checkBox_use_relative_path->isChecked()));
Expand Down Expand Up @@ -467,8 +467,8 @@ void DlgPrefLibrary::slotSearchDebouncingTimeoutMillisChanged(int searchDebounci
WSearchLineEdit::setDebouncingTimeoutMillis(searchDebouncingTimeoutMillis);
}

void DlgPrefLibrary::slotSyncTrackMetadataExportToggled() {
if (isVisible() && checkBox_SyncTrackMetadataExport->isChecked()) {
void DlgPrefLibrary::slotSyncTrackMetadataToggled() {
if (isVisible() && checkBox_SyncTrackMetadata->isChecked()) {
mixxx::DlgTrackMetadataExport::showMessageBoxOncePerSession();
}
}
2 changes: 1 addition & 1 deletion src/preferences/dialog/dlgpreflibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
private slots:
void slotRowHeightValueChanged(int);
void slotSelectFont();
void slotSyncTrackMetadataExportToggled();
void slotSyncTrackMetadataToggled();
void slotSearchDebouncingTimeoutMillisChanged(int);
void slotSeratoMetadataExportClicked(bool);

Expand Down
14 changes: 10 additions & 4 deletions src/preferences/dialog/dlgpreflibrarydlg.ui
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,22 @@
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox_SyncTrackMetadataExport">
<widget class="QCheckBox" name="checkBox_SyncTrackMetadata">
<property name="text">
<string>Automatically write modified track metadata from the library into file tags</string>
<string>Synchronize library track metadata from/to file tags</string>
</property>
<property name="toolTip">
<string>Automatically write modified track metadata from the library into file tags and reimport metadata from updated file tags into the library</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_SeratoMetadataExport">
<property name="text">
<string>Write Serato Metadata to files (experimental)</string>
<string>Synchronize Serato track metadata from/to file tags (experimental)</string>
</property>
<property name="toolTip">
<string>Keeps track color, beat grid, bpm lock, cue points, and loops synchronized with SERATO_MARKERS/MARKERS2 file tags.&lt;br/&gt;&lt;br/&gt;WARNING: Enabling this option also enables the reimport of Serato metadata after files have been modified outside of Mixxx. On reimport existing metadata in Mixxx is replaced with the metadata found in file tags. Custom metadata not included in file tags like loop colors is lost.</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -453,7 +459,7 @@
<tabstop>PushButtonAddDir</tabstop>
<tabstop>PushButtonRelocateDir</tabstop>
<tabstop>PushButtonRemoveDir</tabstop>
<tabstop>checkBox_SyncTrackMetadataExport</tabstop>
<tabstop>checkBox_SyncTrackMetadata</tabstop>
<tabstop>checkBox_library_scan</tabstop>
<tabstop>checkBoxEditMetadataSelectedClicked</tabstop>
<tabstop>checkBox_use_relative_path</tabstop>
Expand Down
27 changes: 27 additions & 0 deletions src/sources/metadatasource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "sources/metadatasource.h"

#include "util/logger.h"

namespace mixxx {

namespace {

const Logger kLogger("MetadataSource");

} // anonymous namespace

// static
QDateTime MetadataSource::getFileSynchronizedAt(const QFileInfo& fileInfo) {
const QDateTime lastModifiedUtc = fileInfo.lastModified().toUTC();
// Ignore bogus values like 1970-01-01T00:00:00.000 UTC
// that are obviously incorrect and don't provide any
// information.
if (lastModifiedUtc.isValid() &&
// Only defined if valid
lastModifiedUtc.toMSecsSinceEpoch() == 0) {
return QDateTime{};
}
return lastModifiedUtc;
}

} // namespace mixxx
4 changes: 3 additions & 1 deletion src/sources/metadatasource.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <QDateTime>
#include <QFileInfo>
#include <QImage>

#include <utility>

#include "track/trackmetadata.h"
Expand All @@ -20,6 +20,8 @@ class MetadataSource {
public:
virtual ~MetadataSource() = default;

static QDateTime getFileSynchronizedAt(const QFileInfo& fileInfo);

enum class ImportResult {
Succeeded,
Failed,
Expand Down
19 changes: 4 additions & 15 deletions src/sources/metadatasourcetaglib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,18 @@ class AiffFile : public TagLib::RIFF::AIFF::File {
}
};

inline QDateTime getSourceSynchronizedAt(const QFileInfo& fileInfo) {
const QDateTime lastModifiedUtc = fileInfo.lastModified().toUTC();
// Ignore bogus values like 1970-01-01T00:00:00.000 UTC
// that are obviously incorrect and don't provide any
// information.
if (lastModifiedUtc.isValid() &&
// Only defined if valid
lastModifiedUtc.toMSecsSinceEpoch() == 0) {
return QDateTime{};
}
return lastModifiedUtc;
}

} // anonymous namespace

std::pair<MetadataSourceTagLib::ImportResult, QDateTime>
MetadataSourceTagLib::afterImport(ImportResult importResult) const {
return std::make_pair(importResult, getSourceSynchronizedAt(QFileInfo(m_fileName)));
return std::make_pair(importResult,
MetadataSource::getFileSynchronizedAt(QFileInfo(m_fileName)));
}

std::pair<MetadataSourceTagLib::ExportResult, QDateTime>
MetadataSourceTagLib::afterExport(ExportResult exportResult) const {
return std::make_pair(exportResult, getSourceSynchronizedAt(QFileInfo(m_fileName)));
return std::make_pair(exportResult,
MetadataSource::getFileSynchronizedAt(QFileInfo(m_fileName)));
}

std::pair<MetadataSource::ImportResult, QDateTime>
Expand Down
Loading