diff --git a/data/data.qrc b/data/data.qrc
index 3ea3e2eaa..c39bd5acb 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -350,6 +350,7 @@
providers/22x22/skydrive.png
providers/22x22/somafm.png
providers/22x22/intergalacticfm.png
+ providers/22x22/radiobrowser.png
providers/22x22/songkick.png
providers/22x22/soundcloud.png
providers/22x22/spotify.png
@@ -383,6 +384,7 @@
providers/32x32/skydrive.png
providers/32x32/somafm.png
providers/32x32/intergalacticfm.png
+ providers/32x32/radiobrowser.png
providers/32x32/songkick.png
providers/32x32/soundcloud.png
providers/32x32/spotify.png
@@ -416,6 +418,7 @@
providers/48x48/skydrive.png
providers/48x48/somafm.png
providers/48x48/intergalacticfm.png
+ providers/48x48/radiobrowser.png
providers/48x48/songkick.png
providers/48x48/soundcloud.png
providers/48x48/spotify.png
diff --git a/data/providers/22x22/radiobrowser.png b/data/providers/22x22/radiobrowser.png
new file mode 100644
index 000000000..c88fa6be9
Binary files /dev/null and b/data/providers/22x22/radiobrowser.png differ
diff --git a/data/providers/32x32/radiobrowser.png b/data/providers/32x32/radiobrowser.png
new file mode 100644
index 000000000..8ff7fab11
Binary files /dev/null and b/data/providers/32x32/radiobrowser.png differ
diff --git a/data/providers/48x48/radiobrowser.png b/data/providers/48x48/radiobrowser.png
new file mode 100644
index 000000000..e6acbc669
Binary files /dev/null and b/data/providers/48x48/radiobrowser.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2d5092d5a..93451d6a2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -155,6 +155,7 @@ set(SOURCES
globalsearch/simplesearchprovider.cpp
globalsearch/somafmsearchprovider.cpp
globalsearch/intergalacticfmsearchprovider.cpp
+ globalsearch/radiobrowsersearchprovider.cpp
globalsearch/suggestionwidget.cpp
globalsearch/urlsearchprovider.cpp
@@ -191,6 +192,8 @@ set(SOURCES
internet/somafm/somafmurlhandler.cpp
internet/intergalacticfm/intergalacticfmservice.cpp
internet/intergalacticfm/intergalacticfmurlhandler.cpp
+ internet/radiobrowser/radiobrowserservice.cpp
+ internet/radiobrowser/radiobrowserurlhandler.cpp
internet/subsonic/subsonicservice.cpp
internet/subsonic/subsonicsettingspage.cpp
internet/subsonic/subsonicurlhandler.cpp
@@ -506,6 +509,8 @@ set(HEADERS
internet/somafm/somafmurlhandler.h
internet/intergalacticfm/intergalacticfmservice.h
internet/intergalacticfm/intergalacticfmurlhandler.h
+ internet/radiobrowser/radiobrowserservice.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 232b4feb2..8312498b5 100644
--- a/src/core/metatypes.cpp
+++ b/src/core/metatypes.cpp
@@ -37,6 +37,7 @@
#include "internet/intergalacticfm/intergalacticfmservice.h"
#include "internet/podcasts/podcast.h"
#include "internet/podcasts/podcastepisode.h"
+#include "internet/radiobrowser/radiobrowserservice.h"
#include "internet/somafm/somafmservice.h"
#include "library/directory.h"
#include "playlist/playlist.h"
@@ -98,6 +99,8 @@ void RegisterMetaTypes() {
qRegisterMetaType("SomaFMService::Stream");
qRegisterMetaType(
"IntergalacticFMService::Stream");
+ qRegisterMetaType(
+ "RadioBrowserService::Stream");
qRegisterMetaType("SongList");
qRegisterMetaType("Song");
qRegisterMetaTypeStreamOperators(
@@ -108,6 +111,8 @@ 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
new file mode 100644
index 000000000..3bce35360
--- /dev/null
+++ b/src/globalsearch/radiobrowsersearchprovider.cpp
@@ -0,0 +1,52 @@
+/* This file is part of Clementine.
+ Copyright 2011, David Sansome
+
+ 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 "radiobrowsersearchprovider.h"
+
+RadioBrowserSearchProvider::RadioBrowserSearchProvider(
+ RadioBrowserServiceBase* service, Application* app, QObject* parent)
+ : SimpleSearchProvider(app, parent), service_(service) {
+ Init(service->name(), service->url_scheme(), service->icon(),
+ CanGiveSuggestions);
+ set_result_limit(3);
+ set_max_suggestion_count(3);
+ icon_ = ScaleAndPad(
+ service->icon().pixmap(service->icon().availableSizes()[0]).toImage());
+
+ connect(service, SIGNAL(StreamsChanged()), SLOT(MaybeRecreateItems()));
+
+ // Load the stream list on startup only if it doesn't involve going to update
+ // info from the server.
+ if (!service_->IsStreamListStale()) RecreateItems();
+}
+
+void RadioBrowserSearchProvider::LoadArtAsync(int id, const Result& result) {
+ emit ArtLoaded(id, icon_);
+}
+
+void RadioBrowserSearchProvider::RecreateItems() {
+ QList- items;
+
+ for (const RadioBrowserService::Stream& stream : service_->Streams()) {
+ Item item;
+ item.metadata_ = stream.ToSong(service_->name());
+ item.keyword_ = stream.name_;
+ items << item;
+ }
+
+ SetItems(items);
+}
diff --git a/src/globalsearch/radiobrowsersearchprovider.h b/src/globalsearch/radiobrowsersearchprovider.h
new file mode 100644
index 000000000..479e67ba9
--- /dev/null
+++ b/src/globalsearch/radiobrowsersearchprovider.h
@@ -0,0 +1,42 @@
+
+/* This file is part of Clementine.
+ Copyright 2011, David Sansome
+
+ 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 RADIOBROWSERSEARCHPROVIDER_H
+#define RADIOBROWSERSEARCHPROVIDER_H
+
+#include "internet/radiobrowser/radiobrowserservice.h"
+#include "simplesearchprovider.h"
+
+class RadioBrowserSearchProvider : public SimpleSearchProvider {
+ public:
+ RadioBrowserSearchProvider(RadioBrowserServiceBase* service,
+ Application* app, QObject* parent);
+ // SearchProvider
+ InternetService* internet_service() override { return service_; }
+
+ void LoadArtAsync(int id, const Result& result) override;
+
+ protected:
+ void RecreateItems() override;
+
+ private:
+ RadioBrowserServiceBase* service_;
+ QImage icon_;
+};
+
+#endif // RADIOBROWSERSEARCHPROVIDER_H
diff --git a/src/internet/core/internetmodel.cpp b/src/internet/core/internetmodel.cpp
index bba5ff400..8c1275c92 100644
--- a/src/internet/core/internetmodel.cpp
+++ b/src/internet/core/internetmodel.cpp
@@ -40,6 +40,7 @@
#include "internet/jamendo/jamendoservice.h"
#include "internet/magnatune/magnatuneservice.h"
#include "internet/podcasts/podcastservice.h"
+#include "internet/radiobrowser/radiobrowserservice.h"
#include "internet/somafm/somafmservice.h"
#include "internet/subsonic/subsonicservice.h"
#include "smartplaylists/generatormimedata.h"
@@ -96,6 +97,7 @@ InternetModel::InternetModel(Application* app, QObject* parent)
AddService(new RadioTunesService(app, this));
AddService(new SomaFMService(app, this));
AddService(new IntergalacticFMService(app, this));
+ AddService(new RadioBrowserService(app, this));
#ifdef HAVE_SPOTIFY
AddService(new SpotifyService(app, this));
#endif
diff --git a/src/internet/radiobrowser/radiobrowserservice.cpp b/src/internet/radiobrowser/radiobrowserservice.cpp
new file mode 100644
index 000000000..cb1a56648
--- /dev/null
+++ b/src/internet/radiobrowser/radiobrowserservice.cpp
@@ -0,0 +1,254 @@
+/* This file is part of Clementine.
+ Copyright 2010-2013, David Sansome
+ Copyright 2011, Tyler Rhodes
+ Copyright 2011, Paweł Bara
+ Copyright 2012, 2014, John Maguire
+ Copyright 2014, Krzysztof Sobiecki
+
+ 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 "radiobrowserservice.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/application.h"
+#include "core/closure.h"
+#include "core/logging.h"
+#include "core/network.h"
+#include "core/player.h"
+#include "core/taskmanager.h"
+#include "core/utilities.h"
+#include "globalsearch/globalsearch.h"
+#include "globalsearch/radiobrowsersearchprovider.h"
+#include "radiobrowserurlhandler.h"
+#include "internet/core/internetmodel.h"
+#include "ui/iconloader.h"
+
+const int RadioBrowserServiceBase::kStreamsCacheDurationSecs =
+ 60 * 60 * 24 * 28; // 4 weeks
+
+bool operator<(const RadioBrowserServiceBase::Stream& a,
+ const RadioBrowserServiceBase::Stream& b) {
+ return a.name_.compare(b.name_, Qt::CaseInsensitive) < 0;
+}
+
+RadioBrowserServiceBase::RadioBrowserServiceBase(
+ Application* app, InternetModel* parent, const QString& name,
+ const QUrl& channel_list_url, const QUrl& homepage_url,
+ const QUrl& donate_page_url, const QIcon& icon)
+ : InternetService(name, app, parent, parent),
+ url_scheme_(name.toLower().remove(' ')),
+ url_handler_(new RadioBrowserUrlHandler(app, this, this)),
+ root_(nullptr),
+ context_menu_(nullptr),
+ network_(new NetworkAccessManager(this)),
+ streams_(name, "streams", kStreamsCacheDurationSecs),
+ name_(name),
+ channel_list_url_(channel_list_url),
+ homepage_url_(homepage_url),
+ donate_page_url_(donate_page_url),
+ icon_(icon) {
+ ReloadSettings();
+
+ app_->player()->RegisterUrlHandler(url_handler_);
+ app_->global_search()->AddProvider(
+ new RadioBrowserSearchProvider(this, app_, this));
+}
+
+RadioBrowserServiceBase::~RadioBrowserServiceBase() {
+ delete context_menu_;
+}
+
+QStandardItem* RadioBrowserServiceBase::CreateRootItem() {
+ root_ = new QStandardItem(icon_, name_);
+ root_->setData(true, InternetModel::Role_CanLazyLoad);
+ return root_;
+}
+
+void RadioBrowserServiceBase::LazyPopulate(QStandardItem* item) {
+ switch (item->data(InternetModel::Role_Type).toInt()) {
+ case InternetModel::Type_Service:
+ RefreshStreams();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void RadioBrowserServiceBase::ShowContextMenu(const QPoint& global_pos) {
+ if (!context_menu_) {
+ context_menu_ = new QMenu;
+ context_menu_->addActions(GetPlaylistActions());
+ context_menu_->addAction(IconLoader::Load("download", IconLoader::Base),
+ tr("Open %1 in browser").arg(homepage_url_.host()),
+ this, SLOT(Homepage()));
+
+ if (!donate_page_url_.isEmpty()) {
+ context_menu_->addAction(IconLoader::Load("download", IconLoader::Base),
+ tr("Donate"), this, SLOT(Donate()));
+ }
+
+ context_menu_->addAction(IconLoader::Load("view-refresh", IconLoader::Base),
+ tr("Refresh channels"), this,
+ SLOT(ForceRefreshStreams()));
+ }
+
+ context_menu_->popup(global_pos);
+}
+
+void RadioBrowserServiceBase::ForceRefreshStreams() {
+ QNetworkReply* reply = network_->get(QNetworkRequest(channel_list_url_));
+ int task_id = app_->task_manager()->StartTask(tr("Getting channels"));
+
+ NewClosure(reply, SIGNAL(finished()), this,
+ SLOT(RefreshStreamsFinished(QNetworkReply*, int)), reply, task_id);
+}
+
+void RadioBrowserServiceBase::RefreshStreamsFinished(QNetworkReply* reply,
+ int task_id) {
+ app_->task_manager()->SetTaskFinished(task_id);
+ reply->deleteLater();
+
+ if (reply->error() != QNetworkReply::NoError) {
+ app_->AddError(
+ tr("Failed to get channel list:\n%1").arg(reply->errorString()));
+ return;
+ }
+
+ StreamList list;
+
+ QJsonParseError error;
+ QJsonDocument document = QJsonDocument::fromJson(reply->readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ app_->AddError(
+ tr("Failed to parse channel list:\n%1").arg(error.errorString()));
+ return;
+ }
+
+ QJsonArray contents = document.array();
+ qLog(Debug) << "RadioBrowser station list found:" << contents.size();
+ for (const QJsonValue& c : contents) {
+ QJsonObject item = c.toObject();
+ ReadStation(item, &list);
+ }
+
+ streams_.Update(list);
+ streams_.Sort();
+
+ // Only update the item's children if it's already been populated
+ if (!root_->data(InternetModel::Role_CanLazyLoad).toBool()) PopulateStreams();
+
+ emit StreamsChanged();
+}
+
+void RadioBrowserServiceBase::ReadStation(QJsonObject& item,
+ StreamList* ret) {
+ Stream stream;
+ stream.name_ = item["name"].toString();
+ QUrl url(item["url"].toString());
+ stream.url_ = url;
+ ret->append(stream);
+}
+
+Song RadioBrowserServiceBase::Stream::ToSong(const QString& prefix) const {
+ QString song_title = name_.trimmed();
+ if (!song_title.startsWith(prefix)) {
+ song_title = prefix + " " + song_title;
+ }
+
+ Song ret;
+ ret.set_valid(true);
+ ret.set_title(song_title);
+ ret.set_artist(name_);
+ ret.set_url(url_);
+ return ret;
+}
+
+void RadioBrowserServiceBase::Homepage() {
+ QDesktopServices::openUrl(homepage_url_);
+}
+
+void RadioBrowserServiceBase::Donate() {
+ QDesktopServices::openUrl(donate_page_url_);
+}
+
+PlaylistItem::Options RadioBrowserServiceBase::playlistitem_options() const {
+ return PlaylistItem::PauseDisabled;
+}
+
+RadioBrowserServiceBase::StreamList RadioBrowserServiceBase::Streams() {
+ if (IsStreamListStale()) {
+ metaObject()->invokeMethod(this, "ForceRefreshStreams",
+ Qt::QueuedConnection);
+ }
+ return streams_;
+}
+
+void RadioBrowserServiceBase::RefreshStreams() {
+ if (IsStreamListStale()) {
+ ForceRefreshStreams();
+ return;
+ }
+ PopulateStreams();
+}
+
+void RadioBrowserServiceBase::PopulateStreams() {
+ if (root_->hasChildren()) root_->removeRows(0, root_->rowCount());
+
+ for (const Stream& stream : streams_) {
+ QStandardItem* item = new QStandardItem(
+ IconLoader::Load("icon_radio", IconLoader::Lastfm), QString());
+ item->setText(stream.name_);
+ item->setData(QVariant::fromValue(stream.ToSong(name_)),
+ InternetModel::Role_SongMetadata);
+ item->setData(InternetModel::PlayBehaviour_SingleItem,
+ InternetModel::Role_PlayBehaviour);
+
+ root_->appendRow(item);
+ }
+}
+
+QDataStream& operator<<(QDataStream& out,
+ const RadioBrowserServiceBase::Stream& stream) {
+ out << stream.name_ << stream.url_;
+ return out;
+}
+
+QDataStream& operator>>(QDataStream& in,
+ RadioBrowserServiceBase::Stream& stream) {
+ in >> stream.name_ >> stream.url_;
+ return in;
+}
+
+void RadioBrowserServiceBase::ReloadSettings() {
+ streams_.Load();
+ streams_.Sort();
+}
+
+RadioBrowserService::RadioBrowserService(Application* app,
+ InternetModel* parent)
+ : RadioBrowserServiceBase(
+ app, parent, "Radio-Browser.info",
+ QUrl("http://all.api.radio-browser.info/json/stations"),
+ QUrl("https://www.radio-browser.info"), QUrl(),
+ IconLoader::Load("radiobrowser", IconLoader::Provider)) {}
diff --git a/src/internet/radiobrowser/radiobrowserservice.h b/src/internet/radiobrowser/radiobrowserservice.h
new file mode 100644
index 000000000..153b882e4
--- /dev/null
+++ b/src/internet/radiobrowser/radiobrowserservice.h
@@ -0,0 +1,117 @@
+/* This file is part of Clementine.
+ Copyright 2010-2013, David Sansome
+ Copyright 2010, 2014, John Maguire
+ Copyright 2014, Krzysztof Sobiecki
+
+ 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_RADIOBROWSERSERVICE_H_
+#define INTERNET_RADIOBROWSER_RADIOBROWSERSERVICE_H_
+
+#include
+
+#include "core/cachedlist.h"
+#include "internet/core/internetservice.h"
+
+class RadioBrowserUrlHandler;
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class QMenu;
+
+class RadioBrowserServiceBase : public InternetService {
+ Q_OBJECT
+
+ public:
+ RadioBrowserServiceBase(Application* app, InternetModel* parent,
+ const QString& name, const QUrl& channel_list_url,
+ const QUrl& homepage_url,
+ const QUrl& donate_page_url, const QIcon& icon);
+ ~RadioBrowserServiceBase();
+
+ enum ItemType {
+ Type_Stream = 2000,
+ };
+
+ struct Stream {
+ QString name_;
+ QUrl url_;
+
+ Song ToSong(const QString& prefix) const;
+ };
+ typedef QList StreamList;
+
+ static const int kStreamsCacheDurationSecs;
+
+ const QString& url_scheme() const { return url_scheme_; }
+ const QIcon& icon() const { return icon_; }
+
+ QStandardItem* CreateRootItem();
+ void LazyPopulate(QStandardItem* item);
+ void ShowContextMenu(const QPoint& global_pos);
+
+ PlaylistItem::Options playlistitem_options() const;
+ QNetworkAccessManager* network() const { return network_; }
+
+ void ReloadSettings();
+
+ bool IsStreamListStale() const { return streams_.IsStale(); }
+ StreamList Streams();
+
+ signals:
+ void StreamsChanged();
+
+ private slots:
+ void ForceRefreshStreams();
+ void RefreshStreams();
+ void RefreshStreamsFinished(QNetworkReply* reply, int task_id);
+
+ void Homepage();
+ void Donate();
+
+ private:
+ void ReadStation(QJsonObject& value, StreamList* ret);
+ void PopulateStreams();
+
+ private:
+ const QString url_scheme_;
+ RadioBrowserUrlHandler* url_handler_;
+
+ QStandardItem* root_;
+ QMenu* context_menu_;
+
+ QNetworkAccessManager* network_;
+
+ CachedList streams_;
+
+ const QString name_;
+ const QUrl channel_list_url_;
+ const QUrl homepage_url_;
+ const QUrl donate_page_url_;
+ const QIcon icon_;
+};
+
+class RadioBrowserService : public RadioBrowserServiceBase {
+ public:
+ RadioBrowserService(Application* app, InternetModel* parent);
+};
+
+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..408e37397
--- /dev/null
+++ b/src/internet/radiobrowser/radiobrowserurlhandler.cpp
@@ -0,0 +1,91 @@
+/* This file is part of Clementine.
+ Copyright 2011-2013, David Sansome
+ Copyright 2012, Olaf Christ
+ Copyright 2014, Krzysztof Sobiecki
+ Copyright 2014, John Maguire
+
+ 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
+#include
+
+#include "core/application.h"
+#include "core/logging.h"
+#include "core/taskmanager.h"
+#include "radiobrowserservice.h"
+#include "internet/core/internetmodel.h"
+#include "playlistparsers/playlistparser.h"
+
+RadioBrowserUrlHandler::RadioBrowserUrlHandler(
+ Application* app, RadioBrowserServiceBase* service, QObject* parent)
+ : UrlHandler(parent), app_(app), service_(service), task_id_(0) {}
+
+QString RadioBrowserUrlHandler::scheme() const {
+ return service_->url_scheme();
+}
+
+QIcon RadioBrowserUrlHandler::icon() const { return service_->icon(); }
+
+UrlHandler::LoadResult RadioBrowserUrlHandler::StartLoading(
+ const QUrl& url) {
+ QUrl playlist_url = url;
+ playlist_url.setScheme("https");
+
+ // Load the playlist
+ QNetworkReply* reply =
+ service_->network()->get(QNetworkRequest(playlist_url));
+ connect(reply, SIGNAL(finished()), SLOT(LoadPlaylistFinished()));
+
+ if (!task_id_)
+ task_id_ = app_->task_manager()->StartTask(tr("Loading stream"));
+
+ return LoadResult(url, LoadResult::WillLoadAsynchronously);
+}
+
+void RadioBrowserUrlHandler::LoadPlaylistFinished() {
+ QNetworkReply* reply = qobject_cast(sender());
+ app_->task_manager()->SetTaskFinished(task_id_);
+ task_id_ = 0;
+
+ QUrl original_url(reply->url());
+ original_url.setScheme(scheme());
+
+ if (reply->error() != QNetworkReply::NoError) {
+ // TODO((David Sansome): Error handling
+ qLog(Error) << reply->errorString();
+ emit AsyncLoadComplete(LoadResult(original_url, LoadResult::NoMoreTracks));
+ return;
+ }
+
+ // Parse the playlist
+ PlaylistParser parser(nullptr);
+ QList songs = parser.LoadFromDevice(reply);
+
+ qLog(Info) << "Loading station finished, got" << songs.count() << "songs";
+
+ // Failed to get playlist?
+ if (songs.count() == 0) {
+ qLog(Error) << "Error loading" << scheme() << "playlist";
+ emit AsyncLoadComplete(LoadResult(original_url, LoadResult::NoMoreTracks));
+ return;
+ }
+
+ emit AsyncLoadComplete(
+ LoadResult(original_url, LoadResult::TrackAvailable, songs[0].url()));
+}
diff --git a/src/internet/radiobrowser/radiobrowserurlhandler.h b/src/internet/radiobrowser/radiobrowserurlhandler.h
new file mode 100644
index 000000000..855636565
--- /dev/null
+++ b/src/internet/radiobrowser/radiobrowserurlhandler.h
@@ -0,0 +1,50 @@
+/* This file is part of Clementine.
+ Copyright 2011-2013, David Sansome
+ Copyright 2014, Krzysztof Sobiecki
+ Copyright 2014, John Maguire
+
+ 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"
+
+class Application;
+class RadioBrowserServiceBase;
+
+class RadioBrowserUrlHandler : public UrlHandler {
+ Q_OBJECT
+
+ public:
+ RadioBrowserUrlHandler(Application* app,
+ RadioBrowserServiceBase* service,
+ QObject* parent);
+
+ QString scheme() const;
+ QIcon icon() const;
+ LoadResult StartLoading(const QUrl& url);
+
+ private slots:
+ void LoadPlaylistFinished();
+
+ private:
+ Application* app_;
+ RadioBrowserServiceBase* service_;
+
+ int task_id_;
+};
+
+#endif // INTERNET_RADIOBROWSER_RADIOBROWSERURLHANDLER_H_