diff options
author | xerus2000 <27jf@pm.me> | 2020-06-11 19:42:13 +0200 |
---|---|---|
committer | xerus2000 <27jf@pm.me> | 2020-06-11 19:42:13 +0200 |
commit | 87575715297e806fc7af33daf7e9572fbf51a607 (patch) | |
tree | 3e5384a9f98aa45161c878343129bff6696bb560 /src/library | |
parent | eb756ee61a4007f3ad91195c57f85e3786f8e1cc (diff) | |
parent | 091253696f83a6b3c8a6e5976f5138e1caaa71f0 (diff) |
Merge branch 'master' into refactor-basetrackset
Diffstat (limited to 'src/library')
-rw-r--r-- | src/library/banshee/bansheeplaylistmodel.h | 2 | ||||
-rw-r--r-- | src/library/coverart.cpp | 110 | ||||
-rw-r--r-- | src/library/coverart.h | 55 | ||||
-rw-r--r-- | src/library/coverartcache.cpp | 107 | ||||
-rw-r--r-- | src/library/coverartcache.h | 4 | ||||
-rw-r--r-- | src/library/coverartdelegate.h | 2 | ||||
-rw-r--r-- | src/library/dao/playlistdao.cpp | 2 | ||||
-rw-r--r-- | src/library/dlgcoverartfullsize.cpp | 5 | ||||
-rw-r--r-- | src/library/hiddentablemodel.h | 2 | ||||
-rw-r--r-- | src/library/missingtablemodel.h | 2 | ||||
-rw-r--r-- | src/library/playlisttablemodel.h | 2 | ||||
-rw-r--r-- | src/library/songdownloader.cpp | 9 | ||||
-rw-r--r-- | src/library/trackcollection.cpp | 51 | ||||
-rw-r--r-- | src/library/trackcollection.h | 8 | ||||
-rw-r--r-- | src/library/trackcollectionmanager.cpp | 27 | ||||
-rw-r--r-- | src/library/trackcollectionmanager.h | 4 | ||||
-rw-r--r-- | src/library/trackset/crate/cratetablemodel.h | 2 |
17 files changed, 287 insertions, 107 deletions
diff --git a/src/library/banshee/bansheeplaylistmodel.h b/src/library/banshee/bansheeplaylistmodel.h index d6cad06cf5..f632c04e67 100644 --- a/src/library/banshee/bansheeplaylistmodel.h +++ b/src/library/banshee/bansheeplaylistmodel.h @@ -11,7 +11,7 @@ #include "library/stardelegate.h" #include "library/basesqltablemodel.h" -class BansheePlaylistModel : public BaseSqlTableModel { +class BansheePlaylistModel final : public BaseSqlTableModel { Q_OBJECT public: BansheePlaylistModel(QObject* pParent, TrackCollectionManager* pTrackCollectionManager, BansheeDbConnection* pConnection); diff --git a/src/library/coverart.cpp b/src/library/coverart.cpp index 491dbbe139..85620cdd87 100644 --- a/src/library/coverart.cpp +++ b/src/library/coverart.cpp @@ -1,5 +1,7 @@ #include "library/coverart.h" +#include <QDebugStateSaver> + #include "library/coverartutils.h" #include "util/debug.h" #include "util/logger.h" @@ -83,19 +85,27 @@ QDebug operator<<(QDebug dbg, const CoverInfoRelative& infoRelative) { .arg(coverInfoRelativeToString(infoRelative)); } -QImage CoverInfo::loadImage( +CoverInfo::LoadedImage CoverInfo::loadImage( const SecurityTokenPointer& pTrackLocationToken) const { + LoadedImage loadedImage(LoadedImage::Result::ErrorUnknown); if (type == CoverInfo::METADATA) { VERIFY_OR_DEBUG_ASSERT(!trackLocation.isEmpty()) { - kLogger.warning() - << "loadImage" - << type - << "cover with empty trackLocation"; - return QImage(); + loadedImage.result = LoadedImage::Result::ErrorMetadataWithEmptyTrackLocation; + return loadedImage; } - return CoverArtUtils::extractEmbeddedCover( + loadedImage.filePath = trackLocation; + loadedImage.image = CoverArtUtils::extractEmbeddedCover( TrackFile(trackLocation), pTrackLocationToken); + if (loadedImage.image.isNull()) { + // TODO: extractEmbeddedCover() should indicate if no image + // is available or if loading the embedded image failed. + // Until then we assume optimistically that no image is + // available instead of presuming that an error occurred. + loadedImage.result = LoadedImage::Result::NoImage; + } else { + loadedImage.result = LoadedImage::Result::Ok; + } } else if (type == CoverInfo::FILE) { auto coverFile = QFileInfo(coverLocation); if (coverFile.isRelative()) { @@ -104,13 +114,9 @@ QImage CoverInfo::loadImage( // must have a valid location, i.e. a file path. Most // likely a programming error, but might also be caused // by yet unknown circumstances. - kLogger.warning() - << "loadImage" - << type - << "cover with empty track location" - << "and relative file path:" - << coverFile.filePath(); - return QImage(); + loadedImage.result = LoadedImage::Result:: + ErrorRelativeFilePathWithEmptyTrackLocation; + return loadedImage; } // Compose track directory with relative path const auto trackFile = TrackFile(trackLocation); @@ -120,28 +126,28 @@ QImage CoverInfo::loadImage( coverLocation); } DEBUG_ASSERT(coverFile.isAbsolute()); + loadedImage.filePath = coverFile.filePath(); if (!coverFile.exists()) { - // Disabled because this code can cause high CPU and thus possibly - // xruns as it might print the warning repeatedly. - // ToDo: Print warning about missing cover image only once. - // kLogger.warning() - // << "loadImage" - // << type - // << "cover does not exist:" - // << coverFile.filePath(); - return QImage(); + loadedImage.result = LoadedImage::Result::ErrorFilePathDoesNotExist; + return loadedImage; } SecurityTokenPointer pToken = Sandbox::openSecurityToken( coverFile, true); - return QImage(coverFile.filePath()); + if (loadedImage.image.load(loadedImage.filePath)) { + DEBUG_ASSERT(!loadedImage.image.isNull()); + loadedImage.result = LoadedImage::Result::Ok; + } else { + DEBUG_ASSERT(loadedImage.image.isNull()); + loadedImage.result = LoadedImage::Result::ErrorLoadingFailed; + } } else if (type == CoverInfo::NONE) { - return QImage(); + loadedImage.result = LoadedImage::Result::NoImage; } else { DEBUG_ASSERT(!"unhandled CoverInfo::Type"); - return QImage(); } + return loadedImage; } bool CoverInfo::refreshImageHash( @@ -154,8 +160,8 @@ bool CoverInfo::refreshImageHash( return false; } QImage image = loadedImage; - if (loadedImage.isNull()) { - image = loadImage(pTrackLocationToken); + if (image.isNull()) { + image = loadImage(pTrackLocationToken).image; } if (image.isNull() && type != CoverInfo::NONE) { kLogger.warning() @@ -185,9 +191,49 @@ QDebug operator<<(QDebug dbg, const CoverInfo& info) { .arg(coverInfoToString(info)); } +QDebug operator<<(QDebug dbg, const CoverInfo::LoadedImage::Result& result) { + switch (result) { + case CoverInfo::LoadedImage::Result::Ok: + return dbg << "Ok"; + case CoverInfo::LoadedImage::Result::NoImage: + return dbg << "NoImage"; + case CoverInfo::LoadedImage::Result::ErrorMetadataWithEmptyTrackLocation: + return dbg << "ErrorMetadataWithEmptyTrackLocation"; + case CoverInfo::LoadedImage::Result::ErrorRelativeFilePathWithEmptyTrackLocation: + return dbg << "ErrorRelativeFilePathWithEmptyTrackLocation"; + case CoverInfo::LoadedImage::Result::ErrorFilePathDoesNotExist: + return dbg << "ErrorFilePathDoesNotExist"; + case CoverInfo::LoadedImage::Result::ErrorLoadingFailed: + return dbg << "ErrorLoadingFailed"; + case CoverInfo::LoadedImage::Result::ErrorUnknown: + return dbg << "ErrorUnknown"; + } + DEBUG_ASSERT(!"unreachable"); + return dbg; +} + +QDebug operator<<(QDebug dbg, const CoverInfo::LoadedImage& loadedImage) { + const QDebugStateSaver saver(dbg); + dbg = dbg.maybeSpace() << "CoverInfo::LoadedImage"; + return dbg.nospace() + << '{' + << loadedImage.filePath + << ',' + << loadedImage.image.size() + << ',' + << loadedImage.result + << '}'; +} + QDebug operator<<(QDebug dbg, const CoverArt& art) { - return dbg.maybeSpace() << QString("CoverArt(%1,%2,%3)") - .arg(coverInfoToString(art), - toDebugString(art.image.size()), - QString::number(art.resizedToWidth)); + const QDebugStateSaver saver(dbg); + dbg = dbg.maybeSpace() << "CoverArt"; + return dbg.nospace() + << '{' + << static_cast<const CoverInfo&>(art) + << ',' + << art.loadedImage + << ',' + << art.resizedToWidth + << '}'; } diff --git a/src/library/coverart.h b/src/library/coverart.h index d9179c3d08..74fa85722a 100644 --- a/src/library/coverart.h +++ b/src/library/coverart.h @@ -85,7 +85,40 @@ class CoverInfo : public CoverInfoRelative { CoverInfo(CoverInfo&&) = default; CoverInfo& operator=(CoverInfo&&) = default; - QImage loadImage( + struct LoadedImage final { + enum class Result { + Ok, + NoImage, + ErrorMetadataWithEmptyTrackLocation, + ErrorRelativeFilePathWithEmptyTrackLocation, + ErrorFilePathDoesNotExist, + ErrorLoadingFailed, // if the image file is not readable or the format is not supported + ErrorUnknown, // should never happen + }; + + LoadedImage(const LoadedImage&) = default; + LoadedImage(LoadedImage&&) = default; + LoadedImage& operator=(const LoadedImage&) = default; + LoadedImage& operator=(LoadedImage&&) = default; + + /// The loaded image if available. + QImage image; + + /// Either the track location if the image was embedded in + /// the metadata or the (absolute) path of the image file. + QString filePath; + + /// The result of the operation. + Result result; + + private: + friend class CoverArt; + friend class CoverInfo; + LoadedImage(Result result) + : result(result) { + } + }; + LoadedImage loadImage( const SecurityTokenPointer& pTrackLocationToken = SecurityTokenPointer()) const; // Verify the image hash and update it if necessary. @@ -103,16 +136,22 @@ bool operator==(const CoverInfo& a, const CoverInfo& b); bool operator!=(const CoverInfo& a, const CoverInfo& b); QDebug operator<<(QDebug dbg, const CoverInfo& info); +QDebug operator<<(QDebug dbg, const CoverInfo::LoadedImage::Result& result); +QDebug operator<<(QDebug dbg, const CoverInfo::LoadedImage& loadedImage); + class CoverArt : public CoverInfo { public: CoverArt() - : resizedToWidth(0) { + : loadedImage(LoadedImage::Result::NoImage), // not loaded = no image + resizedToWidth(0) { } - - CoverArt(const CoverInfo& base, const QImage& img, int rtw) - : CoverInfo(base), - image(img), - resizedToWidth(rtw) { + CoverArt( + const CoverInfo&& base, + CoverInfo::LoadedImage&& loadedImage, + int resizedToWidth) + : CoverInfo(std::move(base)), + loadedImage(std::move(loadedImage)), + resizedToWidth(resizedToWidth) { } // all-default memory management @@ -123,7 +162,7 @@ class CoverArt : public CoverInfo { // it is not a QPixmap, because it is not safe to use pixmaps // outside the GUI thread - QImage image; + CoverInfo::LoadedImage loadedImage; int resizedToWidth; }; diff --git a/src/library/coverartcache.cpp b/src/library/coverartcache.cpp index 019afdb0dd..bcf0093d3b 100644 --- a/src/library/coverartcache.cpp +++ b/src/library/coverartcache.cpp @@ -1,13 +1,14 @@ +#include "library/coverartcache.h" + #include <QFutureWatcher> #include <QPixmapCache> #include <QtConcurrentRun> #include <QtDebug> -#include "library/coverartcache.h" #include "library/coverartutils.h" #include "util/compatibility.h" #include "util/logger.h" - +#include "util/thread_affinity.h" namespace { @@ -176,26 +177,30 @@ CoverArtCache::FutureResult CoverArtCache::loadCover( res.signalWhenDone = signalWhenDone; DEBUG_ASSERT(!res.coverInfoUpdated); - QImage image = coverInfo.loadImage( + auto loadedImage = coverInfo.loadImage( pTrack ? pTrack->getSecurityToken() : SecurityTokenPointer()); + if (!loadedImage.image.isNull()) { + // Refresh hash before resizing the original image! + res.coverInfoUpdated = coverInfo.refreshImageHash(loadedImage.image); + if (pTrack && res.coverInfoUpdated) { + kLogger.info() + << "Updating cover info of track" + << coverInfo.trackLocation; + pTrack->setCoverInfo(coverInfo); + } - // Refresh hash before resizing the original image! - res.coverInfoUpdated = coverInfo.refreshImageHash(image); - if (pTrack && res.coverInfoUpdated) { - kLogger.info() - << "Updating cover info of track" - << coverInfo.trackLocation; - pTrack->setCoverInfo(coverInfo); - } - - // Resize image to requested size - if (!image.isNull() && desiredWidth > 0) { - // Adjust the cover size according to the request - // or downsize the image for efficiency. - image = resizeImageWidth(image, desiredWidth); + // Resize image to requested size + if (desiredWidth > 0) { + // Adjust the cover size according to the request + // or downsize the image for efficiency. + loadedImage.image = resizeImageWidth(loadedImage.image, desiredWidth); + } } - res.cover = CoverArt(coverInfo, image, desiredWidth); + res.coverArt = CoverArt( + std::move(coverInfo), + std::move(loadedImage), + desiredWidth); return res; } @@ -213,30 +218,60 @@ void CoverArtCache::coverLoaded() { } if (kLogger.traceEnabled()) { - kLogger.trace() << "coverLoaded" << res.cover; + kLogger.trace() << "coverLoaded" << res.coverArt; } - // Don't cache full size covers (resizedToWidth = 0) - // Large cover art wastes space in our cache and will likely - // uncache a lot of the small covers we need in the library - // table. - // Full size covers are used in the Skin Widgets, which are - // loaded with an artificial delay anyway and an additional - // re-load delay can be accepted. - - // Create pixmap, GUI thread only - QPixmap pixmap = QPixmap::fromImage(res.cover.image); - if (!pixmap.isNull() && res.cover.resizedToWidth != 0) { - // we have to be sure that res.cover.hash is unique - // because insert replaces the images with the same key - QString cacheKey = pixmapCacheKey( - res.cover.hash, res.cover.resizedToWidth); - QPixmapCache::insert(cacheKey, pixmap); + QPixmap pixmap; + if (res.coverArt.loadedImage.result != CoverInfo::LoadedImage::Result::NoImage) { + if (res.coverArt.loadedImage.result == CoverInfo::LoadedImage::Result::Ok) { + DEBUG_ASSERT(!res.coverArt.loadedImage.filePath.isEmpty()); + } else { + DEBUG_ASSERT(res.coverArt.loadedImage.image.isNull()); + kLogger.warning() + << "Failed to load cover art image" + << res.coverArt.loadedImage + << "for track" + << res.coverArt.trackLocation; + // Substitute missing cover art with a placeholder image to avoid high CPU load + // See also: https://bugs.launchpad.net/mixxx/+bug/1879160 + const int imageSize = math_max(1, res.coverArt.resizedToWidth); + QImage placeholderImage(imageSize, imageSize, QImage::Format_RGB32); + // TODO(uklotzde): Use optional cover art background color (if available) + // instead of Qt::darkGray + placeholderImage.fill( + mixxx::RgbColor::toQColor(std::nullopt /*res.coverArt.color*/, Qt::darkGray)); + res.coverArt.loadedImage.image = placeholderImage; + } + // Create pixmap, GUI thread only! + DEBUG_ASSERT_MAIN_THREAD_AFFINITY(); + DEBUG_ASSERT(!res.coverArt.loadedImage.image.isNull()); + pixmap = QPixmap::fromImage(res.coverArt.loadedImage.image); + // Don't cache full size covers (resizedToWidth = 0) + // Large cover art wastes space in our cache and will likely + // uncache a lot of the small covers we need in the library + // table. + // Full size covers are used in the Skin Widgets, which are + // loaded with an artificial delay anyway and an additional + // re-load delay can be accepted. + if (res.coverArt.resizedToWidth > 0) { + DEBUG_ASSERT(!pixmap.isNull()); + // It is very unlikely that res.coverArt.hash generates the + // same hash for different images. Otherwise the wrong image would + // be displayed when loaded from the cache. + QString cacheKey = pixmapCacheKey( + res.coverArt.hash, res.coverArt.resizedToWidth); + QPixmapCache::insert(cacheKey, pixmap); + } } m_runningRequests.remove(qMakePair(res.pRequestor, res.requestedHash)); if (res.signalWhenDone) { - emit coverFound(res.pRequestor, res.cover, pixmap, res.requestedHash, res.coverInfoUpdated); + emit coverFound( + res.pRequestor, + std::move(res.coverArt), + pixmap, + res.requestedHash, + res.coverInfoUpdated); } } diff --git a/src/library/coverartcache.h b/src/library/coverartcache.h index 170c8b82a3..22ab59a005 100644 --- a/src/library/coverartcache.h +++ b/src/library/coverartcache.h @@ -65,7 +65,7 @@ class CoverArtCache : public QObject, public Singleton<CoverArtCache> { quint16 requestedHash; bool signalWhenDone; - CoverArt cover; + CoverArt coverArt; bool coverInfoUpdated; }; // Load cover from path indicated in coverInfo. WARNING: This is run in a @@ -107,7 +107,7 @@ class CoverArtCache : public QObject, public Singleton<CoverArtCache> { int desiredWidth, Loading loading); - QSet<QPair<const QObject*, quint16> > m_runningRequests; + QSet<QPair<const QObject*, quint16>> m_runningRequests; }; inline diff --git a/src/library/coverartdelegate.h b/src/library/coverartdelegate.h index dd4e31ed0e..c2cecd72d0 100644 --- a/src/library/coverartdelegate.h +++ b/src/library/coverartdelegate.h @@ -2,7 +2,7 @@ #include "library/basecoverartdelegate.h" -class CoverArtDelegate : public BaseCoverArtDelegate { +class CoverArtDelegate final : public BaseCoverArtDelegate { Q_OBJECT public: diff --git a/src/library/dao/playlistdao.cpp b/src/library/dao/playlistdao.cpp index 12b2545775..30736c99a8 100644 --- a/src/library/dao/playlistdao.cpp +++ b/src/library/dao/playlistdao.cpp @@ -885,7 +885,7 @@ void PlaylistDAO::shuffleTracks(const int playlistId, ScopedTransaction transaction(m_database); QSqlQuery query(m_database); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) // Seed the randomness generator qsrand(QDateTime::currentDateTimeUtc().toTime_t()); #endif diff --git a/src/library/dlgcoverartfullsize.cpp b/src/library/dlgcoverartfullsize.cpp index fa1baee113..7ee2e82fd1 100644 --- a/src/library/dlgcoverartfullsize.cpp +++ b/src/library/dlgcoverartfullsize.cpp @@ -247,7 +247,12 @@ void DlgCoverArtFullSize::wheelEvent(QWheelEvent* event) { // To keep the same part of the image under the cursor, shift the // origin (top left point) by the distance the point moves under the cursor. QPoint oldOrigin = geometry().topLeft(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QPoint oldPointUnderCursor = event->position().toPoint(); +#else QPoint oldPointUnderCursor = event->pos(); +#endif + int newPointX = (double) oldPointUnderCursor.x() / oldWidth * newSize.width(); int newPointY = (double) oldPointUnderCursor.y() / oldHeight * newSize.height(); QPoint newOrigin = QPoint( diff --git a/src/library/hiddentablemodel.h b/src/library/hiddentablemodel.h index be559c5f19..d8d1b8028f 100644 --- a/src/library/hiddentablemodel.h +++ b/src/library/hiddentablemodel.h @@ -3,7 +3,7 @@ #include "library/basesqltablemodel.h" -class HiddenTableModel : public BaseSqlTableModel { +class HiddenTableModel final : public BaseSqlTableModel { Q_OBJECT public: HiddenTableModel(QObject* parent, TrackCollectionManager* pTrackCollectionManager); diff --git a/src/library/missingtablemodel.h b/src/library/missingtablemodel.h index dbe4c347e1..716e64d54c 100644 --- a/src/library/missingtablemodel.h +++ b/src/library/missingtablemodel.h @@ -8,7 +8,7 @@ #include "library/basesqltablemodel.h" #include "library/trackmodel.h" -class MissingTableModel : public BaseSqlTableModel { +class MissingTableModel final : public BaseSqlTableModel { Q_OBJECT public: MissingTableModel(QObject* parent, TrackCollectionManager* pTrackCollectionManager); diff --git a/src/library/playlisttablemodel.h b/src/library/playlisttablemodel.h index 591b147d70..aa8ab57336 100644 --- a/src/library/playlisttablemodel.h +++ b/src/library/playlisttablemodel.h @@ -3,7 +3,7 @@ #include "library/basesqltablemodel.h" #include "library/trackset/tracksettablemodel.h" -class PlaylistTableModel : public TrackSetTableModel { +class PlaylistTableModel final : public TrackSetTableModel { Q_OBJECT public: diff --git a/src/library/songdownloader.cpp b/src/library/songdownloader.cpp index 5e97dd4903..1eb211dd76 100644 --- a/src/library/songdownloader.cpp +++ b/src/library/songdownloader.cpp @@ -69,7 +69,14 @@ bool SongDownloader::downloadFromQueue() { &QNetworkReply::readyRead, this, &SongDownloader::slotReadyRead); - connect(m_pReply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &SongDownloader::slotError); + connect(m_pReply, +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + &QNetworkReply::errorOccurred, +#else + QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), +#endif + this, + &SongDownloader::slotError); connect(m_pReply, &QNetworkReply::downloadProgress, this, diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index d3457d4af7..82d2a25f95 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -1,5 +1,4 @@ #include <QApplication> -#include <QThread> #include "library/trackcollection.h" @@ -65,14 +64,14 @@ TrackCollection::~TrackCollection() { } void TrackCollection::repairDatabase(QSqlDatabase database) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); kLogger.info() << "Repairing database"; m_crates.repairDatabase(database); } void TrackCollection::connectDatabase(QSqlDatabase database) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); kLogger.info() << "Connecting database"; m_database = database; @@ -86,7 +85,7 @@ void TrackCollection::connectDatabase(QSqlDatabase database) { } void TrackCollection::disconnectDatabase() { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); kLogger.info() << "Disconnecting database"; m_database = QSqlDatabase(); @@ -95,7 +94,7 @@ void TrackCollection::disconnectDatabase() { } void TrackCollection::connectTrackSource(QSharedPointer<BaseTrackCache> pTrackSource) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); VERIFY_OR_DEBUG_ASSERT(m_pTrackSource.isNull()) { kLogger.warning() << "Track source has already been connected"; @@ -130,7 +129,7 @@ void TrackCollection::connectTrackSource(QSharedPointer<BaseTrackCache> pTrackSo } QWeakPointer<BaseTrackCache> TrackCollection::disconnectTrackSource() { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); auto pWeakPtr = m_pTrackSource.toWeakRef(); if (m_pTrackSource) { @@ -142,6 +141,8 @@ QWeakPointer<BaseTrackCache> TrackCollection::disconnectTrackSource() { } bool TrackCollection::addDirectory(const QString& dir) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + SqlTransaction transaction(m_database); switch (m_directoryDao.addDirectory(dir)) { case SQL_ERROR: @@ -158,6 +159,8 @@ bool TrackCollection::addDirectory(const QString& dir) { } bool TrackCollection::removeDirectory(const QString& dir) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + SqlTransaction transaction(m_database); switch (m_directoryDao.removeDirectory(dir)) { case SQL_ERROR: @@ -172,7 +175,7 @@ bool TrackCollection::removeDirectory(const QString& dir) { } void TrackCollection::relocateDirectory(QString oldDir, QString newDir) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // We only call this method if the user has picked a relocated directory via // a file dialog. This means the system sandboxer (if we are sandboxed) has @@ -236,7 +239,7 @@ QList<TrackId> TrackCollection::resolveTrackIdsFromLocations( } bool TrackCollection::hideTracks(const QList<TrackId>& trackIds) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Warn if tracks have a playlist membership QSet<int> allPlaylistIds; @@ -302,11 +305,13 @@ bool TrackCollection::hideTracks(const QList<TrackId>& trackIds) { } void TrackCollection::hideAllTracks(const QDir& rootDir) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + m_trackDao.hideAllTracks(rootDir); } bool TrackCollection::unhideTracks(const QList<TrackId>& trackIds) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); VERIFY_OR_DEBUG_ASSERT(m_trackDao.unhideTracks(trackIds)) { return false; @@ -330,7 +335,7 @@ bool TrackCollection::unhideTracks(const QList<TrackId>& trackIds) { bool TrackCollection::purgeTracks( const QList<TrackId>& trackIds) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -369,10 +374,12 @@ bool TrackCollection::purgeTracks( bool TrackCollection::purgeAllTracks( const QDir& rootDir) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + QList<TrackRef> trackRefs = m_trackDao.getAllTrackRefs(rootDir); QList<TrackId> trackIds; trackIds.reserve(trackRefs.size()); - for (const auto trackRef : trackRefs) { + for (const auto& trackRef : trackRefs) { DEBUG_ASSERT(trackRef.hasId()); trackIds.append(trackRef.getId()); } @@ -382,7 +389,7 @@ bool TrackCollection::purgeAllTracks( bool TrackCollection::insertCrate( const Crate& crate, CrateId* pCrateId) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -409,7 +416,7 @@ bool TrackCollection::insertCrate( bool TrackCollection::updateCrate( const Crate& crate) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -431,7 +438,7 @@ bool TrackCollection::updateCrate( bool TrackCollection::deleteCrate( CrateId crateId) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -454,7 +461,7 @@ bool TrackCollection::deleteCrate( bool TrackCollection::addCrateTracks( CrateId crateId, const QList<TrackId>& trackIds) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -477,7 +484,7 @@ bool TrackCollection::addCrateTracks( bool TrackCollection::removeCrateTracks( CrateId crateId, const QList<TrackId>& trackIds) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); // Transactional SqlTransaction transaction(m_database); @@ -500,7 +507,7 @@ bool TrackCollection::removeCrateTracks( bool TrackCollection::updateAutoDjCrate( CrateId crateId, bool isAutoDjSource) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); Crate crate; VERIFY_OR_DEBUG_ASSERT(crates().readCrateById(crateId, &crate)) { @@ -514,18 +521,22 @@ bool TrackCollection::updateAutoDjCrate( } void TrackCollection::saveTrack(Track* pTrack) { - DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread()); + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); m_trackDao.saveTrack(pTrack); } TrackPointer TrackCollection::getTrackById( TrackId trackId) const { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + return m_trackDao.getTrackById(trackId); } TrackPointer TrackCollection::getTrackByRef( const TrackRef& trackRef) const { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + return m_trackDao.getTrackByRef(trackRef); } @@ -537,12 +548,16 @@ TrackId TrackCollection::getTrackIdByRef( TrackPointer TrackCollection::getOrAddTrack( const TrackRef& trackRef, bool* pAlreadyInLibrary) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + return m_trackDao.getOrAddTrack(trackRef, pAlreadyInLibrary); } TrackId TrackCollection::addTrack( const TrackPointer& pTrack, bool unremove) { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + m_trackDao.addTracksPrepare(); const auto trackId = m_trackDao.addTracksAddTrack(pTrack, unremove); m_trackDao.addTracksFinish(!trackId.isValid()); diff --git a/src/library/trackcollection.h b/src/library/trackcollection.h index db99eb8e35..01de194a2d 100644 --- a/src/library/trackcollection.h +++ b/src/library/trackcollection.h @@ -13,6 +13,7 @@ #include "library/dao/trackdao.h" #include "library/trackset/crate/cratestorage.h" #include "preferences/usersettings.h" +#include "util/thread_affinity.h" // forward declaration(s) class BaseTrackCache; @@ -36,23 +37,29 @@ class TrackCollection : public QO |