From e4efde900a16b1e1da1b45720d36ed695023bf9f Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Fri, 26 Feb 2016 23:29:13 +0000 Subject: [PATCH] Adding support for subsonic cover art download --- src/covers/albumcoverloader.cpp | 34 ++++++++++++++++++++++- src/covers/albumcoverloader.h | 4 ++- src/internet/subsonic/subsonicservice.cpp | 32 +++++++++++++++++++-- src/internet/subsonic/subsonicservice.h | 9 +++++- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/covers/albumcoverloader.cpp b/src/covers/albumcoverloader.cpp index 8ebf51cce..1f7ca563b 100644 --- a/src/covers/albumcoverloader.cpp +++ b/src/covers/albumcoverloader.cpp @@ -35,13 +35,15 @@ #include "core/utilities.h" #include "internet/core/internetmodel.h" #include "internet/spotify/spotifyservice.h" +#include "internet/subsonic/subsonicservice.h" AlbumCoverLoader::AlbumCoverLoader(QObject* parent) : QObject(parent), stop_requested_(false), next_id_(1), network_(new NetworkAccessManager(this)), - connected_spotify_(false) {} + connected_spotify_(false), + connected_subsonic_(false) {} QString AlbumCoverLoader::ImageCacheDir() { return Utilities::GetConfigPath(Utilities::Path_AlbumCovers); @@ -196,6 +198,26 @@ AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage( QMetaObject::invokeMethod(spotify, "LoadImage", Qt::QueuedConnection, Q_ARG(QString, id)); return TryLoadResult(true, false, QImage()); + } else if (filename.toLower().startsWith("subsonic://image/")) { + // HACK: we should add generic image URL handlers + SubsonicService* subsonic = InternetModel::Service(); + + if (!connected_subsonic_) { + connect(subsonic, SIGNAL(ImageLoaded(QString, QImage)), + SLOT(SubsonicImageLoaded(QString, QImage))); + connected_subsonic_ = true; + } + + QString id = QUrl(filename).path(); + if (id.startsWith('/')) { + id.remove(0, 1); + } + remote_subsonic_tasks_.insert(id, task); + + // Need to schedule this in the subsonic service's thread + QMetaObject::invokeMethod(subsonic, "LoadImage", Qt::QueuedConnection, + Q_ARG(QString, id)); + return TryLoadResult(true, false, QImage()); } QImage image(filename); @@ -214,6 +236,16 @@ void AlbumCoverLoader::SpotifyImageLoaded(const QString& id, emit ImageLoaded(task.id, scaled, image); } +void AlbumCoverLoader::SubsonicImageLoaded(const QString& id, + const QImage& image) { + if (!remote_subsonic_tasks_.contains(id)) return; + + Task task = remote_subsonic_tasks_.take(id); + QImage scaled = ScaleAndPad(task.options, image); + emit ImageLoaded(task.id, scaled); + emit ImageLoaded(task.id, scaled, image); +} + void AlbumCoverLoader::RemoteFetchFinished(QNetworkReply* reply) { reply->deleteLater(); diff --git a/src/covers/albumcoverloader.h b/src/covers/albumcoverloader.h index 4d3e4d571..ac635e037 100644 --- a/src/covers/albumcoverloader.h +++ b/src/covers/albumcoverloader.h @@ -67,6 +67,7 @@ class AlbumCoverLoader : public QObject { void ProcessTasks(); void RemoteFetchFinished(QNetworkReply* reply); void SpotifyImageLoaded(const QString& url, const QImage& image); + void SubsonicImageLoaded(const QString& url, const QImage& image); protected: enum State { State_TryingManual, State_TryingAuto, }; @@ -104,11 +105,12 @@ class AlbumCoverLoader : public QObject { QQueue tasks_; QMap remote_tasks_; QMap remote_spotify_tasks_; + QMap remote_subsonic_tasks_; quint64 next_id_; NetworkAccessManager* network_; - bool connected_spotify_; + bool connected_spotify_, connected_subsonic_; static const int kMaxRedirects = 3; }; diff --git a/src/internet/subsonic/subsonicservice.cpp b/src/internet/subsonic/subsonicservice.cpp index 798bd0004..ca494b50a 100644 --- a/src/internet/subsonic/subsonicservice.cpp +++ b/src/internet/subsonic/subsonicservice.cpp @@ -490,7 +490,7 @@ void SubsonicLibraryScanner::OnGetAlbumListFinished(QNetworkReply* reply, } } -void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply) { +void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply, QString albumid) { reply->deleteLater(); pending_requests_.remove(reply); @@ -534,6 +534,7 @@ void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply) { song.set_bitrate(reader.attributes().value("bitRate").toString().toInt()); song.set_year(reader.attributes().value("year").toString().toInt()); song.set_genre(reader.attributes().value("genre").toString()); + song.set_art_automatic(QString("subsonic://image/%1").arg(albumid)); qint64 length = reader.attributes().value("duration").toString().toInt(); length *= kNsecPerSec; song.set_length_nanosec(length); @@ -566,6 +567,15 @@ void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply) { } } +void SubsonicLibraryScanner::OnGetAlbumCoverFinished(QNetworkReply* reply, QString albumid) { + reply->deleteLater(); + pending_requests_.remove(reply); + + QByteArray image = reply->readAll(); + + service_->emitImageLoaded(albumid, QImage::fromData(image)); +} + void SubsonicLibraryScanner::GetAlbumList(int offset) { QUrl url = service_->BuildRequestUrl("getAlbumList2"); url.addQueryItem("type", "alphabeticalByName"); @@ -576,6 +586,24 @@ void SubsonicLibraryScanner::GetAlbumList(int offset) { SLOT(OnGetAlbumListFinished(QNetworkReply*, int)), reply, offset); } +void SubsonicLibraryScanner::GetAlbumCover(const QString& id) { + QUrl url = service_->BuildRequestUrl("getCoverArt"); + url.addQueryItem("id", id); + url.addQueryItem("size", "1024"); + QNetworkReply* reply = service_->Send(url); + NewClosure(reply, SIGNAL(finished()), this, + SLOT(OnGetAlbumCoverFinished(QNetworkReply*,QString)), reply, id); + pending_requests_.insert(reply); +} + +void SubsonicService::LoadImage(const QString& id) { + scanner_->GetAlbumCover(id); +} + +void SubsonicService::emitImageLoaded(const QString& id, const QImage& image) { + emit ImageLoaded(id, image); +} + void SubsonicLibraryScanner::GetAlbum(const QString& id) { QUrl url = service_->BuildRequestUrl("getAlbum"); url.addQueryItem("id", id); @@ -584,7 +612,7 @@ void SubsonicLibraryScanner::GetAlbum(const QString& id) { } QNetworkReply* reply = service_->Send(url); NewClosure(reply, SIGNAL(finished()), this, - SLOT(OnGetAlbumFinished(QNetworkReply*)), reply); + SLOT(OnGetAlbumFinished(QNetworkReply*, QString)), reply, id); pending_requests_.insert(reply); } diff --git a/src/internet/subsonic/subsonicservice.h b/src/internet/subsonic/subsonicservice.h index 85df53e24..cdba572da 100644 --- a/src/internet/subsonic/subsonicservice.h +++ b/src/internet/subsonic/subsonicservice.h @@ -101,6 +101,9 @@ class SubsonicService : public InternetService { LoginState login_state() const { return login_state_; } + Q_INVOKABLE void LoadImage(const QString& id); + void emitImageLoaded(const QString& id, const QImage& image); + // Subsonic API methods void Ping(); @@ -125,6 +128,7 @@ class SubsonicService : public InternetService { signals: void LoginStateChanged(SubsonicService::LoginState newstate); + void ImageLoaded(const QString& id, const QImage& image); private: void EnsureMenuCreated(); @@ -182,6 +186,8 @@ class SubsonicLibraryScanner : public QObject { static const int kAlbumChunkSize; static const int kConcurrentRequests; + void GetAlbumCover(const QString& id); + signals: void ScanFinished(); @@ -189,7 +195,8 @@ signals: // Step 1: use getAlbumList2 type=alphabeticalByName to list all albums void OnGetAlbumListFinished(QNetworkReply* reply, int offset); // Step 2: use getAlbum id=? to list all songs for each album - void OnGetAlbumFinished(QNetworkReply* reply); + void OnGetAlbumFinished(QNetworkReply* reply, QString albumid); + void OnGetAlbumCoverFinished(QNetworkReply* reply, QString albumid); private: void GetAlbumList(int offset);