summaryrefslogtreecommitdiffstats
path: root/src/library/library.cpp
diff options
context:
space:
mode:
authorUwe Klotz <uklotz@mixxx.org>2019-09-29 09:53:02 +0200
committerUwe Klotz <uklotz@mixxx.org>2019-09-29 09:53:02 +0200
commit1d882d8b5022005a120e23f20496f8cd9795d45c (patch)
tree5089fa8478860f26ec9e0a3f91f25269285bc76e /src/library/library.cpp
parenta1fdea2aef545ee0bd9f25027f0d66f936325cf8 (diff)
parent5998ee24e30361b3d14907d899e191a0aee16a3b (diff)
Merge branch 'master' of git@github.com:mixxxdj/mixxx.git into new-signals-slots-syntax-library
# Conflicts: # src/library/library.cpp # src/library/scanner/libraryscanner.cpp
Diffstat (limited to 'src/library/library.cpp')
-rw-r--r--src/library/library.cpp285
1 files changed, 274 insertions, 11 deletions
diff --git a/src/library/library.cpp b/src/library/library.cpp
index bd9dfa3376..e8225b4d43 100644
--- a/src/library/library.cpp
+++ b/src/library/library.cpp
@@ -40,6 +40,7 @@
#include "controllers/keyboard/keyboardeventfilter.h"
+#include "library/externaltrackcollection.h"
namespace {
@@ -91,14 +92,38 @@ Library::Library(
kLogger.info() << "Connecting database";
m_pTrackCollection->connectDatabase(dbConnection);
+#if defined(__AOIDE__)
+ m_externalTrackCollections += new mixxx::aoide::TrackCollection(pConfig, m_pTrackCollection, this);
+#endif
+
qRegisterMetaType<Library::RemovalType>("Library::RemovalType");
m_pKeyNotation.reset(new ControlObject(ConfigKey(kConfigGroup, "key_notation")));
- connect(&m_scanner, &LibraryScanner::scanStarted, this, &Library::scanStarted);
- connect(&m_scanner, &LibraryScanner::scanFinished, this, &Library::scanFinished);
- // Refresh the library models when the library (re)scan is finished.
- connect(&m_scanner, &LibraryScanner::scanFinished, this, &Library::slotRefreshLibraryModels);
+ connect(&m_scanner,
+ &LibraryScanner::scanStarted,
+ this,
+ &Library::scanStarted);
+ connect(&m_scanner,
+ &LibraryScanner::scanFinished,
+ this,
+ &Library::scanFinished);
+ connect(&m_scanner,
+ &LibraryScanner::scanFinished,
+ this,
+ &Library::slotRefreshLibraryModels);
+ connect(&m_scanner,
+ &LibraryScanner::trackAdded,
+ this,
+ &Library::slotScanTrackAdded);
+ connect(&m_scanner,
+ &LibraryScanner::tracksChanged,
+ this,
+ &Library::slotScanTracksUpdated);
+ connect(&m_scanner,
+ &LibraryScanner::tracksReplaced,
+ this,
+ &Library::slotScanTracksReplaced);
// TODO(rryan) -- turn this construction / adding of features into a static
// method or something -- CreateDefaultLibrary
@@ -164,6 +189,21 @@ Library::Library(
addFeature(new TraktorFeature(this, m_pTrackCollection));
}
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ auto feature = externalTrackCollection->newLibraryFeature(this);
+ if (feature) {
+ kLogger.info()
+ << "Adding library feature for"
+ << externalTrackCollection->name();
+ addFeature(feature);
+ } else {
+ kLogger.info()
+ << "Library feature for"
+ << externalTrackCollection->name()
+ << "is not available";
+ }
+ }
+
// On startup we need to check if all of the user's library folders are
// accessible to us. If the user is using a database from <1.12.0 with
// sandboxing then we will need them to give us permission.
@@ -203,6 +243,10 @@ Library::~Library() {
delete m_pLibraryControl;
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->shutdown();
+ }
+
kLogger.info() << "Disconnecting database";
m_pTrackCollection->disconnectDatabase();
@@ -260,7 +304,12 @@ void Library::bindSidebarWidget(WLibrarySidebar* pSidebarWidget) {
void Library::bindWidget(WLibrary* pLibraryWidget,
KeyboardEventFilter* pKeyboard) {
WTrackTableView* pTrackTableView =
- new WTrackTableView(pLibraryWidget, m_pConfig, m_pTrackCollection);
+ new WTrackTableView(
+ pLibraryWidget,
+ m_pConfig,
+ m_pTrackCollection,
+ true,
+ m_externalTrackCollections);
pTrackTableView->installEventFilter(pKeyboard);
connect(this,
&Library::showTrackModel,
@@ -435,7 +484,7 @@ void Library::slotRequestAddDir(QString dir) {
QDir directory(dir);
Sandbox::createSecurityToken(directory);
- if (!m_pTrackCollection->getDirectoryDAO().addDirectory(dir)) {
+ if (!m_pTrackCollection->addDirectory(dir)) {
QMessageBox::information(0, tr("Add Directory to Library"),
tr("Could not add the directory to your library. Either this "
"directory is already in your library or you are currently "
@@ -457,7 +506,7 @@ void Library::slotRequestRemoveDir(QString dir, RemovalType removalType) {
break;
case Library::PurgeTracks:
// The user requested that we purge all metadata.
- m_pTrackCollection->purgeTracks(dir);
+ purgeAllTracks(dir);
break;
case Library::LeaveTracksUnchanged:
default:
@@ -485,7 +534,7 @@ void Library::slotRequestRemoveDir(QString dir, RemovalType removalType) {
}
void Library::slotRequestRelocateDir(QString oldDir, QString newDir) {
- m_pTrackCollection->relocateDirectory(oldDir, newDir);
+ relocateDirectory(oldDir, newDir);
// also update the config file if necessary so that downgrading is still
// possible
@@ -529,8 +578,222 @@ void Library::saveCachedTrack(Track* pTrack) noexcept {
// concurrently.
m_pTrackCollection->exportTrackMetadata(pTrack);
- // The track must be saved while the cache is locked to
- // prevent that a new track is created from the outdated
- // metadata that is is the database before saving is finished.
+ // Th dirty flag is reset while saving the track in the internal
+ // collection!
+ const bool trackDirty = pTrack->isDirty();
+
+ // This operation must be executed synchronously while the cache is
+ // locked to prevent that a new track is created from outdated
+ // metadata in the database before saving finished.
+ kLogger.debug()
+ << "Saving cached track"
+ << pTrack->getLocation()
+ << "in internal collection";
m_pTrackCollection->saveTrack(pTrack);
+
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ if (pTrack->getId().isValid()) {
+ // Track still exists in the internal collection/database
+ if (trackDirty) {
+ kLogger.debug()
+ << "Saving modified track"
+ << pTrack->getLocation()
+ << "in"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->saveTrack(
+ *pTrack,
+ ExternalTrackCollection::ChangeHint::Modified);
+ }
+ }
+ } else {
+ // Track has been deleted from the local internal collection/database
+ // while it was cached in-memory
+ kLogger.debug()
+ << "Purging deleted track"
+ << pTrack->getLocation()
+ << "from"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->purgeTracks(
+ QStringList{pTrack->getLocation()});
+ }
+ }
+}
+
+void Library::relocateDirectory(QString oldDir, QString newDir) {
+ kLogger.debug()
+ << "Relocating directory in internal track collection:"
+ << oldDir
+ << "->"
+ << newDir;
+ // TODO(XXX): Add error handling in TrackCollection::relocateDirectory()
+ m_pTrackCollection->relocateDirectory(oldDir, newDir);
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ kLogger.debug()
+ << "Relocating directory in"
+ << m_externalTrackCollections.size()
+ << "external track collection(s):"
+ << oldDir
+ << "->"
+ << newDir;
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->relocateDirectory(oldDir, newDir);
+ }
+}
+
+void Library::purgeTracks(const QList<TrackId>& trackIds) {
+ if (trackIds.isEmpty()) {
+ return;
+ }
+ // Collect the corresponding track locations BEFORE purging the
+ // tracks from the internal collection!
+ QList<QString> trackLocations;
+ if (!m_externalTrackCollections.isEmpty()) {
+ trackLocations =
+ m_pTrackCollection->getTrackDAO().getTrackLocations(trackIds);
+ }
+ DEBUG_ASSERT(trackLocations.size() <= trackIds.size());
+ kLogger.debug()
+ << "Purging"
+ << trackIds.size()
+ << "tracks from internal collection";
+ if (!m_pTrackCollection->purgeTracks(trackIds)) {
+ kLogger.warning()
+ << "Failed to purge tracks from internal collection";
+ return;
+ }
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ VERIFY_OR_DEBUG_ASSERT(trackLocations.size() == trackIds.size()) {
+ kLogger.warning()
+ << "Purging only"
+ << trackLocations.size()
+ << "of"
+ << trackIds.size()
+ << "tracks from"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ } else {
+ kLogger.debug()
+ << "Purging"
+ << trackLocations.size()
+ << "tracks from"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ }
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->purgeTracks(trackLocations);
+ }
+}
+
+void Library::purgeAllTracks(const QDir& rootDir) {
+ kLogger.debug()
+ << "Purging directory"
+ << rootDir
+ << "from internal track collection";
+ if (!m_pTrackCollection->purgeAllTracks(rootDir)) {
+ kLogger.warning()
+ << "Failed to purge directory from internal collection";
+ return;
+ }
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ kLogger.debug()
+ << "Purging directory"
+ << rootDir
+ << "from"
+ << m_externalTrackCollections.size()
+ << "external track collection(s)";
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->purgeAllTracks(rootDir);
+ }
+}
+
+void Library::slotScanTrackAdded(TrackPointer pTrack) {
+ DEBUG_ASSERT(pTrack);
+ // Already added to m_pTrackCollection
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ kLogger.debug()
+ << "Adding new track"
+ << pTrack->getLocation()
+ << "to"
+ << m_externalTrackCollections.size()
+ << "external track collection(s)";
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->saveTrack(*pTrack, ExternalTrackCollection::ChangeHint::Added);
+ }
+}
+
+void Library::slotScanTracksUpdated(QSet<TrackId> updatedTrackIds) {
+ // Already updated in m_pTrackCollection
+ if (updatedTrackIds.isEmpty()) {
+ return;
+ }
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ QList<TrackRef> trackRefs;
+ trackRefs.reserve(updatedTrackIds.size());
+ for (const auto& trackId : updatedTrackIds) {
+ auto trackLocation = m_pTrackCollection->getTrackDAO().getTrackLocation(trackId);
+ if (!trackLocation.isEmpty()) {
+ trackRefs.append(TrackRef::fromFileInfo(trackLocation, trackId));
+ }
+ }
+ DEBUG_ASSERT(trackRefs.size() <= updatedTrackIds.size());
+ VERIFY_OR_DEBUG_ASSERT(trackRefs.size() == updatedTrackIds.size()) {
+ kLogger.warning()
+ << "Updating only"
+ << trackRefs.size()
+ << "of"
+ << updatedTrackIds.size()
+ << "track(s) in"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ } else {
+ kLogger.debug()
+ << "Updating"
+ << trackRefs.size()
+ << "track(s) in"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ }
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->updateTracks(trackRefs);
+ }
+}
+
+void Library::slotScanTracksReplaced(QList<QPair<TrackRef, TrackRef>> replacedTracks) {
+ // Already replaced in m_pTrackCollection
+ if (m_externalTrackCollections.isEmpty()) {
+ return;
+ }
+ QList<ExternalTrackCollection::DuplicateTrack> duplicateTracks;
+ duplicateTracks.reserve(replacedTracks.size());
+ for (const auto& replacedTrack : replacedTracks) {
+ ExternalTrackCollection::DuplicateTrack duplicateTrack;
+ duplicateTrack.removed = replacedTrack.first;
+ duplicateTrack.replacedBy = replacedTrack.second;
+ duplicateTracks.append(duplicateTrack);
+ }
+ kLogger.debug()
+ << "Deduplicating"
+ << duplicateTracks.size()
+ << "replaced track(s) in"
+ << m_externalTrackCollections.size()
+ << "external collection(s)";
+ for (const auto& externalTrackCollection : m_externalTrackCollections) {
+ externalTrackCollection->deduplicateTracks(duplicateTracks);
+ }
}