mirror of
https://github.com/strawberrymusicplayer/strawberry
synced 2025-01-31 09:44:50 +01:00
Add new method for updating songs based on song ID
Show status updating database. Fixes #750
This commit is contained in:
parent
120b18b399
commit
d2d7f32c45
@ -76,6 +76,7 @@ set(SOURCES
|
||||
collection/sqlrow.cpp
|
||||
collection/savedgroupingmanager.cpp
|
||||
collection/groupbydialog.cpp
|
||||
collection/collectiontask.cpp
|
||||
|
||||
playlist/playlist.cpp
|
||||
playlist/playlistbackend.cpp
|
||||
|
@ -62,7 +62,7 @@ SCollection::SCollection(Application *app, QObject *parent)
|
||||
backend()->moveToThread(app->database()->thread());
|
||||
qLog(Debug) << backend_ << "moved to thread" << app->database()->thread();
|
||||
|
||||
backend_->Init(app->database(), Song::Source_Collection, kSongsTable, kFtsTable, kDirsTable, kSubdirsTable);
|
||||
backend_->Init(app->database(), app->task_manager(), Song::Source_Collection, kSongsTable, kFtsTable, kDirsTable, kSubdirsTable);
|
||||
|
||||
model_ = new CollectionModel(backend_, app_, this);
|
||||
|
||||
|
@ -46,16 +46,19 @@
|
||||
#include "core/logging.h"
|
||||
#include "core/database.h"
|
||||
#include "core/scopedtransaction.h"
|
||||
#include "core/song.h"
|
||||
#include "smartplaylists/smartplaylistsearch.h"
|
||||
|
||||
#include "directory.h"
|
||||
#include "sqlrow.h"
|
||||
#include "collectionbackend.h"
|
||||
#include "collectionquery.h"
|
||||
#include "sqlrow.h"
|
||||
#include "collectiontask.h"
|
||||
|
||||
CollectionBackend::CollectionBackend(QObject *parent)
|
||||
: CollectionBackendInterface(parent),
|
||||
db_(nullptr),
|
||||
task_manager_(nullptr),
|
||||
source_(Song::Source_Unknown),
|
||||
original_thread_(nullptr) {
|
||||
|
||||
@ -63,13 +66,16 @@ CollectionBackend::CollectionBackend(QObject *parent)
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::Init(Database *db, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table, const QString &subdirs_table) {
|
||||
void CollectionBackend::Init(Database *db, TaskManager *task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table, const QString &subdirs_table) {
|
||||
|
||||
db_ = db;
|
||||
task_manager_ = task_manager;
|
||||
source_ = source;
|
||||
songs_table_ = songs_table;
|
||||
dirs_table_ = dirs_table;
|
||||
subdirs_table_ = subdirs_table;
|
||||
fts_table_ = fts_table;
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::Close() {
|
||||
@ -623,6 +629,8 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
|
||||
id = q.lastInsertId().toInt();
|
||||
}
|
||||
|
||||
if (id == -1) return;
|
||||
|
||||
{ // Add to the FTS index
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("INSERT INTO %1 (ROWID, " + Song::kFtsColumnSpec + ") VALUES (:id, " + Song::kFtsBindSpec + ")").arg(fts_table_));
|
||||
@ -634,9 +642,9 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
|
||||
}
|
||||
}
|
||||
|
||||
Song copy(song);
|
||||
copy.set_id(id);
|
||||
added_songs << copy;
|
||||
Song song_copy(song);
|
||||
song_copy.set_id(id);
|
||||
added_songs << song_copy;
|
||||
|
||||
}
|
||||
|
||||
@ -651,6 +659,136 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongsBySongIDAsync(const SongMap &new_songs) {
|
||||
QMetaObject::invokeMethod(this, "UpdateSongsBySongID", Qt::QueuedConnection, Q_ARG(SongMap, new_songs));
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongsBySongID(const SongMap &new_songs) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
CollectionTask task(task_manager_, tr("Updating %1 database.").arg(Song::TextForSource(source_)));
|
||||
ScopedTransaction transaction(&db);
|
||||
|
||||
SongList added_songs;
|
||||
SongList deleted_songs;
|
||||
|
||||
SongMap old_songs;
|
||||
{
|
||||
CollectionQuery query(db, songs_table_, fts_table_);
|
||||
if (!ExecCollectionQuery(&query, old_songs)) {
|
||||
ReportErrors(query);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add or update songs.
|
||||
for (const Song &new_song : new_songs) {
|
||||
if (old_songs.contains(new_song.song_id())) {
|
||||
|
||||
Song old_song = old_songs[new_song.song_id()];
|
||||
|
||||
if (!new_song.IsMetadataEqual(old_song)) { // Update existing song.
|
||||
|
||||
{
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET " + Song::kUpdateSpec + " WHERE ROWID = :id").arg(songs_table_));
|
||||
new_song.BindToQuery(&q);
|
||||
q.BindValue(":id", old_song.id());
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET " + Song::kFtsUpdateSpec + " WHERE ROWID = :id").arg(fts_table_));
|
||||
new_song.BindToFtsQuery(&q);
|
||||
q.BindValue(":id", old_song.id());
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
deleted_songs << old_song;
|
||||
Song new_song_copy(new_song);
|
||||
new_song_copy.set_id(old_song.id());
|
||||
added_songs << new_song_copy;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else { // Add new song
|
||||
int id = -1;
|
||||
{
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("INSERT INTO %1 (" + Song::kColumnSpec + ") VALUES (" + Song::kBindSpec + ")").arg(songs_table_));
|
||||
new_song.BindToQuery(&q);
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
// Get the new ID
|
||||
id = q.lastInsertId().toInt();
|
||||
}
|
||||
|
||||
if (id == -1) return;
|
||||
|
||||
{ // Add to the FTS index
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("INSERT INTO %1 (ROWID, " + Song::kFtsColumnSpec + ") VALUES (:id, " + Song::kFtsBindSpec + ")").arg(fts_table_));
|
||||
q.BindValue(":id", id);
|
||||
new_song.BindToFtsQuery(&q);
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Song new_song_copy(new_song);
|
||||
new_song_copy.set_id(id);
|
||||
added_songs << new_song_copy;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete songs
|
||||
for (const Song &old_song : old_songs) {
|
||||
if (!new_songs.contains(old_song.song_id())) {
|
||||
{
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("DELETE FROM %1 WHERE ROWID = :id").arg(songs_table_));
|
||||
q.BindValue(":id", old_song.id());
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
SqlQuery q(db);
|
||||
q.prepare(QString("DELETE FROM %1 WHERE ROWID = :id").arg(fts_table_));
|
||||
q.BindValue(":id", old_song.id());
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
deleted_songs << old_song;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
if (!deleted_songs.isEmpty()) emit SongsDeleted(deleted_songs);
|
||||
if (!added_songs.isEmpty()) emit SongsDiscovered(added_songs);
|
||||
|
||||
UpdateTotalSongCountAsync();
|
||||
UpdateTotalArtistCountAsync();
|
||||
UpdateTotalAlbumCountAsync();
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateMTimesOnly(const SongList &songs) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
@ -882,6 +1020,21 @@ bool CollectionBackend::ExecCollectionQuery(CollectionQuery *query, SongList &so
|
||||
|
||||
}
|
||||
|
||||
bool CollectionBackend::ExecCollectionQuery(CollectionQuery *query, SongMap &songs) {
|
||||
|
||||
query->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
|
||||
if (!query->Exec()) return false;
|
||||
|
||||
while (query->Next()) {
|
||||
Song song(source_);
|
||||
song.InitFromQuery(*query, true);
|
||||
songs.insert(song.song_id(), song);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongById(const int id) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "directory.h"
|
||||
|
||||
class QThread;
|
||||
class TaskManager;
|
||||
class Database;
|
||||
class SmartPlaylistSearch;
|
||||
|
||||
@ -127,7 +128,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
||||
|
||||
void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
||||
void Init(Database *db, TaskManager *task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
||||
void Close();
|
||||
|
||||
void ExitAsync();
|
||||
@ -184,6 +185,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void RemoveDirectory(const Directory &dir) override;
|
||||
|
||||
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
||||
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
||||
|
||||
void IncrementPlayCountAsync(const int id);
|
||||
void IncrementSkipCountAsync(const int id, const float progress);
|
||||
@ -202,6 +204,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
Song::Source Source() const;
|
||||
|
||||
void AddOrUpdateSongsAsync(const SongList &songs);
|
||||
void UpdateSongsBySongIDAsync(const SongMap &new_songs);
|
||||
|
||||
void UpdateSongRatingAsync(const int id, const double rating);
|
||||
void UpdateSongsRatingAsync(const QList<int> &ids, const double rating);
|
||||
@ -213,6 +216,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void UpdateTotalArtistCount();
|
||||
void UpdateTotalAlbumCount();
|
||||
void AddOrUpdateSongs(const SongList &songs);
|
||||
void UpdateSongsBySongID(const SongMap &new_songs);
|
||||
void UpdateMTimesOnly(const SongList &songs);
|
||||
void DeleteSongs(const SongList &songs);
|
||||
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
|
||||
@ -279,6 +283,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
private:
|
||||
Database *db_;
|
||||
TaskManager *task_manager_;
|
||||
Song::Source source_;
|
||||
QString songs_table_;
|
||||
QString dirs_table_;
|
||||
|
35
src/collection/collectiontask.cpp
Normal file
35
src/collection/collectiontask.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2021, 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 <QString>
|
||||
|
||||
#include "core/taskmanager.h"
|
||||
#include "collectiontask.h"
|
||||
|
||||
CollectionTask::CollectionTask(TaskManager *task_manager, const QString &message) : task_manager_(task_manager), task_id_(-1) {
|
||||
|
||||
if (task_manager_) task_id_ = task_manager_->StartTask(message);
|
||||
|
||||
}
|
||||
|
||||
CollectionTask::~CollectionTask() {
|
||||
|
||||
if (task_manager_) task_manager_->SetTaskFinished(task_id_);
|
||||
|
||||
}
|
40
src/collection/collectiontask.h
Normal file
40
src/collection/collectiontask.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2021, 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 COLLECTIONTASK_H
|
||||
#define COLLECTIONTASK_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QString>
|
||||
|
||||
class TaskManager;
|
||||
|
||||
class CollectionTask {
|
||||
public:
|
||||
explicit CollectionTask(TaskManager *task_manager, const QString &message);
|
||||
~CollectionTask();
|
||||
|
||||
private:
|
||||
TaskManager *task_manager_;
|
||||
int task_id_;
|
||||
|
||||
Q_DISABLE_COPY(CollectionTask)
|
||||
};
|
||||
|
||||
#endif // COLLECTIONTASK_H
|
@ -471,7 +471,7 @@ void CollectionView::SetShowInVarious(const bool on) {
|
||||
if (on && albums.keys().count() == 1) {
|
||||
const QStringList albums_list = albums.keys();
|
||||
const QString album = albums_list.first();
|
||||
QList<Song> all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
|
||||
SongList all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
|
||||
QSet<QString> other_artists;
|
||||
for (const Song &s : all_of_album) {
|
||||
if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) {
|
||||
|
@ -99,8 +99,10 @@ void RegisterMetaTypes() {
|
||||
qRegisterMetaType<Subdirectory>("Subdirectory");
|
||||
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
|
||||
qRegisterMetaType<Song>("Song");
|
||||
qRegisterMetaType<QList<Song>>("QList<Song>");
|
||||
qRegisterMetaType<SongList>("SongList");
|
||||
qRegisterMetaType<SongMap>("SongMap");
|
||||
qRegisterMetaType<QList<Song>>("QList<Song>");
|
||||
qRegisterMetaType<QMap<QString, Song>>("QMap<QString, Song>");
|
||||
qRegisterMetaType<Engine::EngineType>("EngineType");
|
||||
qRegisterMetaType<Engine::SimpleMetaBundle>("Engine::SimpleMetaBundle");
|
||||
qRegisterMetaType<Engine::State>("Engine::State");
|
||||
|
@ -25,19 +25,23 @@
|
||||
#include "core/logging.h"
|
||||
#include "scopedtransaction.h"
|
||||
|
||||
ScopedTransaction::ScopedTransaction(QSqlDatabase *db)
|
||||
: db_(db), pending_(true) {
|
||||
ScopedTransaction::ScopedTransaction(QSqlDatabase *db) : db_(db), pending_(true) {
|
||||
|
||||
db->transaction();
|
||||
|
||||
}
|
||||
|
||||
ScopedTransaction::~ScopedTransaction() {
|
||||
|
||||
if (pending_) {
|
||||
qLog(Warning) << "Rolling back transaction";
|
||||
db_->rollback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ScopedTransaction::Commit() {
|
||||
|
||||
if (!pending_) {
|
||||
qLog(Warning) << "Tried to commit a ScopedTransaction twice";
|
||||
return;
|
||||
@ -45,4 +49,5 @@ void ScopedTransaction::Commit() {
|
||||
|
||||
db_->commit();
|
||||
pending_ = false;
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <QMetaType>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -396,10 +397,13 @@ class Song {
|
||||
|
||||
QSharedDataPointer<Private> d;
|
||||
};
|
||||
Q_DECLARE_METATYPE(Song)
|
||||
|
||||
typedef QList<Song> SongList;
|
||||
Q_DECLARE_METATYPE(QList<Song>)
|
||||
typedef QMap<QString, Song> SongMap;
|
||||
|
||||
Q_DECLARE_METATYPE(Song)
|
||||
Q_DECLARE_METATYPE(SongList)
|
||||
Q_DECLARE_METATYPE(SongMap)
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
size_t qHash(const Song &song);
|
||||
|
@ -63,6 +63,7 @@ ConnectedDevice::ConnectedDevice(const QUrl &url, DeviceLister *lister, const QS
|
||||
}
|
||||
|
||||
backend_->Init(app_->database(),
|
||||
app_->task_manager(),
|
||||
Song::Source_Device,
|
||||
QString("device_%1_songs").arg(database_id),
|
||||
QString("device_%1_fts").arg(database_id),
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
|
||||
@ -86,7 +87,7 @@ class InternetCollectionView : public AutoExpandingTreeView {
|
||||
void TotalArtistCountUpdated_();
|
||||
void TotalAlbumCountUpdated_();
|
||||
void Error(QString);
|
||||
void RemoveSongs(SongList);
|
||||
void RemoveSongs(SongList songs);
|
||||
|
||||
protected:
|
||||
// QWidget
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
@ -392,12 +393,11 @@ MimeData *InternetSearchModel::LoadTracks(const InternetSearchView::ResultList &
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SongList songs;
|
||||
SongMap songs;
|
||||
QList<QUrl> urls;
|
||||
songs.reserve(results.count());
|
||||
urls.reserve(results.count());
|
||||
for (const InternetSearchView::Result &result : results) {
|
||||
songs << result.metadata_;
|
||||
songs.insert(result.metadata_.song_id(), result.metadata_);
|
||||
urls << result.metadata_.url();
|
||||
}
|
||||
|
||||
|
@ -487,7 +487,7 @@ void InternetSearchView::SearchAsync(const int id, const QString &query, const S
|
||||
|
||||
}
|
||||
|
||||
void InternetSearchView::SearchDone(const int service_id, const SongList &songs, const QString &error) {
|
||||
void InternetSearchView::SearchDone(const int service_id, const SongMap &songs, const QString &error) {
|
||||
|
||||
if (!pending_searches_.contains(service_id)) return;
|
||||
|
||||
@ -758,7 +758,7 @@ void InternetSearchView::AddArtists() {
|
||||
MimeData *mimedata = SelectedMimeData();
|
||||
if (!mimedata) return;
|
||||
if (const InternetSongMimeData *internet_song_data = qobject_cast<const InternetSongMimeData*>(mimedata)) {
|
||||
emit AddArtistsSignal(internet_song_data->songs);
|
||||
emit AddArtistsSignal(internet_song_data->songs.values());
|
||||
}
|
||||
|
||||
}
|
||||
@ -768,7 +768,7 @@ void InternetSearchView::AddAlbums() {
|
||||
MimeData *mimedata = SelectedMimeData();
|
||||
if (!mimedata) return;
|
||||
if (const InternetSongMimeData *internet_song_data = qobject_cast<const InternetSongMimeData*>(mimedata)) {
|
||||
emit AddAlbumsSignal(internet_song_data->songs);
|
||||
emit AddAlbumsSignal(internet_song_data->songs.values());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ class InternetSearchView : public QWidget {
|
||||
void AddToPlaylist(QMimeData*);
|
||||
void AddArtistsSignal(SongList);
|
||||
void AddAlbumsSignal(SongList);
|
||||
void AddSongsSignal(SongList);
|
||||
void AddSongsSignal(SongMap);
|
||||
|
||||
private slots:
|
||||
void SwapModels();
|
||||
void TextEdited(const QString &text);
|
||||
void StartSearch(const QString &query);
|
||||
void SearchDone(const int service_id, const SongList &songs, const QString &error);
|
||||
void SearchDone(const int service_id, const SongMap &songs, const QString &error);
|
||||
|
||||
void UpdateStatus(const int service_id, const QString &text);
|
||||
void ProgressSetMaximum(const int service_id, const int max);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QMetaType>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QIcon>
|
||||
@ -95,38 +96,39 @@ class InternetService : public QObject {
|
||||
void TestComplete(bool success, QString error = QString());
|
||||
|
||||
void Error(QString error);
|
||||
void Results(SongList songs, QString error);
|
||||
void Results(SongMap songs, QString error);
|
||||
void UpdateStatus(QString text);
|
||||
void ProgressSetMaximum(int max);
|
||||
void UpdateProgress(int max);
|
||||
|
||||
void ArtistsResults(SongList songs, QString error);
|
||||
void ArtistsResults(SongMap songs, QString error);
|
||||
void ArtistsUpdateStatus(QString text);
|
||||
void ArtistsProgressSetMaximum(int max);
|
||||
void ArtistsUpdateProgress(int max);
|
||||
|
||||
void AlbumsResults(SongList songs, QString error);
|
||||
void AlbumsResults(SongMap songs, QString error);
|
||||
void AlbumsUpdateStatus(QString text);
|
||||
void AlbumsProgressSetMaximum(int max);
|
||||
void AlbumsUpdateProgress(int max);
|
||||
|
||||
void SongsResults(SongList songs, QString error);
|
||||
void SongsResults(SongMap songs, QString error);
|
||||
void SongsUpdateStatus(QString text);
|
||||
void SongsProgressSetMaximum(int max);
|
||||
void SongsUpdateProgress(int max);
|
||||
|
||||
void SearchResults(int id, SongList songs, QString error);
|
||||
void SearchResults(int id, SongMap songs, QString error);
|
||||
void SearchUpdateStatus(int id, QString text);
|
||||
void SearchProgressSetMaximum(int id, int max);
|
||||
void SearchUpdateProgress(int id, int max);
|
||||
|
||||
void AddArtists(SongList);
|
||||
void AddAlbums(SongList);
|
||||
void AddSongs(SongList);
|
||||
void AddArtists(SongList songs);
|
||||
void AddAlbums(SongList songs);
|
||||
void AddSongs(SongMap songs);
|
||||
|
||||
void RemoveArtists(SongList);
|
||||
void RemoveAlbums(SongList);
|
||||
void RemoveSongs(SongList);
|
||||
void RemoveArtists(SongList songs);
|
||||
void RemoveAlbums(SongList songs);
|
||||
void RemoveSongs(SongList songs);
|
||||
void RemoveSongs(SongMap songs);
|
||||
|
||||
void StreamURLFinished(QUrl original_url, QUrl stream_url, Song::FileType filetype, int samplerate, int bit_depth, qint64 duration, QString error = QString());
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
||||
#ifndef INTERNETSONGMIMEDATA_H
|
||||
#define INTERNETSONGMIMEDATA_H
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include "core/mimedata.h"
|
||||
#include "core/song.h"
|
||||
|
||||
@ -33,7 +35,7 @@ class InternetSongMimeData : public MimeData {
|
||||
explicit InternetSongMimeData(InternetService *_service, QObject* = nullptr) : service(_service) {}
|
||||
|
||||
InternetService *service;
|
||||
SongList songs;
|
||||
SongMap songs;
|
||||
};
|
||||
|
||||
#endif // INTERNETSONGMIMEDATA_H
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QStackedWidget>
|
||||
#include <QContextMenuEvent>
|
||||
@ -62,7 +63,7 @@ InternetSongsView::InternetSongsView(Application *app, InternetService *service,
|
||||
ui_->filter_widget->AddMenuAction(action_configure);
|
||||
|
||||
QObject::connect(ui_->view, &InternetCollectionView::GetSongs, this, &InternetSongsView::GetSongs);
|
||||
QObject::connect(ui_->view, &InternetCollectionView::RemoveSongs, service_, &InternetService::RemoveSongs);
|
||||
QObject::connect(ui_->view, &InternetCollectionView::RemoveSongs, service_, QOverload<SongList>::of(&InternetService::RemoveSongs));
|
||||
|
||||
QObject::connect(ui_->refresh, &QPushButton::clicked, this, &InternetSongsView::GetSongs);
|
||||
QObject::connect(ui_->close, &QPushButton::clicked, this, &InternetSongsView::AbortGetSongs);
|
||||
@ -121,7 +122,7 @@ void InternetSongsView::AbortGetSongs() {
|
||||
|
||||
}
|
||||
|
||||
void InternetSongsView::SongsFinished(const SongList &songs, const QString &error) {
|
||||
void InternetSongsView::SongsFinished(const SongMap &songs, const QString &error) {
|
||||
|
||||
if (songs.isEmpty() && !error.isEmpty()) {
|
||||
ui_->status->setText(error);
|
||||
@ -131,10 +132,9 @@ void InternetSongsView::SongsFinished(const SongList &songs, const QString &erro
|
||||
ui_->close->show();
|
||||
}
|
||||
else {
|
||||
service_->songs_collection_backend()->DeleteAll();
|
||||
ui_->stacked->setCurrentWidget(ui_->internetcollection_page);
|
||||
ui_->status->clear();
|
||||
service_->songs_collection_backend()->AddOrUpdateSongsAsync(songs);
|
||||
service_->songs_collection_backend()->UpdateSongsBySongIDAsync(songs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include "core/song.h"
|
||||
@ -51,7 +52,7 @@ class InternetSongsView : public QWidget {
|
||||
void OpenSettingsDialog();
|
||||
void GetSongs();
|
||||
void AbortGetSongs();
|
||||
void SongsFinished(const SongList &songs, const QString &error);
|
||||
void SongsFinished(const SongMap &songs, const QString &error);
|
||||
|
||||
private:
|
||||
Application *app_;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QLabel>
|
||||
@ -137,7 +138,7 @@ InternetTabsView::InternetTabsView(Application *app, InternetService *service, c
|
||||
ui_->songs_collection->filter_widget()->AddMenuAction(action_configure);
|
||||
|
||||
QObject::connect(ui_->songs_collection->view(), &InternetCollectionView::GetSongs, this, &InternetTabsView::GetSongs);
|
||||
QObject::connect(ui_->songs_collection->view(), &InternetCollectionView::RemoveSongs, service_, &InternetService::RemoveSongs);
|
||||
QObject::connect(ui_->songs_collection->view(), &InternetCollectionView::RemoveSongs, service_, QOverload<SongList>::of(&InternetService::RemoveSongs));
|
||||
|
||||
QObject::connect(ui_->songs_collection->button_refresh(), &QPushButton::clicked, this, &InternetTabsView::GetSongs);
|
||||
QObject::connect(ui_->songs_collection->button_close(), &QPushButton::clicked, this, &InternetTabsView::AbortGetSongs);
|
||||
@ -230,7 +231,7 @@ void InternetTabsView::AbortGetArtists() {
|
||||
|
||||
}
|
||||
|
||||
void InternetTabsView::ArtistsFinished(const SongList &songs, const QString &error) {
|
||||
void InternetTabsView::ArtistsFinished(const SongMap &songs, const QString &error) {
|
||||
|
||||
if (songs.isEmpty() && !error.isEmpty()) {
|
||||
ui_->artists_collection->status()->setText(error);
|
||||
@ -240,10 +241,9 @@ void InternetTabsView::ArtistsFinished(const SongList &songs, const QString &err
|
||||
ui_->artists_collection->button_close()->show();
|
||||
}
|
||||
else {
|
||||
service_->artists_collection_backend()->DeleteAll();
|
||||
ui_->artists_collection->stacked()->setCurrentWidget(ui_->artists_collection->internetcollection_page());
|
||||
ui_->artists_collection->status()->clear();
|
||||
service_->artists_collection_backend()->AddOrUpdateSongsAsync(songs);
|
||||
service_->artists_collection_backend()->UpdateSongsBySongIDAsync(songs);
|
||||
}
|
||||
|
||||
}
|
||||
@ -273,7 +273,7 @@ void InternetTabsView::AbortGetAlbums() {
|
||||
|
||||
}
|
||||
|
||||
void InternetTabsView::AlbumsFinished(const SongList &songs, const QString &error) {
|
||||
void InternetTabsView::AlbumsFinished(const SongMap &songs, const QString &error) {
|
||||
|
||||
if (songs.isEmpty() && !error.isEmpty()) {
|
||||
ui_->albums_collection->status()->setText(error);
|
||||
@ -283,10 +283,9 @@ void InternetTabsView::AlbumsFinished(const SongList &songs, const QString &erro
|
||||
ui_->albums_collection->button_close()->show();
|
||||
}
|
||||
else {
|
||||
service_->albums_collection_backend()->DeleteAll();
|
||||
ui_->albums_collection->stacked()->setCurrentWidget(ui_->albums_collection->internetcollection_page());
|
||||
ui_->albums_collection->status()->clear();
|
||||
service_->albums_collection_backend()->AddOrUpdateSongsAsync(songs);
|
||||
service_->albums_collection_backend()->UpdateSongsBySongIDAsync(songs);
|
||||
}
|
||||
|
||||
}
|
||||
@ -316,7 +315,7 @@ void InternetTabsView::AbortGetSongs() {
|
||||
|
||||
}
|
||||
|
||||
void InternetTabsView::SongsFinished(const SongList &songs, const QString &error) {
|
||||
void InternetTabsView::SongsFinished(const SongMap &songs, const QString &error) {
|
||||
|
||||
if (songs.isEmpty() && !error.isEmpty()) {
|
||||
ui_->songs_collection->status()->setText(error);
|
||||
@ -326,10 +325,9 @@ void InternetTabsView::SongsFinished(const SongList &songs, const QString &error
|
||||
ui_->songs_collection->button_close()->show();
|
||||
}
|
||||
else {
|
||||
service_->songs_collection_backend()->DeleteAll();
|
||||
ui_->songs_collection->stacked()->setCurrentWidget(ui_->songs_collection->internetcollection_page());
|
||||
ui_->songs_collection->status()->clear();
|
||||
service_->songs_collection_backend()->AddOrUpdateSongsAsync(songs);
|
||||
service_->songs_collection_backend()->UpdateSongsBySongIDAsync(songs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include "settings/settingsdialog.h"
|
||||
@ -60,9 +61,9 @@ class InternetTabsView : public QWidget {
|
||||
void AbortGetArtists();
|
||||
void AbortGetAlbums();
|
||||
void AbortGetSongs();
|
||||
void ArtistsFinished(const SongList &songs, const QString &error);
|
||||
void AlbumsFinished(const SongList &songs, const QString &error);
|
||||
void SongsFinished(const SongList &songs, const QString &error);
|
||||
void ArtistsFinished(const SongMap &songs, const QString &error);
|
||||
void AlbumsFinished(const SongMap &songs, const QString &error);
|
||||
void SongsFinished(const SongMap &songs, const QString &error);
|
||||
|
||||
private:
|
||||
Application *app_;
|
||||
|
@ -797,7 +797,7 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
|
||||
InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now);
|
||||
}
|
||||
else if (const InternetSongMimeData *internet_song_data = qobject_cast<const InternetSongMimeData*>(data)) {
|
||||
InsertInternetItems(internet_song_data->service, internet_song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
||||
InsertInternetItems(internet_song_data->service, internet_song_data->songs.values(), row, play_now, enqueue_now, enqueue_next_now);
|
||||
}
|
||||
else if (const RadioMimeData *radio_data = qobject_cast<const RadioMimeData*>(data)) {
|
||||
InsertRadioItems(radio_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
||||
|
@ -214,7 +214,7 @@ QList<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(const int playlist) {
|
||||
|
||||
}
|
||||
|
||||
QList<Song> PlaylistBackend::GetPlaylistSongs(const int playlist) {
|
||||
SongList PlaylistBackend::GetPlaylistSongs(const int playlist) {
|
||||
|
||||
SongList songs;
|
||||
|
||||
@ -230,7 +230,7 @@ QList<Song> PlaylistBackend::GetPlaylistSongs(const int playlist) {
|
||||
q.BindValue(":playlist", playlist);
|
||||
if (!q.Exec()) {
|
||||
db_->ReportErrors(q);
|
||||
return QList<Song>();
|
||||
return SongList();
|
||||
}
|
||||
|
||||
// it's probable that we'll have a few songs associated with the same CUE so we're caching results of parsing CUEs
|
||||
|
@ -76,7 +76,7 @@ class PlaylistBackend : public QObject {
|
||||
PlaylistBackend::Playlist GetPlaylist(const int id);
|
||||
|
||||
QList<PlaylistItemPtr> GetPlaylistItems(const int playlist);
|
||||
QList<Song> GetPlaylistSongs(const int playlist);
|
||||
SongList GetPlaylistSongs(const int playlist);
|
||||
|
||||
void SetPlaylistOrder(const QList<int> &ids);
|
||||
void SetPlaylistUiPath(const int id, const QString &path);
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QMap>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -66,6 +67,24 @@ QString QobuzFavoriteRequest::FavoriteText(const FavoriteType type) {
|
||||
|
||||
}
|
||||
|
||||
QString QobuzFavoriteRequest::FavoriteMethod(const FavoriteType type) {
|
||||
|
||||
switch (type) {
|
||||
case FavoriteType_Artists:
|
||||
return "artist_ids";
|
||||
break;
|
||||
case FavoriteType_Albums:
|
||||
return "album_ids";
|
||||
break;
|
||||
case FavoriteType_Songs:
|
||||
return "track_ids";
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::AddArtists(const SongList &songs) {
|
||||
AddFavorites(FavoriteType_Artists, songs);
|
||||
}
|
||||
@ -74,27 +93,12 @@ void QobuzFavoriteRequest::AddAlbums(const SongList &songs) {
|
||||
AddFavorites(FavoriteType_Albums, songs);
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::AddSongs(const SongList &songs) {
|
||||
AddFavorites(FavoriteType_Songs, songs);
|
||||
void QobuzFavoriteRequest::AddSongs(const SongMap &songs) {
|
||||
AddFavoritesRequest(FavoriteType_Songs, songs.keys(), songs.values());
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::AddFavorites(const FavoriteType type, const SongList &songs) {
|
||||
|
||||
if (songs.isEmpty()) return;
|
||||
|
||||
QString text;
|
||||
switch (type) {
|
||||
case FavoriteType_Artists:
|
||||
text = "artist_ids";
|
||||
break;
|
||||
case FavoriteType_Albums:
|
||||
text = "album_ids";
|
||||
break;
|
||||
case FavoriteType_Songs:
|
||||
text = "track_ids";
|
||||
break;
|
||||
}
|
||||
|
||||
QStringList ids_list;
|
||||
for (const Song &song : songs) {
|
||||
QString id;
|
||||
@ -112,18 +116,22 @@ void QobuzFavoriteRequest::AddFavorites(const FavoriteType type, const SongList
|
||||
id = song.song_id();
|
||||
break;
|
||||
}
|
||||
if (id.isEmpty()) continue;
|
||||
if (!ids_list.contains(id)) {
|
||||
if (!id.isEmpty() && !ids_list.contains(id)) {
|
||||
ids_list << id;
|
||||
}
|
||||
}
|
||||
|
||||
if (ids_list.isEmpty()) return;
|
||||
|
||||
QString ids = ids_list.join(',');
|
||||
AddFavoritesRequest(type, ids_list, songs);
|
||||
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::AddFavoritesRequest(const FavoriteType type, const QStringList &ids_list, const SongList &songs) {
|
||||
|
||||
ParamList params = ParamList() << Param("app_id", app_id())
|
||||
<< Param("user_auth_token", user_auth_token())
|
||||
<< Param(text, ids);
|
||||
<< Param(FavoriteMethod(type), ids_list.join(','));
|
||||
|
||||
QUrlQuery url_query;
|
||||
for (const Param ¶m : params) {
|
||||
@ -180,23 +188,12 @@ void QobuzFavoriteRequest::RemoveSongs(const SongList &songs) {
|
||||
RemoveFavorites(FavoriteType_Songs, songs);
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::RemoveSongs(const SongMap &songs) {
|
||||
RemoveFavoritesRequest(FavoriteType_Songs, songs.keys(), songs.values());
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::RemoveFavorites(const FavoriteType type, const SongList &songs) {
|
||||
|
||||
if (songs.isEmpty()) return;
|
||||
|
||||
QString text;
|
||||
switch (type) {
|
||||
case FavoriteType_Artists:
|
||||
text = "artist_ids";
|
||||
break;
|
||||
case FavoriteType_Albums:
|
||||
text = "album_ids";
|
||||
break;
|
||||
case FavoriteType_Songs:
|
||||
text = "track_ids";
|
||||
break;
|
||||
}
|
||||
|
||||
QStringList ids_list;
|
||||
for (const Song &song : songs) {
|
||||
QString id;
|
||||
@ -214,18 +211,22 @@ void QobuzFavoriteRequest::RemoveFavorites(const FavoriteType type, const SongLi
|
||||
id = song.song_id();
|
||||
break;
|
||||
}
|
||||
if (id.isEmpty()) continue;
|
||||
if (!ids_list.contains(id)) {
|
||||
if (!id.isEmpty() && !ids_list.contains(id)) {
|
||||
ids_list << id;
|
||||
}
|
||||
}
|
||||
|
||||
if (ids_list.isEmpty()) return;
|
||||
|
||||
QString ids = ids_list.join(',');
|
||||
RemoveFavoritesRequest(type, ids_list, songs);
|
||||
|
||||
}
|
||||
|
||||
void QobuzFavoriteRequest::RemoveFavoritesRequest(const FavoriteType type, const QStringList &ids_list, const SongList &songs) {
|
||||
|
||||
ParamList params = ParamList() << Param("app_id", app_id())
|
||||
<< Param("user_auth_token", user_auth_token())
|
||||
<< Param(text, ids);
|
||||
<< Param(FavoriteMethod(type), ids_list.join(','));
|
||||
|
||||
QUrlQuery url_query;
|
||||
for (const Param ¶m : params) {
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
@ -62,16 +63,20 @@ class QobuzFavoriteRequest : public QobuzBaseRequest {
|
||||
public slots:
|
||||
void AddArtists(const SongList &songs);
|
||||
void AddAlbums(const SongList &songs);
|
||||
void AddSongs(const SongList &songs);
|
||||
void AddSongs(const SongMap &songs);
|
||||
void RemoveArtists(const SongList &songs);
|
||||
void RemoveAlbums(const SongList &songs);
|
||||
void RemoveSongs(const SongList &songs);
|
||||
void RemoveSongs(const SongMap &songs);
|
||||
|
||||
private:
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
static QString FavoriteText(const FavoriteType type);
|
||||
static QString FavoriteMethod(const FavoriteType type);
|
||||
void AddFavorites(const FavoriteType type, const SongList &songs);
|
||||
void AddFavoritesRequest(const FavoriteType type, const QStringList &ids_list, const SongList &songs);
|
||||
void RemoveFavorites(const FavoriteType type, const SongList &songs);
|
||||
void RemoveFavoritesRequest(const FavoriteType type, const QStringList &ids_list, const SongList &songs);
|
||||
|
||||
QobuzService *service_;
|
||||
NetworkAccessManager *network_;
|
||||
|
@ -1315,15 +1315,15 @@ void QobuzRequest::FinishCheck() {
|
||||
finished_ = true;
|
||||
if (no_results_ && songs_.isEmpty()) {
|
||||
if (IsSearch())
|
||||
emit Results(query_id_, SongList(), tr("No match."));
|
||||
emit Results(query_id_, SongMap(), tr("No match."));
|
||||
else
|
||||
emit Results(query_id_, SongList(), QString());
|
||||
emit Results(query_id_, SongMap(), QString());
|
||||
}
|
||||
else {
|
||||
if (songs_.isEmpty() && errors_.isEmpty())
|
||||
emit Results(query_id_, songs_.values(), tr("Unknown error"));
|
||||
emit Results(query_id_, songs_, tr("Unknown error"));
|
||||
else
|
||||
emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_));
|
||||
emit Results(query_id_, songs_, ErrorsToHTML(errors_));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class QobuzRequest : public QobuzBaseRequest {
|
||||
signals:
|
||||
void LoginSuccess();
|
||||
void LoginFailure(QString failure_reason);
|
||||
void Results(int id, SongList songs, QString error);
|
||||
void Results(int id, SongMap songs, QString error);
|
||||
void UpdateStatus(int id, QString text);
|
||||
void ProgressSetMaximum(int id, int max);
|
||||
void UpdateProgress(int id, int max);
|
||||
@ -191,7 +191,7 @@ class QobuzRequest : public QobuzBaseRequest {
|
||||
int album_covers_requested_;
|
||||
int album_covers_received_;
|
||||
|
||||
QMap<QString, Song> songs_;
|
||||
SongMap songs_;
|
||||
QStringList errors_;
|
||||
bool no_results_;
|
||||
QList<QNetworkReply*> replies_;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
@ -111,15 +112,15 @@ QobuzService::QobuzService(Application *app, QObject *parent)
|
||||
|
||||
artists_collection_backend_ = new CollectionBackend();
|
||||
artists_collection_backend_->moveToThread(app_->database()->thread());
|
||||
artists_collection_backend_->Init(app_->database(), Song::Source_Qobuz, kArtistsSongsTable, kArtistsSongsFtsTable);
|
||||
artists_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Qobuz, kArtistsSongsTable, kArtistsSongsFtsTable);
|
||||
|
||||
albums_collection_backend_ = new CollectionBackend();
|
||||
albums_collection_backend_->moveToThread(app_->database()->thread());
|
||||
albums_collection_backend_->Init(app_->database(), Song::Source_Qobuz, kAlbumsSongsTable, kAlbumsSongsFtsTable);
|
||||
albums_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Qobuz, kAlbumsSongsTable, kAlbumsSongsFtsTable);
|
||||
|
||||
songs_collection_backend_ = new CollectionBackend();
|
||||
songs_collection_backend_->moveToThread(app_->database()->thread());
|
||||
songs_collection_backend_->Init(app_->database(), Song::Source_Qobuz, kSongsTable, kSongsFtsTable);
|
||||
songs_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Qobuz, kSongsTable, kSongsFtsTable);
|
||||
|
||||
artists_collection_model_ = new CollectionModel(artists_collection_backend_, app_, this);
|
||||
albums_collection_model_ = new CollectionModel(albums_collection_backend_, app_, this);
|
||||
@ -160,7 +161,8 @@ QobuzService::QobuzService(Application *app, QObject *parent)
|
||||
|
||||
QObject::connect(this, &QobuzService::RemoveArtists, favorite_request_, &QobuzFavoriteRequest::RemoveArtists);
|
||||
QObject::connect(this, &QobuzService::RemoveAlbums, favorite_request_, &QobuzFavoriteRequest::RemoveAlbums);
|
||||
QObject::connect(this, &QobuzService::RemoveSongs, favorite_request_, &QobuzFavoriteRequest::RemoveSongs);
|
||||
QObject::connect(this, QOverload<SongList>::of(&QobuzService::RemoveSongs), favorite_request_, QOverload<const SongList&>::of(&QobuzFavoriteRequest::RemoveSongs));
|
||||
QObject::connect(this, QOverload<SongMap>::of(&QobuzService::RemoveSongs), favorite_request_, QOverload<const SongMap&>::of(&QobuzFavoriteRequest::RemoveSongs));
|
||||
|
||||
QObject::connect(favorite_request_, &QobuzFavoriteRequest::ArtistsAdded, artists_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||
QObject::connect(favorite_request_, &QobuzFavoriteRequest::AlbumsAdded, albums_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||
@ -285,7 +287,7 @@ void QobuzService::SendLoginWithCredentials(const QString &app_id, const QString
|
||||
QObject::connect(reply, &QNetworkReply::sslErrors, this, &QobuzService::HandleLoginSSLErrors);
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() { HandleAuthReply(reply); });
|
||||
|
||||
qLog(Debug) << "Qobuz: Sending request" << url << query;
|
||||
//qLog(Debug) << "Qobuz: Sending request" << url << query;
|
||||
|
||||
}
|
||||
|
||||
@ -496,12 +498,12 @@ void QobuzService::ResetArtistsRequest() {
|
||||
void QobuzService::GetArtists() {
|
||||
|
||||
if (app_id().isEmpty()) {
|
||||
emit ArtistsResults(SongList(), tr("Missing Qobuz app ID."));
|
||||
emit ArtistsResults(SongMap(), tr("Missing Qobuz app ID."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authenticated()) {
|
||||
emit ArtistsResults(SongList(), tr("Not authenticated with Qobuz."));
|
||||
emit ArtistsResults(SongMap(), tr("Not authenticated with Qobuz."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -518,7 +520,7 @@ void QobuzService::GetArtists() {
|
||||
|
||||
}
|
||||
|
||||
void QobuzService::ArtistsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void QobuzService::ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit ArtistsResults(songs, error);
|
||||
}
|
||||
@ -551,12 +553,12 @@ void QobuzService::ResetAlbumsRequest() {
|
||||
void QobuzService::GetAlbums() {
|
||||
|
||||
if (app_id().isEmpty()) {
|
||||
emit AlbumsResults(SongList(), tr("Missing Qobuz app ID."));
|
||||
emit AlbumsResults(SongMap(), tr("Missing Qobuz app ID."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authenticated()) {
|
||||
emit AlbumsResults(SongList(), tr("Not authenticated with Qobuz."));
|
||||
emit AlbumsResults(SongMap(), tr("Not authenticated with Qobuz."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -571,7 +573,7 @@ void QobuzService::GetAlbums() {
|
||||
|
||||
}
|
||||
|
||||
void QobuzService::AlbumsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void QobuzService::AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit AlbumsResults(songs, error);
|
||||
}
|
||||
@ -604,12 +606,12 @@ void QobuzService::ResetSongsRequest() {
|
||||
void QobuzService::GetSongs() {
|
||||
|
||||
if (app_id().isEmpty()) {
|
||||
emit SongsResults(SongList(), tr("Missing Qobuz app ID."));
|
||||
emit SongsResults(SongMap(), tr("Missing Qobuz app ID."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authenticated()) {
|
||||
emit SongsResults(SongList(), tr("Not authenticated with Qobuz."));
|
||||
emit SongsResults(SongMap(), tr("Not authenticated with Qobuz."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -624,7 +626,7 @@ void QobuzService::GetSongs() {
|
||||
|
||||
}
|
||||
|
||||
void QobuzService::SongsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void QobuzService::SongsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit SongsResults(songs, error);
|
||||
}
|
||||
@ -669,7 +671,7 @@ void QobuzService::StartSearch() {
|
||||
search_text_ = pending_search_text_;
|
||||
|
||||
if (app_id_.isEmpty()) { // App ID is the only thing needed to search.
|
||||
emit SearchResults(search_id_, SongList(), tr("Missing Qobuz app ID."));
|
||||
emit SearchResults(search_id_, SongMap(), tr("Missing Qobuz app ID."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -708,7 +710,7 @@ void QobuzService::SendSearch() {
|
||||
|
||||
}
|
||||
|
||||
void QobuzService::SearchResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void QobuzService::SearchResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
emit SearchResults(id, songs, error);
|
||||
}
|
||||
|
||||
|
@ -125,10 +125,10 @@ class QobuzService : public InternetService {
|
||||
void HandleAuthReply(QNetworkReply *reply);
|
||||
void ResetLoginAttempts();
|
||||
void StartSearch();
|
||||
void ArtistsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void AlbumsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void SongsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void SearchResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void SongsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void SearchResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void ArtistsUpdateStatusReceived(const int id, const QString &text);
|
||||
void AlbumsUpdateStatusReceived(const int id, const QString &text);
|
||||
void SongsUpdateStatusReceived(const int id, const QString &text);
|
||||
|
@ -151,7 +151,7 @@ void SomaFMService::GetStreamUrlsReply(QNetworkReply *reply, const int task_id,
|
||||
reply->deleteLater();
|
||||
|
||||
PlaylistParser parser;
|
||||
QList<Song> songs = parser.LoadFromDevice(reply);
|
||||
SongList songs = parser.LoadFromDevice(reply);
|
||||
if (!songs.isEmpty()) {
|
||||
channel.url = songs.first().url();
|
||||
}
|
||||
|
@ -872,14 +872,14 @@ void SubsonicRequest::FinishCheck() {
|
||||
) {
|
||||
finished_ = true;
|
||||
if (no_results_ && songs_.isEmpty()) {
|
||||
emit Results(SongList(), QString());
|
||||
emit Results(SongMap(), QString());
|
||||
}
|
||||
else {
|
||||
if (songs_.isEmpty() && errors_.isEmpty()) {
|
||||
emit Results(songs_.values(), tr("Unknown error"));
|
||||
emit Results(songs_, tr("Unknown error"));
|
||||
}
|
||||
else {
|
||||
emit Results(songs_.values(), ErrorsToHTML(errors_));
|
||||
emit Results(songs_, ErrorsToHTML(errors_));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
|
||||
void Reset();
|
||||
|
||||
signals:
|
||||
void Results(SongList songs, QString error);
|
||||
void Results(SongMap songs, QString error);
|
||||
void UpdateStatus(QString text);
|
||||
void ProgressSetMaximum(int max);
|
||||
void UpdateProgress(int max);
|
||||
@ -140,7 +140,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
|
||||
int album_covers_requested_;
|
||||
int album_covers_received_;
|
||||
|
||||
QMap<QString, Song> songs_;
|
||||
SongMap songs_;
|
||||
QStringList errors_;
|
||||
bool no_results_;
|
||||
QList<QNetworkReply*> replies_;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QUrl>
|
||||
@ -86,7 +87,7 @@ SubsonicService::SubsonicService(Application *app, QObject *parent)
|
||||
|
||||
collection_backend_ = new CollectionBackend();
|
||||
collection_backend_->moveToThread(app_->database()->thread());
|
||||
collection_backend_->Init(app_->database(), Song::Source_Subsonic, kSongsTable, kSongsFtsTable);
|
||||
collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Subsonic, kSongsTable, kSongsFtsTable);
|
||||
|
||||
// Model
|
||||
|
||||
@ -425,12 +426,12 @@ void SubsonicService::ResetSongsRequest() {
|
||||
void SubsonicService::GetSongs() {
|
||||
|
||||
if (!server_url().isValid()) {
|
||||
emit SongsResults(SongList(), tr("Server URL is invalid."));
|
||||
emit SongsResults(SongMap(), tr("Server URL is invalid."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (username().isEmpty() || password().isEmpty()) {
|
||||
emit SongsResults(SongList(), tr("Missing username or password."));
|
||||
emit SongsResults(SongMap(), tr("Missing username or password."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -445,7 +446,7 @@ void SubsonicService::GetSongs() {
|
||||
|
||||
}
|
||||
|
||||
void SubsonicService::SongsResultsReceived(const SongList &songs, const QString &error) {
|
||||
void SubsonicService::SongsResultsReceived(const SongMap &songs, const QString &error) {
|
||||
|
||||
emit SongsResults(songs, error);
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
@ -96,7 +97,7 @@ class SubsonicService : public InternetService {
|
||||
private slots:
|
||||
void HandlePingSSLErrors(const QList<QSslError> &ssl_errors);
|
||||
void HandlePingReply(QNetworkReply *reply, const QUrl &url, const QString &username, const QString &password, const SubsonicSettingsPage::AuthMethod auth_method);
|
||||
void SongsResultsReceived(const SongList &songs, const QString &error);
|
||||
void SongsResultsReceived(const SongMap &songs, const QString &error);
|
||||
|
||||
private:
|
||||
typedef QPair<QString, QString> Param;
|
||||
|
@ -66,10 +66,26 @@ QString TidalFavoriteRequest::FavoriteText(const FavoriteType type) {
|
||||
case FavoriteType_Albums:
|
||||
return "albums";
|
||||
case FavoriteType_Songs:
|
||||
default:
|
||||
return "tracks";
|
||||
}
|
||||
|
||||
return QString();
|
||||
|
||||
}
|
||||
|
||||
QString TidalFavoriteRequest::FavoriteMethod(const FavoriteType type) {
|
||||
|
||||
switch (type) {
|
||||
case FavoriteType_Artists:
|
||||
return "artistIds";
|
||||
case FavoriteType_Albums:
|
||||
return "albumIds";
|
||||
case FavoriteType_Songs:
|
||||
return "trackIds";
|
||||
}
|
||||
|
||||
return QString();
|
||||
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::AddArtists(const SongList &songs) {
|
||||
@ -80,27 +96,12 @@ void TidalFavoriteRequest::AddAlbums(const SongList &songs) {
|
||||
AddFavorites(FavoriteType_Albums, songs);
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::AddSongs(const SongList &songs) {
|
||||
AddFavorites(FavoriteType_Songs, songs);
|
||||
void TidalFavoriteRequest::AddSongs(const SongMap &songs) {
|
||||
AddFavoritesRequest(FavoriteType_Songs, songs.keys(), songs.values());
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::AddFavorites(const FavoriteType type, const SongList &songs) {
|
||||
|
||||
if (songs.isEmpty()) return;
|
||||
|
||||
QString text;
|
||||
switch (type) {
|
||||
case FavoriteType_Artists:
|
||||
text = "artistIds";
|
||||
break;
|
||||
case FavoriteType_Albums:
|
||||
text = "albumIds";
|
||||
break;
|
||||
case FavoriteType_Songs:
|
||||
text = "trackIds";
|
||||
break;
|
||||
}
|
||||
|
||||
QStringList id_list;
|
||||
for (const Song &song : songs) {
|
||||
QString id;
|
||||
@ -118,15 +119,21 @@ void TidalFavoriteRequest::AddFavorites(const FavoriteType type, const SongList
|
||||
id = song.song_id();
|
||||
break;
|
||||
}
|
||||
if (id.isEmpty()) continue;
|
||||
if (!id_list.contains(id)) {
|
||||
if (!id.isEmpty() && !id_list.contains(id)) {
|
||||
id_list << id;
|
||||
}
|
||||
}
|
||||
|
||||
if (id_list.isEmpty()) return;
|
||||
|
||||
AddFavoritesRequest(type, id_list, songs);
|
||||
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::AddFavoritesRequest(const FavoriteType type, const QStringList &id_list, const SongList &songs) {
|
||||
|
||||
ParamList params = ParamList() << Param("countryCode", country_code())
|
||||
<< Param(text, id_list.join(','));
|
||||
<< Param(FavoriteMethod(type), id_list.join(','));
|
||||
|
||||
QUrlQuery url_query;
|
||||
for (const Param ¶m : params) {
|
||||
@ -197,11 +204,17 @@ void TidalFavoriteRequest::RemoveSongs(const SongList &songs) {
|
||||
RemoveFavorites(FavoriteType_Songs, songs);
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::RemoveSongs(const SongMap &songs) {
|
||||
|
||||
SongList songs_list = songs.values();
|
||||
for (const Song &song : songs_list) {
|
||||
RemoveFavoritesRequest(FavoriteType_Songs, song.song_id(), SongList() << song);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::RemoveFavorites(const FavoriteType type, const SongList &songs) {
|
||||
|
||||
if (songs.isEmpty()) return;
|
||||
|
||||
QStringList ids;
|
||||
QMultiMap<QString, Song> songs_map;
|
||||
for (const Song &song : songs) {
|
||||
QString id;
|
||||
@ -219,18 +232,19 @@ void TidalFavoriteRequest::RemoveFavorites(const FavoriteType type, const SongLi
|
||||
id = song.song_id();
|
||||
break;
|
||||
}
|
||||
if (!ids.contains(id)) ids << id;
|
||||
songs_map.insert(id, song);
|
||||
if (!id.isEmpty()) {
|
||||
songs_map.insert(id, song);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList ids = songs_map.uniqueKeys();
|
||||
for (const QString &id : ids) {
|
||||
SongList songs_list = songs_map.values(id);
|
||||
RemoveFavorites(type, id, songs_list);
|
||||
RemoveFavoritesRequest(type, id, songs_map.values(id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::RemoveFavorites(const FavoriteType type, const QString &id, const SongList &songs) {
|
||||
void TidalFavoriteRequest::RemoveFavoritesRequest(const FavoriteType type, const QString &id, const SongList &songs) {
|
||||
|
||||
ParamList params = ParamList() << Param("countryCode", country_code());
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
@ -65,18 +66,22 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
||||
public slots:
|
||||
void AddArtists(const SongList &songs);
|
||||
void AddAlbums(const SongList &songs);
|
||||
void AddSongs(const SongList &songs);
|
||||
void AddSongs(const SongMap &songs);
|
||||
|
||||
void RemoveArtists(const SongList &songs);
|
||||
void RemoveAlbums(const SongList &songs);
|
||||
void RemoveSongs(const SongList &songs);
|
||||
void RemoveSongs(const SongMap &songs);
|
||||
|
||||
private:
|
||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||
static QString FavoriteText(const FavoriteType type);
|
||||
static QString FavoriteMethod(const FavoriteType type);
|
||||
void AddFavorites(const FavoriteType type, const SongList &songs);
|
||||
void AddFavoritesRequest(const FavoriteType type, const QStringList &id_list, const SongList &songs);
|
||||
void RemoveFavorites(const FavoriteType type, const SongList &songs);
|
||||
void RemoveFavorites(const FavoriteType type, const QString &id, const SongList &songs);
|
||||
void RemoveFavoritesRequest(const FavoriteType type, const QString &id, const SongList &songs);
|
||||
|
||||
TidalService *service_;
|
||||
NetworkAccessManager *network_;
|
||||
|
@ -1270,15 +1270,17 @@ void TidalRequest::FinishCheck() {
|
||||
finished_ = true;
|
||||
if (no_results_ && songs_.isEmpty()) {
|
||||
if (IsSearch())
|
||||
emit Results(query_id_, SongList(), tr("No match."));
|
||||
emit Results(query_id_, SongMap(), tr("No match."));
|
||||
else
|
||||
emit Results(query_id_, SongList(), QString());
|
||||
emit Results(query_id_, SongMap(), QString());
|
||||
}
|
||||
else {
|
||||
if (songs_.isEmpty() && errors_.isEmpty())
|
||||
emit Results(query_id_, songs_.values(), tr("Unknown error"));
|
||||
else
|
||||
emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_));
|
||||
if (songs_.isEmpty() && errors_.isEmpty()) {
|
||||
emit Results(query_id_, songs_, tr("Unknown error"));
|
||||
}
|
||||
else {
|
||||
emit Results(query_id_, songs_, ErrorsToHTML(errors_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
signals:
|
||||
void LoginSuccess();
|
||||
void LoginFailure(QString failure_reason);
|
||||
void Results(int id, SongList songs, QString error);
|
||||
void Results(int id, SongMap songs, QString error);
|
||||
void UpdateStatus(int id, QString text);
|
||||
void ProgressSetMaximum(int id, int max);
|
||||
void UpdateProgress(int id, int max);
|
||||
@ -199,7 +199,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
int album_covers_requested_;
|
||||
int album_covers_received_;
|
||||
|
||||
QMap<QString, Song> songs_;
|
||||
SongMap songs_;
|
||||
QStringList errors_;
|
||||
bool need_login_;
|
||||
bool no_results_;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QChar>
|
||||
#include <QUrl>
|
||||
@ -127,15 +128,15 @@ TidalService::TidalService(Application *app, QObject *parent)
|
||||
|
||||
artists_collection_backend_ = new CollectionBackend();
|
||||
artists_collection_backend_->moveToThread(app_->database()->thread());
|
||||
artists_collection_backend_->Init(app_->database(), Song::Source_Tidal, kArtistsSongsTable, kArtistsSongsFtsTable);
|
||||
artists_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Tidal, kArtistsSongsTable, kArtistsSongsFtsTable);
|
||||
|
||||
albums_collection_backend_ = new CollectionBackend();
|
||||
albums_collection_backend_->moveToThread(app_->database()->thread());
|
||||
albums_collection_backend_->Init(app_->database(), Song::Source_Tidal, kAlbumsSongsTable, kAlbumsSongsFtsTable);
|
||||
albums_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Tidal, kAlbumsSongsTable, kAlbumsSongsFtsTable);
|
||||
|
||||
songs_collection_backend_ = new CollectionBackend();
|
||||
songs_collection_backend_->moveToThread(app_->database()->thread());
|
||||
songs_collection_backend_->Init(app_->database(), Song::Source_Tidal, kSongsTable, kSongsFtsTable);
|
||||
songs_collection_backend_->Init(app_->database(), app->task_manager(), Song::Source_Tidal, kSongsTable, kSongsFtsTable);
|
||||
|
||||
artists_collection_model_ = new CollectionModel(artists_collection_backend_, app_, this);
|
||||
albums_collection_model_ = new CollectionModel(albums_collection_backend_, app_, this);
|
||||
@ -180,7 +181,8 @@ TidalService::TidalService(Application *app, QObject *parent)
|
||||
|
||||
QObject::connect(this, &TidalService::RemoveArtists, favorite_request_, &TidalFavoriteRequest::RemoveArtists);
|
||||
QObject::connect(this, &TidalService::RemoveAlbums, favorite_request_, &TidalFavoriteRequest::RemoveAlbums);
|
||||
QObject::connect(this, &TidalService::RemoveSongs, favorite_request_, &TidalFavoriteRequest::RemoveSongs);
|
||||
QObject::connect(this, QOverload<SongList>::of(&TidalService::RemoveSongs), favorite_request_, QOverload<const SongList&>::of(&TidalFavoriteRequest::RemoveSongs));
|
||||
QObject::connect(this, QOverload<SongMap>::of(&TidalService::RemoveSongs), favorite_request_, QOverload<const SongMap&>::of(&TidalFavoriteRequest::RemoveSongs));
|
||||
|
||||
QObject::connect(favorite_request_, &TidalFavoriteRequest::RequestLogin, this, &TidalService::SendLogin);
|
||||
|
||||
@ -743,12 +745,12 @@ void TidalService::GetArtists() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
emit ArtistsResults(SongList(), tr("Not authenticated with Tidal."));
|
||||
emit ArtistsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
||||
emit ArtistsResults(SongList(), tr("Missing Tidal API token, username or password."));
|
||||
emit ArtistsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
@ -769,7 +771,7 @@ void TidalService::GetArtists() {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::ArtistsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void TidalService::ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit ArtistsResults(songs, error);
|
||||
}
|
||||
@ -803,12 +805,12 @@ void TidalService::GetAlbums() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
emit AlbumsResults(SongList(), tr("Not authenticated with Tidal."));
|
||||
emit AlbumsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
||||
emit AlbumsResults(SongList(), tr("Missing Tidal API token, username or password."));
|
||||
emit AlbumsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
@ -827,7 +829,7 @@ void TidalService::GetAlbums() {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::AlbumsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void TidalService::AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit AlbumsResults(songs, error);
|
||||
}
|
||||
@ -861,12 +863,12 @@ void TidalService::GetSongs() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
emit SongsResults(SongList(), tr("Not authenticated with Tidal."));
|
||||
emit SongsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
||||
emit SongsResults(SongList(), tr("Missing Tidal API token, username or password."));
|
||||
emit SongsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
@ -885,7 +887,7 @@ void TidalService::GetSongs() {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::SongsResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void TidalService::SongsResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
Q_UNUSED(id);
|
||||
emit SongsResults(songs, error);
|
||||
}
|
||||
@ -927,12 +929,12 @@ void TidalService::StartSearch() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
emit SearchResults(pending_search_id_, SongList(), tr("Not authenticated with Tidal."));
|
||||
emit SearchResults(pending_search_id_, SongMap(), tr("Not authenticated with Tidal."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
||||
emit SearchResults(pending_search_id_, SongList(), tr("Missing Tidal API token, username or password."));
|
||||
emit SearchResults(pending_search_id_, SongMap(), tr("Missing Tidal API token, username or password."));
|
||||
ShowConfig();
|
||||
return;
|
||||
}
|
||||
@ -981,7 +983,7 @@ void TidalService::SendSearch() {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::SearchResultsReceived(const int id, const SongList &songs, const QString &error) {
|
||||
void TidalService::SearchResultsReceived(const int id, const SongMap &songs, const QString &error) {
|
||||
emit SearchResults(id, songs, error);
|
||||
}
|
||||
|
||||
|
@ -138,10 +138,10 @@ class TidalService : public InternetService {
|
||||
void HandleAuthReply(QNetworkReply *reply);
|
||||
void ResetLoginAttempts();
|
||||
void StartSearch();
|
||||
void ArtistsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void AlbumsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void SongsResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void SearchResultsReceived(const int id, const SongList &songs, const QString &error);
|
||||
void ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void SongsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void SearchResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||
void ArtistsUpdateStatusReceived(const int id, const QString &text);
|
||||
void AlbumsUpdateStatusReceived(const int id, const QString &text);
|
||||
void SongsUpdateStatusReceived(const int id, const QString &text);
|
||||
|
@ -46,7 +46,7 @@ class CollectionBackendTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
database_.reset(new MemoryDatabase(nullptr));
|
||||
backend_ = std::make_unique<CollectionBackend>();
|
||||
backend_->Init(database_.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
|
||||
backend_->Init(database_.get(), nullptr, Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
|
||||
}
|
||||
|
||||
static Song MakeDummySong(int directory_id) {
|
||||
@ -431,4 +431,175 @@ TEST_F(SingleSong, TestUrls) {
|
||||
|
||||
}
|
||||
|
||||
TEST_F(CollectionBackendTest, UpdateSongsBySongID) {
|
||||
|
||||
QStringList song_ids = QStringList() << "song1"
|
||||
<< "song2"
|
||||
<< "song3"
|
||||
<< "song4"
|
||||
<< "song5"
|
||||
<< "song6";
|
||||
|
||||
{ // Add songs
|
||||
SongMap songs;
|
||||
|
||||
for (const QString &song_id : song_ids) {
|
||||
|
||||
QUrl url;
|
||||
url.setScheme("file");
|
||||
url.setPath("/music/" + song_id);
|
||||
|
||||
Song song(Song::Source_Collection);
|
||||
song.set_song_id(song_id);
|
||||
song.set_directory_id(1);
|
||||
song.set_title("Test Title " + song_id);
|
||||
song.set_album("Test Album");
|
||||
song.set_artist("Test Artist");
|
||||
song.set_url(url);
|
||||
song.set_length_nanosec(kNsecPerSec);
|
||||
song.set_mtime(1);
|
||||
song.set_ctime(1);
|
||||
song.set_filesize(1);
|
||||
song.set_valid(true);
|
||||
|
||||
songs.insert(song_id, song);
|
||||
|
||||
}
|
||||
|
||||
QSignalSpy spy(backend_.get(), &CollectionBackend::SongsDiscovered);
|
||||
|
||||
backend_->UpdateSongsBySongID(songs);
|
||||
|
||||
ASSERT_EQ(1, spy.count());
|
||||
SongList new_songs = spy[0][0].value<SongList>();
|
||||
EXPECT_EQ(new_songs.count(), song_ids.count());
|
||||
EXPECT_EQ(song_ids[0], new_songs[0].song_id());
|
||||
EXPECT_EQ(song_ids[1], new_songs[1].song_id());
|
||||
EXPECT_EQ(song_ids[2], new_songs[2].song_id());
|
||||
EXPECT_EQ(song_ids[3], new_songs[3].song_id());
|
||||
EXPECT_EQ(song_ids[4], new_songs[4].song_id());
|
||||
EXPECT_EQ(song_ids[5], new_songs[5].song_id());
|
||||
|
||||
}
|
||||
|
||||
{ // Check that all songs are added.
|
||||
|
||||
SongMap songs;
|
||||
{
|
||||
QSqlDatabase db(database_->Connect());
|
||||
CollectionQuery query(db, SCollection::kSongsTable, SCollection::kFtsTable);
|
||||
EXPECT_TRUE(backend_->ExecCollectionQuery(&query, songs));
|
||||
}
|
||||
|
||||
EXPECT_EQ(songs.count(), song_ids.count());
|
||||
|
||||
for (QMap<QString, Song>::const_iterator it = songs.begin() ; it != songs.end() ; ++it) {
|
||||
EXPECT_EQ(it.key(), it.value().song_id());
|
||||
}
|
||||
|
||||
for (const QString &song_id : song_ids) {
|
||||
EXPECT_TRUE(songs.contains(song_id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{ // Remove some songs
|
||||
QSignalSpy spy1(backend_.get(), &CollectionBackend::SongsDiscovered);
|
||||
QSignalSpy spy2(backend_.get(), &CollectionBackend::SongsDeleted);
|
||||
|
||||
SongMap songs;
|
||||
|
||||
QStringList song_ids2 = QStringList() << "song1"
|
||||
<< "song4"
|
||||
<< "song5"
|
||||
<< "song6";
|
||||
|
||||
for (const QString &song_id : song_ids2) {
|
||||
|
||||
QUrl url;
|
||||
url.setScheme("file");
|
||||
url.setPath("/music/" + song_id);
|
||||
|
||||
Song song(Song::Source_Collection);
|
||||
song.set_song_id(song_id);
|
||||
song.set_directory_id(1);
|
||||
song.set_title("Test Title " + song_id);
|
||||
song.set_album("Test Album");
|
||||
song.set_artist("Test Artist");
|
||||
song.set_url(url);
|
||||
song.set_length_nanosec(kNsecPerSec);
|
||||
song.set_mtime(1);
|
||||
song.set_ctime(1);
|
||||
song.set_filesize(1);
|
||||
song.set_valid(true);
|
||||
|
||||
songs.insert(song_id, song);
|
||||
|
||||
}
|
||||
|
||||
backend_->UpdateSongsBySongID(songs);
|
||||
|
||||
ASSERT_EQ(0, spy1.count());
|
||||
ASSERT_EQ(1, spy2.count());
|
||||
SongList deleted_songs = spy2[0][0].value<SongList>();
|
||||
EXPECT_EQ(deleted_songs.count(), 2);
|
||||
EXPECT_EQ(deleted_songs[0].song_id(), "song2");
|
||||
EXPECT_EQ(deleted_songs[1].song_id(), "song3");
|
||||
|
||||
}
|
||||
|
||||
{ // Update some songs
|
||||
QSignalSpy spy1(backend_.get(), &CollectionBackend::SongsDeleted);
|
||||
QSignalSpy spy2(backend_.get(), &CollectionBackend::SongsDiscovered);
|
||||
|
||||
SongMap songs;
|
||||
|
||||
QStringList song_ids2 = QStringList() << "song1"
|
||||
<< "song4"
|
||||
<< "song5"
|
||||
<< "song6";
|
||||
|
||||
for (const QString &song_id : song_ids2) {
|
||||
|
||||
QUrl url;
|
||||
url.setScheme("file");
|
||||
url.setPath("/music/" + song_id);
|
||||
|
||||
Song song(Song::Source_Collection);
|
||||
song.set_song_id(song_id);
|
||||
song.set_directory_id(1);
|
||||
song.set_title("Test Title " + song_id);
|
||||
song.set_album("Test Album");
|
||||
song.set_artist("Test Artist");
|
||||
song.set_url(url);
|
||||
song.set_length_nanosec(kNsecPerSec);
|
||||
song.set_mtime(1);
|
||||
song.set_ctime(1);
|
||||
song.set_filesize(1);
|
||||
song.set_valid(true);
|
||||
|
||||
songs.insert(song_id, song);
|
||||
|
||||
}
|
||||
|
||||
songs["song1"].set_artist("New artist");
|
||||
songs["song6"].set_artist("New artist");
|
||||
|
||||
backend_->UpdateSongsBySongID(songs);
|
||||
|
||||
ASSERT_EQ(1, spy1.count());
|
||||
ASSERT_EQ(1, spy2.count());
|
||||
SongList deleted_songs = spy1[0][0].value<SongList>();
|
||||
SongList added_songs = spy2[0][0].value<SongList>();
|
||||
EXPECT_EQ(deleted_songs.count(), 2);
|
||||
EXPECT_EQ(added_songs.count(), 2);
|
||||
EXPECT_EQ(deleted_songs[0].song_id(), "song1");
|
||||
EXPECT_EQ(deleted_songs[1].song_id(), "song6");
|
||||
EXPECT_EQ(added_songs[0].song_id(), "song1");
|
||||
EXPECT_EQ(added_songs[1].song_id(), "song6");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -51,7 +51,7 @@ class CollectionModelTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
database_ = std::make_shared<MemoryDatabase>(nullptr);
|
||||
backend_ = std::make_unique<CollectionBackend>();
|
||||
backend_->Init(database_.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
|
||||
backend_->Init(database_.get(), nullptr, Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
|
||||
model_ = std::make_unique<CollectionModel>(backend_.get(), nullptr);
|
||||
|
||||
added_dir_ = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user