2011-11-03 00:41:46 +01:00
|
|
|
/* This file is part of Clementine.
|
2014-12-17 19:02:21 +01:00
|
|
|
Copyright 2011-2012, David Sansome <me@davidsansome.com>
|
|
|
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
|
|
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
2011-11-03 00:41:46 +01:00
|
|
|
|
|
|
|
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 <qjson/parser.h>
|
|
|
|
|
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QNetworkRequest>
|
|
|
|
|
2014-12-19 00:40:30 +01:00
|
|
|
#include "core/logging.h"
|
|
|
|
#include "core/network.h"
|
|
|
|
|
2011-11-03 00:41:46 +01:00
|
|
|
// The API used here is undocumented - it was reverse engineered by watching
|
2015-01-15 18:48:04 +01:00
|
|
|
// calls made by the sky.fm (now RadioTunes) android app:
|
2011-11-03 00:41:46 +01:00
|
|
|
// https://market.android.com/details?id=com.audioaddict.sky
|
|
|
|
|
|
|
|
const char* DigitallyImportedClient::kApiUsername = "ephemeron";
|
|
|
|
const char* DigitallyImportedClient::kApiPassword = "dayeiph0ne@pp";
|
|
|
|
|
|
|
|
const char* DigitallyImportedClient::kAuthUrl =
|
2012-05-13 16:44:11 +02:00
|
|
|
"http://api.audioaddict.com/v1/%1/members/authenticate";
|
2011-11-03 00:41:46 +01:00
|
|
|
|
2011-11-04 23:31:19 +01:00
|
|
|
const char* DigitallyImportedClient::kChannelListUrl =
|
2014-02-07 16:34:20 +01:00
|
|
|
"http://api.v2.audioaddict.com/v1/%1/mobile/"
|
|
|
|
"batch_update?asset_group_key=mobile_icons&stream_set_key=";
|
2011-11-04 23:31:19 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
DigitallyImportedClient::DigitallyImportedClient(const QString& service_name,
|
|
|
|
QObject* parent)
|
|
|
|
: QObject(parent),
|
|
|
|
network_(new NetworkAccessManager(this)),
|
|
|
|
service_name_(service_name) {}
|
2011-11-03 00:41:46 +01:00
|
|
|
|
2014-12-17 19:02:21 +01:00
|
|
|
void DigitallyImportedClient::SetAuthorisationHeader(
|
|
|
|
QNetworkRequest* req) const {
|
|
|
|
req->setRawHeader("Authorization", "Basic " +
|
|
|
|
QString("%1:%2")
|
|
|
|
.arg(kApiUsername, kApiPassword)
|
|
|
|
.toAscii()
|
|
|
|
.toBase64());
|
2011-11-04 23:31:19 +01:00
|
|
|
}
|
|
|
|
|
2011-11-03 00:41:46 +01:00
|
|
|
QNetworkReply* DigitallyImportedClient::Auth(const QString& username,
|
|
|
|
const QString& password) {
|
|
|
|
QNetworkRequest req(QUrl(QString(kAuthUrl).arg(service_name_)));
|
2011-11-04 23:31:19 +01:00
|
|
|
SetAuthorisationHeader(&req);
|
2011-11-03 00:41:46 +01:00
|
|
|
|
|
|
|
QByteArray postdata = "username=" + QUrl::toPercentEncoding(username) +
|
2014-02-07 16:34:20 +01:00
|
|
|
"&password=" + QUrl::toPercentEncoding(password);
|
2011-11-03 00:41:46 +01:00
|
|
|
|
|
|
|
return network_->post(req, postdata);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
DigitallyImportedClient::AuthReply DigitallyImportedClient::ParseAuthReply(
|
|
|
|
QNetworkReply* reply) const {
|
2011-11-03 00:41:46 +01:00
|
|
|
AuthReply ret;
|
|
|
|
ret.success_ = false;
|
|
|
|
ret.error_reason_ = tr("Unknown error");
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
const int http_status =
|
|
|
|
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
2012-05-13 16:44:11 +02:00
|
|
|
if (http_status == 403) {
|
|
|
|
ret.error_reason_ = reply->readAll();
|
|
|
|
return ret;
|
|
|
|
} else if (http_status != 200) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-03 00:41:46 +01:00
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantMap data = parser.parse(reply).toMap();
|
|
|
|
|
2012-05-13 16:44:11 +02:00
|
|
|
if (!data.contains("subscriptions")) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QVariantList subscriptions =
|
|
|
|
data.value("subscriptions", QVariantList()).toList();
|
2012-05-13 16:44:11 +02:00
|
|
|
if (subscriptions.isEmpty() ||
|
|
|
|
subscriptions[0].toMap().value("status").toString() != "active") {
|
|
|
|
ret.error_reason_ = tr("You do not have an active subscription");
|
2011-11-03 00:41:46 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!data.contains("first_name") || !data.contains("last_name") ||
|
2012-05-13 16:44:11 +02:00
|
|
|
!subscriptions[0].toMap().contains("expires_on") ||
|
|
|
|
!data.contains("listen_key"))
|
2011-11-03 00:41:46 +01:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret.success_ = true;
|
2012-05-13 16:44:11 +02:00
|
|
|
ret.first_name_ = data["first_name"].toString();
|
|
|
|
ret.last_name_ = data["last_name"].toString();
|
2014-02-07 16:34:20 +01:00
|
|
|
ret.expires_ = QDateTime::fromString(
|
|
|
|
subscriptions[0].toMap()["expires_on"].toString(), Qt::ISODate);
|
2012-05-13 16:44:11 +02:00
|
|
|
ret.listen_hash_ = data["listen_key"].toString();
|
2011-11-03 00:41:46 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2011-11-04 23:31:19 +01:00
|
|
|
|
|
|
|
QNetworkReply* DigitallyImportedClient::GetChannelList() {
|
2014-02-07 16:34:20 +01:00
|
|
|
// QNetworkRequest req(QUrl(QString(kChannelListUrl)));
|
2011-11-04 23:31:19 +01:00
|
|
|
QNetworkRequest req(QUrl(QString(kChannelListUrl).arg(service_name_)));
|
|
|
|
SetAuthorisationHeader(&req);
|
|
|
|
|
|
|
|
return network_->get(req);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
DigitallyImportedClient::ChannelList DigitallyImportedClient::ParseChannelList(
|
|
|
|
QNetworkReply* reply) const {
|
2011-11-04 23:31:19 +01:00
|
|
|
ChannelList ret;
|
|
|
|
|
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantMap data = parser.parse(reply).toMap();
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!data.contains("channel_filters")) return ret;
|
2011-11-04 23:31:19 +01:00
|
|
|
|
|
|
|
QVariantList filters = data["channel_filters"].toList();
|
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QVariant& filter : filters) {
|
2011-11-04 23:31:19 +01:00
|
|
|
// Find the filter called "All"
|
|
|
|
QVariantMap filter_map = filter.toMap();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (filter_map.value("name", QString()).toString() != "All") continue;
|
2011-11-04 23:31:19 +01:00
|
|
|
|
|
|
|
// Add all its stations to the result
|
2014-02-07 16:34:20 +01:00
|
|
|
QVariantList channels =
|
|
|
|
filter_map.value("channels", QVariantList()).toList();
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QVariant& channel_var : channels) {
|
2011-11-04 23:31:19 +01:00
|
|
|
QVariantMap channel_map = channel_var.toMap();
|
|
|
|
|
|
|
|
Channel channel;
|
|
|
|
channel.art_url_ = QUrl(channel_map.value("asset_url").toString());
|
|
|
|
channel.description_ = channel_map.value("description").toString();
|
|
|
|
channel.director_ = channel_map.value("channel_director").toString();
|
|
|
|
channel.key_ = channel_map.value("key").toString();
|
|
|
|
channel.name_ = channel_map.value("name").toString();
|
|
|
|
ret << channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QDataStream& operator<<(QDataStream& out,
|
|
|
|
const DigitallyImportedClient::Channel& channel) {
|
|
|
|
out << channel.art_url_ << channel.director_ << channel.description_
|
|
|
|
<< channel.name_ << channel.key_;
|
2011-11-05 23:56:10 +01:00
|
|
|
return out;
|
2011-11-04 23:31:19 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QDataStream& operator>>(QDataStream& in,
|
|
|
|
DigitallyImportedClient::Channel& channel) {
|
|
|
|
in >> channel.art_url_ >> channel.director_ >> channel.description_ >>
|
|
|
|
channel.name_ >> channel.key_;
|
2011-11-05 23:56:10 +01:00
|
|
|
return in;
|
2011-11-04 23:31:19 +01:00
|
|
|
}
|