Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
38 changes: 38 additions & 0 deletions src/library/dao/trackdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1778,6 +1778,44 @@ bool TrackDAO::updateTrack(const Track& track) const {
return true;
}

// Relocate the file linked to the track
bool TrackDAO::relocateTrack(const Track& track, const mixxx::FileInfo& newLocation) const {
const TrackId trackId = track.getId();
DEBUG_ASSERT(trackId.isValid());

kLogger.debug() << "Relocating track" << trackId
<< "from" << track.getLocation()
<< "to" << newLocation;

SqlTransaction transaction(m_database);
FwdSqlQuery query(m_database,
"UPDATE track_locations SET "
"location = :location,"
"directory = :directory,"
"filename = :filename,"
"filesize = :filesize,"
"fs_deleted = 0,"
"needs_verification = 0 "
"WHERE id=(SELECT location FROM library WHERE id =:trackId)");
query.bindValue(":location", newLocation.location());
query.bindValue(":directory", newLocation.locationPath());
query.bindValue(":filename", newLocation.fileName());
query.bindValue(":filesize", QVariant::fromValue(newLocation.sizeInBytes()));
query.bindValue(":trackId", trackId.toVariant());

if (query.hasError() || !query.execPrepared()) {
return false;
}

if (query.numRowsAffected() == 0) {
kLogger.warning() << "relocateTrack had no effect: trackId " << trackId << "invalid.";
return false;
}
transaction.commit();

return true;
}
Comment thread
louisld marked this conversation as resolved.

@ronso0 ronso0 Jul 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The similarly named variables in this function make it a bit hard to understand the flow.
Let's rename for example
newTrackLocationId -> existingTrackLocationId
queryNewLocation -> queryExistingTrackLocation

and also add some comments:

Which cases it supposed to cover? (library scan already added a track with new location vs. new location not in db yet)
etc.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The else case covers the case where the new file is outside of the library folder.


// Make sure that `directory` in in track_locations table is indeed a
// directory path. This works around / removes residues of a bug where tracks
// are falsely marked missing because `directory` == `location`.
Expand Down
1 change: 1 addition & 0 deletions src/library/dao/trackdao.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class TrackDAO : public QObject, public virtual DAO, public virtual GlobalTrackC
void addTracksFinish(bool rollback = false);

bool updateTrack(const Track& track) const;
bool relocateTrack(const Track& track, const mixxx::FileInfo& newLocation) const;

void hideAllTracks(const QDir& rootDir) const;

