Collection: Make sure RunQuery
does not access collection items
- Rename `QueryOptions` to `CollectionFilterOptions`. - Create new class `CollectionQueryOptions` for passing options from model to `CollectionQuery`. - Rename `Directory` to `CollectionDirectory`. Fixes #1095
This commit is contained in:
parent
41f2710dea
commit
b5fa401db9
@ -82,9 +82,11 @@ set(SOURCES
|
|||||||
collection/collectionitemdelegate.cpp
|
collection/collectionitemdelegate.cpp
|
||||||
collection/collectionviewcontainer.cpp
|
collection/collectionviewcontainer.cpp
|
||||||
collection/collectiondirectorymodel.cpp
|
collection/collectiondirectorymodel.cpp
|
||||||
|
collection/collectionfilteroptions.cpp
|
||||||
collection/collectionfilterwidget.cpp
|
collection/collectionfilterwidget.cpp
|
||||||
collection/collectionplaylistitem.cpp
|
collection/collectionplaylistitem.cpp
|
||||||
collection/collectionquery.cpp
|
collection/collectionquery.cpp
|
||||||
|
collection/collectionqueryoptions.cpp
|
||||||
collection/savedgroupingmanager.cpp
|
collection/savedgroupingmanager.cpp
|
||||||
collection/groupbydialog.cpp
|
collection/groupbydialog.cpp
|
||||||
collection/collectiontask.cpp
|
collection/collectiontask.cpp
|
||||||
|
@ -50,8 +50,9 @@
|
|||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
#include "smartplaylists/smartplaylistsearch.h"
|
#include "smartplaylists/smartplaylistsearch.h"
|
||||||
|
|
||||||
#include "directory.h"
|
#include "collectiondirectory.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "collectiontask.h"
|
#include "collectiontask.h"
|
||||||
|
|
||||||
@ -145,12 +146,12 @@ void CollectionBackend::ResetStatisticsAsync(const int id) {
|
|||||||
|
|
||||||
void CollectionBackend::LoadDirectories() {
|
void CollectionBackend::LoadDirectories() {
|
||||||
|
|
||||||
DirectoryList dirs = GetAllDirectories();
|
CollectionDirectoryList dirs = GetAllDirectories();
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
for (const Directory &dir : dirs) {
|
for (const CollectionDirectory &dir : dirs) {
|
||||||
emit DirectoryDiscovered(dir, SubdirsInDirectory(dir.id, db));
|
emit DirectoryDiscovered(dir, SubdirsInDirectory(dir.id, db));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,12 +208,12 @@ void CollectionBackend::ChangeDirPath(const int id, const QString &old_path, con
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectoryList CollectionBackend::GetAllDirectories() {
|
CollectionDirectoryList CollectionBackend::GetAllDirectories() {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
DirectoryList ret;
|
CollectionDirectoryList ret;
|
||||||
|
|
||||||
SqlQuery q(db);
|
SqlQuery q(db);
|
||||||
q.prepare(QString("SELECT ROWID, path FROM %1").arg(dirs_table_));
|
q.prepare(QString("SELECT ROWID, path FROM %1").arg(dirs_table_));
|
||||||
@ -222,7 +223,7 @@ DirectoryList CollectionBackend::GetAllDirectories() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (q.next()) {
|
while (q.next()) {
|
||||||
Directory dir;
|
CollectionDirectory dir;
|
||||||
dir.id = q.value(0).toInt();
|
dir.id = q.value(0).toInt();
|
||||||
dir.path = q.value(1).toString();
|
dir.path = q.value(1).toString();
|
||||||
|
|
||||||
@ -232,7 +233,7 @@ DirectoryList CollectionBackend::GetAllDirectories() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(const int id) {
|
CollectionSubdirectoryList CollectionBackend::SubdirsInDirectory(const int id) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db = db_->Connect();
|
QSqlDatabase db = db_->Connect();
|
||||||
@ -240,19 +241,19 @@ SubdirectoryList CollectionBackend::SubdirsInDirectory(const int id) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(const int id, QSqlDatabase &db) {
|
CollectionSubdirectoryList CollectionBackend::SubdirsInDirectory(const int id, QSqlDatabase &db) {
|
||||||
|
|
||||||
SqlQuery q(db);
|
SqlQuery q(db);
|
||||||
q.prepare(QString("SELECT path, mtime FROM %1 WHERE directory_id = :dir").arg(subdirs_table_));
|
q.prepare(QString("SELECT path, mtime FROM %1 WHERE directory_id = :dir").arg(subdirs_table_));
|
||||||
q.BindValue(":dir", id);
|
q.BindValue(":dir", id);
|
||||||
if (!q.Exec()) {
|
if (!q.Exec()) {
|
||||||
db_->ReportErrors(q);
|
db_->ReportErrors(q);
|
||||||
return SubdirectoryList();
|
return CollectionSubdirectoryList();
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList subdirs;
|
CollectionSubdirectoryList subdirs;
|
||||||
while (q.next()) {
|
while (q.next()) {
|
||||||
Subdirectory subdir;
|
CollectionSubdirectory subdir;
|
||||||
subdir.directory_id = id;
|
subdir.directory_id = id;
|
||||||
subdir.path = q.value(0).toString();
|
subdir.path = q.value(0).toString();
|
||||||
subdir.mtime = q.value(1).toLongLong();
|
subdir.mtime = q.value(1).toLongLong();
|
||||||
@ -339,15 +340,15 @@ void CollectionBackend::AddDirectory(const QString &path) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory dir;
|
CollectionDirectory dir;
|
||||||
dir.path = canonical_path;
|
dir.path = canonical_path;
|
||||||
dir.id = q.lastInsertId().toInt();
|
dir.id = q.lastInsertId().toInt();
|
||||||
|
|
||||||
emit DirectoryDiscovered(dir, SubdirectoryList());
|
emit DirectoryDiscovered(dir, CollectionSubdirectoryList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionBackend::RemoveDirectory(const Directory &dir) {
|
void CollectionBackend::RemoveDirectory(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
@ -447,13 +448,13 @@ void CollectionBackend::SongPathChanged(const Song &song, const QFileInfo &new_f
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionBackend::AddOrUpdateSubdirs(const SubdirectoryList &subdirs) {
|
void CollectionBackend::AddOrUpdateSubdirs(const CollectionSubdirectoryList &subdirs) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
ScopedTransaction transaction(&db);
|
ScopedTransaction transaction(&db);
|
||||||
for (const Subdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
if (subdir.mtime == 0) {
|
if (subdir.mtime == 0) {
|
||||||
// Delete the subdirectory
|
// Delete the subdirectory
|
||||||
SqlQuery q(db);
|
SqlQuery q(db);
|
||||||
@ -900,12 +901,12 @@ void CollectionBackend::MarkSongsUnavailable(const SongList &songs, const bool u
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CollectionBackend::GetAll(const QString &column, const QueryOptions &opt) {
|
QStringList CollectionBackend::GetAll(const QString &column, const CollectionFilterOptions &filter_options) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
CollectionQuery query(db, songs_table_, fts_table_, opt);
|
CollectionQuery query(db, songs_table_, fts_table_, filter_options);
|
||||||
query.SetColumnSpec("DISTINCT " + column);
|
query.SetColumnSpec("DISTINCT " + column);
|
||||||
query.AddCompilationRequirement(false);
|
query.AddCompilationRequirement(false);
|
||||||
|
|
||||||
@ -922,12 +923,12 @@ QStringList CollectionBackend::GetAll(const QString &column, const QueryOptions
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CollectionBackend::GetAllArtists(const QueryOptions &opt) {
|
QStringList CollectionBackend::GetAllArtists(const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
return GetAll("artist", opt);
|
return GetAll("artist", opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt) {
|
QStringList CollectionBackend::GetAllArtistsWithAlbums(const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
@ -967,15 +968,15 @@ QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionBackend::AlbumList CollectionBackend::GetAllAlbums(const QueryOptions &opt) {
|
CollectionBackend::AlbumList CollectionBackend::GetAllAlbums(const CollectionFilterOptions &opt) {
|
||||||
return GetAlbums(QString(), false, opt);
|
return GetAlbums(QString(), false, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionBackend::AlbumList CollectionBackend::GetAlbumsByArtist(const QString &artist, const QueryOptions &opt) {
|
CollectionBackend::AlbumList CollectionBackend::GetAlbumsByArtist(const QString &artist, const CollectionFilterOptions &opt) {
|
||||||
return GetAlbums(artist, false, opt);
|
return GetAlbums(artist, false, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList CollectionBackend::GetArtistSongs(const QString &effective_albumartist, const QueryOptions &opt) {
|
SongList CollectionBackend::GetArtistSongs(const QString &effective_albumartist, const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
@ -993,7 +994,7 @@ SongList CollectionBackend::GetArtistSongs(const QString &effective_albumartist,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList CollectionBackend::GetAlbumSongs(const QString &effective_albumartist, const QString &album, const QueryOptions &opt) {
|
SongList CollectionBackend::GetAlbumSongs(const QString &effective_albumartist, const QString &album, const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
@ -1012,7 +1013,7 @@ SongList CollectionBackend::GetAlbumSongs(const QString &effective_albumartist,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList CollectionBackend::GetSongsByAlbum(const QString &album, const QueryOptions &opt) {
|
SongList CollectionBackend::GetSongsByAlbum(const QString &album, const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
@ -1285,11 +1286,11 @@ SongList CollectionBackend::GetSongsByFingerprint(const QString &fingerprint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CollectionBackend::AlbumList CollectionBackend::GetCompilationAlbums(const QueryOptions &opt) {
|
CollectionBackend::AlbumList CollectionBackend::GetCompilationAlbums(const CollectionFilterOptions &opt) {
|
||||||
return GetAlbums(QString(), true, opt);
|
return GetAlbums(QString(), true, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList CollectionBackend::GetCompilationSongs(const QString &album, const QueryOptions &opt) {
|
SongList CollectionBackend::GetCompilationSongs(const QString &album, const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
@ -1426,7 +1427,7 @@ bool CollectionBackend::UpdateCompilations(const QSqlDatabase &db, SongList &del
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt) {
|
CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist, const bool compilation_required, const CollectionFilterOptions &opt) {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
@ -1516,7 +1517,7 @@ CollectionBackend::Album CollectionBackend::GetAlbumArt(const QString &effective
|
|||||||
ret.album = album;
|
ret.album = album;
|
||||||
ret.album_artist = effective_albumartist;
|
ret.album_artist = effective_albumartist;
|
||||||
|
|
||||||
CollectionQuery query(db, songs_table_, fts_table_, QueryOptions());
|
CollectionQuery query(db, songs_table_, fts_table_);
|
||||||
query.SetColumnSpec("art_automatic, art_manual, url");
|
query.SetColumnSpec("art_automatic, art_manual, url");
|
||||||
if (!effective_albumartist.isEmpty()) {
|
if (!effective_albumartist.isEmpty()) {
|
||||||
query.AddWhere("effective_albumartist", effective_albumartist);
|
query.AddWhere("effective_albumartist", effective_albumartist);
|
||||||
|
@ -38,8 +38,9 @@
|
|||||||
|
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/sqlquery.h"
|
#include "core/sqlquery.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "directory.h"
|
#include "collectiondirectory.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
class TaskManager;
|
class TaskManager;
|
||||||
@ -90,23 +91,23 @@ class CollectionBackendInterface : public QObject {
|
|||||||
|
|
||||||
virtual SongList FindSongsInDirectory(const int id) = 0;
|
virtual SongList FindSongsInDirectory(const int id) = 0;
|
||||||
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
|
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
|
||||||
virtual SubdirectoryList SubdirsInDirectory(const int id) = 0;
|
virtual CollectionSubdirectoryList SubdirsInDirectory(const int id) = 0;
|
||||||
virtual DirectoryList GetAllDirectories() = 0;
|
virtual CollectionDirectoryList GetAllDirectories() = 0;
|
||||||
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
|
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
|
||||||
|
|
||||||
virtual SongList GetAllSongs() = 0;
|
virtual SongList GetAllSongs() = 0;
|
||||||
|
|
||||||
virtual QStringList GetAllArtists(const QueryOptions &opt = QueryOptions()) = 0;
|
virtual QStringList GetAllArtists(const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual QStringList GetAllArtistsWithAlbums(const QueryOptions &opt = QueryOptions()) = 0;
|
virtual QStringList GetAllArtistsWithAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual SongList GetArtistSongs(const QString &effective_albumartist, const QueryOptions &opt = QueryOptions()) = 0;
|
virtual SongList GetArtistSongs(const QString &effective_albumartist, const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual SongList GetAlbumSongs(const QString &effective_albumartist, const QString &album, const QueryOptions &opt = QueryOptions()) = 0;
|
virtual SongList GetAlbumSongs(const QString &effective_albumartist, const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual SongList GetSongsByAlbum(const QString &album, const QueryOptions &opt = QueryOptions()) = 0;
|
virtual SongList GetSongsByAlbum(const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
|
|
||||||
virtual SongList GetCompilationSongs(const QString &album, const QueryOptions &opt = QueryOptions()) = 0;
|
virtual SongList GetCompilationSongs(const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
|
|
||||||
virtual AlbumList GetAllAlbums(const QueryOptions &opt = QueryOptions()) = 0;
|
virtual AlbumList GetAllAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual AlbumList GetAlbumsByArtist(const QString &artist, const QueryOptions &opt = QueryOptions()) = 0;
|
virtual AlbumList GetAlbumsByArtist(const QString &artist, const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
virtual AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) = 0;
|
virtual AlbumList GetCompilationAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) = 0;
|
||||||
|
|
||||||
virtual void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) = 0;
|
virtual void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) = 0;
|
||||||
virtual void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false) = 0;
|
virtual void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false) = 0;
|
||||||
@ -124,7 +125,7 @@ class CollectionBackendInterface : public QObject {
|
|||||||
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
||||||
|
|
||||||
virtual void AddDirectory(const QString &path) = 0;
|
virtual void AddDirectory(const QString &path) = 0;
|
||||||
virtual void RemoveDirectory(const Directory &dir) = 0;
|
virtual void RemoveDirectory(const CollectionDirectory &dir) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CollectionBackend : public CollectionBackendInterface {
|
class CollectionBackend : public CollectionBackendInterface {
|
||||||
@ -159,24 +160,24 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
|
|
||||||
SongList FindSongsInDirectory(const int id) override;
|
SongList FindSongsInDirectory(const int id) override;
|
||||||
SongList SongsWithMissingFingerprint(const int id) override;
|
SongList SongsWithMissingFingerprint(const int id) override;
|
||||||
SubdirectoryList SubdirsInDirectory(const int id) override;
|
CollectionSubdirectoryList SubdirsInDirectory(const int id) override;
|
||||||
DirectoryList GetAllDirectories() override;
|
CollectionDirectoryList GetAllDirectories() override;
|
||||||
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
|
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
|
||||||
|
|
||||||
SongList GetAllSongs() override;
|
SongList GetAllSongs() override;
|
||||||
|
|
||||||
QStringList GetAll(const QString &column, const QueryOptions &opt = QueryOptions());
|
QStringList GetAll(const QString &column, const CollectionFilterOptions &filter_options = CollectionFilterOptions());
|
||||||
QStringList GetAllArtists(const QueryOptions &opt = QueryOptions()) override;
|
QStringList GetAllArtists(const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
QStringList GetAllArtistsWithAlbums(const QueryOptions &opt = QueryOptions()) override;
|
QStringList GetAllArtistsWithAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
SongList GetArtistSongs(const QString &effective_albumartist, const QueryOptions &opt = QueryOptions()) override;
|
SongList GetArtistSongs(const QString &effective_albumartist, const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
SongList GetAlbumSongs(const QString &effective_albumartist, const QString &album, const QueryOptions &opt = QueryOptions()) override;
|
SongList GetAlbumSongs(const QString &effective_albumartist, const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
SongList GetSongsByAlbum(const QString &album, const QueryOptions &opt = QueryOptions()) override;
|
SongList GetSongsByAlbum(const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
|
|
||||||
SongList GetCompilationSongs(const QString &album, const QueryOptions &opt = QueryOptions()) override;
|
SongList GetCompilationSongs(const QString &album, const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
|
|
||||||
AlbumList GetAllAlbums(const QueryOptions &opt = QueryOptions()) override;
|
AlbumList GetAllAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) override;
|
AlbumList GetCompilationAlbums(const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
AlbumList GetAlbumsByArtist(const QString &artist, const QueryOptions &opt = QueryOptions()) override;
|
AlbumList GetAlbumsByArtist(const QString &artist, const CollectionFilterOptions &opt = CollectionFilterOptions()) override;
|
||||||
|
|
||||||
void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
|
void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
|
||||||
void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false) override;
|
void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false) override;
|
||||||
@ -192,7 +193,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
|
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
|
||||||
|
|
||||||
void AddDirectory(const QString &path) override;
|
void AddDirectory(const QString &path) override;
|
||||||
void RemoveDirectory(const Directory &dir) override;
|
void RemoveDirectory(const CollectionDirectory &dir) override;
|
||||||
|
|
||||||
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
||||||
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
||||||
@ -228,7 +229,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
void UpdateMTimesOnly(const SongList &songs);
|
void UpdateMTimesOnly(const SongList &songs);
|
||||||
void DeleteSongs(const SongList &songs);
|
void DeleteSongs(const SongList &songs);
|
||||||
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
|
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
|
||||||
void AddOrUpdateSubdirs(const SubdirectoryList &subdirs);
|
void AddOrUpdateSubdirs(const CollectionSubdirectoryList &subdirs);
|
||||||
void CompilationsNeedUpdating();
|
void CompilationsNeedUpdating();
|
||||||
void UpdateManualAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false);
|
void UpdateManualAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false);
|
||||||
void UpdateAutomaticAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false);
|
void UpdateAutomaticAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_manual = false);
|
||||||
@ -250,8 +251,8 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
|
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void DirectoryDiscovered(Directory, SubdirectoryList);
|
void DirectoryDiscovered(CollectionDirectory, CollectionSubdirectoryList);
|
||||||
void DirectoryDeleted(Directory);
|
void DirectoryDeleted(CollectionDirectory);
|
||||||
|
|
||||||
void SongsDiscovered(SongList);
|
void SongsDiscovered(SongList);
|
||||||
void SongsDeleted(SongList);
|
void SongsDeleted(SongList);
|
||||||
@ -280,9 +281,9 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||||
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const QueryOptions &opt = QueryOptions());
|
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
||||||
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt = QueryOptions());
|
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
||||||
SubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
|
CollectionSubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
|
||||||
|
|
||||||
Song GetSongById(const int id, QSqlDatabase &db);
|
Song GetSongById(const int id, QSqlDatabase &db);
|
||||||
SongList GetSongsById(const QStringList &ids, QSqlDatabase &db);
|
SongList GetSongsById(const QStringList &ids, QSqlDatabase &db);
|
||||||
|
57
src/collection/collectiondirectory.h
Normal file
57
src/collection/collectiondirectory.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COLLECTIONDIRECTORY_H
|
||||||
|
#define COLLECTIONDIRECTORY_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
struct CollectionDirectory {
|
||||||
|
CollectionDirectory() : id(-1) {}
|
||||||
|
|
||||||
|
bool operator==(const CollectionDirectory &other) const {
|
||||||
|
return path == other.path && id == other.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(CollectionDirectory)
|
||||||
|
|
||||||
|
using CollectionDirectoryList = QList<CollectionDirectory>;
|
||||||
|
Q_DECLARE_METATYPE(CollectionDirectoryList)
|
||||||
|
|
||||||
|
struct CollectionSubdirectory {
|
||||||
|
CollectionSubdirectory() : directory_id(-1), mtime(0) {}
|
||||||
|
|
||||||
|
int directory_id;
|
||||||
|
QString path;
|
||||||
|
qint64 mtime;
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(CollectionSubdirectory)
|
||||||
|
|
||||||
|
using CollectionSubdirectoryList = QList<CollectionSubdirectory>;
|
||||||
|
Q_DECLARE_METATYPE(CollectionSubdirectoryList)
|
||||||
|
|
||||||
|
#endif // COLLECTIONDIRECTORY_H
|
@ -30,7 +30,7 @@
|
|||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/musicstorage.h"
|
#include "core/musicstorage.h"
|
||||||
#include "utilities/diskutils.h"
|
#include "utilities/diskutils.h"
|
||||||
#include "directory.h"
|
#include "collectiondirectory.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
#include "collectiondirectorymodel.h"
|
#include "collectiondirectorymodel.h"
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ CollectionDirectoryModel::CollectionDirectoryModel(CollectionBackend *backend, Q
|
|||||||
|
|
||||||
CollectionDirectoryModel::~CollectionDirectoryModel() = default;
|
CollectionDirectoryModel::~CollectionDirectoryModel() = default;
|
||||||
|
|
||||||
void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
|
void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
QStandardItem *item = new QStandardItem(dir.path);
|
QStandardItem *item = new QStandardItem(dir.path);
|
||||||
item->setData(dir.id, kIdRole);
|
item->setData(dir.id, kIdRole);
|
||||||
@ -56,7 +56,7 @@ void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionDirectoryModel::DirectoryDeleted(const Directory &dir) {
|
void CollectionDirectoryModel::DirectoryDeleted(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
for (int i = 0; i < rowCount(); ++i) {
|
for (int i = 0; i < rowCount(); ++i) {
|
||||||
if (item(i, 0)->data(kIdRole).toInt() == dir.id) {
|
if (item(i, 0)->data(kIdRole).toInt() == dir.id) {
|
||||||
@ -80,7 +80,7 @@ void CollectionDirectoryModel::RemoveDirectory(const QModelIndex &idx) {
|
|||||||
|
|
||||||
if (!backend_ || !idx.isValid()) return;
|
if (!backend_ || !idx.isValid()) return;
|
||||||
|
|
||||||
Directory dir;
|
CollectionDirectory dir;
|
||||||
dir.path = idx.data().toString();
|
dir.path = idx.data().toString();
|
||||||
dir.id = idx.data(kIdRole).toInt();
|
dir.id = idx.data(kIdRole).toInt();
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
|
||||||
struct Directory;
|
struct CollectionDirectory;
|
||||||
class CollectionBackend;
|
class CollectionBackend;
|
||||||
class MusicStorage;
|
class MusicStorage;
|
||||||
|
|
||||||
@ -53,8 +53,8 @@ class CollectionDirectoryModel : public QStandardItemModel {
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// To be called by the backend
|
// To be called by the backend
|
||||||
void DirectoryDiscovered(const Directory &directories);
|
void DirectoryDiscovered(const CollectionDirectory &directories);
|
||||||
void DirectoryDeleted(const Directory &directories);
|
void DirectoryDeleted(const CollectionDirectory &directories);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kIdRole = Qt::UserRole + 1;
|
static const int kIdRole = Qt::UserRole + 1;
|
||||||
|
42
src/collection/collectionfilteroptions.cpp
Normal file
42
src/collection/collectionfilteroptions.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
|
|
||||||
|
CollectionFilterOptions::CollectionFilterOptions() : filter_mode_(FilterMode_All), max_age_(-1) {}
|
||||||
|
|
||||||
|
bool CollectionFilterOptions::Matches(const Song &song) const {
|
||||||
|
|
||||||
|
if (max_age_ != -1) {
|
||||||
|
const qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
|
||||||
|
if (song.ctime() <= cutoff) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter_text_.isNull()) {
|
||||||
|
return song.artist().contains(filter_text_, Qt::CaseInsensitive) || song.album().contains(filter_text_, Qt::CaseInsensitive) || song.title().contains(filter_text_, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
65
src/collection/collectionfilteroptions.h
Normal file
65
src/collection/collectionfilteroptions.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COLLECTIONFILTEROPTIONS_H
|
||||||
|
#define COLLECTIONFILTEROPTIONS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
class CollectionFilterOptions {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit CollectionFilterOptions();
|
||||||
|
|
||||||
|
// Filter mode:
|
||||||
|
// - use the all songs table
|
||||||
|
// - use the duplicated songs view; by duplicated we mean those songs for which the (artist, album, title) tuple is found more than once in the songs table
|
||||||
|
// - use the untagged songs view; by untagged we mean those for which at least one of the (artist, album, title) tags is empty
|
||||||
|
// Please note that additional filtering based on FTS table (the filter attribute) won't work in Duplicates and Untagged modes.
|
||||||
|
enum FilterMode {
|
||||||
|
FilterMode_All,
|
||||||
|
FilterMode_Duplicates,
|
||||||
|
FilterMode_Untagged
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterMode filter_mode() const { return filter_mode_; }
|
||||||
|
int max_age() const { return max_age_; }
|
||||||
|
QString filter_text() const { return filter_text_; }
|
||||||
|
|
||||||
|
void set_filter_mode(const FilterMode filter_mode) {
|
||||||
|
filter_mode_ = filter_mode;
|
||||||
|
filter_text_.clear();
|
||||||
|
}
|
||||||
|
void set_max_age(const int max_age) { max_age_ = max_age; }
|
||||||
|
void set_filter_text(const QString &filter_text) {
|
||||||
|
filter_mode_ = FilterMode_All;
|
||||||
|
filter_text_ = filter_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Matches(const Song &song) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FilterMode filter_mode_;
|
||||||
|
int max_age_;
|
||||||
|
QString filter_text_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLLECTIONFILTEROPTIONS_H
|
@ -46,6 +46,7 @@
|
|||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "savedgroupingmanager.h"
|
#include "savedgroupingmanager.h"
|
||||||
@ -455,12 +456,12 @@ void CollectionFilterWidget::SetFilterHint(const QString &hint) {
|
|||||||
ui_->search_field->setPlaceholderText(hint);
|
ui_->search_field->setPlaceholderText(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionFilterWidget::SetQueryMode(QueryOptions::QueryMode query_mode) {
|
void CollectionFilterWidget::SetFilterMode(CollectionFilterOptions::FilterMode filter_mode) {
|
||||||
|
|
||||||
ui_->search_field->clear();
|
ui_->search_field->clear();
|
||||||
ui_->search_field->setEnabled(query_mode == QueryOptions::QueryMode_All);
|
ui_->search_field->setEnabled(filter_mode == CollectionFilterOptions::FilterMode_All);
|
||||||
|
|
||||||
model_->SetFilterQueryMode(query_mode);
|
model_->SetFilterMode(filter_mode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
@ -88,7 +89,7 @@ class CollectionFilterWidget : public QWidget {
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void UpdateGroupByActions();
|
void UpdateGroupByActions();
|
||||||
void SetQueryMode(QueryOptions::QueryMode query_mode);
|
void SetFilterMode(CollectionFilterOptions::FilterMode filter_mode);
|
||||||
void FocusOnFilter(QKeyEvent *e);
|
void FocusOnFilter(QKeyEvent *e);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -60,7 +60,9 @@
|
|||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
#include "collectiondirectorymodel.h"
|
#include "collectiondirectorymodel.h"
|
||||||
#include "collectionitem.h"
|
#include "collectionitem.h"
|
||||||
@ -210,7 +212,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
|||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
|
|
||||||
// Sanity check to make sure we don't add songs that are outside the user's filter
|
// Sanity check to make sure we don't add songs that are outside the user's filter
|
||||||
if (!query_options_.Matches(song)) continue;
|
if (!filter_options_.Matches(song)) continue;
|
||||||
|
|
||||||
// Hey, we've already got that one!
|
// Hey, we've already got that one!
|
||||||
if (song_nodes_.contains(song.id())) continue;
|
if (song_nodes_.contains(song.id())) continue;
|
||||||
@ -805,16 +807,13 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollectionModel::HasCompilations(const QSqlDatabase &db, const CollectionQuery &query) {
|
bool CollectionModel::HasCompilations(const QSqlDatabase &db, const CollectionFilterOptions &filter_options, const CollectionQueryOptions &query_options) {
|
||||||
|
|
||||||
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_);
|
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), filter_options);
|
||||||
|
q.SetColumnSpec(query_options.column_spec());
|
||||||
q.SetColumnSpec(query.column_spec());
|
for (const CollectionQueryOptions::Where &where_clauses : query_options.where_clauses()) {
|
||||||
q.SetOrderBy(query.order_by());
|
q.AddWhere(where_clauses.column, where_clauses.value, where_clauses.op);
|
||||||
q.SetWhereClauses(query.where_clauses());
|
}
|
||||||
q.SetBoundValues(query.bound_values());
|
|
||||||
q.SetIncludeUnavailable(query.include_unavailable());
|
|
||||||
q.SetDuplicatesOnly(query.duplicates_only());
|
|
||||||
q.AddCompilationRequirement(true);
|
q.AddCompilationRequirement(true);
|
||||||
q.SetLimit(1);
|
q.SetLimit(1);
|
||||||
|
|
||||||
@ -827,57 +826,66 @@ bool CollectionModel::HasCompilations(const QSqlDatabase &db, const CollectionQu
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) {
|
CollectionQueryOptions CollectionModel::PrepareQuery(CollectionItem *parent) {
|
||||||
|
|
||||||
QueryResult result;
|
|
||||||
|
|
||||||
// Information about what we want the children to be
|
// Information about what we want the children to be
|
||||||
int child_level = parent == root_ ? 0 : parent->container_level + 1;
|
const int child_level = parent == root_ ? 0 : parent->container_level + 1;
|
||||||
GroupBy child_group_by = child_level >= 3 ? GroupBy_None : group_by_[child_level];
|
const GroupBy child_group_by = child_level >= 3 ? GroupBy_None : group_by_[child_level];
|
||||||
|
|
||||||
|
CollectionQueryOptions query_options;
|
||||||
|
|
||||||
// Initialize the query. child_group_by says what type of thing we want (artists, songs, etc.)
|
// Initialize the query. child_group_by says what type of thing we want (artists, songs, etc.)
|
||||||
|
SetQueryColumnSpec(child_group_by, separate_albums_by_grouping_, &query_options);
|
||||||
|
|
||||||
|
// Walk up through the item's parents adding filters as necessary
|
||||||
|
for (CollectionItem *p = parent; p && p->type == CollectionItem::Type_Container; p = p->parent) {
|
||||||
|
AddQueryWhere(group_by_[p->container_level], separate_albums_by_grouping_, p, &query_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artists GroupBy is special - we don't want compilation albums appearing
|
||||||
|
if (show_various_artists_ && IsArtistGroupBy(child_group_by)) {
|
||||||
|
query_options.set_query_have_compilations(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query_options;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionModel::QueryResult CollectionModel::RunQuery(const CollectionFilterOptions &filter_options, const CollectionQueryOptions &query_options) {
|
||||||
|
|
||||||
|
QMutexLocker l(backend_->db()->Mutex());
|
||||||
|
|
||||||
|
QueryResult result;
|
||||||
{
|
{
|
||||||
QMutexLocker l(backend_->db()->Mutex());
|
|
||||||
{
|
|
||||||
QSqlDatabase db(backend_->db()->Connect());
|
|
||||||
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_);
|
|
||||||
InitQuery(child_group_by, separate_albums_by_grouping_, &q);
|
|
||||||
|
|
||||||
// Walk up through the item's parents adding filters as necessary
|
|
||||||
for (CollectionItem *p = parent; p && p->type == CollectionItem::Type_Container; p = p->parent) {
|
|
||||||
FilterQuery(group_by_[p->container_level], separate_albums_by_grouping_, p, &q);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Artists GroupBy is special - we don't want compilation albums appearing
|
|
||||||
if (IsArtistGroupBy(child_group_by)) {
|
|
||||||
// Add the special Various artists node
|
|
||||||
if (show_various_artists_ && HasCompilations(db, q)) {
|
|
||||||
result.create_va = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't show compilations again outside the Various artists node
|
|
||||||
q.AddCompilationRequirement(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the query
|
|
||||||
if (q.Exec()) {
|
|
||||||
while (q.Next()) {
|
|
||||||
result.rows << SqlRow(q);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
backend_->ReportErrors(q);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
QSqlDatabase db(backend_->db()->Connect());
|
||||||
|
// Add the special Various artists node
|
||||||
|
if (query_options.query_have_compilations() && HasCompilations(db, filter_options, query_options)) {
|
||||||
|
result.create_va = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) {
|
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), filter_options);
|
||||||
backend_->db()->Close();
|
q.SetColumnSpec(query_options.column_spec());
|
||||||
|
for (const CollectionQueryOptions::Where &where_clauses : query_options.where_clauses()) {
|
||||||
|
q.AddWhere(where_clauses.column, where_clauses.value, where_clauses.op);
|
||||||
|
}
|
||||||
|
q.AddCompilationRequirement(query_options.compilation_requirement());
|
||||||
|
|
||||||
|
if (q.Exec()) {
|
||||||
|
while (q.Next()) {
|
||||||
|
result.rows << SqlRow(q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
backend_->ReportErrors(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) {
|
||||||
|
backend_->db()->Close();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -913,17 +921,20 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, const bool signal) {
|
|||||||
if (parent->lazy_loaded) return;
|
if (parent->lazy_loaded) return;
|
||||||
parent->lazy_loaded = true;
|
parent->lazy_loaded = true;
|
||||||
|
|
||||||
QueryResult result = RunQuery(parent);
|
CollectionQueryOptions query_options = PrepareQuery(parent);
|
||||||
|
QueryResult result = RunQuery(filter_options_, query_options);
|
||||||
PostQuery(parent, result, signal);
|
PostQuery(parent, result, signal);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::ResetAsync() {
|
void CollectionModel::ResetAsync() {
|
||||||
|
|
||||||
|
CollectionQueryOptions query_options = PrepareQuery(root_);
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(&CollectionModel::RunQuery, this, root_);
|
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(&CollectionModel::RunQuery, this, filter_options_, query_options);
|
||||||
#else
|
#else
|
||||||
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_);
|
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(this, &CollectionModel::RunQuery, filter_options_, query_options);
|
||||||
#endif
|
#endif
|
||||||
QFutureWatcher<CollectionModel::QueryResult> *watcher = new QFutureWatcher<CollectionModel::QueryResult>();
|
QFutureWatcher<CollectionModel::QueryResult> *watcher = new QFutureWatcher<CollectionModel::QueryResult>();
|
||||||
QObject::connect(watcher, &QFutureWatcher<CollectionModel::QueryResult>::finished, this, &CollectionModel::ResetAsyncQueryFinished);
|
QObject::connect(watcher, &QFutureWatcher<CollectionModel::QueryResult>::finished, this, &CollectionModel::ResetAsyncQueryFinished);
|
||||||
@ -937,10 +948,6 @@ void CollectionModel::ResetAsyncQueryFinished() {
|
|||||||
const struct QueryResult result = watcher->result();
|
const struct QueryResult result = watcher->result();
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) {
|
|
||||||
backend_->Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
BeginReset();
|
BeginReset();
|
||||||
root_->lazy_loaded = true;
|
root_->lazy_loaded = true;
|
||||||
|
|
||||||
@ -986,197 +993,197 @@ void CollectionModel::Reset() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::InitQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQuery *q) {
|
void CollectionModel::SetQueryColumnSpec(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQueryOptions *query_options) {
|
||||||
|
|
||||||
// Say what group_by of thing we want to get back from the database.
|
// Say what group_by of thing we want to get back from the database.
|
||||||
switch (group_by) {
|
switch (group_by) {
|
||||||
case GroupBy_AlbumArtist:
|
case GroupBy_AlbumArtist:
|
||||||
q->SetColumnSpec("DISTINCT effective_albumartist");
|
query_options->set_column_spec("DISTINCT effective_albumartist");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Artist:
|
case GroupBy_Artist:
|
||||||
q->SetColumnSpec("DISTINCT artist");
|
query_options->set_column_spec("DISTINCT artist");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Album:{
|
case GroupBy_Album:{
|
||||||
QString query("DISTINCT album, album_id");
|
QString query("DISTINCT album, album_id");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_AlbumDisc:{
|
case GroupBy_AlbumDisc:{
|
||||||
QString query("DISTINCT album, album_id, disc");
|
QString query("DISTINCT album, album_id, disc");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_YearAlbum:{
|
case GroupBy_YearAlbum:{
|
||||||
QString query("DISTINCT year, album, album_id");
|
QString query("DISTINCT year, album, album_id");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_YearAlbumDisc:{
|
case GroupBy_YearAlbumDisc:{
|
||||||
QString query("DISTINCT year, album, album_id, disc");
|
QString query("DISTINCT year, album, album_id, disc");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_OriginalYearAlbum:{
|
case GroupBy_OriginalYearAlbum:{
|
||||||
QString query("DISTINCT year, originalyear, album, album_id");
|
QString query("DISTINCT year, originalyear, album, album_id");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_OriginalYearAlbumDisc:{
|
case GroupBy_OriginalYearAlbumDisc:{
|
||||||
QString query("DISTINCT year, originalyear, album, album_id, disc");
|
QString query("DISTINCT year, originalyear, album, album_id, disc");
|
||||||
if (separate_albums_by_grouping) query.append(", grouping");
|
if (separate_albums_by_grouping) query.append(", grouping");
|
||||||
q->SetColumnSpec(query);
|
query_options->set_column_spec(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupBy_Disc:
|
case GroupBy_Disc:
|
||||||
q->SetColumnSpec("DISTINCT disc");
|
query_options->set_column_spec("DISTINCT disc");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Year:
|
case GroupBy_Year:
|
||||||
q->SetColumnSpec("DISTINCT year");
|
query_options->set_column_spec("DISTINCT year");
|
||||||
break;
|
break;
|
||||||
case GroupBy_OriginalYear:
|
case GroupBy_OriginalYear:
|
||||||
q->SetColumnSpec("DISTINCT effective_originalyear");
|
query_options->set_column_spec("DISTINCT effective_originalyear");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Genre:
|
case GroupBy_Genre:
|
||||||
q->SetColumnSpec("DISTINCT genre");
|
query_options->set_column_spec("DISTINCT genre");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Composer:
|
case GroupBy_Composer:
|
||||||
q->SetColumnSpec("DISTINCT composer");
|
query_options->set_column_spec("DISTINCT composer");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Performer:
|
case GroupBy_Performer:
|
||||||
q->SetColumnSpec("DISTINCT performer");
|
query_options->set_column_spec("DISTINCT performer");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Grouping:
|
case GroupBy_Grouping:
|
||||||
q->SetColumnSpec("DISTINCT grouping");
|
query_options->set_column_spec("DISTINCT grouping");
|
||||||
break;
|
break;
|
||||||
case GroupBy_FileType:
|
case GroupBy_FileType:
|
||||||
q->SetColumnSpec("DISTINCT filetype");
|
query_options->set_column_spec("DISTINCT filetype");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Format:
|
case GroupBy_Format:
|
||||||
q->SetColumnSpec("DISTINCT filetype, samplerate, bitdepth");
|
query_options->set_column_spec("DISTINCT filetype, samplerate, bitdepth");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Samplerate:
|
case GroupBy_Samplerate:
|
||||||
q->SetColumnSpec("DISTINCT samplerate");
|
query_options->set_column_spec("DISTINCT samplerate");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Bitdepth:
|
case GroupBy_Bitdepth:
|
||||||
q->SetColumnSpec("DISTINCT bitdepth");
|
query_options->set_column_spec("DISTINCT bitdepth");
|
||||||
break;
|
break;
|
||||||
case GroupBy_Bitrate:
|
case GroupBy_Bitrate:
|
||||||
q->SetColumnSpec("DISTINCT bitrate");
|
query_options->set_column_spec("DISTINCT bitrate");
|
||||||
break;
|
break;
|
||||||
case GroupBy_None:
|
case GroupBy_None:
|
||||||
case GroupByCount:
|
case GroupByCount:
|
||||||
q->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
query_options->set_column_spec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::FilterQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQuery *q) {
|
void CollectionModel::AddQueryWhere(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQueryOptions *query_options) {
|
||||||
|
|
||||||
// Say how we want the query to be filtered. This is done once for each parent going up the tree.
|
// Say how we want the query to be filtered. This is done once for each parent going up the tree.
|
||||||
|
|
||||||
switch (group_by) {
|
switch (group_by) {
|
||||||
case GroupBy_AlbumArtist:
|
case GroupBy_AlbumArtist:
|
||||||
if (IsCompilationArtistNode(item)) {
|
if (IsCompilationArtistNode(item)) {
|
||||||
q->AddCompilationRequirement(true);
|
query_options->set_compilation_requirement(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Don't duplicate compilations outside the Various artists node
|
// Don't duplicate compilations outside the Various artists node
|
||||||
q->AddCompilationRequirement(false);
|
query_options->set_compilation_requirement(false);
|
||||||
q->AddWhere("effective_albumartist", item->metadata.effective_albumartist());
|
query_options->AddWhere("effective_albumartist", item->metadata.effective_albumartist());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GroupBy_Artist:
|
case GroupBy_Artist:
|
||||||
if (IsCompilationArtistNode(item)) {
|
if (IsCompilationArtistNode(item)) {
|
||||||
q->AddCompilationRequirement(true);
|
query_options->set_compilation_requirement(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Don't duplicate compilations outside the Various artists node
|
// Don't duplicate compilations outside the Various artists node
|
||||||
q->AddCompilationRequirement(false);
|
query_options->set_compilation_requirement(false);
|
||||||
q->AddWhere("artist", item->metadata.artist());
|
query_options->AddWhere("artist", item->metadata.artist());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GroupBy_Album:
|
case GroupBy_Album:
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_AlbumDisc:
|
case GroupBy_AlbumDisc:
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
q->AddWhere("disc", item->metadata.disc());
|
query_options->AddWhere("disc", item->metadata.disc());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_YearAlbum:
|
case GroupBy_YearAlbum:
|
||||||
q->AddWhere("year", item->metadata.year());
|
query_options->AddWhere("year", item->metadata.year());
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_YearAlbumDisc:
|
case GroupBy_YearAlbumDisc:
|
||||||
q->AddWhere("year", item->metadata.year());
|
query_options->AddWhere("year", item->metadata.year());
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
q->AddWhere("disc", item->metadata.disc());
|
query_options->AddWhere("disc", item->metadata.disc());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_OriginalYearAlbum:
|
case GroupBy_OriginalYearAlbum:
|
||||||
q->AddWhere("year", item->metadata.year());
|
query_options->AddWhere("year", item->metadata.year());
|
||||||
q->AddWhere("originalyear", item->metadata.originalyear());
|
query_options->AddWhere("originalyear", item->metadata.originalyear());
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_OriginalYearAlbumDisc:
|
case GroupBy_OriginalYearAlbumDisc:
|
||||||
q->AddWhere("year", item->metadata.year());
|
query_options->AddWhere("year", item->metadata.year());
|
||||||
q->AddWhere("originalyear", item->metadata.originalyear());
|
query_options->AddWhere("originalyear", item->metadata.originalyear());
|
||||||
q->AddWhere("album", item->metadata.album());
|
query_options->AddWhere("album", item->metadata.album());
|
||||||
q->AddWhere("album_id", item->metadata.album_id());
|
query_options->AddWhere("album_id", item->metadata.album_id());
|
||||||
q->AddWhere("disc", item->metadata.disc());
|
query_options->AddWhere("disc", item->metadata.disc());
|
||||||
if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping());
|
if (separate_albums_by_grouping) query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Disc:
|
case GroupBy_Disc:
|
||||||
q->AddWhere("disc", item->metadata.disc());
|
query_options->AddWhere("disc", item->metadata.disc());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Year:
|
case GroupBy_Year:
|
||||||
q->AddWhere("year", item->metadata.year());
|
query_options->AddWhere("year", item->metadata.year());
|
||||||
break;
|
break;
|
||||||
case GroupBy_OriginalYear:
|
case GroupBy_OriginalYear:
|
||||||
q->AddWhere("effective_originalyear", item->metadata.effective_originalyear());
|
query_options->AddWhere("effective_originalyear", item->metadata.effective_originalyear());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Genre:
|
case GroupBy_Genre:
|
||||||
q->AddWhere("genre", item->metadata.genre());
|
query_options->AddWhere("genre", item->metadata.genre());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Composer:
|
case GroupBy_Composer:
|
||||||
q->AddWhere("composer", item->metadata.composer());
|
query_options->AddWhere("composer", item->metadata.composer());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Performer:
|
case GroupBy_Performer:
|
||||||
q->AddWhere("performer", item->metadata.performer());
|
query_options->AddWhere("performer", item->metadata.performer());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Grouping:
|
case GroupBy_Grouping:
|
||||||
q->AddWhere("grouping", item->metadata.grouping());
|
query_options->AddWhere("grouping", item->metadata.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy_FileType:
|
case GroupBy_FileType:
|
||||||
q->AddWhere("filetype", item->metadata.filetype());
|
query_options->AddWhere("filetype", item->metadata.filetype());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Format:
|
case GroupBy_Format:
|
||||||
q->AddWhere("filetype", item->metadata.filetype());
|
query_options->AddWhere("filetype", item->metadata.filetype());
|
||||||
q->AddWhere("samplerate", item->metadata.samplerate());
|
query_options->AddWhere("samplerate", item->metadata.samplerate());
|
||||||
q->AddWhere("bitdepth", item->metadata.bitdepth());
|
query_options->AddWhere("bitdepth", item->metadata.bitdepth());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Samplerate:
|
case GroupBy_Samplerate:
|
||||||
q->AddWhere("samplerate", item->metadata.samplerate());
|
query_options->AddWhere("samplerate", item->metadata.samplerate());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Bitdepth:
|
case GroupBy_Bitdepth:
|
||||||
q->AddWhere("bitdepth", item->metadata.bitdepth());
|
query_options->AddWhere("bitdepth", item->metadata.bitdepth());
|
||||||
break;
|
break;
|
||||||
case GroupBy_Bitrate:
|
case GroupBy_Bitrate:
|
||||||
q->AddWhere("bitrate", item->metadata.bitrate());
|
query_options->AddWhere("bitrate", item->metadata.bitrate());
|
||||||
break;
|
break;
|
||||||
case GroupBy_None:
|
case GroupBy_None:
|
||||||
case GroupByCount:
|
case GroupByCount:
|
||||||
@ -1851,21 +1858,19 @@ SongList CollectionModel::GetChildSongs(const QModelIndex &idx) const {
|
|||||||
return GetChildSongs(QModelIndexList() << idx);
|
return GetChildSongs(QModelIndexList() << idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::SetFilterAge(const int age) {
|
void CollectionModel::SetFilterMode(CollectionFilterOptions::FilterMode filter_mode) {
|
||||||
query_options_.set_max_age(age);
|
filter_options_.set_filter_mode(filter_mode);
|
||||||
ResetAsync();
|
ResetAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::SetFilterText(const QString &text) {
|
void CollectionModel::SetFilterAge(const int filter_age) {
|
||||||
query_options_.set_filter(text);
|
filter_options_.set_max_age(filter_age);
|
||||||
ResetAsync();
|
ResetAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::SetFilterQueryMode(QueryOptions::QueryMode query_mode) {
|
void CollectionModel::SetFilterText(const QString &filter_text) {
|
||||||
query_options_.set_query_mode(query_mode);
|
filter_options_.set_filter_text(filter_text);
|
||||||
ResetAsync();
|
ResetAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollectionModel::canFetchMore(const QModelIndex &parent) const {
|
bool CollectionModel::canFetchMore(const QModelIndex &parent) const {
|
||||||
|
@ -49,7 +49,9 @@
|
|||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
#include "covermanager/albumcoverloader.h"
|
#include "covermanager/albumcoverloader.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
#include "collectionitem.h"
|
#include "collectionitem.h"
|
||||||
#include "covermanager/albumcoverloaderoptions.h"
|
#include "covermanager/albumcoverloaderoptions.h"
|
||||||
|
|
||||||
@ -203,9 +205,9 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
void GroupingChanged(CollectionModel::Grouping g, bool separate_albums_by_grouping);
|
void GroupingChanged(CollectionModel::Grouping g, bool separate_albums_by_grouping);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetFilterAge(const int age);
|
void SetFilterMode(CollectionFilterOptions::FilterMode filter_mode);
|
||||||
void SetFilterText(const QString &text);
|
void SetFilterAge(const int filter_age);
|
||||||
void SetFilterQueryMode(QueryOptions::QueryMode query_mode);
|
void SetFilterText(const QString &filter_text);
|
||||||
|
|
||||||
void Init(const bool async = true);
|
void Init(const bool async = true);
|
||||||
void Reset();
|
void Reset();
|
||||||
@ -232,20 +234,21 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Provides some optimisations for loading the list of items in the root.
|
// Provides some optimizations for loading the list of items in the root.
|
||||||
// This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread.
|
// This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread.
|
||||||
QueryResult RunQuery(CollectionItem *parent);
|
CollectionQueryOptions PrepareQuery(CollectionItem *parent);
|
||||||
|
QueryResult RunQuery(const CollectionFilterOptions &filter_options = CollectionFilterOptions(), const CollectionQueryOptions &query_options = CollectionQueryOptions());
|
||||||
void PostQuery(CollectionItem *parent, const QueryResult &result, const bool signal);
|
void PostQuery(CollectionItem *parent, const QueryResult &result, const bool signal);
|
||||||
|
|
||||||
bool HasCompilations(const QSqlDatabase &db, const CollectionQuery &query);
|
bool HasCompilations(const QSqlDatabase &db, const CollectionFilterOptions &filter_options, const CollectionQueryOptions &query_options);
|
||||||
|
|
||||||
void BeginReset();
|
void BeginReset();
|
||||||
|
|
||||||
// Functions for working with queries and creating items.
|
// Functions for working with queries and creating items.
|
||||||
// When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items.
|
// When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items.
|
||||||
// Filters are added for each parent item, restricting the songs returned to a particular album or artist for example.
|
// Filters are added for each parent item, restricting the songs returned to a particular album or artist for example.
|
||||||
static void InitQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQuery *q);
|
static void SetQueryColumnSpec(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQueryOptions *query_options);
|
||||||
static void FilterQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQuery *q);
|
static void AddQueryWhere(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQueryOptions *query_options);
|
||||||
|
|
||||||
// Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend.
|
// Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend.
|
||||||
CollectionItem *ItemFromQuery(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level);
|
CollectionItem *ItemFromQuery(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level);
|
||||||
@ -279,7 +282,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
int total_artist_count_;
|
int total_artist_count_;
|
||||||
int total_album_count_;
|
int total_album_count_;
|
||||||
|
|
||||||
QueryOptions query_options_;
|
CollectionFilterOptions filter_options_;
|
||||||
Grouping group_by_;
|
Grouping group_by_;
|
||||||
bool separate_albums_by_grouping_;
|
bool separate_albums_by_grouping_;
|
||||||
|
|
||||||
|
@ -31,16 +31,16 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <QSqlError>
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/sqlquery.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
|
|
||||||
QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {}
|
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const CollectionFilterOptions &filter_options)
|
||||||
|
|
||||||
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const QueryOptions &options)
|
|
||||||
: QSqlQuery(db),
|
: QSqlQuery(db),
|
||||||
songs_table_(songs_table),
|
songs_table_(songs_table),
|
||||||
fts_table_(fts_table),
|
fts_table_(fts_table),
|
||||||
@ -49,7 +49,7 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
|
|||||||
duplicates_only_(false),
|
duplicates_only_(false),
|
||||||
limit_(-1) {
|
limit_(-1) {
|
||||||
|
|
||||||
if (!options.filter().isEmpty()) {
|
if (!filter_options.filter_text().isEmpty()) {
|
||||||
// We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS5:
|
// We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS5:
|
||||||
// 1) Append * to all tokens.
|
// 1) Append * to all tokens.
|
||||||
// 2) Prefix "fts" to column names.
|
// 2) Prefix "fts" to column names.
|
||||||
@ -57,9 +57,9 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
|
|||||||
|
|
||||||
// Split on whitespace
|
// Split on whitespace
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
QStringList tokens(options.filter().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts));
|
QStringList tokens(filter_options.filter_text().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts));
|
||||||
#else
|
#else
|
||||||
QStringList tokens(options.filter().split(QRegularExpression("\\s+"), QString::SkipEmptyParts));
|
QStringList tokens(filter_options.filter_text().split(QRegularExpression("\\s+"), QString::SkipEmptyParts));
|
||||||
#endif
|
#endif
|
||||||
QString query;
|
QString query;
|
||||||
for (QString token : tokens) {
|
for (QString token : tokens) {
|
||||||
@ -100,49 +100,40 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.max_age() != -1) {
|
if (filter_options.max_age() != -1) {
|
||||||
qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - options.max_age();
|
qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - filter_options.max_age();
|
||||||
|
|
||||||
where_clauses_ << "ctime > ?";
|
where_clauses_ << "ctime > ?";
|
||||||
bound_values_ << cutoff;
|
bound_values_ << cutoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Currently you cannot use any QueryMode other than All and FTS at the same time.
|
// TODO: Currently you cannot use any FilterMode other than All and FTS at the same time.
|
||||||
// Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
|
// Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
|
||||||
// The query takes about 20 seconds on my machine then. Why?
|
// The query takes about 20 seconds on my machine then. Why?
|
||||||
// Untagged mode could work with additional filtering but I'm disabling it just to be consistent
|
// Untagged mode could work with additional filtering but I'm disabling it just to be consistent
|
||||||
// this way filtering is available only in the All mode.
|
// this way filtering is available only in the All mode.
|
||||||
// Remember though that when you fix the Duplicates + FTS cooperation, enable the filtering in both Duplicates and Untagged modes.
|
// Remember though that when you fix the Duplicates + FTS cooperation, enable the filtering in both Duplicates and Untagged modes.
|
||||||
duplicates_only_ = options.query_mode() == QueryOptions::QueryMode_Duplicates;
|
duplicates_only_ = filter_options.filter_mode() == CollectionFilterOptions::FilterMode_Duplicates;
|
||||||
|
|
||||||
if (options.query_mode() == QueryOptions::QueryMode_Untagged) {
|
if (filter_options.filter_mode() == CollectionFilterOptions::FilterMode_Untagged) {
|
||||||
where_clauses_ << "(artist = '' OR album = '' OR title ='')";
|
where_clauses_ << "(artist = '' OR album = '' OR title ='')";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionQuery::GetInnerQuery() const {
|
|
||||||
return duplicates_only_
|
|
||||||
? QString(" INNER JOIN (select * from duplicated_songs) dsongs "
|
|
||||||
"ON (%songs_table.artist = dsongs.dup_artist "
|
|
||||||
"AND %songs_table.album = dsongs.dup_album "
|
|
||||||
"AND %songs_table.title = dsongs.dup_title) ")
|
|
||||||
: QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
||||||
|
|
||||||
// Ignore 'literal' for IN
|
// Ignore 'literal' for IN
|
||||||
if (op.compare("IN", Qt::CaseInsensitive) == 0) {
|
if (op.compare("IN", Qt::CaseInsensitive) == 0) {
|
||||||
QStringList values = value.toStringList();
|
QStringList values = value.toStringList();
|
||||||
QStringList final;
|
QStringList final_values;
|
||||||
final.reserve(values.count());
|
final_values.reserve(values.count());
|
||||||
for (const QString &single_value : values) {
|
for (const QString &single_value : values) {
|
||||||
final.append("?");
|
final_values.append("?");
|
||||||
bound_values_ << single_value;
|
bound_values_ << single_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
where_clauses_ << QString("%1 IN (" + final.join(",") + ")").arg(column);
|
where_clauses_ << QString("%1 IN (" + final_values.join(",") + ")").arg(column);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
||||||
@ -187,6 +178,15 @@ void CollectionQuery::AddCompilationRequirement(const bool compilation) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CollectionQuery::GetInnerQuery() const {
|
||||||
|
return duplicates_only_
|
||||||
|
? QString(" INNER JOIN (select * from duplicated_songs) dsongs "
|
||||||
|
"ON (%songs_table.artist = dsongs.dup_artist "
|
||||||
|
"AND %songs_table.album = dsongs.dup_album "
|
||||||
|
"AND %songs_table.title = dsongs.dup_title) ")
|
||||||
|
: QString();
|
||||||
|
}
|
||||||
|
|
||||||
bool CollectionQuery::Exec() {
|
bool CollectionQuery::Exec() {
|
||||||
|
|
||||||
QString sql;
|
QString sql;
|
||||||
@ -213,32 +213,17 @@ bool CollectionQuery::Exec() {
|
|||||||
sql.replace("%fts_table_noprefix", fts_table_.section('.', -1, -1));
|
sql.replace("%fts_table_noprefix", fts_table_.section('.', -1, -1));
|
||||||
sql.replace("%fts_table", fts_table_);
|
sql.replace("%fts_table", fts_table_);
|
||||||
|
|
||||||
prepare(sql);
|
QSqlQuery::prepare(sql);
|
||||||
|
|
||||||
// Bind values
|
// Bind values
|
||||||
for (const QVariant &value : bound_values_) {
|
for (const QVariant &value : bound_values_) {
|
||||||
addBindValue(value);
|
QSqlQuery::addBindValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return exec();
|
return QSqlQuery::exec();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollectionQuery::Next() { return next(); }
|
bool CollectionQuery::Next() { return QSqlQuery::next(); }
|
||||||
|
|
||||||
QVariant CollectionQuery::Value(const int column) const { return value(column); }
|
QVariant CollectionQuery::Value(const int column) const { return QSqlQuery::value(column); }
|
||||||
|
|
||||||
bool QueryOptions::Matches(const Song &song) const {
|
|
||||||
|
|
||||||
if (max_age_ != -1) {
|
|
||||||
const qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
|
|
||||||
if (song.ctime() <= cutoff) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filter_.isNull()) {
|
|
||||||
return song.artist().contains(filter_, Qt::CaseInsensitive) || song.album().contains(filter_, Qt::CaseInsensitive) || song.title().contains(filter_, Qt::CaseInsensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -28,75 +28,23 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QMap>
|
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
|
||||||
class Song;
|
#include "collectionfilteroptions.h"
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
// This structure let's you customize behaviour of any CollectionQuery.
|
|
||||||
struct QueryOptions {
|
|
||||||
// Modes of CollectionQuery:
|
|
||||||
// - use the all songs table
|
|
||||||
// - use the duplicated songs view; by duplicated we mean those songs for which the (artist, album, title) tuple is found more than once in the songs table
|
|
||||||
// - use the untagged songs view; by untagged we mean those for which at least one of the (artist, album, title) tags is empty
|
|
||||||
// Please note that additional filtering based on FTS table (the filter attribute) won't work in Duplicates and Untagged modes.
|
|
||||||
enum QueryMode {
|
|
||||||
QueryMode_All,
|
|
||||||
QueryMode_Duplicates,
|
|
||||||
QueryMode_Untagged
|
|
||||||
};
|
|
||||||
|
|
||||||
QueryOptions();
|
|
||||||
|
|
||||||
bool Matches(const Song &song) const;
|
|
||||||
|
|
||||||
QString filter() const { return filter_; }
|
|
||||||
void set_filter(const QString &filter) {
|
|
||||||
filter_ = filter;
|
|
||||||
query_mode_ = QueryMode_All;
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_age() const { return max_age_; }
|
|
||||||
void set_max_age(int max_age) { max_age_ = max_age; }
|
|
||||||
|
|
||||||
QueryMode query_mode() const { return query_mode_; }
|
|
||||||
void set_query_mode(QueryMode query_mode) {
|
|
||||||
query_mode_ = query_mode;
|
|
||||||
filter_ = QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString filter_;
|
|
||||||
int max_age_;
|
|
||||||
QueryMode query_mode_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CollectionQuery : public QSqlQuery {
|
class CollectionQuery : public QSqlQuery {
|
||||||
public:
|
public:
|
||||||
explicit CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const QueryOptions &options = QueryOptions());
|
explicit CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const CollectionFilterOptions &filter_options = CollectionFilterOptions());
|
||||||
|
|
||||||
// Sets contents of SELECT clause on the query (list of columns to get).
|
QVariant Value(const int column) const;
|
||||||
void SetColumnSpec(const QString &spec) { column_spec_ = spec; }
|
QVariant value(const int column) const { return Value(column); }
|
||||||
|
|
||||||
// Sets an ORDER BY clause on the query.
|
|
||||||
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
|
|
||||||
|
|
||||||
// Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
|
|
||||||
// Please note that IN operator expects a QStringList as value.
|
|
||||||
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
|
||||||
void AddWhereArtist(const QVariant &value);
|
|
||||||
|
|
||||||
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
|
||||||
void SetBoundValues(const QVariantList &bound_values) { bound_values_ = bound_values; }
|
|
||||||
void SetDuplicatesOnly(const bool duplicates_only) { duplicates_only_ = duplicates_only; }
|
|
||||||
void SetIncludeUnavailable(const bool include_unavailable) { include_unavailable_ = include_unavailable; }
|
|
||||||
void SetLimit(const int limit) { limit_ = limit; }
|
|
||||||
void AddCompilationRequirement(const bool compilation);
|
|
||||||
|
|
||||||
bool Exec();
|
bool Exec();
|
||||||
|
bool exec() { return QSqlQuery::exec(); }
|
||||||
|
|
||||||
bool Next();
|
bool Next();
|
||||||
QVariant Value(const int column) const;
|
|
||||||
|
|
||||||
QString column_spec() const { return column_spec_; }
|
QString column_spec() const { return column_spec_; }
|
||||||
QString order_by() const { return order_by_; }
|
QString order_by() const { return order_by_; }
|
||||||
@ -107,6 +55,24 @@ class CollectionQuery : public QSqlQuery {
|
|||||||
bool duplicates_only() const { return duplicates_only_; }
|
bool duplicates_only() const { return duplicates_only_; }
|
||||||
int limit() const { return limit_; }
|
int limit() const { return limit_; }
|
||||||
|
|
||||||
|
// Sets contents of SELECT clause on the query (list of columns to get).
|
||||||
|
void SetColumnSpec(const QString &column_spec) { column_spec_ = column_spec; }
|
||||||
|
|
||||||
|
// Sets an ORDER BY clause on the query.
|
||||||
|
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
|
||||||
|
|
||||||
|
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
||||||
|
// Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
|
||||||
|
// Please note that IN operator expects a QStringList as value.
|
||||||
|
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
||||||
|
void AddWhereArtist(const QVariant &value);
|
||||||
|
|
||||||
|
void SetBoundValues(const QVariantList &bound_values) { bound_values_ = bound_values; }
|
||||||
|
void SetDuplicatesOnly(const bool duplicates_only) { duplicates_only_ = duplicates_only; }
|
||||||
|
void SetIncludeUnavailable(const bool include_unavailable) { include_unavailable_ = include_unavailable; }
|
||||||
|
void SetLimit(const int limit) { limit_ = limit; }
|
||||||
|
void AddCompilationRequirement(const bool compilation);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString GetInnerQuery() const;
|
QString GetInnerQuery() const;
|
||||||
|
|
||||||
|
34
src/collection/collectionqueryoptions.cpp
Normal file
34
src/collection/collectionqueryoptions.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "collectionqueryoptions.h"
|
||||||
|
#include "collectionfilteroptions.h"
|
||||||
|
|
||||||
|
CollectionQueryOptions::CollectionQueryOptions()
|
||||||
|
: compilation_requirement_(false),
|
||||||
|
query_have_compilations_(false) {}
|
||||||
|
|
||||||
|
void CollectionQueryOptions::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
||||||
|
|
||||||
|
where_clauses_ << Where(column, value, op);
|
||||||
|
|
||||||
|
}
|
57
src/collection/collectionqueryoptions.h
Normal file
57
src/collection/collectionqueryoptions.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COLLECTIONQUERYOPTIONS_H
|
||||||
|
#define COLLECTIONQUERYOPTIONS_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class CollectionQueryOptions {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit CollectionQueryOptions();
|
||||||
|
|
||||||
|
struct Where {
|
||||||
|
explicit Where(const QString _column = QString(), const QVariant _value = QString(), const QString _op = QString()) : column(_column), value(_value), op(_op) {}
|
||||||
|
QString column;
|
||||||
|
QVariant value;
|
||||||
|
QString op;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString column_spec() const { return column_spec_; }
|
||||||
|
bool compilation_requirement() const { return compilation_requirement_; }
|
||||||
|
bool query_have_compilations() const { return query_have_compilations_; }
|
||||||
|
|
||||||
|
void set_column_spec(const QString &column_spec) { column_spec_ = column_spec; }
|
||||||
|
void set_compilation_requirement(const bool compilation_requirement) { compilation_requirement_ = compilation_requirement; }
|
||||||
|
void set_query_have_compilations(const bool query_have_compilations) { query_have_compilations_ = query_have_compilations; }
|
||||||
|
|
||||||
|
QList<Where> where_clauses() const { return where_clauses_; }
|
||||||
|
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString column_spec_;
|
||||||
|
bool compilation_requirement_;
|
||||||
|
bool query_have_compilations_;
|
||||||
|
QList<Where> where_clauses_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLLECTIONQUERYOPTIONS_H
|
@ -50,7 +50,7 @@
|
|||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
#include "utilities/imageutils.h"
|
#include "utilities/imageutils.h"
|
||||||
#include "utilities/timeconstants.h"
|
#include "utilities/timeconstants.h"
|
||||||
#include "directory.h"
|
#include "collectiondirectory.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
#include "collectionwatcher.h"
|
#include "collectionwatcher.h"
|
||||||
#include "playlistparsers/cueparser.h"
|
#include "playlistparsers/cueparser.h"
|
||||||
@ -167,9 +167,9 @@ void CollectionWatcher::ReloadSettings() {
|
|||||||
}
|
}
|
||||||
else if (monitor_ && !was_monitoring_before) {
|
else if (monitor_ && !was_monitoring_before) {
|
||||||
// Add all directories to all QFileSystemWatchers again
|
// Add all directories to all QFileSystemWatchers again
|
||||||
for (const Directory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
SubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
|
CollectionSubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
|
||||||
for (const Subdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
AddWatch(dir, subdir.path);
|
AddWatch(dir, subdir.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ void CollectionWatcher::ScanTransaction::CommitNewOrUpdatedSongs() {
|
|||||||
touched_subdirs.clear();
|
touched_subdirs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Subdirectory &subdir : deleted_subdirs) {
|
for (const CollectionSubdirectory &subdir : deleted_subdirs) {
|
||||||
if (watcher_->watched_dirs_.contains(dir_)) {
|
if (watcher_->watched_dirs_.contains(dir_)) {
|
||||||
watcher_->RemoveWatch(watcher_->watched_dirs_[dir_], subdir);
|
watcher_->RemoveWatch(watcher_->watched_dirs_[dir_], subdir);
|
||||||
}
|
}
|
||||||
@ -281,7 +281,7 @@ void CollectionWatcher::ScanTransaction::CommitNewOrUpdatedSongs() {
|
|||||||
|
|
||||||
if (watcher_->monitor_) {
|
if (watcher_->monitor_) {
|
||||||
// Watch the new subdirectories
|
// Watch the new subdirectories
|
||||||
for (const Subdirectory &subdir : new_subdirs) {
|
for (const CollectionSubdirectory &subdir : new_subdirs) {
|
||||||
if (watcher_->watched_dirs_.contains(dir_)) {
|
if (watcher_->watched_dirs_.contains(dir_)) {
|
||||||
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
|
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ bool CollectionWatcher::ScanTransaction::HasSongsWithMissingFingerprint(const QS
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::ScanTransaction::SetKnownSubdirs(const SubdirectoryList &subdirs) {
|
void CollectionWatcher::ScanTransaction::SetKnownSubdirs(const CollectionSubdirectoryList &subdirs) {
|
||||||
|
|
||||||
known_subdirs_ = subdirs;
|
known_subdirs_ = subdirs;
|
||||||
known_subdirs_dirty_ = false;
|
known_subdirs_dirty_ = false;
|
||||||
@ -342,18 +342,18 @@ bool CollectionWatcher::ScanTransaction::HasSeenSubdir(const QString &path) {
|
|||||||
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::any_of(known_subdirs_.begin(), known_subdirs_.end(), [path](const Subdirectory &subdir) { return subdir.path == path && subdir.mtime != 0; });
|
return std::any_of(known_subdirs_.begin(), known_subdirs_.end(), [path](const CollectionSubdirectory &subdir) { return subdir.path == path && subdir.mtime != 0; });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdirs(const QString &path) {
|
CollectionSubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdirs(const QString &path) {
|
||||||
|
|
||||||
if (known_subdirs_dirty_) {
|
if (known_subdirs_dirty_) {
|
||||||
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList ret;
|
CollectionSubdirectoryList ret;
|
||||||
for (const Subdirectory &subdir : known_subdirs_) {
|
for (const CollectionSubdirectory &subdir : known_subdirs_) {
|
||||||
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
|
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
|
||||||
ret << subdir;
|
ret << subdir;
|
||||||
}
|
}
|
||||||
@ -363,7 +363,7 @@ SubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdirs(const Q
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubdirectoryList CollectionWatcher::ScanTransaction::GetAllSubdirs() {
|
CollectionSubdirectoryList CollectionWatcher::ScanTransaction::GetAllSubdirs() {
|
||||||
|
|
||||||
if (known_subdirs_dirty_) {
|
if (known_subdirs_dirty_) {
|
||||||
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
|
||||||
@ -373,7 +373,7 @@ SubdirectoryList CollectionWatcher::ScanTransaction::GetAllSubdirs() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryList &subdirs) {
|
void CollectionWatcher::AddDirectory(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdirs) {
|
||||||
|
|
||||||
stop_requested_ = false;
|
stop_requested_ = false;
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryLis
|
|||||||
const quint64 files_count = FilesCountForPath(&transaction, dir.path);
|
const quint64 files_count = FilesCountForPath(&transaction, dir.path);
|
||||||
transaction.SetKnownSubdirs(subdirs);
|
transaction.SetKnownSubdirs(subdirs);
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
ScanSubdirectory(dir.path, Subdirectory(), files_count, &transaction);
|
ScanSubdirectory(dir.path, CollectionSubdirectory(), files_count, &transaction);
|
||||||
last_scan_time_ = QDateTime::currentDateTime().toSecsSinceEpoch();
|
last_scan_time_ = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -395,7 +395,7 @@ void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryLis
|
|||||||
const quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
const quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
||||||
transaction.SetKnownSubdirs(subdirs);
|
transaction.SetKnownSubdirs(subdirs);
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
for (const Subdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
|
|
||||||
if (scan_on_startup_) ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
if (scan_on_startup_) ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
||||||
@ -411,14 +411,14 @@ void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryLis
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory &subdir, const quint64 files_count, ScanTransaction *t, const bool force_noincremental) {
|
void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSubdirectory &subdir, const quint64 files_count, ScanTransaction *t, const bool force_noincremental) {
|
||||||
|
|
||||||
QFileInfo path_info(path);
|
QFileInfo path_info(path);
|
||||||
|
|
||||||
// Do not scan symlinked dirs that are already in collection
|
// Do not scan symlinked dirs that are already in collection
|
||||||
if (path_info.isSymLink()) {
|
if (path_info.isSymLink()) {
|
||||||
QString real_path = path_info.symLinkTarget();
|
QString real_path = path_info.symLinkTarget();
|
||||||
for (const Directory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
if (real_path.startsWith(dir.path)) {
|
if (real_path.startsWith(dir.path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -440,12 +440,12 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
|||||||
|
|
||||||
QMap<QString, QStringList> album_art;
|
QMap<QString, QStringList> album_art;
|
||||||
QStringList files_on_disk;
|
QStringList files_on_disk;
|
||||||
SubdirectoryList my_new_subdirs;
|
CollectionSubdirectoryList my_new_subdirs;
|
||||||
|
|
||||||
// If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist anymore.
|
// If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist anymore.
|
||||||
// If one has been removed, "rescan" it to get the deleted songs
|
// If one has been removed, "rescan" it to get the deleted songs
|
||||||
SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
|
CollectionSubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
|
||||||
for (const Subdirectory &prev_subdir : previous_subdirs) {
|
for (const CollectionSubdirectory &prev_subdir : previous_subdirs) {
|
||||||
if (!QFile::exists(prev_subdir.path) && prev_subdir.path != path) {
|
if (!QFile::exists(prev_subdir.path) && prev_subdir.path != path) {
|
||||||
ScanSubdirectory(prev_subdir.path, prev_subdir, 0, t, true);
|
ScanSubdirectory(prev_subdir.path, prev_subdir, 0, t, true);
|
||||||
}
|
}
|
||||||
@ -463,7 +463,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
|||||||
if (child_info.isDir()) {
|
if (child_info.isDir()) {
|
||||||
if (!t->HasSeenSubdir(child)) {
|
if (!t->HasSeenSubdir(child)) {
|
||||||
// We haven't seen this subdirectory before - add it to a list, and later we'll tell the backend about it and scan it.
|
// We haven't seen this subdirectory before - add it to a list, and later we'll tell the backend about it and scan it.
|
||||||
Subdirectory new_subdir;
|
CollectionSubdirectory new_subdir;
|
||||||
new_subdir.directory_id = -1;
|
new_subdir.directory_id = -1;
|
||||||
new_subdir.path = child;
|
new_subdir.path = child;
|
||||||
new_subdir.mtime = child_info.lastModified().toSecsSinceEpoch();
|
new_subdir.mtime = child_info.lastModified().toSecsSinceEpoch();
|
||||||
@ -676,7 +676,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add this subdir to the new or touched list
|
// Add this subdir to the new or touched list
|
||||||
Subdirectory updated_subdir;
|
CollectionSubdirectory updated_subdir;
|
||||||
updated_subdir.directory_id = t->dir();
|
updated_subdir.directory_id = t->dir();
|
||||||
updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toSecsSinceEpoch() : 0;
|
updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toSecsSinceEpoch() : 0;
|
||||||
updated_subdir.path = path;
|
updated_subdir.path = path;
|
||||||
@ -688,12 +688,12 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
|||||||
t->touched_subdirs << updated_subdir;
|
t->touched_subdirs << updated_subdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updated_subdir.mtime == 0) { // Subdirectory deleted, mark it for removal from the watcher.
|
if (updated_subdir.mtime == 0) { // CollectionSubdirectory deleted, mark it for removal from the watcher.
|
||||||
t->deleted_subdirs << updated_subdir;
|
t->deleted_subdirs << updated_subdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse into the new subdirs that we found
|
// Recurse into the new subdirs that we found
|
||||||
for (const Subdirectory &my_new_subdir : my_new_subdirs) {
|
for (const CollectionSubdirectory &my_new_subdir : my_new_subdirs) {
|
||||||
if (stop_requested_ || abort_requested_) return;
|
if (stop_requested_ || abort_requested_) return;
|
||||||
ScanSubdirectory(my_new_subdir.path, my_new_subdir, 0, t, true);
|
ScanSubdirectory(my_new_subdir.path, my_new_subdir, 0, t, true);
|
||||||
}
|
}
|
||||||
@ -897,7 +897,7 @@ quint64 CollectionWatcher::GetMtimeForCue(const QString &cue_path) {
|
|||||||
return cue_last_modified.isValid() ? cue_last_modified.toSecsSinceEpoch() : 0;
|
return cue_last_modified.isValid() ? cue_last_modified.toSecsSinceEpoch() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::AddWatch(const Directory &dir, const QString &path) {
|
void CollectionWatcher::AddWatch(const CollectionDirectory &dir, const QString &path) {
|
||||||
|
|
||||||
if (!QFile::exists(path)) return;
|
if (!QFile::exists(path)) return;
|
||||||
|
|
||||||
@ -907,7 +907,7 @@ void CollectionWatcher::AddWatch(const Directory &dir, const QString &path) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::RemoveWatch(const Directory &dir, const Subdirectory &subdir) {
|
void CollectionWatcher::RemoveWatch(const CollectionDirectory &dir, const CollectionSubdirectory &subdir) {
|
||||||
|
|
||||||
QStringList subdir_paths = subdir_mapping_.keys(dir);
|
QStringList subdir_paths = subdir_mapping_.keys(dir);
|
||||||
for (const QString &subdir_path : subdir_paths) {
|
for (const QString &subdir_path : subdir_paths) {
|
||||||
@ -919,7 +919,7 @@ void CollectionWatcher::RemoveWatch(const Directory &dir, const Subdirectory &su
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::RemoveDirectory(const Directory &dir) {
|
void CollectionWatcher::RemoveDirectory(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
rescan_queue_.remove(dir.id);
|
rescan_queue_.remove(dir.id);
|
||||||
watched_dirs_.remove(dir.id);
|
watched_dirs_.remove(dir.id);
|
||||||
@ -979,11 +979,11 @@ bool CollectionWatcher::FindSongsByFingerprint(const QString &file, const SongLi
|
|||||||
void CollectionWatcher::DirectoryChanged(const QString &subdir) {
|
void CollectionWatcher::DirectoryChanged(const QString &subdir) {
|
||||||
|
|
||||||
// Find what dir it was in
|
// Find what dir it was in
|
||||||
QHash<QString, Directory>::const_iterator it = subdir_mapping_.constFind(subdir);
|
QHash<QString, CollectionDirectory>::const_iterator it = subdir_mapping_.constFind(subdir);
|
||||||
if (it == subdir_mapping_.constEnd()) {
|
if (it == subdir_mapping_.constEnd()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Directory dir = *it;
|
CollectionDirectory dir = *it;
|
||||||
|
|
||||||
qLog(Debug) << "Subdir" << subdir << "changed under directory" << dir.path << "id" << dir.id;
|
qLog(Debug) << "Subdir" << subdir << "changed under directory" << dir.path << "id" << dir.id;
|
||||||
|
|
||||||
@ -1010,7 +1010,7 @@ void CollectionWatcher::RescanPathsNow() {
|
|||||||
|
|
||||||
for (const QString &path : rescan_queue_[dir]) {
|
for (const QString &path : rescan_queue_[dir]) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
Subdirectory subdir;
|
CollectionSubdirectory subdir;
|
||||||
subdir.directory_id = dir;
|
subdir.directory_id = dir;
|
||||||
subdir.mtime = 0;
|
subdir.mtime = 0;
|
||||||
subdir.path = path;
|
subdir.path = path;
|
||||||
@ -1154,7 +1154,7 @@ void CollectionWatcher::RescanTracksNow() {
|
|||||||
qLog(Debug) << "Song" << song.title() << "dir id" << song.directory_id() << "dir" << songdir;
|
qLog(Debug) << "Song" << song.title() << "dir id" << song.directory_id() << "dir" << songdir;
|
||||||
ScanTransaction transaction(this, song.directory_id(), false, false, mark_songs_unavailable_);
|
ScanTransaction transaction(this, song.directory_id(), false, false, mark_songs_unavailable_);
|
||||||
quint64 files_count = FilesCountForPath(&transaction, songdir);
|
quint64 files_count = FilesCountForPath(&transaction, songdir);
|
||||||
ScanSubdirectory(songdir, Subdirectory(), files_count, &transaction);
|
ScanSubdirectory(songdir, CollectionSubdirectory(), files_count, &transaction);
|
||||||
scanned_dirs << songdir;
|
scanned_dirs << songdir;
|
||||||
emit CompilationsNeedUpdating();
|
emit CompilationsNeedUpdating();
|
||||||
}
|
}
|
||||||
@ -1171,16 +1171,16 @@ void CollectionWatcher::PerformScan(const bool incremental, const bool ignore_mt
|
|||||||
|
|
||||||
stop_requested_ = false;
|
stop_requested_ = false;
|
||||||
|
|
||||||
for (const Directory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
|
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
|
|
||||||
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes, mark_songs_unavailable_);
|
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes, mark_songs_unavailable_);
|
||||||
SubdirectoryList subdirs(transaction.GetAllSubdirs());
|
CollectionSubdirectoryList subdirs(transaction.GetAllSubdirs());
|
||||||
|
|
||||||
if (subdirs.isEmpty()) {
|
if (subdirs.isEmpty()) {
|
||||||
qLog(Debug) << "Collection directory wasn't in subdir list.";
|
qLog(Debug) << "Collection directory wasn't in subdir list.";
|
||||||
Subdirectory subdir;
|
CollectionSubdirectory subdir;
|
||||||
subdir.path = dir.path;
|
subdir.path = dir.path;
|
||||||
subdir.directory_id = dir.id;
|
subdir.directory_id = dir.id;
|
||||||
subdirs << subdir;
|
subdirs << subdir;
|
||||||
@ -1190,7 +1190,7 @@ void CollectionWatcher::PerformScan(const bool incremental, const bool ignore_mt
|
|||||||
quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
|
|
||||||
for (const Subdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
||||||
}
|
}
|
||||||
@ -1217,7 +1217,7 @@ quint64 CollectionWatcher::FilesCountForPath(ScanTransaction *t, const QString &
|
|||||||
if (path_info.isDir()) {
|
if (path_info.isDir()) {
|
||||||
if (path_info.isSymLink()) {
|
if (path_info.isSymLink()) {
|
||||||
QString real_path = path_info.symLinkTarget();
|
QString real_path = path_info.symLinkTarget();
|
||||||
for (const Directory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
if (real_path.startsWith(dir.path)) {
|
if (real_path.startsWith(dir.path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1239,10 +1239,10 @@ quint64 CollectionWatcher::FilesCountForPath(ScanTransaction *t, const QString &
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 CollectionWatcher::FilesCountForSubdirs(ScanTransaction *t, const SubdirectoryList &subdirs, QMap<QString, quint64> &subdir_files_count) {
|
quint64 CollectionWatcher::FilesCountForSubdirs(ScanTransaction *t, const CollectionSubdirectoryList &subdirs, QMap<QString, quint64> &subdir_files_count) {
|
||||||
|
|
||||||
quint64 i = 0;
|
quint64 i = 0;
|
||||||
for (const Subdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
const quint64 files_count = FilesCountForPath(t, subdir.path);
|
const quint64 files_count = FilesCountForPath(t, subdir.path);
|
||||||
subdir_files_count[subdir.path] = files_count;
|
subdir_files_count[subdir.path] = files_count;
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "directory.h"
|
#include "collectiondirectory.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
@ -74,8 +74,8 @@ class CollectionWatcher : public QObject {
|
|||||||
void SongsDeleted(SongList);
|
void SongsDeleted(SongList);
|
||||||
void SongsUnavailable(SongList songs, bool unavailable = true);
|
void SongsUnavailable(SongList songs, bool unavailable = true);
|
||||||
void SongsReadded(SongList songs, bool unavailable = false);
|
void SongsReadded(SongList songs, bool unavailable = false);
|
||||||
void SubdirsDiscovered(SubdirectoryList subdirs);
|
void SubdirsDiscovered(CollectionSubdirectoryList subdirs);
|
||||||
void SubdirsMTimeUpdated(SubdirectoryList subdirs);
|
void SubdirsMTimeUpdated(CollectionSubdirectoryList subdirs);
|
||||||
void CompilationsNeedUpdating();
|
void CompilationsNeedUpdating();
|
||||||
void UpdateLastSeen(int directory_id, int expire_unavailable_songs_days);
|
void UpdateLastSeen(int directory_id, int expire_unavailable_songs_days);
|
||||||
void ExitFinished();
|
void ExitFinished();
|
||||||
@ -83,8 +83,8 @@ class CollectionWatcher : public QObject {
|
|||||||
void ScanStarted(int task_id);
|
void ScanStarted(int task_id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void AddDirectory(const Directory &dir, const SubdirectoryList &subdirs);
|
void AddDirectory(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdirs);
|
||||||
void RemoveDirectory(const Directory &dir);
|
void RemoveDirectory(const CollectionDirectory &dir);
|
||||||
void SetRescanPaused(bool pause);
|
void SetRescanPaused(bool pause);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -102,9 +102,9 @@ class CollectionWatcher : public QObject {
|
|||||||
SongList FindSongsInSubdirectory(const QString &path);
|
SongList FindSongsInSubdirectory(const QString &path);
|
||||||
bool HasSongsWithMissingFingerprint(const QString &path);
|
bool HasSongsWithMissingFingerprint(const QString &path);
|
||||||
bool HasSeenSubdir(const QString &path);
|
bool HasSeenSubdir(const QString &path);
|
||||||
void SetKnownSubdirs(const SubdirectoryList &subdirs);
|
void SetKnownSubdirs(const CollectionSubdirectoryList &subdirs);
|
||||||
SubdirectoryList GetImmediateSubdirs(const QString &path);
|
CollectionSubdirectoryList GetImmediateSubdirs(const QString &path);
|
||||||
SubdirectoryList GetAllSubdirs();
|
CollectionSubdirectoryList GetAllSubdirs();
|
||||||
|
|
||||||
void AddToProgress(const quint64 n = 1);
|
void AddToProgress(const quint64 n = 1);
|
||||||
void AddToProgressMax(const quint64 n);
|
void AddToProgressMax(const quint64 n);
|
||||||
@ -120,9 +120,9 @@ class CollectionWatcher : public QObject {
|
|||||||
SongList readded_songs;
|
SongList readded_songs;
|
||||||
SongList new_songs;
|
SongList new_songs;
|
||||||
SongList touched_songs;
|
SongList touched_songs;
|
||||||
SubdirectoryList new_subdirs;
|
CollectionSubdirectoryList new_subdirs;
|
||||||
SubdirectoryList touched_subdirs;
|
CollectionSubdirectoryList touched_subdirs;
|
||||||
SubdirectoryList deleted_subdirs;
|
CollectionSubdirectoryList deleted_subdirs;
|
||||||
|
|
||||||
QStringList files_changed_path_;
|
QStringList files_changed_path_;
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ class CollectionWatcher : public QObject {
|
|||||||
QMultiMap<QString, Song> cached_songs_missing_fingerprint_;
|
QMultiMap<QString, Song> cached_songs_missing_fingerprint_;
|
||||||
bool cached_songs_missing_fingerprint_dirty_;
|
bool cached_songs_missing_fingerprint_dirty_;
|
||||||
|
|
||||||
SubdirectoryList known_subdirs_;
|
CollectionSubdirectoryList known_subdirs_;
|
||||||
bool known_subdirs_dirty_;
|
bool known_subdirs_dirty_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ class CollectionWatcher : public QObject {
|
|||||||
void FullScanNow();
|
void FullScanNow();
|
||||||
void RescanTracksNow();
|
void RescanTracksNow();
|
||||||
void RescanPathsNow();
|
void RescanPathsNow();
|
||||||
void ScanSubdirectory(const QString &path, const Subdirectory &subdir, const quint64 files_count, CollectionWatcher::ScanTransaction *t, const bool force_noincremental = false);
|
void ScanSubdirectory(const QString &path, const CollectionSubdirectory &subdir, const quint64 files_count, CollectionWatcher::ScanTransaction *t, const bool force_noincremental = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool FindSongsByPath(const SongList &songs, const QString &path, SongList *out);
|
static bool FindSongsByPath(const SongList &songs, const QString &path, SongList *out);
|
||||||
@ -179,8 +179,8 @@ class CollectionWatcher : public QObject {
|
|||||||
inline static QString DirectoryPart(const QString &fileName);
|
inline static QString DirectoryPart(const QString &fileName);
|
||||||
QString PickBestImage(const QStringList &images);
|
QString PickBestImage(const QStringList &images);
|
||||||
QUrl ImageForSong(const QString &path, QMap<QString, QStringList> &album_art);
|
QUrl ImageForSong(const QString &path, QMap<QString, QStringList> &album_art);
|
||||||
void AddWatch(const Directory &dir, const QString &path);
|
void AddWatch(const CollectionDirectory &dir, const QString &path);
|
||||||
void RemoveWatch(const Directory &dir, const Subdirectory &subdir);
|
void RemoveWatch(const CollectionDirectory &dir, const CollectionSubdirectory &subdir);
|
||||||
static quint64 GetMtimeForCue(const QString &cue_path);
|
static quint64 GetMtimeForCue(const QString &cue_path);
|
||||||
void PerformScan(const bool incremental, const bool ignore_mtimes);
|
void PerformScan(const bool incremental, const bool ignore_mtimes);
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ class CollectionWatcher : public QObject {
|
|||||||
static void AddChangedSong(const QString &file, const Song &matching_song, const Song &new_song, ScanTransaction *t);
|
static void AddChangedSong(const QString &file, const Song &matching_song, const Song &new_song, ScanTransaction *t);
|
||||||
|
|
||||||
quint64 FilesCountForPath(ScanTransaction *t, const QString &path);
|
quint64 FilesCountForPath(ScanTransaction *t, const QString &path);
|
||||||
quint64 FilesCountForSubdirs(ScanTransaction *t, const SubdirectoryList &subdirs, QMap<QString, quint64> &subdir_files_count);
|
quint64 FilesCountForSubdirs(ScanTransaction *t, const CollectionSubdirectoryList &subdirs, QMap<QString, quint64> &subdir_files_count);
|
||||||
|
|
||||||
QString FindCueFilename(const QString &filename);
|
QString FindCueFilename(const QString &filename);
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ class CollectionWatcher : public QObject {
|
|||||||
|
|
||||||
FileSystemWatcherInterface *fs_watcher_;
|
FileSystemWatcherInterface *fs_watcher_;
|
||||||
QThread *original_thread_;
|
QThread *original_thread_;
|
||||||
QHash<QString, Directory> subdir_mapping_;
|
QHash<QString, CollectionDirectory> subdir_mapping_;
|
||||||
|
|
||||||
// A list of words use to try to identify the (likely) best image found in an directory to use as cover artwork.
|
// A list of words use to try to identify the (likely) best image found in an directory to use as cover artwork.
|
||||||
// e.g. using ["front", "cover"] would identify front.jpg and exclude back.jpg.
|
// e.g. using ["front", "cover"] would identify front.jpg and exclude back.jpg.
|
||||||
@ -225,7 +225,7 @@ class CollectionWatcher : public QObject {
|
|||||||
bool abort_requested_;
|
bool abort_requested_;
|
||||||
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.
|
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.
|
||||||
|
|
||||||
QMap<int, Directory> watched_dirs_;
|
QMap<int, CollectionDirectory> watched_dirs_;
|
||||||
QTimer *rescan_timer_;
|
QTimer *rescan_timer_;
|
||||||
QTimer *periodic_scan_timer_;
|
QTimer *periodic_scan_timer_;
|
||||||
QMap<int, QStringList> rescan_queue_; // dir id -> list of subdirs to be scanned
|
QMap<int, QStringList> rescan_queue_; // dir id -> list of subdirs to be scanned
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Strawberry Music Player
|
|
||||||
* This file was part of Clementine.
|
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
*
|
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Strawberry is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DIRECTORY_H
|
|
||||||
#define DIRECTORY_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <QMetaType>
|
|
||||||
#include <QList>
|
|
||||||
#include <QString>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
|
|
||||||
struct Directory {
|
|
||||||
Directory() : id(-1) {}
|
|
||||||
|
|
||||||
bool operator==(const Directory &other) const {
|
|
||||||
return path == other.path && id == other.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString path;
|
|
||||||
int id;
|
|
||||||
};
|
|
||||||
Q_DECLARE_METATYPE(Directory)
|
|
||||||
|
|
||||||
using DirectoryList = QList<Directory>;
|
|
||||||
Q_DECLARE_METATYPE(DirectoryList)
|
|
||||||
|
|
||||||
|
|
||||||
struct Subdirectory {
|
|
||||||
Subdirectory() : directory_id(-1), mtime(0) {}
|
|
||||||
|
|
||||||
int directory_id;
|
|
||||||
QString path;
|
|
||||||
qint64 mtime;
|
|
||||||
};
|
|
||||||
Q_DECLARE_METATYPE(Subdirectory)
|
|
||||||
|
|
||||||
using SubdirectoryList = QList<Subdirectory>;
|
|
||||||
Q_DECLARE_METATYPE(SubdirectoryList)
|
|
||||||
|
|
||||||
#endif // DIRECTORY_H
|
|
||||||
|
|
@ -675,7 +675,7 @@ MainWindow::MainWindow(Application *app, std::shared_ptr<SystemTrayIcon> tray_ic
|
|||||||
collection_show_untagged_->setCheckable(true);
|
collection_show_untagged_->setCheckable(true);
|
||||||
collection_show_all_->setChecked(true);
|
collection_show_all_->setChecked(true);
|
||||||
|
|
||||||
QObject::connect(collection_view_group, &QActionGroup::triggered, this, &MainWindow::ChangeCollectionQueryMode);
|
QObject::connect(collection_view_group, &QActionGroup::triggered, this, &MainWindow::ChangeCollectionFilterMode);
|
||||||
|
|
||||||
QAction *collection_config_action = new QAction(IconLoader::Load("configure"), tr("Configure collection..."), this);
|
QAction *collection_config_action = new QAction(IconLoader::Load("configure"), tr("Configure collection..."), this);
|
||||||
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::ShowCollectionConfig);
|
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::ShowCollectionConfig);
|
||||||
@ -2752,16 +2752,16 @@ void MainWindow::PlaylistCopyToDevice() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ChangeCollectionQueryMode(QAction *action) {
|
void MainWindow::ChangeCollectionFilterMode(QAction *action) {
|
||||||
|
|
||||||
if (action == collection_show_duplicates_) {
|
if (action == collection_show_duplicates_) {
|
||||||
collection_view_->filter_widget()->SetQueryMode(QueryOptions::QueryMode_Duplicates);
|
collection_view_->filter_widget()->SetFilterMode(CollectionFilterOptions::FilterMode_Duplicates);
|
||||||
}
|
}
|
||||||
else if (action == collection_show_untagged_) {
|
else if (action == collection_show_untagged_) {
|
||||||
collection_view_->filter_widget()->SetQueryMode(QueryOptions::QueryMode_Untagged);
|
collection_view_->filter_widget()->SetFilterMode(CollectionFilterOptions::FilterMode_Untagged);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
collection_view_->filter_widget()->SetQueryMode(QueryOptions::QueryMode_All);
|
collection_view_->filter_widget()->SetFilterMode(CollectionFilterOptions::FilterMode_All);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
void PlaylistCopyUrl();
|
void PlaylistCopyUrl();
|
||||||
void ShowInCollection();
|
void ShowInCollection();
|
||||||
|
|
||||||
void ChangeCollectionQueryMode(QAction *action);
|
void ChangeCollectionFilterMode(QAction *action);
|
||||||
|
|
||||||
void PlayIndex(const QModelIndex &idx, Playlist::AutoScroll autoscroll);
|
void PlayIndex(const QModelIndex &idx, Playlist::AutoScroll autoscroll);
|
||||||
void PlaylistDoubleClick(const QModelIndex &idx);
|
void PlaylistDoubleClick(const QModelIndex &idx);
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
#ifdef HAVE_GSTREAMER
|
#ifdef HAVE_GSTREAMER
|
||||||
# include "engine/gstenginepipeline.h"
|
# include "engine/gstenginepipeline.h"
|
||||||
#endif
|
#endif
|
||||||
#include "collection/directory.h"
|
#include "collection/collectiondirectory.h"
|
||||||
#include "playlist/playlistitem.h"
|
#include "playlist/playlistitem.h"
|
||||||
#include "playlist/playlistsequence.h"
|
#include "playlist/playlistsequence.h"
|
||||||
#include "covermanager/albumcoverloaderresult.h"
|
#include "covermanager/albumcoverloaderresult.h"
|
||||||
@ -98,10 +98,10 @@ void RegisterMetaTypes() {
|
|||||||
qRegisterMetaTypeStreamOperators<QMap<int, Qt::Alignment>>("ColumnAlignmentMap");
|
qRegisterMetaTypeStreamOperators<QMap<int, Qt::Alignment>>("ColumnAlignmentMap");
|
||||||
qRegisterMetaTypeStreamOperators<QMap<int, int>>("ColumnAlignmentIntMap");
|
qRegisterMetaTypeStreamOperators<QMap<int, int>>("ColumnAlignmentIntMap");
|
||||||
#endif
|
#endif
|
||||||
qRegisterMetaType<Directory>("Directory");
|
qRegisterMetaType<CollectionDirectory>("Directory");
|
||||||
qRegisterMetaType<DirectoryList>("DirectoryList");
|
qRegisterMetaType<CollectionDirectoryList>("DirectoryList");
|
||||||
qRegisterMetaType<Subdirectory>("Subdirectory");
|
qRegisterMetaType<CollectionSubdirectory>("Subdirectory");
|
||||||
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
|
qRegisterMetaType<CollectionSubdirectoryList>("SubdirectoryList");
|
||||||
qRegisterMetaType<Song>("Song");
|
qRegisterMetaType<Song>("Song");
|
||||||
qRegisterMetaType<SongList>("SongList");
|
qRegisterMetaType<SongList>("SongList");
|
||||||
qRegisterMetaType<SongMap>("SongMap");
|
qRegisterMetaType<SongMap>("SongMap");
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
|
|
||||||
|
#include "collection/collectionfilteroptions.h"
|
||||||
#include "collection/collectionbackend.h"
|
#include "collection/collectionbackend.h"
|
||||||
#include "settings/collectionsettingspage.h"
|
#include "settings/collectionsettingspage.h"
|
||||||
#include "organize/organizeformat.h"
|
#include "organize/organizeformat.h"
|
||||||
@ -654,9 +655,9 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedAutomatic(const Song &song, co
|
|||||||
|
|
||||||
if (song.source() == Song::Source_Collection) {
|
if (song.source() == Song::Source_Collection) {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, app_->collection_backend(), song.effective_albumartist(), song.effective_album(), QueryOptions());
|
QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, app_->collection_backend(), song.effective_albumartist(), song.effective_album(), CollectionFilterOptions());
|
||||||
#else
|
#else
|
||||||
QFuture<SongList> future = QtConcurrent::run(app_->collection_backend(), &CollectionBackend::GetAlbumSongs, song.effective_albumartist(), song.effective_album(), QueryOptions());
|
QFuture<SongList> future = QtConcurrent::run(app_->collection_backend(), &CollectionBackend::GetAlbumSongs, song.effective_albumartist(), song.effective_album(), CollectionFilterOptions());
|
||||||
#endif
|
#endif
|
||||||
QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>();
|
QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>();
|
||||||
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, song, result]() {
|
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, song, result]() {
|
||||||
@ -699,9 +700,9 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedAutomatic(const Song &song, co
|
|||||||
|
|
||||||
if (song.source() == Song::Source_Collection) {
|
if (song.source() == Song::Source_Collection) {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, app_->collection_backend(), song.effective_albumartist(), song.effective_album(), QueryOptions());
|
QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, app_->collection_backend(), song.effective_albumartist(), song.effective_album(), CollectionFilterOptions());
|
||||||
#else
|
#else
|
||||||
QFuture<SongList> future = QtConcurrent::run(app_->collection_backend(), &CollectionBackend::GetAlbumSongs, song.effective_albumartist(), song.effective_album(), QueryOptions());
|
QFuture<SongList> future = QtConcurrent::run(app_->collection_backend(), &CollectionBackend::GetAlbumSongs, song.effective_albumartist(), song.effective_album(), CollectionFilterOptions());
|
||||||
#endif
|
#endif
|
||||||
QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>();
|
QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>();
|
||||||
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, song, cover_filename]() {
|
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, song, cover_filename]() {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
#include "collection/collectionbackend.h"
|
#include "collection/collectionbackend.h"
|
||||||
#include "collection/collectionmodel.h"
|
#include "collection/collectionmodel.h"
|
||||||
#include "collection/directory.h"
|
#include "collection/collectiondirectory.h"
|
||||||
#include "connecteddevice.h"
|
#include "connecteddevice.h"
|
||||||
#include "devicelister.h"
|
#include "devicelister.h"
|
||||||
#include "devicemanager.h"
|
#include "devicemanager.h"
|
||||||
@ -78,7 +78,7 @@ ConnectedDevice::~ConnectedDevice() {
|
|||||||
|
|
||||||
void ConnectedDevice::InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path) {
|
void ConnectedDevice::InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path) {
|
||||||
|
|
||||||
QList<Directory> directories = backend_->GetAllDirectories();
|
QList<CollectionDirectory> directories = backend_->GetAllDirectories();
|
||||||
if (first_time || directories.isEmpty()) {
|
if (first_time || directories.isEmpty()) {
|
||||||
backend_->AddDirectory(mount_point);
|
backend_->AddDirectory(mount_point);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ void ConnectedDevice::InitBackendDirectory(const QString &mount_point, const boo
|
|||||||
// This can be done entirely in sqlite so it's relatively fast...
|
// This can be done entirely in sqlite so it's relatively fast...
|
||||||
|
|
||||||
// Get the directory it was mounted at last time. Devices only have one directory (the root).
|
// Get the directory it was mounted at last time. Devices only have one directory (the root).
|
||||||
Directory dir = directories[0];
|
CollectionDirectory dir = directories[0];
|
||||||
if (dir.path != mount_point) {
|
if (dir.path != mount_point) {
|
||||||
// The directory is different, commence the munging.
|
// The directory is different, commence the munging.
|
||||||
qLog(Info) << "Changing path from" << dir.path << "to" << mount_point;
|
qLog(Info) << "Changing path from" << dir.path << "to" << mount_point;
|
||||||
|
@ -83,7 +83,7 @@ TEST_F(CollectionBackendTest, AddDirectory) {
|
|||||||
|
|
||||||
// Check the signal was emitted correctly
|
// Check the signal was emitted correctly
|
||||||
ASSERT_EQ(1, spy.count());
|
ASSERT_EQ(1, spy.count());
|
||||||
Directory dir = spy[0][0].value<Directory>();
|
CollectionDirectory dir = spy[0][0].value<Directory>();
|
||||||
EXPECT_EQ(QFileInfo("/tmp").canonicalFilePath(), dir.path);
|
EXPECT_EQ(QFileInfo("/tmp").canonicalFilePath(), dir.path);
|
||||||
EXPECT_EQ(1, dir.id);
|
EXPECT_EQ(1, dir.id);
|
||||||
EXPECT_EQ(0, spy[0][1].value<SubdirectoryList>().size());
|
EXPECT_EQ(0, spy[0][1].value<SubdirectoryList>().size());
|
||||||
@ -93,7 +93,7 @@ TEST_F(CollectionBackendTest, AddDirectory) {
|
|||||||
TEST_F(CollectionBackendTest, RemoveDirectory) {
|
TEST_F(CollectionBackendTest, RemoveDirectory) {
|
||||||
|
|
||||||
// Add a directory
|
// Add a directory
|
||||||
Directory dir;
|
CollectionDirectory dir;
|
||||||
dir.id = 1;
|
dir.id = 1;
|
||||||
dir.path = "/tmp";
|
dir.path = "/tmp";
|
||||||
backend_->AddDirectory(dir.path);
|
backend_->AddDirectory(dir.path);
|
||||||
|
@ -28,16 +28,16 @@
|
|||||||
|
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/songloader.h"
|
#include "core/songloader.h"
|
||||||
#include "collection/directory.h"
|
#include "collection/collectiondirectory.h"
|
||||||
|
|
||||||
class MetatypesEnvironment : public ::testing::Environment {
|
class MetatypesEnvironment : public ::testing::Environment {
|
||||||
public:
|
public:
|
||||||
MetatypesEnvironment() = default;
|
MetatypesEnvironment() = default;
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
qRegisterMetaType<Directory>("Directory");
|
qRegisterMetaType<CollectionDirectory>("Directory");
|
||||||
qRegisterMetaType<DirectoryList>("DirectoryList");
|
qRegisterMetaType<CollectionDirectoryList>("DirectoryList");
|
||||||
qRegisterMetaType<Subdirectory>("Subdirectory");
|
qRegisterMetaType<CollectionSubdirectory>("Subdirectory");
|
||||||
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
|
qRegisterMetaType<CollectionSubdirectoryList>("SubdirectoryList");
|
||||||
qRegisterMetaType<SongList>("SongList");
|
qRegisterMetaType<SongList>("SongList");
|
||||||
qRegisterMetaType<QModelIndex>("QModelIndex");
|
qRegisterMetaType<QModelIndex>("QModelIndex");
|
||||||
qRegisterMetaType<SongLoader::Result>("SongLoader::Result");
|
qRegisterMetaType<SongLoader::Result>("SongLoader::Result");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user