diff --git a/data/data.qrc b/data/data.qrc
index 3d7d6dabc..11f36f9c9 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -340,5 +340,8 @@
pythonlibs/uic/uiparser.py
pythonlibs/clementinelogging.py
nyancat.png
+ providers/digitallyimported.png
+ providers/skyfm.png
+ providers/digitallyimported-32.png
diff --git a/data/providers/digitallyimported-32.png b/data/providers/digitallyimported-32.png
new file mode 100644
index 000000000..bfef73f5c
Binary files /dev/null and b/data/providers/digitallyimported-32.png differ
diff --git a/data/providers/digitallyimported.png b/data/providers/digitallyimported.png
new file mode 100644
index 000000000..7e5840876
Binary files /dev/null and b/data/providers/digitallyimported.png differ
diff --git a/data/providers/skyfm.png b/data/providers/skyfm.png
new file mode 100644
index 000000000..e96adaa98
Binary files /dev/null and b/data/providers/skyfm.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 054fe5288..0ed1d8ec7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -118,6 +118,10 @@ set(SOURCES
engines/gstenginepipeline.cpp
engines/gstelementdeleter.cpp
+ internet/digitallyimportedconfig.cpp
+ internet/digitallyimportedservice.cpp
+ internet/digitallyimportedservicebase.cpp
+ internet/digitallyimportedurlhandler.cpp
internet/icecastbackend.cpp
internet/icecastfilterwidget.cpp
internet/icecastmodel.cpp
@@ -136,6 +140,7 @@ set(SOURCES
internet/magnatuneservice.cpp
internet/magnatuneurlhandler.cpp
internet/savedradio.cpp
+ internet/skyfmservice.cpp
internet/somafmservice.cpp
internet/somafmurlhandler.cpp
@@ -346,6 +351,8 @@ set(HEADERS
engines/gstenginepipeline.h
engines/gstelementdeleter.h
+ internet/digitallyimportedconfig.h
+ internet/digitallyimportedservicebase.h
internet/icecastbackend.h
internet/icecastfilterwidget.h
internet/icecastmodel.h
@@ -361,6 +368,7 @@ set(HEADERS
internet/magnatunedownloaddialog.h
internet/magnatuneservice.h
internet/savedradio.h
+ internet/skyfmservice.h
internet/somafmservice.h
internet/somafmurlhandler.h
@@ -502,6 +510,7 @@ set(UI
devices/deviceproperties.ui
+ internet/digitallyimportedconfig.ui
internet/icecastfilterwidget.ui
internet/magnatuneconfig.ui
internet/magnatunedownloaddialog.ui
diff --git a/src/internet/digitallyimportedconfig.cpp b/src/internet/digitallyimportedconfig.cpp
new file mode 100644
index 000000000..7265a3c49
--- /dev/null
+++ b/src/internet/digitallyimportedconfig.cpp
@@ -0,0 +1,54 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 "digitallyimportedconfig.h"
+#include "digitallyimportedservicebase.h"
+#include "ui_digitallyimportedconfig.h"
+
+#include
+
+
+DigitallyImportedConfig::DigitallyImportedConfig(QWidget *parent)
+ : QWidget(parent),
+ ui_(new Ui_DigitallyImportedConfig)
+{
+ ui_->setupUi(this);
+}
+
+DigitallyImportedConfig::~DigitallyImportedConfig() {
+ delete ui_;
+}
+
+void DigitallyImportedConfig::Load() {
+ QSettings s;
+ s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
+
+ ui_->audio_type->setCurrentIndex(s.value("audio_type", 0).toInt());
+ ui_->username->setText(s.value("username").toString());
+ ui_->password->setText(s.value("password").toString());
+}
+
+void DigitallyImportedConfig::Save() {
+ QSettings s;
+ s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
+
+ s.setValue("audio_type", ui_->audio_type->currentIndex());
+ s.setValue("username", ui_->username->text());
+ s.setValue("password", ui_->password->text());
+}
+
+
diff --git a/src/internet/digitallyimportedconfig.h b/src/internet/digitallyimportedconfig.h
new file mode 100644
index 000000000..d4b27b247
--- /dev/null
+++ b/src/internet/digitallyimportedconfig.h
@@ -0,0 +1,40 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 DIGITALLYIMPORTEDCONFIG_H
+#define DIGITALLYIMPORTEDCONFIG_H
+
+#include
+
+class Ui_DigitallyImportedConfig;
+
+class DigitallyImportedConfig : public QWidget {
+ Q_OBJECT
+
+public:
+ DigitallyImportedConfig(QWidget* parent = 0);
+ ~DigitallyImportedConfig();
+
+public slots:
+ void Load();
+ void Save();
+
+private:
+ Ui_DigitallyImportedConfig* ui_;
+};
+
+#endif // DIGITALLYIMPORTEDCONFIG_H
diff --git a/src/internet/digitallyimportedconfig.ui b/src/internet/digitallyimportedconfig.ui
new file mode 100644
index 000000000..4e68c65af
--- /dev/null
+++ b/src/internet/digitallyimportedconfig.ui
@@ -0,0 +1,162 @@
+
+
+ DigitallyImportedConfig
+
+
+
+ 0
+ 0
+ 715
+ 425
+
+
+
+ Form
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Account details (Premium)
+
+
+
-
+
+
+ Digitally Imported username
+
+
+
+ -
+
+
+ -
+
+
+ Digitally Imported password
+
+
+
+ -
+
+
+ QLineEdit::Password
+
+
+
+ -
+
+
+ You can <b>listen for free</b> without an account, but Premium members can listen to <b>higher quality</b> streams without advertisements.
+
+
+ true
+
+
+
+ -
+
+
+ <a href="http://www.di.fm/premium/">Upgrade to Premium now</a>
+
+
+ true
+
+
+ Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Preferences
+
+
+
-
+
+
+ Audio type
+
+
+
+ -
+
+
-
+
+ MP3 96k
+
+
+ -
+
+ MP3 256k (Premium only)
+
+
+ -
+
+ AAC 32k
+
+
+ -
+
+ AAC 64k (Premium only)
+
+
+ -
+
+ AAC 128k (Premium only)
+
+
+ -
+
+ Windows Media 40k
+
+
+ -
+
+ Windows Media 64k (Premium only)
+
+
+ -
+
+ Windows Media 128k (Premium only)
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 166
+
+
+
+
+
+
+
+
+
diff --git a/src/internet/digitallyimportedservice.cpp b/src/internet/digitallyimportedservice.cpp
new file mode 100644
index 000000000..3f40f08ca
--- /dev/null
+++ b/src/internet/digitallyimportedservice.cpp
@@ -0,0 +1,64 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 "digitallyimportedservice.h"
+#include "core/logging.h"
+
+#include
+#include
+#include
+
+DigitallyImportedService::DigitallyImportedService(InternetModel* model, QObject* parent)
+ : DigitallyImportedServiceBase(
+ "DigitallyImported", "Digitally Imported", QUrl("http://www.di.fm"),
+ "di.fm", QUrl("http://listen.di.fm"), "digitallyimported",
+ QIcon(":/providers/digitallyimported.png"), model, parent)
+{
+ playlists_ = QList()
+ << Playlist(false, "http://listen.di.fm/public3/%1.pls")
+ << Playlist(true, "http://www.di.fm/listen/%1/premium.pls")
+ << Playlist(false, "http://listen.di.fm/public2/%1.pls")
+ << Playlist(true, "http://www.di.fm/listen/%1/64k.pls")
+ << Playlist(true, "http://www.di.fm/listen/%1/128k.pls")
+ << Playlist(false, "http://listen.di.fm/public5/%1.asx")
+ << Playlist(true, "http://www.di.fm/listen/%1/64k.asx")
+ << Playlist(true, "http://www.di.fm/listen/%1/128k.asx");
+}
+
+void DigitallyImportedService::ReloadSettings() {
+ DigitallyImportedServiceBase::ReloadSettings();
+
+ QNetworkCookieJar* cookies = new QNetworkCookieJar;
+
+ if (is_premium_account()) {
+ qLog(Debug) << "Setting premium account cookies";
+ cookies->setCookiesFromUrl(QList()
+ << QNetworkCookie("_amember_ru", username_.toUtf8())
+ << QNetworkCookie("_amember_rp", password_.toUtf8()),
+ QUrl("http://www.di.fm/"));
+ }
+
+ network_->setCookieJar(cookies);
+}
+
+void DigitallyImportedService::LoadStation(const QString& key) {
+ QUrl playlist_url(playlists_[audio_type_].url_template_.arg(key));
+ qLog(Debug) << "Getting playlist URL" << playlist_url;
+
+ QNetworkReply* reply = network_->get(QNetworkRequest(playlist_url));
+ connect(reply, SIGNAL(finished()), SLOT(LoadPlaylistFinished()));
+}
diff --git a/src/internet/digitallyimportedservice.h b/src/internet/digitallyimportedservice.h
new file mode 100644
index 000000000..6add8c5f7
--- /dev/null
+++ b/src/internet/digitallyimportedservice.h
@@ -0,0 +1,32 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 DIGITALLYIMPORTEDSERVICE_H
+#define DIGITALLYIMPORTEDSERVICE_H
+
+#include "digitallyimportedservicebase.h"
+
+class DigitallyImportedService : public DigitallyImportedServiceBase {
+public:
+ DigitallyImportedService(InternetModel* model, QObject* parent = NULL);
+
+ void ReloadSettings();
+
+ void LoadStation(const QString& key);
+};
+
+#endif // DIGITALLYIMPORTEDSERVICE_H
diff --git a/src/internet/digitallyimportedservicebase.cpp b/src/internet/digitallyimportedservicebase.cpp
new file mode 100644
index 000000000..f4487fb11
--- /dev/null
+++ b/src/internet/digitallyimportedservicebase.cpp
@@ -0,0 +1,221 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 "digitallyimportedservicebase.h"
+#include "digitallyimportedurlhandler.h"
+#include "internetmodel.h"
+#include "core/logging.h"
+#include "core/network.h"
+#include "core/player.h"
+#include "core/taskmanager.h"
+#include "ui/iconloader.h"
+
+#include
+#include
+#include
+#include
+
+const char* DigitallyImportedServiceBase::kSettingsGroup = "digitally_imported";
+
+
+DigitallyImportedServiceBase::DigitallyImportedServiceBase(
+ const QString& name, const QString& description, const QUrl& homepage_url,
+ const QString& homepage_name, const QUrl& stream_list_url,
+ const QString& url_scheme, const QIcon& icon,
+ InternetModel* model, QObject* parent)
+ : InternetService(name, model, parent),
+ network_(new NetworkAccessManager(this)),
+ url_handler_(new DigitallyImportedUrlHandler(this)),
+ audio_type_(0),
+ task_id_(-1),
+ homepage_url_(homepage_url),
+ homepage_name_(homepage_name),
+ stream_list_url_(stream_list_url),
+ icon_(icon),
+ service_description_(description),
+ url_scheme_(url_scheme),
+ root_(NULL),
+ context_menu_(NULL),
+ context_item_(NULL)
+{
+ model->player()->RegisterUrlHandler(url_handler_);
+}
+
+DigitallyImportedServiceBase::~DigitallyImportedServiceBase() {
+ delete context_menu_;
+}
+
+QStandardItem* DigitallyImportedServiceBase::CreateRootItem() {
+ root_ = new QStandardItem(icon_, service_description_);
+ root_->setData(true, InternetModel::Role_CanLazyLoad);
+ return root_;
+}
+
+void DigitallyImportedServiceBase::LazyPopulate(QStandardItem* parent) {
+ if (parent == root_) {
+ RefreshStreams();
+ }
+}
+
+void DigitallyImportedServiceBase::RefreshStreams() {
+ if (task_id_ != -1) {
+ return;
+ }
+
+ qLog(Info) << "Getting stream list from" << stream_list_url_;
+
+ // Get the list of streams
+ QNetworkReply* reply = network_->get(QNetworkRequest(stream_list_url_));
+ connect(reply, SIGNAL(finished()), SLOT(RefreshStreamsFinished()));
+
+ // Start a task to tell the user we're busy
+ task_id_ = model()->task_manager()->StartTask(tr("Getting streams"));
+}
+
+void DigitallyImportedServiceBase::RefreshStreamsFinished() {
+ QNetworkReply* reply = qobject_cast(sender());
+ if (!reply) {
+ return;
+ }
+
+ model()->task_manager()->SetTaskFinished(task_id_);
+ reply->deleteLater();
+
+ const QString data = QString::fromUtf8(reply->readAll());
+
+ // Poor man's JSON parser that's good enough for the stream lists and means
+ // we don't have to pull in QJSON as a dependency.
+ const QRegExp re("\\{"
+ "\"id\":(\\d+),"
+ "\"key\":\"([^\"]+)\","
+ "\"name\":\"([^\"]+)\","
+ "\"description\":\"([^\"]+)\"");
+
+ QList streams;
+
+ int pos = 0;
+ while (pos >= 0) {
+ pos = re.indexIn(data, pos);
+ if (pos == -1) {
+ break;
+ }
+ pos += re.matchedLength();
+
+ Stream stream;
+ stream.id_ = re.cap(1).toInt();
+ stream.key_ = re.cap(2).replace("\\/", "/");
+ stream.name_ = re.cap(3).replace("\\/", "/");
+ stream.description_ = re.cap(4).replace("\\/", "/");
+ streams << stream;
+ }
+
+ // Sort by name
+ qSort(streams);
+
+ // Add each stream to the model
+ foreach (const Stream& stream, streams) {
+ Song song;
+ song.set_title(stream.name_);
+ song.set_artist(service_description_);
+ song.set_url(QUrl(url_scheme_ + "://" + stream.key_));
+
+ QStandardItem* item = new QStandardItem(QIcon(":/last.fm/icon_radio.png"),
+ stream.name_);
+ item->setData(stream.description_, Qt::ToolTipRole);
+ item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour);
+ item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
+ root_->appendRow(item);
+ }
+}
+
+void DigitallyImportedServiceBase::Homepage() {
+ QDesktopServices::openUrl(homepage_url_);
+}
+
+void DigitallyImportedServiceBase::ReloadSettings() {
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+
+ audio_type_ = s.value("audio_type", 0).toInt();
+ username_ = s.value("username").toString();
+ password_ = s.value("password").toString();
+}
+
+void DigitallyImportedServiceBase::ShowContextMenu(
+ const QModelIndex& index, const QPoint& global_pos) {
+ if (!context_menu_) {
+ context_menu_ = new QMenu;
+ context_menu_->addActions(GetPlaylistActions());
+ context_menu_->addAction(IconLoader::Load("download"),
+ tr("Open %1 in browser").arg(homepage_name_),
+ this, SLOT(Homepage()));
+ context_menu_->addAction(IconLoader::Load("view-refresh"),
+ tr("Refresh streams"),
+ this, SLOT(RefreshStreams()));
+ context_menu_->addSeparator();
+ context_menu_->addAction(IconLoader::Load("configure"),
+ tr("Configure..."),
+ this, SLOT(ShowSettingsDialog()));
+ }
+
+ context_item_ = model()->itemFromIndex(index);
+ context_menu_->popup(global_pos);
+}
+
+QModelIndex DigitallyImportedServiceBase::GetCurrentIndex() {
+ return context_item_->index();
+}
+
+bool DigitallyImportedServiceBase::is_valid_stream_selected() const {
+ return audio_type_ >= 0 && audio_type_ < playlists_.count();
+}
+
+bool DigitallyImportedServiceBase::is_premium_account() const {
+ return !username_.isEmpty() && !password_.isEmpty();
+}
+
+bool DigitallyImportedServiceBase::is_premium_stream_selected() const {
+ if (!is_valid_stream_selected()) {
+ return false;
+ }
+
+ return playlists_[audio_type_].premium_;
+}
+
+void DigitallyImportedServiceBase::LoadPlaylistFinished() {
+ QNetworkReply* reply = qobject_cast(sender());
+ if (!reply) {
+ return;
+ }
+ reply->deleteLater();
+
+ if (reply->header(QNetworkRequest::ContentTypeHeader).toString() == "text/html") {
+ url_handler_->CancelTask();
+
+ if (is_premium_stream_selected()) {
+ emit StreamError(tr("Invalid di.fm username or password"));
+ } else {
+ emit StreamError(tr("Error loading di.fm playlist"));
+ }
+ } else {
+ url_handler_->LoadPlaylistFinished(reply);
+ }
+}
+
+void DigitallyImportedServiceBase::ShowSettingsDialog() {
+ emit OpenSettingsAtPage(SettingsDialog::Page_DigitallyImported);
+}
diff --git a/src/internet/digitallyimportedservicebase.h b/src/internet/digitallyimportedservicebase.h
new file mode 100644
index 000000000..9d1d559b5
--- /dev/null
+++ b/src/internet/digitallyimportedservicebase.h
@@ -0,0 +1,113 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 DIGITALLYIMPORTEDSERVICEBASE_H
+#define DIGITALLYIMPORTEDSERVICEBASE_H
+
+#include "internetservice.h"
+
+class DigitallyImportedUrlHandler;
+
+class QNetworkAccessManager;
+
+
+class DigitallyImportedServiceBase : public InternetService {
+ Q_OBJECT
+ friend class DigitallyImportedUrlHandler;
+
+public:
+ DigitallyImportedServiceBase(
+ const QString& name, const QString& description, const QUrl& homepage_url,
+ const QString& homepage_name, const QUrl& stream_list_url,
+ const QString& url_scheme, const QIcon& icon,
+ InternetModel* model, QObject* parent = NULL);
+ ~DigitallyImportedServiceBase();
+
+ static const char* kSettingsGroup;
+
+ QStandardItem* CreateRootItem();
+ void LazyPopulate(QStandardItem* parent);
+ void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
+
+ void ReloadSettings();
+
+ bool is_valid_stream_selected() const;
+ bool is_premium_stream_selected() const;
+ bool is_premium_account() const;
+
+protected:
+ struct Playlist {
+ Playlist(bool premium, const QString& url_template)
+ : premium_(premium), url_template_(url_template) {}
+
+ bool premium_;
+ QString url_template_;
+ };
+
+ QModelIndex GetCurrentIndex();
+
+ // Called by DigitallyImportedUrlHandler, implemented by subclasses, must
+ // call LoadPlaylistFinished eventually.
+ virtual void LoadStation(const QString& key) = 0;
+
+protected slots:
+ void LoadPlaylistFinished();
+
+private slots:
+ void Homepage();
+ void RefreshStreams();
+ void RefreshStreamsFinished();
+ void ShowSettingsDialog();
+
+protected:
+ QNetworkAccessManager* network_;
+ DigitallyImportedUrlHandler* url_handler_;
+
+ int audio_type_;
+ QString username_;
+ QString password_;
+
+ int task_id_;
+
+ QList playlists_;
+
+private:
+ struct Stream {
+ int id_;
+ QString key_;
+ QString name_;
+ QString description_;
+
+ bool operator <(const Stream& other) const { return name_ < other.name_; }
+ };
+
+private:
+ // Set by subclasses through the constructor
+ QUrl homepage_url_;
+ QString homepage_name_;
+ QUrl stream_list_url_;
+ QIcon icon_;
+ QString service_description_;
+ QString url_scheme_;
+
+ QStandardItem* root_;
+
+ QMenu* context_menu_;
+ QStandardItem* context_item_;
+};
+
+#endif // DIGITALLYIMPORTEDSERVICEBASE_H
diff --git a/src/internet/digitallyimportedurlhandler.cpp b/src/internet/digitallyimportedurlhandler.cpp
new file mode 100644
index 000000000..cd7045879
--- /dev/null
+++ b/src/internet/digitallyimportedurlhandler.cpp
@@ -0,0 +1,96 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 "digitallyimportedservicebase.h"
+#include "digitallyimportedurlhandler.h"
+#include "internetmodel.h"
+#include "core/logging.h"
+#include "core/taskmanager.h"
+#include "playlistparsers/playlistparser.h"
+
+DigitallyImportedUrlHandler::DigitallyImportedUrlHandler(DigitallyImportedServiceBase* service)
+ : UrlHandler(service),
+ service_(service),
+ task_id_(-1)
+{
+}
+
+QString DigitallyImportedUrlHandler::scheme() const {
+ return service_->url_scheme_;
+}
+
+UrlHandler_LoadResult DigitallyImportedUrlHandler::StartLoading(const QUrl& url) {
+ UrlHandler_LoadResult ret(url);
+ if (task_id_ != -1) {
+ return ret;
+ }
+
+ if (!service_->is_valid_stream_selected()) {
+ service_->StreamError(tr("You have selected an invalid audio type setting"));
+ return ret;
+ }
+
+ if (service_->is_premium_stream_selected() && !service_->is_premium_account()) {
+ service_->StreamError(tr("You have selected a Premium-only audio type but do not have any account details entered"));
+ return ret;
+ }
+
+ // Start loading the station
+ const QString key = url.host();
+ qLog(Info) << "Loading station" << key;
+ service_->LoadStation(key);
+
+ // Save the URL so we can emit it in the finished signal later
+ last_original_url_ = url;
+
+ // Tell the user what's happening
+ task_id_ = service_->model()->task_manager()->StartTask(tr("Loading stream"));
+
+ ret.type_ = UrlHandler_LoadResult::WillLoadAsynchronously;
+ return ret;
+}
+
+void DigitallyImportedUrlHandler::LoadPlaylistFinished(QIODevice* device) {
+ if (task_id_ == -1) {
+ return;
+ }
+
+ // Stop the spinner in the status bar
+ CancelTask();
+
+ // Try to parse the playlist
+ PlaylistParser parser(NULL);
+ QList songs = parser.LoadFromDevice(device);
+
+ qLog(Info) << "Loading station finished, got" << songs.count() << "songs";
+
+ // Failed to get playlist?
+ if (songs.count() == 0) {
+ service_->StreamError(tr("Error loading di.fm playlist"));
+ return;
+ }
+
+ emit AsyncLoadComplete(UrlHandler_LoadResult(
+ last_original_url_,
+ UrlHandler_LoadResult::TrackAvailable,
+ songs[0].url()));
+}
+
+void DigitallyImportedUrlHandler::CancelTask() {
+ service_->model()->task_manager()->SetTaskFinished(task_id_);
+ task_id_ = -1;
+}
diff --git a/src/internet/digitallyimportedurlhandler.h b/src/internet/digitallyimportedurlhandler.h
new file mode 100644
index 000000000..32d8a0437
--- /dev/null
+++ b/src/internet/digitallyimportedurlhandler.h
@@ -0,0 +1,43 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 DIGITALLYIMPORTEDURLHANDLER_H
+#define DIGITALLYIMPORTEDURLHANDLER_H
+
+#include "core/urlhandler.h"
+
+class DigitallyImportedServiceBase;
+
+
+class DigitallyImportedUrlHandler : public UrlHandler {
+public:
+ DigitallyImportedUrlHandler(DigitallyImportedServiceBase* service);
+
+ QString scheme() const;
+ UrlHandler_LoadResult StartLoading(const QUrl& url);
+
+ void CancelTask();
+ void LoadPlaylistFinished(QIODevice* device);
+
+private:
+ DigitallyImportedServiceBase* service_;
+ int task_id_;
+
+ QUrl last_original_url_;
+};
+
+#endif // DIGITALLYIMPORTEDURLHANDLER_H
diff --git a/src/internet/internetmodel.cpp b/src/internet/internetmodel.cpp
index ca1c0924f..c13f2eeed 100644
--- a/src/internet/internetmodel.cpp
+++ b/src/internet/internetmodel.cpp
@@ -15,6 +15,7 @@
along with Clementine. If not, see .
*/
+#include "digitallyimportedservice.h"
#include "icecastservice.h"
#include "jamendoservice.h"
#include "magnatuneservice.h"
@@ -22,6 +23,7 @@
#include "internetmodel.h"
#include "internetservice.h"
#include "savedradio.h"
+#include "skyfmservice.h"
#include "somafmservice.h"
#include "core/logging.h"
#include "core/mergedproxymodel.h"
@@ -54,17 +56,19 @@ InternetModel::InternetModel(BackgroundThread* db_thread,
merged_model_->setSourceModel(this);
+ AddService(new DigitallyImportedService(this));
+ AddService(new IcecastService(this));
+ AddService(new JamendoService(this));
#ifdef HAVE_LIBLASTFM
AddService(new LastFMService(this));
#endif
-#ifdef HAVE_SPOTIFY
- AddService(new SpotifyService(task_manager, this));
-#endif
- AddService(new SomaFMService(this));
AddService(new MagnatuneService(this));
- AddService(new JamendoService(this));
- AddService(new IcecastService(this));
AddService(new SavedRadio(this));
+ AddService(new SkyFmService(this));
+ AddService(new SomaFMService(this));
+#ifdef HAVE_SPOTIFY
+ AddService(new SpotifyService(this));
+#endif
}
void InternetModel::AddService(InternetService *service) {
@@ -86,6 +90,8 @@ void InternetModel::AddService(InternetService *service) {
connect(service, SIGNAL(OpenSettingsAtPage(SettingsDialog::Page)), SIGNAL(OpenSettingsAtPage(SettingsDialog::Page)));
connect(service, SIGNAL(AddToPlaylistSignal(QMimeData*)), SIGNAL(AddToPlaylist(QMimeData*)));
connect(service, SIGNAL(destroyed()), SLOT(ServiceDeleted()));
+
+ service->ReloadSettings();
}
void InternetModel::RemoveService(InternetService* service) {
diff --git a/src/internet/lastfmservice.cpp b/src/internet/lastfmservice.cpp
index d5826c5a1..9e8a43a48 100644
--- a/src/internet/lastfmservice.cpp
+++ b/src/internet/lastfmservice.cpp
@@ -84,7 +84,6 @@ LastFMService::LastFMService(InternetModel* parent)
neighbours_list_(NULL),
connection_problems_(false)
{
- ReloadSettings();
//we emit the signal the first time to be sure the buttons are in the right state
emit ScrobblingEnabledChanged(scrobbling_enabled_);
diff --git a/src/internet/magnatuneservice.cpp b/src/internet/magnatuneservice.cpp
index 80a527a7d..8a60931a2 100644
--- a/src/internet/magnatuneservice.cpp
+++ b/src/internet/magnatuneservice.cpp
@@ -76,8 +76,6 @@ MagnatuneService::MagnatuneService(InternetModel* parent)
total_song_count_(0),
network_(new NetworkAccessManager(this))
{
- ReloadSettings();
-
// Create the library backend in the database thread
library_backend_ = new LibraryBackend;
library_backend_->moveToThread(parent->db_thread());
diff --git a/src/internet/skyfmservice.cpp b/src/internet/skyfmservice.cpp
new file mode 100644
index 000000000..f7fdaae56
--- /dev/null
+++ b/src/internet/skyfmservice.cpp
@@ -0,0 +1,91 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 "digitallyimportedurlhandler.h"
+#include "internetmodel.h"
+#include "skyfmservice.h"
+#include "core/taskmanager.h"
+
+#include
+#include
+
+SkyFmService::SkyFmService(InternetModel* model, QObject* parent)
+ : DigitallyImportedServiceBase(
+ "SKY.fm", "SKY.fm", QUrl("http://www.sky.fm"), "sky.fm",
+ QUrl("http://listen.sky.fm"), "skyfm", QIcon(":/providers/skyfm.png"),
+ model, parent)
+{
+ playlists_ = QList()
+ << Playlist(false, "http://listen.sky.fm/public3/%1.pls")
+ << Playlist(true, "http://listen.sky.fm/premium_high/%1.pls?hash=%2")
+ << Playlist(false, "http://listen.sky.fm/public1/%1.pls")
+ << Playlist(true, "http://listen.sky.fm/premium_medium/%1.pls?hash=%2")
+ << Playlist(true, "http://listen.sky.fm/premium/%1.pls?hash=%2")
+ << Playlist(false, "http://listen.sky.fm/public5/%1.asx")
+ << Playlist(true, "http://listen.sky.fm/premium_wma_low/%1.asx?hash=%2")
+ << Playlist(true, "http://listen.sky.fm/premium_wma/%1.asx?hash=%2");
+}
+
+void SkyFmService::LoadStation(const QString& key) {
+ if (!is_premium_stream_selected()) {
+ // Non-premium streams can just start loading straight away
+ LoadPlaylist(key);
+ return;
+ }
+
+ // Otherwise we have to get the user's hashKey
+ QNetworkRequest req(QUrl("http://www.sky.fm/configure_player.php"));
+ QByteArray postdata = "amember_login=" + QUrl::toPercentEncoding(username_) +
+ "&amember_pass=" + QUrl::toPercentEncoding(password_);
+
+ QNetworkReply* reply = network_->post(req, postdata);
+ connect(reply, SIGNAL(finished()), SLOT(LoadHashKeyFinished()));
+
+ last_key_ = key;
+}
+
+void SkyFmService::LoadHashKeyFinished() {
+ QNetworkReply* reply = qobject_cast(sender());
+ if (!reply) {
+ return;
+ }
+
+ const QString page_data = QString::fromUtf8(reply->readAll().data());
+ QRegExp re("hashKey\\s*=\\s*'([0-9a-f]+)'");
+
+ if (re.indexIn(page_data) == -1) {
+ url_handler_->CancelTask();
+ emit StreamError(tr("Invalid SKY.fm username or password"));
+ return;
+ }
+
+ LoadPlaylist(last_key_, re.cap(1));
+}
+
+void SkyFmService::LoadPlaylist(const QString& key, const QString& hash_key) {
+ QString url_template = playlists_[audio_type_].url_template_;
+ QUrl url;
+
+ if (hash_key.isEmpty()) {
+ url = QUrl(url_template.arg(key));
+ } else {
+ url = QUrl(url_template.arg(key, hash_key));
+ }
+
+ QNetworkReply* reply = network_->get(QNetworkRequest(url));
+ connect(reply, SIGNAL(finished()), SLOT(LoadPlaylistFinished()));
+}
diff --git a/src/internet/skyfmservice.h b/src/internet/skyfmservice.h
new file mode 100644
index 000000000..034b21e5d
--- /dev/null
+++ b/src/internet/skyfmservice.h
@@ -0,0 +1,41 @@
+/* This file is part of Clementine.
+ Copyright 2010, 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 SKYFMSERVICE_H
+#define SKYFMSERVICE_H
+
+#include "digitallyimportedservicebase.h"
+
+class SkyFmService : public DigitallyImportedServiceBase {
+ Q_OBJECT
+
+public:
+ SkyFmService(InternetModel* model, QObject* parent = NULL);
+
+ void LoadStation(const QString& key);
+
+private:
+ void LoadPlaylist(const QString& key, const QString& hash_key = QString());
+
+private slots:
+ void LoadHashKeyFinished();
+
+private:
+ QString last_key_;
+};
+
+#endif // SKYFMSERVICE_H
diff --git a/src/internet/spotifyservice.cpp b/src/internet/spotifyservice.cpp
index 6356d9414..3af3ef0fb 100644
--- a/src/internet/spotifyservice.cpp
+++ b/src/internet/spotifyservice.cpp
@@ -34,7 +34,7 @@ const char* SpotifyService::kSettingsGroup = "Spotify";
const char* SpotifyService::kBlobDownloadUrl = "http://spotify.clementine-player.org/";
const int SpotifyService::kSearchDelayMsec = 400;
-SpotifyService::SpotifyService(TaskManager* task_manager, InternetModel* parent)
+SpotifyService::SpotifyService(InternetModel* parent)
: InternetService(kServiceName, parent, parent),
server_(NULL),
url_handler_(new SpotifyUrlHandler(this, this)),
@@ -46,8 +46,7 @@ SpotifyService::SpotifyService(TaskManager* task_manager, InternetModel* parent)
login_task_id_(0),
pending_search_playlist_(NULL),
context_menu_(NULL),
- search_delay_(new QTimer(this)),
- task_manager_(task_manager) {
+ search_delay_(new QTimer(this)) {
// Build the search path for the binary blob.
// Look for one distributed alongside clementine first, then check in the
// user's home directory for any that have been downloaded.
@@ -441,16 +440,16 @@ void SpotifyService::SyncPlaylist() {
int index = item->data(Role_UserPlaylistIndex).toInt();
server_->SyncUserPlaylist(index);
playlist_sync_ids_[index] =
- task_manager_->StartTask(tr("Syncing Spotify playlist"));
+ model()->task_manager()->StartTask(tr("Syncing Spotify playlist"));
break;
}
case Type_InboxPlaylist:
server_->SyncInbox();
- inbox_sync_id_ = task_manager_->StartTask(tr("Syncing Spotify inbox"));
+ inbox_sync_id_ = model()->task_manager()->StartTask(tr("Syncing Spotify inbox"));
break;
case Type_StarredPlaylist:
server_->SyncStarred();
- starred_sync_id_ = task_manager_->StartTask(tr("Syncing Spotify starred tracks"));
+ starred_sync_id_ = model()->task_manager()->StartTask(tr("Syncing Spotify starred tracks"));
break;
default:
break;
@@ -582,9 +581,9 @@ void SpotifyService::SyncPlaylistProgress(
qLog(Warning) << "Received sync progress for unknown playlist";
return;
}
- task_manager_->SetTaskProgress(task_id, progress.sync_progress(), 100);
+ model()->task_manager()->SetTaskProgress(task_id, progress.sync_progress(), 100);
if (progress.sync_progress() == 100) {
- task_manager_->SetTaskFinished(task_id);
+ model()->task_manager()->SetTaskFinished(task_id);
if (progress.request().type() == protobuf::UserPlaylist) {
playlist_sync_ids_.remove(task_id);
}
diff --git a/src/internet/spotifyservice.h b/src/internet/spotifyservice.h
index c867b90da..d33bf0b5a 100644
--- a/src/internet/spotifyservice.h
+++ b/src/internet/spotifyservice.h
@@ -20,7 +20,7 @@ class SpotifyService : public InternetService {
Q_OBJECT
public:
- SpotifyService(TaskManager* task_manager, InternetModel* parent);
+ SpotifyService(InternetModel* parent);
~SpotifyService();
enum Type {
@@ -119,7 +119,6 @@ private:
QTimer* search_delay_;
- TaskManager* task_manager_;
int inbox_sync_id_;
int starred_sync_id_;
QMap playlist_sync_ids_;
diff --git a/src/ui/settingsdialog.cpp b/src/ui/settingsdialog.cpp
index 4aaef2c77..47254b20c 100644
--- a/src/ui/settingsdialog.cpp
+++ b/src/ui/settingsdialog.cpp
@@ -468,6 +468,7 @@ void SettingsDialog::accept() {
ui_->library_config->Save();
ui_->magnatune->Save();
+ ui_->digitally_imported->Save();
ui_->global_shortcuts->Save();
streams_->SaveStreams();
@@ -558,6 +559,9 @@ void SettingsDialog::showEvent(QShowEvent*) {
// Magnatune
ui_->magnatune->Load();
+ // Digitally Imported
+ ui_->digitally_imported->Load();
+
// Global Shortcuts
ui_->global_shortcuts->Load();
diff --git a/src/ui/settingsdialog.h b/src/ui/settingsdialog.h
index 509e90982..570f08ce8 100644
--- a/src/ui/settingsdialog.h
+++ b/src/ui/settingsdialog.h
@@ -72,6 +72,7 @@ class SettingsDialog : public QDialog {
Page_Spotify,
#endif
Page_Magnatune,
+ Page_DigitallyImported,
Page_BackgroundStreams,
Page_Proxy,
Page_Transcoding,
diff --git a/src/ui/settingsdialog.ui b/src/ui/settingsdialog.ui
index fd33e16b8..687ba3639 100644
--- a/src/ui/settingsdialog.ui
+++ b/src/ui/settingsdialog.ui
@@ -93,6 +93,15 @@
:/providers/magnatune.png:/providers/magnatune.png
+ -
+
+ Digitally Imported
+
+
+
+ :/providers/digitallyimported-32.png:/providers/digitallyimported-32.png
+
+
-
Background Streams
@@ -125,7 +134,7 @@
-
- 1
+ 7
@@ -1095,6 +1104,19 @@
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
-
@@ -1564,6 +1586,12 @@
transcoder/transcoderoptionswma.h
1
+
+ DigitallyImportedConfig
+ QWidget
+ internet/digitallyimportedconfig.h
+ 1
+
list