Expand Down
16 changes: 10 additions & 6 deletions src/library/missing_hidden/dlgmissing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ DlgMissing::DlgMissing(

connect(btnPurge, &QPushButton::clicked, m_pTrackTableView, &WTrackTableView::slotPurge);
connect(btnSelect, &QPushButton::clicked, this, &DlgMissing::selectAll);
connect(btnRelocate,
&QPushButton::clicked,
m_pTrackTableView,
&WTrackTableView::slotRelocateTrack);
connect(m_pTrackTableView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
Expand Down Expand Up @@ -75,14 +79,14 @@ void DlgMissing::selectAll() {
m_pTrackTableView->selectAll();
}

void DlgMissing::activateButtons(bool enable) {
btnPurge->setEnabled(enable);
void DlgMissing::activateButtons(int nbSelected) {
btnPurge->setEnabled(nbSelected >= 1);
btnRelocate->setEnabled(nbSelected == 1);
Comment thread
louisld marked this conversation as resolved.
Outdated
}

void DlgMissing::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected) {
Q_UNUSED(deselected);
activateButtons(!selected.indexes().isEmpty());
void DlgMissing::selectionChanged([[maybe_unused]] const QItemSelection& selected,
[[maybe_unused]] const QItemSelection& deselected) {
activateButtons(m_pTrackTableView->selectionModel()->selectedRows().count());
}

bool DlgMissing::hasFocus() const {
Expand Down
2 changes: 1 addition & 1 deletion src/library/missing_hidden/dlgmissing.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DlgMissing : public QWidget, public Ui::DlgMissing, public LibraryView {
void trackSelected(TrackPointer pTrack);

private:
void activateButtons(bool enable);
void activateButtons(int nbSelected);
Comment thread
louisld marked this conversation as resolved.
Outdated
WTrackTableView* m_pTrackTableView;
MissingTableModel* m_pMissingTableModel;
};
16 changes: 16 additions & 0 deletions src/library/missing_hidden/dlgmissing.ui
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRelocate">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Relocate the source file of the selected track from the library.</string>
Comment thread
louisld marked this conversation as resolved.
Outdated
</property>
<property name="text">
<string>Relocate</string>
Comment thread
louisld marked this conversation as resolved.
Outdated
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
Expand Down
8 changes: 8 additions & 0 deletions src/library/missing_hidden/missingtablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ void MissingTableModel::purgeTracks(const QModelIndexList& indices) {
select(); //Repopulate the data model.
}

void MissingTableModel::relocateTrack(
const QModelIndex& index, const mixxx::FileInfo& newLocation) {
TrackPointer pTrack = getTrack(index);
if (m_pTrackCollectionManager->relocateTrack(*pTrack, newLocation)) {
Comment thread
louisld marked this conversation as resolved.
Outdated
select(); // Repopulate the data model
}
}

bool MissingTableModel::isColumnInternal(int column) {
return column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_ID) ||
column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED) ||
Expand Down
1 change: 1 addition & 0 deletions src/library/missing_hidden/missingtablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class MissingTableModel final : public BaseSqlTableModel {

bool isColumnInternal(int column) final;
void purgeTracks(const QModelIndexList& indices) final;
void relocateTrack(const QModelIndex& index, const mixxx::FileInfo& newLocation) final;
Qt::ItemFlags flags(const QModelIndex& index) const final;
Capabilities getCapabilities() const final;

Expand Down
7 changes: 7 additions & 0 deletions src/library/trackcollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,13 @@ bool TrackCollection::purgeAllTracks(
return purgeTracks(trackIds);
}

bool TrackCollection::relocateTrack(const Track& track,
const mixxx::FileInfo& newLocation) {
DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);

return m_trackDao.relocateTrack(track, newLocation);
}

bool TrackCollection::insertCrate(
const Crate& crate,
CrateId* pCrateId) {
Expand Down
2 changes: 2 additions & 0 deletions src/library/trackcollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ class TrackCollection : public QObject,
bool purgeTracks(const QList<TrackId>& trackIds);
bool purgeAllTracks(const QDir& rootDir);

bool relocateTrack(const Track& track, const mixxx::FileInfo& newLocation);

DirectoryDAO::AddResult addDirectory(const mixxx::FileInfo& rootDir);
DirectoryDAO::RemoveResult removeDirectory(const mixxx::FileInfo& rootDir);
DirectoryDAO::RelocateResult relocateDirectory(const QString& oldDir, const QString& newDir);
Expand Down
7 changes: 7 additions & 0 deletions src/library/trackcollectionmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,13 @@ void TrackCollectionManager::purgeAllTracks(const QDir& rootDir) const {
}
}

bool TrackCollectionManager::relocateTrack(const Track& track,
const mixxx::FileInfo& newLocation) {
DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);

return m_pInternalCollection->relocateTrack(track, newLocation);
}

TrackPointer TrackCollectionManager::getOrAddTrack(
const TrackRef& trackRef,
bool* pAlreadyInLibrary) const {
Expand Down
2 changes: 2 additions & 0 deletions src/library/trackcollectionmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class TrackCollectionManager: public QObject,
void purgeTracks(const QList<TrackRef>& trackRefs) const;
void purgeAllTracks(const QDir& rootDir) const;

bool relocateTrack(const Track& track, const mixxx::FileInfo& newLocation);

DirectoryDAO::AddResult addDirectory(const mixxx::FileInfo& newDir) const;
DirectoryDAO::RemoveResult removeDirectory(const mixxx::FileInfo& oldDir) const;
DirectoryDAO::RelocateResult relocateDirectory(
Expand Down
3 changes: 3 additions & 0 deletions src/library/trackmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ class TrackModel {
virtual void purgeTracks(const QModelIndexList& indices) {
Q_UNUSED(indices);
}
virtual void relocateTrack([[maybe_unused]] const QModelIndex& index,
[[maybe_unused]] const mixxx::FileInfo& newLocation) {
}
Comment thread
louisld marked this conversation as resolved.
Outdated
virtual int addTracks(const QModelIndex& index, const QList<QString>& locations) {
Q_UNUSED(index);
Q_UNUSED(locations);
Expand Down
37 changes: 37 additions & 0 deletions src/widget/wtracktableview.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "widget/wtracktableview.h"

#include <qlogging.h>
Comment thread
louisld marked this conversation as resolved.
Outdated

#include <QDrag>
#include <QFileDialog>
#include <QModelIndex>
#include <QScrollBar>
#include <QShortcut>
Expand Down Expand Up @@ -514,6 +517,40 @@ void WTrackTableView::slotPurge() {
restoreCurrentIndex();
}

void WTrackTableView::slotRelocateTrack() {
TrackModel* pTrackModel = getTrackModel();
if (!pTrackModel) {
return;
}
// Only works for one selected track
const QModelIndexList indices = selectionModel()->selectedRows();
if (indices.count() != 1) {
return;
}
TrackPointer pTrack = pTrackModel->getTrack(indices[0]);
if (!pTrack) {
return;
}
QString location = QFileInfo(pTrack->getLocation()).absolutePath();
if (location.isEmpty() || !QDir(location).exists()) {
location = QDir::homePath();
}

const QString newLocation = QFileDialog::getOpenFileName(
this,
tr("Locate missing file: %1").arg(pTrack->getTitle()),
location,
QString("Audio Files (%1)")
.arg(SoundSourceProxy::getSupportedFileNamePatterns().join(" ")));

if (newLocation.isEmpty()) {
return;
}

const mixxx::FileInfo fileInfo(newLocation);
pTrackModel->relocateTrack(indices[0], fileInfo);
}

void WTrackTableView::slotDeleteTracksFromDisk() {
const QModelIndexList indices = getSelectedRows();
if (indices.isEmpty()) {
Expand Down
1 change: 1 addition & 0 deletions src/widget/wtracktableview.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class WTrackTableView : public WLibraryTableView {
void slotMouseDoubleClicked(const QModelIndex &);
void slotUnhide();
void slotPurge();
void slotRelocateTrack();
void slotDeleteTracksFromDisk();
void slotShowHideTrackMenu(bool show);

Expand Down