Use an undocumented sky.fm/di.fm api to handle premium account logins, allowing us to remove dodgy code to scrape login information for each service individually
This commit is contained in:
parent
8e314ef4d8
commit
63140f83cf
@ -132,6 +132,7 @@ set(SOURCES
|
|||||||
globalsearch/tooltipactionwidget.cpp
|
globalsearch/tooltipactionwidget.cpp
|
||||||
globalsearch/tooltipresultwidget.cpp
|
globalsearch/tooltipresultwidget.cpp
|
||||||
|
|
||||||
|
internet/digitallyimportedclient.cpp
|
||||||
internet/digitallyimportedservice.cpp
|
internet/digitallyimportedservice.cpp
|
||||||
internet/digitallyimportedservicebase.cpp
|
internet/digitallyimportedservicebase.cpp
|
||||||
internet/digitallyimportedsettingspage.cpp
|
internet/digitallyimportedsettingspage.cpp
|
||||||
@ -381,6 +382,7 @@ set(HEADERS
|
|||||||
globalsearch/tooltipactionwidget.h
|
globalsearch/tooltipactionwidget.h
|
||||||
globalsearch/tooltipresultwidget.h
|
globalsearch/tooltipresultwidget.h
|
||||||
|
|
||||||
|
internet/digitallyimportedclient.h
|
||||||
internet/digitallyimportedservicebase.h
|
internet/digitallyimportedservicebase.h
|
||||||
internet/digitallyimportedsettingspage.h
|
internet/digitallyimportedsettingspage.h
|
||||||
internet/groovesharksearchplaylisttype.h
|
internet/groovesharksearchplaylisttype.h
|
||||||
|
85
src/internet/digitallyimportedclient.cpp
Normal file
85
src/internet/digitallyimportedclient.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2011, 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 "digitallyimportedclient.h"
|
||||||
|
#include "core/network.h"
|
||||||
|
|
||||||
|
#include <qjson/parser.h>
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
// The API used here is undocumented - it was reverse engineered by watching
|
||||||
|
// calls made by the sky.fm android app:
|
||||||
|
// https://market.android.com/details?id=com.audioaddict.sky
|
||||||
|
|
||||||
|
const char* DigitallyImportedClient::kApiUsername = "ephemeron";
|
||||||
|
const char* DigitallyImportedClient::kApiPassword = "dayeiph0ne@pp";
|
||||||
|
|
||||||
|
const char* DigitallyImportedClient::kAuthUrl =
|
||||||
|
"http://api.audioaddict.com/%1/premium/auth";
|
||||||
|
|
||||||
|
|
||||||
|
DigitallyImportedClient::DigitallyImportedClient(const QString& service_name, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
network_(new NetworkAccessManager(this)),
|
||||||
|
service_name_(service_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply* DigitallyImportedClient::Auth(const QString& username,
|
||||||
|
const QString& password) {
|
||||||
|
QNetworkRequest req(QUrl(QString(kAuthUrl).arg(service_name_)));
|
||||||
|
req.setRawHeader("Authorization",
|
||||||
|
"Basic " + QString("%1:%2").arg(kApiUsername, kApiPassword)
|
||||||
|
.toAscii().toBase64());
|
||||||
|
|
||||||
|
QByteArray postdata = "username=" + QUrl::toPercentEncoding(username) +
|
||||||
|
"&password=" + QUrl::toPercentEncoding(password);
|
||||||
|
|
||||||
|
return network_->post(req, postdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
DigitallyImportedClient::AuthReply
|
||||||
|
DigitallyImportedClient::ParseAuthReply(QNetworkReply* reply) const {
|
||||||
|
AuthReply ret;
|
||||||
|
ret.success_ = false;
|
||||||
|
ret.error_reason_ = tr("Unknown error");
|
||||||
|
|
||||||
|
QJson::Parser parser;
|
||||||
|
QVariantMap data = parser.parse(reply).toMap();
|
||||||
|
|
||||||
|
if (data.value("status", QString()).toString() != "OK" ||
|
||||||
|
!data.contains("user")) {
|
||||||
|
ret.error_reason_ = data.value("reason", ret.error_reason_).toString();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap user = data["user"].toMap();
|
||||||
|
if (!user.contains("first_name") ||
|
||||||
|
!user.contains("last_name") ||
|
||||||
|
!user.contains("expires") ||
|
||||||
|
!user.contains("listen_hash"))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret.success_ = true;
|
||||||
|
ret.first_name_ = user["first_name"].toString();
|
||||||
|
ret.last_name_ = user["last_name"].toString();
|
||||||
|
ret.expires_ = QDateTime::fromString(user["expires"].toString(), Qt::ISODate);
|
||||||
|
ret.listen_hash_ = user["listen_hash"].toString();
|
||||||
|
return ret;
|
||||||
|
}
|
59
src/internet/digitallyimportedclient.h
Normal file
59
src/internet/digitallyimportedclient.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2011, 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 DIGITALLYIMPORTEDCLIENT_H
|
||||||
|
#define DIGITALLYIMPORTEDCLIENT_H
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class DigitallyImportedClient : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DigitallyImportedClient(const QString& service_name, QObject* parent = 0);
|
||||||
|
|
||||||
|
static const char* kApiUsername;
|
||||||
|
static const char* kApiPassword;
|
||||||
|
static const char* kAuthUrl;
|
||||||
|
|
||||||
|
struct AuthReply {
|
||||||
|
bool success_;
|
||||||
|
|
||||||
|
// Set if success_ == false
|
||||||
|
QString error_reason_;
|
||||||
|
|
||||||
|
// Set if success_ == true
|
||||||
|
QString first_name_;
|
||||||
|
QString last_name_;
|
||||||
|
QDateTime expires_;
|
||||||
|
QString listen_hash_;
|
||||||
|
};
|
||||||
|
|
||||||
|
QNetworkReply* Auth(const QString& username, const QString& password);
|
||||||
|
AuthReply ParseAuthReply(QNetworkReply* reply) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager* network_;
|
||||||
|
|
||||||
|
QString service_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIGITALLYIMPORTEDCLIENT_H
|
@ -23,42 +23,10 @@
|
|||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
|
||||||
DigitallyImportedService::DigitallyImportedService(InternetModel* model, QObject* parent)
|
DigitallyImportedService::DigitallyImportedService(InternetModel* model, QObject* parent)
|
||||||
: DigitallyImportedServiceBase(
|
: DigitallyImportedServiceBase("DigitallyImported", model, parent)
|
||||||
"DigitallyImported", "Digitally Imported", QUrl("http://www.di.fm"),
|
|
||||||
"di.fm", QUrl("http://listen.di.fm"), "digitallyimported",
|
|
||||||
":/providers/digitallyimported.png", model, parent)
|
|
||||||
{
|
{
|
||||||
playlists_ = QList<Playlist>()
|
Init("Digitally Imported", QUrl("http://www.di.fm"),
|
||||||
<< Playlist(false, "http://listen.di.fm/public3/%1.pls")
|
"di.fm", QUrl("http://listen.di.fm"), "digitallyimported",
|
||||||
<< Playlist(true, "http://www.di.fm/listen/%1/premium.pls")
|
":/providers/digitallyimported.png", "di");
|
||||||
<< 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()));
|
|
||||||
}
|
|
||||||
|
@ -23,10 +23,6 @@
|
|||||||
class DigitallyImportedService : public DigitallyImportedServiceBase {
|
class DigitallyImportedService : public DigitallyImportedServiceBase {
|
||||||
public:
|
public:
|
||||||
DigitallyImportedService(InternetModel* model, QObject* parent = NULL);
|
DigitallyImportedService(InternetModel* model, QObject* parent = NULL);
|
||||||
|
|
||||||
void ReloadSettings();
|
|
||||||
|
|
||||||
void LoadStation(const QString& key);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DIGITALLYIMPORTEDSERVICE_H
|
#endif // DIGITALLYIMPORTEDSERVICE_H
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "digitallyimportedclient.h"
|
||||||
#include "digitallyimportedservicebase.h"
|
#include "digitallyimportedservicebase.h"
|
||||||
#include "digitallyimportedurlhandler.h"
|
#include "digitallyimportedurlhandler.h"
|
||||||
#include "internetmodel.h"
|
#include "internetmodel.h"
|
||||||
@ -37,29 +38,48 @@ const int DigitallyImportedServiceBase::kStreamsCacheDurationSecs =
|
|||||||
|
|
||||||
|
|
||||||
DigitallyImportedServiceBase::DigitallyImportedServiceBase(
|
DigitallyImportedServiceBase::DigitallyImportedServiceBase(
|
||||||
const QString& name, const QString& description, const QUrl& homepage_url,
|
const QString& name, InternetModel* model, QObject* parent)
|
||||||
const QString& homepage_name, const QUrl& stream_list_url,
|
|
||||||
const QString& url_scheme, const QString& icon_path,
|
|
||||||
InternetModel* model, QObject* parent)
|
|
||||||
: InternetService(name, model, parent),
|
: InternetService(name, model, parent),
|
||||||
network_(new NetworkAccessManager(this)),
|
network_(new NetworkAccessManager(this)),
|
||||||
url_handler_(new DigitallyImportedUrlHandler(this)),
|
url_handler_(new DigitallyImportedUrlHandler(this)),
|
||||||
audio_type_(0),
|
basic_audio_type_(1),
|
||||||
|
premium_audio_type_(2),
|
||||||
task_id_(-1),
|
task_id_(-1),
|
||||||
homepage_url_(homepage_url),
|
|
||||||
homepage_name_(homepage_name),
|
|
||||||
stream_list_url_(stream_list_url),
|
|
||||||
icon_path_(icon_path),
|
|
||||||
icon_(icon_path),
|
|
||||||
service_description_(description),
|
|
||||||
url_scheme_(url_scheme),
|
|
||||||
root_(NULL),
|
root_(NULL),
|
||||||
context_menu_(NULL),
|
context_menu_(NULL),
|
||||||
context_item_(NULL)
|
context_item_(NULL),
|
||||||
|
api_client_(NULL)
|
||||||
{
|
{
|
||||||
model->player()->RegisterUrlHandler(url_handler_);
|
basic_playlists_
|
||||||
|
<< "http://listen.%1/public3/%2.pls"
|
||||||
|
<< "http://listen.%1/public1/%2.pls"
|
||||||
|
<< "http://listen.%1/public5/%2.asx";
|
||||||
|
|
||||||
model->global_search()->AddProvider(new DigitallyImportedSearchProvider(this, this));
|
premium_playlists_
|
||||||
|
<< "http://listen.%1/premium_high/%3.pls?hash=%3"
|
||||||
|
<< "http://listen.%1/premium_medium/%2.pls?hash=%3"
|
||||||
|
<< "http://listen.%1/premium/%2.pls?hash=%3"
|
||||||
|
<< "http://listen.%1/premium_wma_low/%2.asx?hash=%3"
|
||||||
|
<< "http://listen.%1/premium_wma/%2.asx?hash=%3";
|
||||||
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedServiceBase::Init(
|
||||||
|
const QString& description, const QUrl& homepage_url,
|
||||||
|
const QString& homepage_name, const QUrl& stream_list_url,
|
||||||
|
const QString& url_scheme, const QString& icon_path,
|
||||||
|
const QString& api_service_name) {
|
||||||
|
service_description_ = description;
|
||||||
|
homepage_url_ = homepage_url;
|
||||||
|
homepage_name_ = homepage_name;
|
||||||
|
stream_list_url_ = stream_list_url;
|
||||||
|
url_scheme_ = url_scheme;
|
||||||
|
icon_path_ = icon_path;
|
||||||
|
api_service_name_ = api_service_name;
|
||||||
|
|
||||||
|
model()->player()->RegisterUrlHandler(url_handler_);
|
||||||
|
model()->global_search()->AddProvider(new DigitallyImportedSearchProvider(this, this));
|
||||||
|
|
||||||
|
api_client_ = new DigitallyImportedClient(api_service_name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
DigitallyImportedServiceBase::~DigitallyImportedServiceBase() {
|
DigitallyImportedServiceBase::~DigitallyImportedServiceBase() {
|
||||||
@ -176,9 +196,10 @@ void DigitallyImportedServiceBase::ReloadSettings() {
|
|||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
audio_type_ = s.value("audio_type", 0).toInt();
|
basic_audio_type_ = s.value("basic_audio_type", 1).toInt();
|
||||||
|
premium_audio_type_ = s.value("premium_audio_type", 2).toInt();
|
||||||
username_ = s.value("username").toString();
|
username_ = s.value("username").toString();
|
||||||
password_ = s.value("password").toString();
|
listen_hash_ = s.value("listen_hash").toString();
|
||||||
last_refreshed_streams_ = s.value("last_refreshed_" + url_scheme_).toDateTime();
|
last_refreshed_streams_ = s.value("last_refreshed_" + url_scheme_).toDateTime();
|
||||||
saved_streams_ = LoadStreams();
|
saved_streams_ = LoadStreams();
|
||||||
}
|
}
|
||||||
@ -204,24 +225,8 @@ void DigitallyImportedServiceBase::ShowContextMenu(
|
|||||||
context_menu_->popup(global_pos);
|
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 {
|
bool DigitallyImportedServiceBase::is_premium_account() const {
|
||||||
return !username_.isEmpty() && !password_.isEmpty();
|
return !listen_hash_.isEmpty();
|
||||||
}
|
|
||||||
|
|
||||||
bool DigitallyImportedServiceBase::is_premium_stream_selected() const {
|
|
||||||
if (!is_valid_stream_selected()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return playlists_[audio_type_].premium_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DigitallyImportedServiceBase::LoadPlaylistFinished() {
|
void DigitallyImportedServiceBase::LoadPlaylistFinished() {
|
||||||
@ -234,11 +239,7 @@ void DigitallyImportedServiceBase::LoadPlaylistFinished() {
|
|||||||
if (reply->header(QNetworkRequest::ContentTypeHeader).toString() == "text/html") {
|
if (reply->header(QNetworkRequest::ContentTypeHeader).toString() == "text/html") {
|
||||||
url_handler_->CancelTask();
|
url_handler_->CancelTask();
|
||||||
|
|
||||||
if (is_premium_stream_selected()) {
|
emit StreamError(tr("Error loading di.fm playlist"));
|
||||||
emit StreamError(tr("Invalid di.fm username or password"));
|
|
||||||
} else {
|
|
||||||
emit StreamError(tr("Error loading di.fm playlist"));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
url_handler_->LoadPlaylistFinished(reply);
|
url_handler_->LoadPlaylistFinished(reply);
|
||||||
}
|
}
|
||||||
@ -302,3 +303,24 @@ DigitallyImportedServiceBase::StreamList DigitallyImportedServiceBase::Streams()
|
|||||||
|
|
||||||
return saved_streams_;
|
return saved_streams_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedServiceBase::LoadStation(const QString& key) {
|
||||||
|
QUrl playlist_url;
|
||||||
|
|
||||||
|
if (is_premium_account()) {
|
||||||
|
playlist_url = QUrl(premium_playlists_[premium_audio_type_].arg(
|
||||||
|
homepage_name_, key, listen_hash_));
|
||||||
|
} else {
|
||||||
|
playlist_url = QUrl(basic_playlists_[basic_audio_type_].arg(
|
||||||
|
homepage_name_, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
qLog(Debug) << "Getting playlist URL" << playlist_url;
|
||||||
|
|
||||||
|
QNetworkReply* reply = network_->get(QNetworkRequest(playlist_url));
|
||||||
|
connect(reply, SIGNAL(finished()), SLOT(LoadPlaylistFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DigitallyImportedServiceBase::GetCurrentIndex() {
|
||||||
|
return context_item_->index();
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "internetservice.h"
|
#include "internetservice.h"
|
||||||
|
|
||||||
|
class DigitallyImportedClient;
|
||||||
class DigitallyImportedUrlHandler;
|
class DigitallyImportedUrlHandler;
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
@ -30,11 +31,8 @@ class DigitallyImportedServiceBase : public InternetService {
|
|||||||
friend class DigitallyImportedUrlHandler;
|
friend class DigitallyImportedUrlHandler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DigitallyImportedServiceBase(
|
DigitallyImportedServiceBase(const QString& name, InternetModel* model,
|
||||||
const QString& name, const QString& description, const QUrl& homepage_url,
|
QObject* parent = NULL);
|
||||||
const QString& homepage_name, const QUrl& stream_list_url,
|
|
||||||
const QString& url_scheme, const QString& icon_path,
|
|
||||||
InternetModel* model, QObject* parent = NULL);
|
|
||||||
~DigitallyImportedServiceBase();
|
~DigitallyImportedServiceBase();
|
||||||
|
|
||||||
static const char* kSettingsGroup;
|
static const char* kSettingsGroup;
|
||||||
@ -46,8 +44,6 @@ public:
|
|||||||
|
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
bool is_valid_stream_selected() const;
|
|
||||||
bool is_premium_stream_selected() const;
|
|
||||||
bool is_premium_account() const;
|
bool is_premium_account() const;
|
||||||
|
|
||||||
const QUrl& homepage_url() const { return homepage_url_; }
|
const QUrl& homepage_url() const { return homepage_url_; }
|
||||||
@ -57,6 +53,7 @@ public:
|
|||||||
const QIcon& icon() const { return icon_; }
|
const QIcon& icon() const { return icon_; }
|
||||||
const QString& service_description() const { return service_description_; }
|
const QString& service_description() const { return service_description_; }
|
||||||
const QString& url_scheme() const { return url_scheme_; }
|
const QString& url_scheme() const { return url_scheme_; }
|
||||||
|
const QString& api_service_name() const { return api_service_name_; }
|
||||||
|
|
||||||
// Public for the global search provider.
|
// Public for the global search provider.
|
||||||
struct Stream {
|
struct Stream {
|
||||||
@ -76,20 +73,14 @@ signals:
|
|||||||
void StreamsChanged();
|
void StreamsChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct Playlist {
|
// Subclasses must call this from their constructor
|
||||||
Playlist(bool premium, const QString& url_template)
|
void Init(const QString& description, const QUrl& homepage_url,
|
||||||
: premium_(premium), url_template_(url_template) {}
|
const QString& homepage_name, const QUrl& stream_list_url,
|
||||||
|
const QString& url_scheme, const QString& icon_path,
|
||||||
bool premium_;
|
const QString& api_service_name);
|
||||||
QString url_template_;
|
|
||||||
};
|
|
||||||
|
|
||||||
QModelIndex GetCurrentIndex();
|
QModelIndex GetCurrentIndex();
|
||||||
|
|
||||||
// Called by DigitallyImportedUrlHandler, implemented by subclasses, must
|
|
||||||
// call LoadPlaylistFinished eventually.
|
|
||||||
virtual void LoadStation(const QString& key) = 0;
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void LoadPlaylistFinished();
|
void LoadPlaylistFinished();
|
||||||
|
|
||||||
@ -100,23 +91,13 @@ private slots:
|
|||||||
void RefreshStreamsFinished();
|
void RefreshStreamsFinished();
|
||||||
void ShowSettingsDialog();
|
void ShowSettingsDialog();
|
||||||
|
|
||||||
protected:
|
|
||||||
QNetworkAccessManager* network_;
|
|
||||||
DigitallyImportedUrlHandler* url_handler_;
|
|
||||||
|
|
||||||
int audio_type_;
|
|
||||||
QString username_;
|
|
||||||
QString password_;
|
|
||||||
|
|
||||||
int task_id_;
|
|
||||||
|
|
||||||
QList<Playlist> playlists_;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PopulateStreams();
|
void PopulateStreams();
|
||||||
StreamList LoadStreams() const;
|
StreamList LoadStreams() const;
|
||||||
void SaveStreams(const StreamList& streams);
|
void SaveStreams(const StreamList& streams);
|
||||||
|
|
||||||
|
void LoadStation(const QString& key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Set by subclasses through the constructor
|
// Set by subclasses through the constructor
|
||||||
QUrl homepage_url_;
|
QUrl homepage_url_;
|
||||||
@ -126,6 +107,20 @@ private:
|
|||||||
QIcon icon_;
|
QIcon icon_;
|
||||||
QString service_description_;
|
QString service_description_;
|
||||||
QString url_scheme_;
|
QString url_scheme_;
|
||||||
|
QString api_service_name_;
|
||||||
|
|
||||||
|
QStringList basic_playlists_;
|
||||||
|
QStringList premium_playlists_;
|
||||||
|
|
||||||
|
QNetworkAccessManager* network_;
|
||||||
|
DigitallyImportedUrlHandler* url_handler_;
|
||||||
|
|
||||||
|
int basic_audio_type_;
|
||||||
|
int premium_audio_type_;
|
||||||
|
QString username_;
|
||||||
|
QString listen_hash_;
|
||||||
|
|
||||||
|
int task_id_;
|
||||||
|
|
||||||
QStandardItem* root_;
|
QStandardItem* root_;
|
||||||
|
|
||||||
@ -134,6 +129,8 @@ private:
|
|||||||
|
|
||||||
QList<Stream> saved_streams_;
|
QList<Stream> saved_streams_;
|
||||||
QDateTime last_refreshed_streams_;
|
QDateTime last_refreshed_streams_;
|
||||||
|
|
||||||
|
DigitallyImportedClient* api_client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DIGITALLYIMPORTEDSERVICEBASE_H
|
#endif // DIGITALLYIMPORTEDSERVICEBASE_H
|
||||||
|
@ -15,47 +15,119 @@
|
|||||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "digitallyimportedclient.h"
|
||||||
#include "digitallyimportedservicebase.h"
|
#include "digitallyimportedservicebase.h"
|
||||||
#include "digitallyimportedsettingspage.h"
|
#include "digitallyimportedsettingspage.h"
|
||||||
#include "ui_digitallyimportedsettingspage.h"
|
#include "ui_digitallyimportedsettingspage.h"
|
||||||
|
#include "core/closure.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
|
|
||||||
DigitallyImportedSettingsPage::DigitallyImportedSettingsPage(SettingsDialog* dialog)
|
DigitallyImportedSettingsPage::DigitallyImportedSettingsPage(SettingsDialog* dialog)
|
||||||
: SettingsPage(dialog),
|
: SettingsPage(dialog),
|
||||||
ui_(new Ui_DigitallyImportedSettingsPage)
|
ui_(new Ui_DigitallyImportedSettingsPage),
|
||||||
|
client_(new DigitallyImportedClient("di", this))
|
||||||
{
|
{
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
setWindowIcon(QIcon(":/providers/digitallyimported-32.png"));
|
setWindowIcon(QIcon(":/providers/digitallyimported-32.png"));
|
||||||
|
|
||||||
|
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
|
||||||
|
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
|
||||||
|
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
|
||||||
|
|
||||||
|
ui_->login_state->AddCredentialField(ui_->username);
|
||||||
|
ui_->login_state->AddCredentialField(ui_->password);
|
||||||
|
ui_->login_state->AddCredentialGroup(ui_->credential_group);
|
||||||
|
|
||||||
ui_->login_state->SetAccountTypeText(tr(
|
ui_->login_state->SetAccountTypeText(tr(
|
||||||
"You can listen for free without an account, but Premium members can "
|
"You can listen for free without an account, but Premium members can "
|
||||||
"listen to higher quality streams without advertisements."));
|
"listen to higher quality streams without advertisements."));
|
||||||
ui_->login_state->SetAccountTypeVisible(true);
|
ui_->login_state->SetAccountTypeVisible(true);
|
||||||
ui_->login_state->HideLoggedInState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DigitallyImportedSettingsPage::~DigitallyImportedSettingsPage() {
|
DigitallyImportedSettingsPage::~DigitallyImportedSettingsPage() {
|
||||||
delete ui_;
|
delete ui_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedSettingsPage::Login() {
|
||||||
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
|
||||||
|
|
||||||
|
QNetworkReply* reply = client_->Auth(ui_->username->text(), ui_->password->text());
|
||||||
|
NewClosure(reply, SIGNAL(finished()),
|
||||||
|
this, SLOT(LoginFinished(QNetworkReply*)), reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedSettingsPage::LoginFinished(QNetworkReply* reply) {
|
||||||
|
DigitallyImportedClient::AuthReply result = client_->ParseAuthReply(reply);
|
||||||
|
|
||||||
|
QString name = QString("%1 %2").arg(result.first_name_, result.last_name_);
|
||||||
|
UpdateLoginState(result.listen_hash_, name, result.expires_);
|
||||||
|
|
||||||
|
if (result.success_) {
|
||||||
|
// Clear the password field just to be sure
|
||||||
|
ui_->password->clear();
|
||||||
|
|
||||||
|
// Save the listen key and account information
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
||||||
|
s.setValue("listen_hash", result.listen_hash_);
|
||||||
|
s.setValue("full_name", name);
|
||||||
|
s.setValue("expires", result.expires_);
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("Authentication failed"),
|
||||||
|
result.error_reason_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedSettingsPage::UpdateLoginState(
|
||||||
|
const QString& listen_hash, const QString& name, const QDateTime& expires) {
|
||||||
|
if (listen_hash.isEmpty()) {
|
||||||
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
|
||||||
|
ui_->login_state->SetAccountTypeVisible(true);
|
||||||
|
} else {
|
||||||
|
ui_->login_state->SetLoggedIn(
|
||||||
|
LoginStateWidget::LoggedIn,
|
||||||
|
name + " (" + tr("Expires on %1").arg(expires.date().toString(Qt::SystemLocaleLongDate)) + ")");
|
||||||
|
ui_->login_state->SetAccountTypeVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_->premium_audio_type->setEnabled(!listen_hash.isEmpty());
|
||||||
|
ui_->premium_audio_label->setEnabled(!listen_hash.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DigitallyImportedSettingsPage::Logout() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
||||||
|
s.setValue("listen_hash", QString());
|
||||||
|
s.setValue("full_name", QString());
|
||||||
|
s.setValue("expires", QDateTime());
|
||||||
|
|
||||||
|
UpdateLoginState(QString(), QString(), QDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
void DigitallyImportedSettingsPage::Load() {
|
void DigitallyImportedSettingsPage::Load() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
||||||
|
|
||||||
ui_->audio_type->setCurrentIndex(s.value("audio_type", 0).toInt());
|
ui_->basic_audio_type->setCurrentIndex(s.value("basic_audio_type", 1).toInt());
|
||||||
|
ui_->premium_audio_type->setCurrentIndex(s.value("premium_audio_type", 2).toInt());
|
||||||
ui_->username->setText(s.value("username").toString());
|
ui_->username->setText(s.value("username").toString());
|
||||||
ui_->password->setText(s.value("password").toString());
|
|
||||||
|
UpdateLoginState(s.value("listen_hash").toString(),
|
||||||
|
s.value("full_name").toString(),
|
||||||
|
s.value("expires").toDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DigitallyImportedSettingsPage::Save() {
|
void DigitallyImportedSettingsPage::Save() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
s.beginGroup(DigitallyImportedServiceBase::kSettingsGroup);
|
||||||
|
|
||||||
s.setValue("audio_type", ui_->audio_type->currentIndex());
|
s.setValue("basic_audio_type", ui_->basic_audio_type->currentIndex());
|
||||||
|
s.setValue("premium_audio_type", ui_->premium_audio_type->currentIndex());
|
||||||
s.setValue("username", ui_->username->text());
|
s.setValue("username", ui_->username->text());
|
||||||
s.setValue("password", ui_->password->text());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "ui/settingspage.h"
|
#include "ui/settingspage.h"
|
||||||
|
|
||||||
|
class DigitallyImportedClient;
|
||||||
class Ui_DigitallyImportedSettingsPage;
|
class Ui_DigitallyImportedSettingsPage;
|
||||||
|
|
||||||
class DigitallyImportedSettingsPage : public SettingsPage {
|
class DigitallyImportedSettingsPage : public SettingsPage {
|
||||||
@ -32,8 +33,20 @@ public:
|
|||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Login();
|
||||||
|
void Logout();
|
||||||
|
|
||||||
|
void LoginFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateLoginState(const QString& listen_hash, const QString& name,
|
||||||
|
const QDateTime& expires);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui_DigitallyImportedSettingsPage* ui_;
|
Ui_DigitallyImportedSettingsPage* ui_;
|
||||||
|
|
||||||
|
DigitallyImportedClient* client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DIGITALLYIMPORTEDSETTINGSPAGE_H
|
#endif // DIGITALLYIMPORTEDSETTINGSPAGE_H
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<widget class="LoginStateWidget" name="login_state" native="true"/>
|
<widget class="LoginStateWidget" name="login_state" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_10">
|
<widget class="QGroupBox" name="credential_group">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Account details (Premium)</string>
|
<string>Account details (Premium)</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_22">
|
<widget class="QLabel" name="label_22">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -37,7 +37,18 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="username"/>
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="username"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="login">
|
||||||
|
<property name="text">
|
||||||
|
<string>Login</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_23">
|
<widget class="QLabel" name="label_23">
|
||||||
@ -53,7 +64,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="upgrade_link">
|
<widget class="QLabel" name="upgrade_link">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><a href="http://www.di.fm/premium/">Upgrade to Premium now</a></string>
|
<string><a href="http://www.di.fm/premium/">Upgrade to Premium now</a></string>
|
||||||
@ -82,52 +93,63 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_21">
|
<widget class="QLabel" name="basic_audio_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Audio type</string>
|
<string>Basic audio type</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="audio_type">
|
<widget class="QComboBox" name="basic_audio_type">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>MP3 96k</string>
|
<string>MP3 96k</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>MP3 256k (Premium only)</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>AAC 32k</string>
|
<string>AAC 32k</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Windows Media 40k</string>
|
<string>Windows Media 40k</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="premium_audio_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Premium audio type</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="premium_audio_type">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Windows Media 64k (Premium only)</string>
|
<string>MP3 256k</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Windows Media 128k (Premium only)</string>
|
<string>AAC 64k</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>AAC 128k</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Windows Media 64k</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Windows Media 128k</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
@ -158,6 +180,12 @@
|
|||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>username</tabstop>
|
||||||
|
<tabstop>password</tabstop>
|
||||||
|
<tabstop>login</tabstop>
|
||||||
|
<tabstop>basic_audio_type</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -39,16 +39,6 @@ UrlHandler::LoadResult DigitallyImportedUrlHandler::StartLoading(const QUrl& url
|
|||||||
return ret;
|
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
|
// Start loading the station
|
||||||
const QString key = url.host();
|
const QString key = url.host();
|
||||||
qLog(Info) << "Loading station" << key;
|
qLog(Info) << "Loading station" << key;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "digitallyimportedurlhandler.h"
|
#include "digitallyimportedurlhandler.h"
|
||||||
#include "internetmodel.h"
|
#include "internetmodel.h"
|
||||||
#include "skyfmservice.h"
|
#include "skyfmservice.h"
|
||||||
|
#include "core/logging.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
@ -25,67 +26,8 @@
|
|||||||
|
|
||||||
SkyFmService::SkyFmService(InternetModel* model, QObject* parent)
|
SkyFmService::SkyFmService(InternetModel* model, QObject* parent)
|
||||||
: DigitallyImportedServiceBase(
|
: DigitallyImportedServiceBase(
|
||||||
"SKY.fm", "SKY.fm", QUrl("http://www.sky.fm"), "sky.fm",
|
"SKY.fm", model, parent)
|
||||||
QUrl("http://listen.sky.fm"), "skyfm", ":/providers/skyfm.png",
|
|
||||||
model, parent)
|
|
||||||
{
|
{
|
||||||
playlists_ = QList<Playlist>()
|
Init("SKY.fm", QUrl("http://www.sky.fm"), "sky.fm",
|
||||||
<< Playlist(false, "http://listen.sky.fm/public3/%1.pls")
|
QUrl("http://listen.sky.fm"), "skyfm", ":/providers/skyfm.png", "sky");
|
||||||
<< 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()));
|
|
||||||
}
|
}
|
||||||
|
@ -21,21 +21,8 @@
|
|||||||
#include "digitallyimportedservicebase.h"
|
#include "digitallyimportedservicebase.h"
|
||||||
|
|
||||||
class SkyFmService : public DigitallyImportedServiceBase {
|
class SkyFmService : public DigitallyImportedServiceBase {
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SkyFmService(InternetModel* model, QObject* parent = NULL);
|
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
|
#endif // SKYFMSERVICE_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user