From d022f974a1415b136be537bc9e2b83866458cf3a Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Mon, 30 Nov 2015 22:13:37 -0800 Subject: [PATCH 1/4] Add "Smart Playlists" for subsonic Notes: - These playlists allow access to the subsonic feature of pulling new, frequently played, and so on albums. - See: http://www.subsonic.org/pages/api.jsp#getAlbumList for the subsonic description - The subsonic api is inherently album oriented. Therefore at the moment the "count" argument for GenerateMore is used as the number of albums to pull, which hopefully isn't a problem. - Could be made more efficient by multi-threading the fetch of the songs for each album --- src/CMakeLists.txt | 2 + .../subsonic/subsonicdynamicplaylist.cpp | 259 ++++++++++++++++++ .../subsonic/subsonicdynamicplaylist.h | 75 +++++ src/internet/subsonic/subsonicservice.cpp | 30 +- src/internet/subsonic/subsonicservice.h | 3 + src/smartplaylists/generator.cpp | 4 + 6 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 src/internet/subsonic/subsonicdynamicplaylist.cpp create mode 100644 src/internet/subsonic/subsonicdynamicplaylist.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f1046911b..0cb379451 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,6 +197,7 @@ set(SOURCES internet/subsonic/subsonicservice.cpp internet/subsonic/subsonicsettingspage.cpp internet/subsonic/subsonicurlhandler.cpp + internet/subsonic/subsonicdynamicplaylist.cpp library/groupbydialog.cpp library/library.cpp @@ -499,6 +500,7 @@ set(HEADERS internet/subsonic/subsonicservice.h internet/subsonic/subsonicsettingspage.h internet/subsonic/subsonicurlhandler.h + internet/subsonic/subsonicdynamicplaylist.h library/groupbydialog.h library/library.h diff --git a/src/internet/subsonic/subsonicdynamicplaylist.cpp b/src/internet/subsonic/subsonicdynamicplaylist.cpp new file mode 100644 index 000000000..e5691acb3 --- /dev/null +++ b/src/internet/subsonic/subsonicdynamicplaylist.cpp @@ -0,0 +1,259 @@ +/* This file is part of Clementine. + Copyright 2015, Nick Lanham + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include "subsonicdynamicplaylist.h" + +#include +#include +#include +#include + +#include "core/application.h" +#include "core/logging.h" +#include "core/network.h" +#include "core/taskmanager.h" +#include "core/timeconstants.h" +#include "internet/core/internetplaylistitem.h" + +SubsonicDynamicPlaylist::SubsonicDynamicPlaylist() + : stat_(QueryStat_Newest), + offset_(0) {} + +SubsonicDynamicPlaylist::SubsonicDynamicPlaylist(const QString& name, + QueryStat stat) + : stat_(stat), + offset_(0) { + set_name(name); +} + +void SubsonicDynamicPlaylist::Load(const QByteArray& data) { + QDataStream s(data); + s >> *this; +} + +void SubsonicDynamicPlaylist::Load(QueryStat stat) { + stat_ = stat; +} + +QByteArray SubsonicDynamicPlaylist::Save() const { + QByteArray ret; + QDataStream s(&ret, QIODevice::WriteOnly); + s << *this; + return ret; +} + +// copied from SubsonicService +QNetworkReply* SubsonicDynamicPlaylist::Send(QNetworkAccessManager& network, const QUrl& url, const bool usesslv3) { + QNetworkRequest request(url); + // Don't try and check the authenticity of the SSL certificate - it'll almost + // certainly be self-signed. + QSslConfiguration sslconfig = QSslConfiguration::defaultConfiguration(); + sslconfig.setPeerVerifyMode(QSslSocket::VerifyNone); + if (usesslv3) { + sslconfig.setProtocol(QSsl::SslV3); + } + request.setSslConfiguration(sslconfig); + QNetworkReply* reply = network.get(request); + return reply; +} + +PlaylistItemList SubsonicDynamicPlaylist::Generate() { return GenerateMore(10); } + +PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { + SubsonicService* service = InternetModel::Service(); + int task_id = service->app_->task_manager()->StartTask(tr("Fetching Playlist Items")); + QUrl url = service->BuildRequestUrl("GetAlbumList"); + QNetworkAccessManager network; + + switch (stat_) { + case QueryStat::QueryStat_Newest: + url.addQueryItem("type","newest"); + break; + case QueryStat::QueryStat_Highest: + url.addQueryItem("type","highest"); + break; + case QueryStat::QueryStat_Frequent: + url.addQueryItem("type","frequent"); + break; + case QueryStat::QueryStat_Recent: + url.addQueryItem("type","recent"); + break; + case QueryStat::QueryStat_Starred: + url.addQueryItem("type","starred"); + break; + case QueryStat::QueryStat_Random: + url.addQueryItem("type","random"); + break; + } + + if (count > 500) count = 500; // 500 limit per subsonic api + if (count != 10) { // 10 is default + url.addQueryItem("size",QString::number(count)); + } + + if (offset_ != 0) { // 0 is default + url.addQueryItem("offset",QString::number(offset_)); + } + + PlaylistItemList items; + + QNetworkReply* reply = Send(network,url,service->usesslv3_); + + // wait for reply + { + QEventLoop loop; + connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + } + + if (reply->error() != QNetworkReply::NoError) { + qLog(Warning) << "HTTP error returned from Subsonic:" << reply->errorString() + << ", url:" << url.toString(); + service->app_->task_manager()->SetTaskFinished(task_id); + return items; // empty + } + + reply->deleteLater(); + QXmlStreamReader reader(reply); + reader.readNextStartElement(); + if (reader.name() != "subsonic-response") { + qLog(Warning) << "Not a subsonic-response, aboring playlist fetch"; + service->app_->task_manager()->SetTaskFinished(task_id); + return items; + } + + if (reader.attributes().value("status") != "ok") { + reader.readNextStartElement(); + int error = reader.attributes().value("code").toString().toInt(); + qLog(Warning) << "An error occured fetching data. Code: "<app_->task_manager()->SetTaskFinished(task_id); + return items; + } + + while (reader.readNextStartElement()) { + if (reader.name() != "album") { + qLog(Warning) << "album tag expected. Aboring playlist fetch"; + service->app_->task_manager()->SetTaskFinished(task_id); + return items; + } + + qLog(Debug) << "Getting album: "<usesslv3_); + + reader.skipCurrentElement(); + } + offset_+=count; + service->app_->task_manager()->SetTaskFinished(task_id); + return items; +} + +void SubsonicDynamicPlaylist::GetAlbum(SubsonicService* service, PlaylistItemList& list, QString id, QNetworkAccessManager& network, const bool usesslv3) { + QUrl url = service->BuildRequestUrl("getAlbum"); + url.addQueryItem("id", id); + QNetworkReply* reply = Send(network, url, usesslv3); + + { // wait for reply + QEventLoop loop; + connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + } + + if (reply->error() != QNetworkReply::NoError) { + qLog(Warning) << "HTTP error returned from Subsonic:" << reply->errorString() + << ", url:" << url.toString(); + return; + } + + QXmlStreamReader reader(reply); + reader.readNextStartElement(); + + if (reader.name() != "subsonic-response") { + qLog(Warning) << "Not a subsonic-response. Aborting playlist fetch."; + return; + } + + if (reader.attributes().value("status") != "ok") { + qLog(Warning) << "Status not okay. Aborting playlist fetch."; + return; + } + + // Read album information + reader.readNextStartElement(); + if (reader.name() != "album") { + qLog(Warning) << "album tag expected. Aborting playlist fetch."; + return; + } + + QString album_artist = reader.attributes().value("artist").toString(); + + // Read song information + while (reader.readNextStartElement()) { + if (reader.name() != "song") { + qLog(Warning) << "song tag expected. Aborting playlist fetch."; + return; + } + + Song song; + QString id = reader.attributes().value("id").toString(); + song.set_title(reader.attributes().value("title").toString()); + song.set_album(reader.attributes().value("album").toString()); + song.set_track(reader.attributes().value("track").toString().toInt()); + song.set_disc(reader.attributes().value("discNumber").toString().toInt()); + song.set_artist(reader.attributes().value("artist").toString()); + song.set_albumartist(album_artist); + 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()); + qint64 length = reader.attributes().value("duration").toString().toInt(); + length *= kNsecPerSec; + song.set_length_nanosec(length); + QUrl url = QUrl(QString("subsonic://%1").arg(id)); + song.set_url(url); + song.set_filesize(reader.attributes().value("size").toString().toInt()); + QFileInfo fi(reader.attributes().value("path").toString()); + song.set_basefilename(fi.fileName()); + // We need to set these to satisfy the database constraints + song.set_directory_id(0); + song.set_mtime(0); + song.set_ctime(0); + + + list << std::shared_ptr( + new InternetPlaylistItem(service,song)); + + reader.skipCurrentElement(); + } +} + +QDataStream& operator<<(QDataStream& s, const SubsonicDynamicPlaylist& p) { + s << quint8(p.stat_); + return s; +} + +QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p) { + quint8 stat; + s >> stat; + p.stat_ = SubsonicDynamicPlaylist::QueryStat(stat); + return s; +} + + diff --git a/src/internet/subsonic/subsonicdynamicplaylist.h b/src/internet/subsonic/subsonicdynamicplaylist.h new file mode 100644 index 000000000..bb7409076 --- /dev/null +++ b/src/internet/subsonic/subsonicdynamicplaylist.h @@ -0,0 +1,75 @@ +/* This file is part of Clementine. + Copyright 2015, Nick Lanham + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef INTERNET_SUBSONIC_SUBSONICDYNAMICPLAYLIST_H_ +#define INTERNET_SUBSONIC_SUBSONICDYNAMICPLAYLIST_H_ + +#include "smartplaylists/generator.h" + +#include + +class SubsonicService; + +class SubsonicDynamicPlaylist : public smart_playlists::Generator { + Q_OBJECT + friend QDataStream& operator<<(QDataStream& s, + const SubsonicDynamicPlaylist& p); + friend QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p); + + public: + + // things that subsonic can return to us, persisted so only add at end + enum QueryStat { + QueryStat_Newest = 0, + QueryStat_Highest = 1, + QueryStat_Frequent = 2, + QueryStat_Recent = 3, + QueryStat_Starred = 4, + QueryStat_Random =5, + }; + + SubsonicDynamicPlaylist(); + SubsonicDynamicPlaylist(const QString& name, QueryStat stat); + + QString type() const { return "Subsonic"; } + + void Load(const QByteArray& data); + void Load(QueryStat stat); + QByteArray Save() const; + + PlaylistItemList Generate(); + + bool is_dynamic() const { return true; } + PlaylistItemList GenerateMore(int count); + + private: + void GetAlbum(SubsonicService* service, PlaylistItemList& list, QString id, QNetworkAccessManager& network, const bool usesslv3); + // need our own one since we run in a different thread from service + QNetworkReply* Send(QNetworkAccessManager& network, const QUrl& url, const bool usesslv3); + + private: + QueryStat stat_; + int offset_; + SubsonicService* service_; +}; + +#include "subsonicservice.h" + +QDataStream& operator<<(QDataStream& s, const SubsonicDynamicPlaylist& p); +QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p); + +#endif // INTERNET_SUBSONIC_SUBSONICDYNAMICPLAYLIST_H_ diff --git a/src/internet/subsonic/subsonicservice.cpp b/src/internet/subsonic/subsonicservice.cpp index 4d1b73fe1..2cd454f12 100644 --- a/src/internet/subsonic/subsonicservice.cpp +++ b/src/internet/subsonic/subsonicservice.cpp @@ -43,8 +43,11 @@ #include "globalsearch/librarysearchprovider.h" #include "internet/core/internetmodel.h" #include "internet/subsonic/subsonicurlhandler.h" +#include "internet/subsonic/subsonicdynamicplaylist.h" #include "library/librarybackend.h" #include "library/libraryfilterwidget.h" +#include "smartplaylists/generator.h" +#include "smartplaylists/querygenerator.h" #include "ui/iconloader.h" const char* SubsonicService::kServiceName = "Subsonic"; @@ -83,9 +86,34 @@ SubsonicService::SubsonicService(Application* app, InternetModel* parent) connect(library_backend_, SIGNAL(TotalSongCountUpdated(int)), SLOT(UpdateTotalSongCount(int))); + using smart_playlists::Generator; + using smart_playlists::GeneratorPtr; + library_model_ = new LibraryModel(library_backend_, app_, this); library_model_->set_show_various_artists(false); - library_model_->set_show_smart_playlists(false); + library_model_->set_show_smart_playlists(true); + library_model_->set_default_smart_playlists( + LibraryModel::DefaultGenerators() + << (LibraryModel::GeneratorList() + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Newest"), + SubsonicDynamicPlaylist::QueryStat_Newest)) + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Random"), + SubsonicDynamicPlaylist::QueryStat_Random)) + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Frequently Played"), + SubsonicDynamicPlaylist::QueryStat_Frequent)) + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Top Rated"), + SubsonicDynamicPlaylist::QueryStat_Highest)) + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Recently Played"), + SubsonicDynamicPlaylist::QueryStat_Recent)) + << GeneratorPtr(new SubsonicDynamicPlaylist( + tr("Starred"), + SubsonicDynamicPlaylist::QueryStat_Starred)) + )); library_filter_ = new LibraryFilterWidget(0); library_filter_->SetSettingsGroup(kSettingsGroup); diff --git a/src/internet/subsonic/subsonicservice.h b/src/internet/subsonic/subsonicservice.h index a9df65c68..6ce80b3b5 100644 --- a/src/internet/subsonic/subsonicservice.h +++ b/src/internet/subsonic/subsonicservice.h @@ -26,6 +26,7 @@ #include "internet/core/internetmodel.h" #include "internet/core/internetservice.h" +#include "internet/subsonic/subsonicdynamicplaylist.h" class QNetworkAccessManager; class QNetworkReply; @@ -109,6 +110,8 @@ class SubsonicService : public InternetService { // boilerplate. QNetworkReply* Send(const QUrl& url); + friend PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int); + static const char* kServiceName; static const char* kSettingsGroup; static const char* kApiVersion; diff --git a/src/smartplaylists/generator.cpp b/src/smartplaylists/generator.cpp index 961e9c4f9..c3c7c4b75 100644 --- a/src/smartplaylists/generator.cpp +++ b/src/smartplaylists/generator.cpp @@ -19,6 +19,7 @@ #include "querygenerator.h" #include "core/logging.h" #include "internet/jamendo/jamendodynamicplaylist.h" +#include "internet/subsonic/subsonicdynamicplaylist.h" #include @@ -35,6 +36,9 @@ GeneratorPtr Generator::Create(const QString& type) { return GeneratorPtr(new QueryGenerator); else if (type == "Jamendo") return GeneratorPtr(new JamendoDynamicPlaylist); + else if (type == "Subsonic") { + return GeneratorPtr(new SubsonicDynamicPlaylist); + } qLog(Warning) << "Invalid playlist generator type:" << type; return GeneratorPtr(); From 310cf001afdd9b69bd00a5eecb8fc6b6ccd1e561 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Tue, 1 Dec 2015 14:46:10 -0800 Subject: [PATCH 2/4] Updates from hatstand's comments --- .../subsonic/subsonicdynamicplaylist.cpp | 118 +++++++++--------- .../subsonic/subsonicdynamicplaylist.h | 35 ++++-- 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/src/internet/subsonic/subsonicdynamicplaylist.cpp b/src/internet/subsonic/subsonicdynamicplaylist.cpp index e5691acb3..a61055087 100644 --- a/src/internet/subsonic/subsonicdynamicplaylist.cpp +++ b/src/internet/subsonic/subsonicdynamicplaylist.cpp @@ -16,6 +16,7 @@ */ #include "subsonicdynamicplaylist.h" +#include "subsonicservice.h" #include #include @@ -29,14 +30,19 @@ #include "core/timeconstants.h" #include "internet/core/internetplaylistitem.h" +#include + +// 500 limit per subsonic api +const int SubsonicDynamicPlaylist::kMaxCount = 500; +const int SubsonicDynamicPlaylist::kDefaultCount = 10; +const int SubsonicDynamicPlaylist::kDefaultOffset = 0; + SubsonicDynamicPlaylist::SubsonicDynamicPlaylist() - : stat_(QueryStat_Newest), - offset_(0) {} + : stat_(QueryStat_Newest), offset_(0) {} SubsonicDynamicPlaylist::SubsonicDynamicPlaylist(const QString& name, QueryStat stat) - : stat_(stat), - offset_(0) { + : stat_(stat), offset_(0) { set_name(name); } @@ -45,9 +51,7 @@ void SubsonicDynamicPlaylist::Load(const QByteArray& data) { s >> *this; } -void SubsonicDynamicPlaylist::Load(QueryStat stat) { - stat_ = stat; -} +void SubsonicDynamicPlaylist::Load(QueryStat stat) { stat_ = stat; } QByteArray SubsonicDynamicPlaylist::Save() const { QByteArray ret; @@ -57,7 +61,9 @@ QByteArray SubsonicDynamicPlaylist::Save() const { } // copied from SubsonicService -QNetworkReply* SubsonicDynamicPlaylist::Send(QNetworkAccessManager& network, const QUrl& url, const bool usesslv3) { +QNetworkReply* SubsonicDynamicPlaylist::Send(QNetworkAccessManager& network, + const QUrl& url, + const bool usesslv3) { QNetworkRequest request(url); // Don't try and check the authenticity of the SSL certificate - it'll almost // certainly be self-signed. @@ -71,47 +77,38 @@ QNetworkReply* SubsonicDynamicPlaylist::Send(QNetworkAccessManager& network, con return reply; } -PlaylistItemList SubsonicDynamicPlaylist::Generate() { return GenerateMore(10); } +PlaylistItemList SubsonicDynamicPlaylist::Generate() { + return GenerateMore(10); +} PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { SubsonicService* service = InternetModel::Service(); - int task_id = service->app_->task_manager()->StartTask(tr("Fetching Playlist Items")); + const int task_id = + service->app_->task_manager()->StartTask(tr("Fetching Playlist Items")); + + BOOST_SCOPE_EXIT((service)(task_id)) { + // stop task when we're done + service->app_->task_manager()->SetTaskFinished(task_id); + } + BOOST_SCOPE_EXIT_END + QUrl url = service->BuildRequestUrl("GetAlbumList"); QNetworkAccessManager network; - switch (stat_) { - case QueryStat::QueryStat_Newest: - url.addQueryItem("type","newest"); - break; - case QueryStat::QueryStat_Highest: - url.addQueryItem("type","highest"); - break; - case QueryStat::QueryStat_Frequent: - url.addQueryItem("type","frequent"); - break; - case QueryStat::QueryStat_Recent: - url.addQueryItem("type","recent"); - break; - case QueryStat::QueryStat_Starred: - url.addQueryItem("type","starred"); - break; - case QueryStat::QueryStat_Random: - url.addQueryItem("type","random"); - break; + url.addQueryItem("type", GetTypeString()); + + if (count > kMaxCount) count = kMaxCount; + if (count != kDefaultCount) { + url.addQueryItem("size", QString::number(count)); } - if (count > 500) count = 500; // 500 limit per subsonic api - if (count != 10) { // 10 is default - url.addQueryItem("size",QString::number(count)); - } - - if (offset_ != 0) { // 0 is default - url.addQueryItem("offset",QString::number(offset_)); + if (offset_ != kDefaultOffset) { + url.addQueryItem("offset", QString::number(offset_)); } PlaylistItemList items; - QNetworkReply* reply = Send(network,url,service->usesslv3_); + QNetworkReply* reply = Send(network, url, service->usesslv3_); // wait for reply { @@ -120,70 +117,74 @@ PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { loop.exec(); } + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { - qLog(Warning) << "HTTP error returned from Subsonic:" << reply->errorString() - << ", url:" << url.toString(); - service->app_->task_manager()->SetTaskFinished(task_id); - return items; // empty + qLog(Warning) << "HTTP error returned from Subsonic:" + << reply->errorString() << ", url:" << url.toString(); + return items; // empty } - reply->deleteLater(); QXmlStreamReader reader(reply); reader.readNextStartElement(); if (reader.name() != "subsonic-response") { qLog(Warning) << "Not a subsonic-response, aboring playlist fetch"; - service->app_->task_manager()->SetTaskFinished(task_id); return items; } if (reader.attributes().value("status") != "ok") { reader.readNextStartElement(); int error = reader.attributes().value("code").toString().toInt(); - qLog(Warning) << "An error occured fetching data. Code: "<app_->task_manager()->SetTaskFinished(task_id); return items; } while (reader.readNextStartElement()) { if (reader.name() != "album") { qLog(Warning) << "album tag expected. Aboring playlist fetch"; - service->app_->task_manager()->SetTaskFinished(task_id); return items; } - qLog(Debug) << "Getting album: "<usesslv3_); - + qLog(Debug) << "Getting album: " + << reader.attributes().value("album").toString(); + GetAlbum(service, items, reader.attributes().value("id").toString(), + network, service->usesslv3_); reader.skipCurrentElement(); } - offset_+=count; - service->app_->task_manager()->SetTaskFinished(task_id); + offset_ += count; return items; } -void SubsonicDynamicPlaylist::GetAlbum(SubsonicService* service, PlaylistItemList& list, QString id, QNetworkAccessManager& network, const bool usesslv3) { +void SubsonicDynamicPlaylist::GetAlbum(SubsonicService* service, + PlaylistItemList& list, QString id, + QNetworkAccessManager& network, + const bool usesslv3) { QUrl url = service->BuildRequestUrl("getAlbum"); url.addQueryItem("id", id); QNetworkReply* reply = Send(network, url, usesslv3); - { // wait for reply + { // wait for reply QEventLoop loop; connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); } + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { - qLog(Warning) << "HTTP error returned from Subsonic:" << reply->errorString() - << ", url:" << url.toString(); + qLog(Warning) << "HTTP error returned from Subsonic:" + << reply->errorString() << ", url:" << url.toString(); return; } - QXmlStreamReader reader(reply); + QXmlStreamReader reader(reply); reader.readNextStartElement(); if (reader.name() != "subsonic-response") { @@ -236,9 +237,8 @@ void SubsonicDynamicPlaylist::GetAlbum(SubsonicService* service, PlaylistItemLis song.set_mtime(0); song.set_ctime(0); - list << std::shared_ptr( - new InternetPlaylistItem(service,song)); + new InternetPlaylistItem(service, song)); reader.skipCurrentElement(); } @@ -255,5 +255,3 @@ QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p) { p.stat_ = SubsonicDynamicPlaylist::QueryStat(stat); return s; } - - diff --git a/src/internet/subsonic/subsonicdynamicplaylist.h b/src/internet/subsonic/subsonicdynamicplaylist.h index bb7409076..31231f3b3 100644 --- a/src/internet/subsonic/subsonicdynamicplaylist.h +++ b/src/internet/subsonic/subsonicdynamicplaylist.h @@ -31,7 +31,6 @@ class SubsonicDynamicPlaylist : public smart_playlists::Generator { friend QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p); public: - // things that subsonic can return to us, persisted so only add at end enum QueryStat { QueryStat_Newest = 0, @@ -39,7 +38,7 @@ class SubsonicDynamicPlaylist : public smart_playlists::Generator { QueryStat_Frequent = 2, QueryStat_Recent = 3, QueryStat_Starred = 4, - QueryStat_Random =5, + QueryStat_Random = 5, }; SubsonicDynamicPlaylist(); @@ -56,10 +55,34 @@ class SubsonicDynamicPlaylist : public smart_playlists::Generator { bool is_dynamic() const { return true; } PlaylistItemList GenerateMore(int count); + static const int kMaxCount; + static const int kDefaultCount; + static const int kDefaultOffset; + private: - void GetAlbum(SubsonicService* service, PlaylistItemList& list, QString id, QNetworkAccessManager& network, const bool usesslv3); + void GetAlbum(SubsonicService* service, PlaylistItemList& list, QString id, + QNetworkAccessManager& network, const bool usesslv3); // need our own one since we run in a different thread from service - QNetworkReply* Send(QNetworkAccessManager& network, const QUrl& url, const bool usesslv3); + QNetworkReply* Send(QNetworkAccessManager& network, const QUrl& url, + const bool usesslv3); + QString GetTypeString() const { + switch (stat_) { + case QueryStat::QueryStat_Newest: + return "newest"; + case QueryStat::QueryStat_Highest: + return "highest"; + case QueryStat::QueryStat_Frequent: + return "frequent"; + case QueryStat::QueryStat_Recent: + return "recent"; + case QueryStat::QueryStat_Starred: + return "starred"; + case QueryStat::QueryStat_Random: + return "random"; + default: + return "newest"; + } + } private: QueryStat stat_; @@ -67,9 +90,7 @@ class SubsonicDynamicPlaylist : public smart_playlists::Generator { SubsonicService* service_; }; -#include "subsonicservice.h" - QDataStream& operator<<(QDataStream& s, const SubsonicDynamicPlaylist& p); QDataStream& operator>>(QDataStream& s, SubsonicDynamicPlaylist& p); -#endif // INTERNET_SUBSONIC_SUBSONICDYNAMICPLAYLIST_H_ +#endif // INTERNET_SUBSONIC_SUBSONICDYNAMICPLAYLIST_H_ From da537d9a4b8680711842817b6e4c0e5c030bdc4e Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Thu, 3 Dec 2015 17:25:00 -0800 Subject: [PATCH 3/4] use WaitForSignal --- .../subsonic/subsonicdynamicplaylist.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/internet/subsonic/subsonicdynamicplaylist.cpp b/src/internet/subsonic/subsonicdynamicplaylist.cpp index a61055087..37a9f3c5f 100644 --- a/src/internet/subsonic/subsonicdynamicplaylist.cpp +++ b/src/internet/subsonic/subsonicdynamicplaylist.cpp @@ -28,6 +28,7 @@ #include "core/network.h" #include "core/taskmanager.h" #include "core/timeconstants.h" +#include "core/waitforsignal.h" #include "internet/core/internetplaylistitem.h" #include @@ -109,13 +110,7 @@ PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { PlaylistItemList items; QNetworkReply* reply = Send(network, url, service->usesslv3_); - - // wait for reply - { - QEventLoop loop; - connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - } + WaitForSignal(reply, SIGNAL(finished())); reply->deleteLater(); @@ -169,13 +164,7 @@ void SubsonicDynamicPlaylist::GetAlbum(SubsonicService* service, QUrl url = service->BuildRequestUrl("getAlbum"); url.addQueryItem("id", id); QNetworkReply* reply = Send(network, url, usesslv3); - - { // wait for reply - QEventLoop loop; - connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - } - + WaitForSignal(reply, SIGNAL(finished())); reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { From 8dc65637184587f4a433b36b6fcdec87918d90b8 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Mon, 7 Dec 2015 11:00:03 -0800 Subject: [PATCH 4/4] Always pass size and count parameters, init to defaults --- .../subsonic/subsonicdynamicplaylist.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/internet/subsonic/subsonicdynamicplaylist.cpp b/src/internet/subsonic/subsonicdynamicplaylist.cpp index 37a9f3c5f..c655dc061 100644 --- a/src/internet/subsonic/subsonicdynamicplaylist.cpp +++ b/src/internet/subsonic/subsonicdynamicplaylist.cpp @@ -39,11 +39,11 @@ const int SubsonicDynamicPlaylist::kDefaultCount = 10; const int SubsonicDynamicPlaylist::kDefaultOffset = 0; SubsonicDynamicPlaylist::SubsonicDynamicPlaylist() - : stat_(QueryStat_Newest), offset_(0) {} + : stat_(QueryStat_Newest), offset_(kDefaultOffset) {} SubsonicDynamicPlaylist::SubsonicDynamicPlaylist(const QString& name, QueryStat stat) - : stat_(stat), offset_(0) { + : stat_(stat), offset_(kDefaultOffset) { set_name(name); } @@ -79,7 +79,7 @@ QNetworkReply* SubsonicDynamicPlaylist::Send(QNetworkAccessManager& network, } PlaylistItemList SubsonicDynamicPlaylist::Generate() { - return GenerateMore(10); + return GenerateMore(kDefaultCount); } PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { @@ -96,16 +96,11 @@ PlaylistItemList SubsonicDynamicPlaylist::GenerateMore(int count) { QUrl url = service->BuildRequestUrl("GetAlbumList"); QNetworkAccessManager network; - url.addQueryItem("type", GetTypeString()); - if (count > kMaxCount) count = kMaxCount; - if (count != kDefaultCount) { - url.addQueryItem("size", QString::number(count)); - } - if (offset_ != kDefaultOffset) { - url.addQueryItem("offset", QString::number(offset_)); - } + url.addQueryItem("type", GetTypeString()); + url.addQueryItem("size", QString::number(count)); + url.addQueryItem("offset", QString::number(offset_)); PlaylistItemList items;