Merge pull request #5270 from davidgfnet/master

Adding support for subsonic cover art download
This commit is contained in:
John Maguire 2016-03-29 16:44:21 +01:00
commit ca487a5166
4 changed files with 75 additions and 4 deletions

View File

@ -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<SubsonicService>();
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();

View File

@ -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,13 @@ class AlbumCoverLoader : public QObject {
QQueue<Task> tasks_;
QMap<QNetworkReply*, Task> remote_tasks_;
QMap<QString, Task> remote_spotify_tasks_;
QMap<QString, Task> remote_subsonic_tasks_;
quint64 next_id_;
NetworkAccessManager* network_;
bool connected_spotify_;
bool connected_subsonic_;
static const int kMaxRedirects = 3;
};

View File

@ -59,6 +59,7 @@ const char* SubsonicService::kSongsTable = "subsonic_songs";
const char* SubsonicService::kFtsTable = "subsonic_songs_fts";
const int SubsonicService::kMaxRedirects = 10;
const int SubsonicService::kCoverArtSize = 1024;
SubsonicService::SubsonicService(Application* app, InternetModel* parent)
: InternetService(kServiceName, app, parent, parent),
@ -490,7 +491,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 +535,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 +568,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 +587,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", QString::number(kCoverArtSize));
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 +613,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);
}

View File

@ -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);