diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b7236c71b..d893cf309 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -194,6 +194,7 @@ set(SOURCES internet/intergalacticfm/intergalacticfmurlhandler.cpp internet/radiobrowser/radiobrowserservice.cpp internet/radiobrowser/radiobrowsersettingspage.cpp + internet/radiobrowser/radiobrowserurlhandler.cpp internet/subsonic/subsonicservice.cpp internet/subsonic/subsonicsettingspage.cpp internet/subsonic/subsonicurlhandler.cpp @@ -512,6 +513,7 @@ set(HEADERS internet/intergalacticfm/intergalacticfmurlhandler.h internet/radiobrowser/radiobrowserservice.h internet/radiobrowser/radiobrowsersettingspage.h + internet/radiobrowser/radiobrowserurlhandler.h internet/subsonic/subsonicservice.h internet/subsonic/subsonicsettingspage.h internet/subsonic/subsonicurlhandler.h diff --git a/src/core/metatypes.cpp b/src/core/metatypes.cpp index a6e3c5d1e..e8fc6efbe 100644 --- a/src/core/metatypes.cpp +++ b/src/core/metatypes.cpp @@ -99,7 +99,6 @@ void RegisterMetaTypes() { qRegisterMetaType("SomaFMService::Stream"); qRegisterMetaType( "IntergalacticFMService::Stream"); - qRegisterMetaType("RadioBrowserService::Stream"); qRegisterMetaType("SongList"); qRegisterMetaType("Song"); qRegisterMetaTypeStreamOperators( @@ -110,8 +109,6 @@ void RegisterMetaTypes() { "SomaFMService::Stream"); qRegisterMetaTypeStreamOperators( "IntergalacticFMService::Stream"); - qRegisterMetaTypeStreamOperators( - "RadioBrowserService::Stream"); qRegisterMetaType("SubdirectoryList"); qRegisterMetaType("Subdirectory"); qRegisterMetaType>("QList"); diff --git a/src/globalsearch/radiobrowsersearchprovider.cpp b/src/globalsearch/radiobrowsersearchprovider.cpp index 5768c7689..a062fa1a0 100644 --- a/src/globalsearch/radiobrowsersearchprovider.cpp +++ b/src/globalsearch/radiobrowsersearchprovider.cpp @@ -29,18 +29,12 @@ RadioBrowserSearchProvider::RadioBrowserSearchProvider( Init(RadioBrowserService::kServiceName, "radiobrowser", IconLoader::Load("radiobrowser", IconLoader::Provider), WantsDelayedQueries); - connect(service_, - SIGNAL(SearchFinished(int, RadioBrowserService::StreamList)), - SLOT(SearchFinishedSlot(int, RadioBrowserService::StreamList))); + connect(service_, &RadioBrowserService::SearchFinished, this, + &RadioBrowserSearchProvider::SearchFinishedSlot); } void RadioBrowserSearchProvider::SearchAsync(int id, const QString& query) { - PendingState state; - state.orig_id_ = id; - state.tokens_ = TokenizeQuery(query); - - const QString query_string = state.tokens_.join(" "); - service_->Search(id, query_string, kSearchStationLimit); + service_->Search(id, query, kSearchStationLimit); } void RadioBrowserSearchProvider::SearchFinishedSlot( @@ -57,10 +51,3 @@ void RadioBrowserSearchProvider::SearchFinishedSlot( emit ResultsAvailable(search_id, ret); emit SearchFinished(search_id); } -/* -void RadioBrowserSearchProvider::ShowConfig() { - if (service_) { - return service_->ShowConfig(); - } -} -*/ \ No newline at end of file diff --git a/src/globalsearch/radiobrowsersearchprovider.h b/src/globalsearch/radiobrowsersearchprovider.h index 7e77f9bdc..a2bcd7dc1 100644 --- a/src/globalsearch/radiobrowsersearchprovider.h +++ b/src/globalsearch/radiobrowsersearchprovider.h @@ -28,7 +28,6 @@ class RadioBrowserSearchProvider : public SearchProvider { RadioBrowserSearchProvider(Application* app, RadioBrowserService* service, QObject* parent = nullptr); void SearchAsync(int id, const QString& query) override; - // void ShowConfig() override; InternetService* internet_service() override { return service_; } public slots: diff --git a/src/internet/core/internetmodel.cpp b/src/internet/core/internetmodel.cpp index 4826186fd..8c1275c92 100644 --- a/src/internet/core/internetmodel.cpp +++ b/src/internet/core/internetmodel.cpp @@ -332,10 +332,6 @@ QMimeData* InternetModel::mimeData(const QModelIndexList& indexes) const { if (urls.isEmpty()) return nullptr; - for (const QModelIndex& index : new_indexes) { - InternetModel::ServiceForIndex(index)->ItemNowPlaying(itemFromIndex(index)); - } - InternetMimeData* data = new InternetMimeData(this); data->setUrls(urls); data->indexes = new_indexes; diff --git a/src/internet/radiobrowser/radiobrowserservice.cpp b/src/internet/radiobrowser/radiobrowserservice.cpp index 037346943..805671700 100644 --- a/src/internet/radiobrowser/radiobrowserservice.cpp +++ b/src/internet/radiobrowser/radiobrowserservice.cpp @@ -27,7 +27,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "core/logging.h" #include "core/network.h" #include "core/player.h" @@ -35,6 +34,7 @@ #include "core/utilities.h" #include "globalsearch/globalsearch.h" #include "globalsearch/radiobrowsersearchprovider.h" +#include "radiobrowserurlhandler.h" #include "ui/iconloader.h" bool operator<(const RadioBrowserService::Stream& a, @@ -44,6 +44,7 @@ bool operator<(const RadioBrowserService::Stream& a, const char* RadioBrowserService::kServiceName = "Radio-Browser.info"; const char* RadioBrowserService::kSettingsGroup = "RadioBrowser"; +const char* RadioBrowserService::kSchemeName = "radiobrowser"; QString RadioBrowserService::SearchUrl = "%1/json/stations/byname/%2?limit=%3"; QString RadioBrowserService::PlayClickUrl = "%1/json/url/%2"; @@ -73,8 +74,11 @@ RadioBrowserService::RadioBrowserService(Application* app, context_menu_(nullptr), network_(new NetworkAccessManager(this)), name_(kServiceName), + url_scheme_(kSchemeName), + url_handler_(new RadioBrowserUrlHandler(app, this, this)), homepage_url_(QUrl("https://www.radio-browser.info")), icon_(IconLoader::Load("radiobrowser", IconLoader::Provider)) { + app_->player()->RegisterUrlHandler(url_handler_); app_->global_search()->AddProvider( new RadioBrowserSearchProvider(app_, this, this)); } @@ -134,9 +138,8 @@ void RadioBrowserService::RefreshCategory(QStandardItem* item) { QNetworkReply* reply = network_->get(QNetworkRequest(url)); int task_id = app_->task_manager()->StartTask(tr("Getting channels")); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(RefreshCategoryFinished(QNetworkReply*, int, QStandardItem*)), - reply, task_id, item); + connect(reply, &QNetworkReply::finished, + [=] { this->RefreshCategoryFinished(reply, task_id, item); }); } void RadioBrowserService::RefreshCategoryItem(QStandardItem* item) { @@ -149,9 +152,8 @@ void RadioBrowserService::RefreshCategoryItem(QStandardItem* item) { QNetworkReply* reply = network_->get(QNetworkRequest(url)); int task_id = app_->task_manager()->StartTask(tr("Getting channels")); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(RefreshStreamsFinished(QNetworkReply*, int, QStandardItem*)), - reply, task_id, item); + connect(reply, &QNetworkReply::finished, + [=] { this->RefreshStreamsFinished(reply, task_id, item); }); } void RadioBrowserService::RefreshTop100(QStandardItem* item) { @@ -163,9 +165,8 @@ void RadioBrowserService::RefreshTop100(QStandardItem* item) { QNetworkReply* reply = network_->get(QNetworkRequest(url)); int task_id = app_->task_manager()->StartTask(tr("Getting channels")); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(RefreshStreamsFinished(QNetworkReply*, int, QStandardItem*)), - reply, task_id, item); + connect(reply, &QNetworkReply::finished, + [=] { this->RefreshStreamsFinished(reply, task_id, item); }); } void RadioBrowserService::ShowContextMenu(const QPoint& global_pos) { @@ -219,8 +220,8 @@ void RadioBrowserService::RefreshStreamsFinished(QNetworkReply* reply, QJsonArray contents = document.array(); qLog(Debug) << "RadioBrowser station list found:" << contents.size(); for (const QJsonValue& c : contents) { - QJsonObject item = c.toObject(); - ReadStation(item, &list); + QJsonObject station = c.toObject(); + ReadStation(station, &list); } PopulateStreams(item, list); @@ -229,8 +230,8 @@ void RadioBrowserService::RefreshStreamsFinished(QNetworkReply* reply, void RadioBrowserService::ReadStation(QJsonObject& item, StreamList* ret) { Stream stream; stream.name_ = item["name"].toString(); - stream.uuid_ = item["stationuuid"].toString(); - QUrl url(item["url"].toString()); + QUrl url(item["stationuuid"].toString()); + url.setScheme(url_handler_->scheme()); stream.url_ = url; QUrl favicon(item["favicon"].toString()); stream.favicon_ = favicon; @@ -289,23 +290,11 @@ void RadioBrowserService::PopulateStreams(QStandardItem* parentItem, InternetModel::Role_SongMetadata); item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour); - item->setData(stream.uuid_, RadioBrowserService::Role_StationUuid); parentItem->appendRow(item); } } -QDataStream& operator<<(QDataStream& out, - const RadioBrowserService::Stream& stream) { - out << stream.name_ << stream.url_; - return out; -} - -QDataStream& operator>>(QDataStream& in, RadioBrowserService::Stream& stream) { - in >> stream.name_ >> stream.url_; - return in; -} - void RadioBrowserService::ReloadSettings() { QSettings s; s.beginGroup(kSettingsGroup); @@ -338,9 +327,8 @@ void RadioBrowserService::Search(int search_id, const QString& query, QNetworkReply* reply = network_->get(QNetworkRequest(url)); int task_id = app_->task_manager()->StartTask(tr("Getting channels")); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(SearchFinishedInternal(QNetworkReply*, int, int)), reply, - task_id, search_id); + connect(reply, &QNetworkReply::finished, + [=] { this->SearchFinishedInternal(reply, task_id, search_id); }); } void RadioBrowserService::SearchFinishedInternal(QNetworkReply* reply, @@ -360,17 +348,27 @@ void RadioBrowserService::SearchFinishedInternal(QNetworkReply* reply, emit SearchFinished(search_id, list); } -void RadioBrowserService::ItemNowPlaying(QStandardItem* item) { - QString station_uuid = - item->data(RadioBrowserService::Role_StationUuid).toString(); - if (station_uuid.isEmpty()) return; - - QString determinedUrl = - RadioBrowserService::PlayClickUrl.arg(main_server_url_, station_uuid); +void RadioBrowserService::ResolveStationUrl(const QUrl& original_url) { + QString determinedUrl = RadioBrowserService::PlayClickUrl.arg( + main_server_url_, original_url.path()); QUrl url(determinedUrl); - qLog(Debug) << "RadioBrowser station played:" << determinedUrl; QNetworkReply* reply = network_->get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, - [this, reply]() { reply->deleteLater(); }); -} \ No newline at end of file + int task_id = app_->task_manager()->StartTask(tr("Getting station")); + + connect(reply, &QNetworkReply::finished, [=] { + this->ResolveStationUrlFinished(reply, task_id, original_url); + }); +} + +void RadioBrowserService::ResolveStationUrlFinished(QNetworkReply* reply, + int task_id, + const QUrl& original_url) { + app_->task_manager()->SetTaskFinished(task_id); + reply->deleteLater(); + QJsonDocument document = ParseJsonReply(reply); + + QJsonObject item = document.object(); + QUrl url(item["url"].toString()); + emit StationUrlResolved(original_url, url); +} diff --git a/src/internet/radiobrowser/radiobrowserservice.h b/src/internet/radiobrowser/radiobrowserservice.h index c2dd34cbb..14b033e3f 100644 --- a/src/internet/radiobrowser/radiobrowserservice.h +++ b/src/internet/radiobrowser/radiobrowserservice.h @@ -50,13 +50,11 @@ class RadioBrowserService : public InternetService { enum Role { Role_ListUrl = InternetModel::RoleCount, Role_ItemsUrl, - Role_StationUuid, }; struct Stream { QString name_; QUrl url_; - QString uuid_; QUrl favicon_; Song ToSong(const QString& prefix) const; @@ -77,8 +75,11 @@ class RadioBrowserService : public InternetService { static const char* kServiceName; static const char* kSettingsGroup; + static const char* kSchemeName; + const QString& url_scheme() const { return url_scheme_; } const QIcon& icon() const { return icon_; } + QNetworkAccessManager* network() const { return network_; } QStandardItem* CreateRootItem(); void ShowContextMenu(const QPoint& global_pos); @@ -86,10 +87,11 @@ class RadioBrowserService : public InternetService { PlaylistItem::Options playlistitem_options() const; void Search(int search_id, const QString& query, const int limit); - void ItemNowPlaying(QStandardItem* item) override; + void ResolveStationUrl(const QUrl& original_url); signals: void SearchFinished(int search_id, RadioBrowserService::StreamList streams); + void StationUrlResolved(const QUrl& original_url, const QUrl& url); public slots: void ReloadSettings(); @@ -106,6 +108,8 @@ class RadioBrowserService : public InternetService { QStandardItem* item); void RefreshStreamsFinished(QNetworkReply* reply, int task_id, QStandardItem* item); + void ResolveStationUrlFinished(QNetworkReply* reply, int task_id, + const QUrl& original_url); void SearchFinishedInternal(QNetworkReply* reply, int task_id, int search_id); void Homepage(); @@ -124,14 +128,11 @@ class RadioBrowserService : public InternetService { QNetworkAccessManager* network_; const QString name_; + const QString url_scheme_; + RadioBrowserUrlHandler* url_handler_; QString main_server_url_; const QUrl homepage_url_; const QIcon icon_; }; -QDataStream& operator<<(QDataStream& out, - const RadioBrowserService::Stream& stream); -QDataStream& operator>>(QDataStream& in, RadioBrowserService::Stream& stream); -Q_DECLARE_METATYPE(RadioBrowserService::Stream) - #endif // INTERNET_RADIOBROWSER_RADIOBROWSERSERVICE_H_ diff --git a/src/internet/radiobrowser/radiobrowserurlhandler.cpp b/src/internet/radiobrowser/radiobrowserurlhandler.cpp new file mode 100644 index 000000000..3750349e3 --- /dev/null +++ b/src/internet/radiobrowser/radiobrowserurlhandler.cpp @@ -0,0 +1,55 @@ +/* This file is part of Clementine. + Copyright 2021, Fabio Bas + + 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 "radiobrowserurlhandler.h" + +#include +#include + +#include "core/application.h" +#include "core/taskmanager.h" +#include "radiobrowserservice.h" + +RadioBrowserUrlHandler::RadioBrowserUrlHandler(Application* app, + RadioBrowserService* service, + QObject* parent) + : UrlHandler(parent), app_(app), service_(service) { + connect(service_, &RadioBrowserService::StationUrlResolved, this, + &RadioBrowserUrlHandler::LoadStationFinished); +} + +QString RadioBrowserUrlHandler::scheme() const { + return RadioBrowserService::kSchemeName; +} + +QIcon RadioBrowserUrlHandler::icon() const { return service_->icon(); } + +UrlHandler::LoadResult RadioBrowserUrlHandler::StartLoading(const QUrl& url) { + service_->ResolveStationUrl(url); + return LoadResult(url, LoadResult::WillLoadAsynchronously); +} + +void RadioBrowserUrlHandler::LoadStationFinished(const QUrl& original_url, + const QUrl& url) { + if (url.isValid()) { + emit AsyncLoadComplete( + LoadResult(original_url, LoadResult::TrackAvailable, url)); + } else { + qLog(Error) << "Error loading" << original_url; + emit AsyncLoadComplete(LoadResult(original_url, LoadResult::NoMoreTracks)); + } +} \ No newline at end of file diff --git a/src/internet/radiobrowser/radiobrowserurlhandler.h b/src/internet/radiobrowser/radiobrowserurlhandler.h new file mode 100644 index 000000000..86a0369fa --- /dev/null +++ b/src/internet/radiobrowser/radiobrowserurlhandler.h @@ -0,0 +1,47 @@ +/* This file is part of Clementine. + Copyright 2021, Fabio Bas + + 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_RADIOBROWSER_RADIOBROWSERURLHANDLER_H_ +#define INTERNET_RADIOBROWSER_RADIOBROWSERURLHANDLER_H_ + +#include "core/urlhandler.h" +#include "ui/iconloader.h" + +class RadioBrowserService; +class Application; + +// RadioBrowser URL handler: radiobrowser://uuid +class RadioBrowserUrlHandler : public UrlHandler { + Q_OBJECT + public: + RadioBrowserUrlHandler(Application* app, RadioBrowserService* service, + QObject* parent); + + QString scheme() const; + QIcon icon() const; + LoadResult StartLoading(const QUrl& url); + + private slots: + void LoadStationFinished(const QUrl& original_url, const QUrl& url); + + private: + Application* app_; + RadioBrowserService* service_; + int task_id_; +}; + +#endif // INTERNET_RADIOBROWSER_RADIOBROWSERURLHANDLER_H_