Add c++ implementations of the di.fm and sky.fm radio services

This commit is contained in:
David Sansome 2011-07-21 00:03:55 +01:00
parent b6ee23dc43
commit a0258a9e81
24 changed files with 1023 additions and 20 deletions

View File

@ -340,5 +340,8 @@
<file>pythonlibs/uic/uiparser.py</file>
<file>pythonlibs/clementinelogging.py</file>
<file>nyancat.png</file>
<file>providers/digitallyimported.png</file>
<file>providers/skyfm.png</file>
<file>providers/digitallyimported-32.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

BIN
data/providers/skyfm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

View File

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

View File

@ -0,0 +1,54 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "digitallyimportedconfig.h"
#include "digitallyimportedservicebase.h"
#include "ui_digitallyimportedconfig.h"
#include <QSettings>
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());
}

View File

@ -0,0 +1,40 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef DIGITALLYIMPORTEDCONFIG_H
#define DIGITALLYIMPORTEDCONFIG_H
#include <QWidget>
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

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DigitallyImportedConfig</class>
<widget class="QWidget" name="DigitallyImportedConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>425</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Account details (Premium)</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Digitally Imported username</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Digitally Imported password</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_24">
<property name="text">
<string>You can &lt;b&gt;listen for free&lt;/b&gt; without an account, but Premium members can listen to &lt;b&gt;higher quality&lt;/b&gt; streams without advertisements.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="upgrade_link">
<property name="text">
<string>&lt;a href=&quot;http://www.di.fm/premium/&quot;&gt;Upgrade to Premium now&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Preferences</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Audio type</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="audio_type">
<item>
<property name="text">
<string>MP3 96k</string>
</property>
</item>
<item>
<property name="text">
<string>MP3 256k (Premium only)</string>
</property>
</item>
<item>
<property name="text">
<string>AAC 32k</string>
</property>
</item>
<item>
<property name="text">
<string>AAC 64k (Premium only)</string>
</property>
</item>
<item>
<property name="text">
<string>AAC 128k (Premium only)</string>
</property>
</item>
<item>
<property name="text">
<string>Windows Media 40k</string>
</property>
</item>
<item>
<property name="text">
<string>Windows Media 64k (Premium only)</string>
</property>
</item>
<item>
<property name="text">
<string>Windows Media 128k (Premium only)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>166</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,64 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "digitallyimportedservice.h"
#include "core/logging.h"
#include <QNetworkAccessManager>
#include <QNetworkCookieJar>
#include <QNetworkReply>
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>()
<< 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>()
<< 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()));
}

View File

@ -0,0 +1,32 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -0,0 +1,221 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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 <QDesktopServices>
#include <QMenu>
#include <QNetworkReply>
#include <QSettings>
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<QNetworkReply*>(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<Stream> 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<QNetworkReply*>(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);
}

View File

@ -0,0 +1,113 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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<Playlist> 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

View File

@ -0,0 +1,96 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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<Song> 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;
}

View File

@ -0,0 +1,43 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -15,6 +15,7 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#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<Database>* 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) {

View File

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

View File

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

View File

@ -0,0 +1,91 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "digitallyimportedurlhandler.h"
#include "internetmodel.h"
#include "skyfmservice.h"
#include "core/taskmanager.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
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>()
<< 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<QNetworkReply*>(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()));
}

View File

@ -0,0 +1,41 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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

View File

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

View File

@ -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<int, int> playlist_sync_ids_;

View File

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

View File

@ -72,6 +72,7 @@ class SettingsDialog : public QDialog {
Page_Spotify,
#endif
Page_Magnatune,
Page_DigitallyImported,
Page_BackgroundStreams,
Page_Proxy,
Page_Transcoding,

View File

@ -93,6 +93,15 @@
<normaloff>:/providers/magnatune.png</normaloff>:/providers/magnatune.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Digitally Imported</string>
</property>
<property name="icon">
<iconset resource="../../data/data.qrc">
<normaloff>:/providers/digitallyimported-32.png</normaloff>:/providers/digitallyimported-32.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Background Streams</string>
@ -125,7 +134,7 @@
<item>
<widget class="QStackedWidget" name="stacked_widget">
<property name="currentIndex">
<number>1</number>
<number>7</number>
</property>
<widget class="QWidget" name="playback_page">
<layout class="QVBoxLayout" name="verticalLayout_10">
@ -1095,6 +1104,19 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="digitally_imported_page">
<layout class="QVBoxLayout" name="verticalLayout_27">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="DigitallyImportedConfig" name="digitally_imported" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="streams_page">
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
@ -1564,6 +1586,12 @@
<header>transcoder/transcoderoptionswma.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>DigitallyImportedConfig</class>
<extends>QWidget</extends>
<header>internet/digitallyimportedconfig.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>list</tabstop>