1
0
mirror of https://github.com/strawberrymusicplayer/strawberry synced 2024-12-28 01:01:21 +01:00

More Tidal fixes

This commit is contained in:
Jonas Kvinge 2018-09-22 15:37:42 +02:00
parent c77cb002f3
commit 8a57356f64
8 changed files with 59 additions and 38 deletions

View File

@ -9,6 +9,10 @@ Unreleased:
* Fixed bug in pipeline not setting url
* Fixed bug setting wrong temporary metadata
* Removed device module from windows, since it's not implemented for windows
* Added support for both ALSA hw and plughw
* Added option to change url stream scheme for Tidal
* Added encoding of Tidal token in the source code
* Added encoding of Tidal password in the configuration
Version 0.3.1:

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -690,21 +691,6 @@ bool IsLaptop() {
}
QString SystemLanguageName() {
#if QT_VERSION >= 0x040800
QString system_language = QLocale::system().uiLanguages().empty() ? QLocale::system().name() : QLocale::system().uiLanguages().first();
// uiLanguages returns strings with "-" as separators for language/region;
// however QTranslator needs "_" separators
system_language.replace("-", "_");
#else
QString system_language = QLocale::system().name();
#endif
return system_language;
}
bool UrlOnSameDriveAsStrawberry(const QUrl &url) {
if (url.scheme() != "file") return false;
@ -723,18 +709,13 @@ bool UrlOnSameDriveAsStrawberry(const QUrl &url) {
}
QUrl GetRelativePathToStrawberryBin(const QUrl &url) {
QDir appPath(QCoreApplication::applicationDirPath());
return QUrl::fromLocalFile(appPath.relativeFilePath(url.toLocalFile()));
}
QString PathWithoutFilenameExtension(const QString &filename) {
if (filename.section('/', -1, -1).contains('.'))
return filename.section('.', 0, -2);
if (filename.section('/', -1, -1).contains('.')) return filename.section('.', 0, -2);
return filename;
}
QString FiddleFileExtension(const QString &filename, const QString &new_extension) {
@ -786,6 +767,28 @@ void CheckPortable() {
}
QString GetRandomStringWithChars(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
return GetRandomString(len, UseCharacters);
}
QString GetRandomStringWithCharsAndNumbers(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
return GetRandomString(len, UseCharacters);
}
QString GetRandomString(const int len, const QString &UseCharacters) {
QString randstr;
for(int i=0 ; i < len ; ++i) {
int index = qrand() % UseCharacters.length();
QChar nextchar = UseCharacters.at(index);
randstr.append(nextchar);
}
return randstr;
}
} // namespace Utilities
ScopedWCharArray::ScopedWCharArray(const QString &str)

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -149,7 +150,10 @@ int GetThreadId();
// Returns true if this machine has a battery.
bool IsLaptop();
QString SystemLanguageName();
QString GetRandomStringWithChars(const int len);
QString GetRandomStringWithCharsAndNumbers(const int len);
QString GetRandomString(const int len, const QString &UseCharacters);
}
class ScopedWCharArray {

View File

@ -75,7 +75,9 @@ void TidalSettingsPage::Load() {
s.beginGroup(kSettingsGroup);
ui_->username->setText(s.value("username").toString());
ui_->password->setText(s.value("password").toString());
QByteArray password = s.value("password").toByteArray();
if (password.isEmpty()) ui_->password->setText("");
else ui_->password->setText(QByteArray::fromBase64(password));
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_quality, "quality", "HIGH");
ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 100).toInt());
@ -94,7 +96,7 @@ void TidalSettingsPage::Save() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("username", ui_->username->text());
s.setValue("password", ui_->password->text());
s.setValue("password", ui_->password->text().toUtf8().toBase64());
s.setValue("quality", ui_->combobox_quality->itemData(ui_->combobox_quality->currentIndex()));
s.setValue("searchdelay", ui_->spinbox_searchdelay->value());
s.setValue("albumssearchlimit", ui_->spinbox_albumssearchlimit->value());

View File

@ -49,7 +49,7 @@ class TidalSettingsPage : public SettingsPage {
bool eventFilter(QObject *object, QEvent *event);
signals:
void Login(const QString &username, const QString &password, const int search_id = 0);
void Login(const QString &username, const QString &password);
private slots:
void LoginClicked();

View File

@ -59,7 +59,7 @@ const Song::Source TidalService::kSource = Song::Source_Tidal;
const char *TidalService::kApiUrl = "https://listen.tidal.com/v1";
const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username";
const char *TidalService::kResourcesUrl = "http://resources.tidal.com";
const char *TidalService::kApiToken = "P5Xbeo5LFvESeDy6";
const char *TidalService::kApiTokenB64 = "UDVYYmVvNUxGdkVTZUR5Ng==";
const int TidalService::kLoginAttempts = 2;
typedef QPair<QString, QString> Param;
@ -76,7 +76,11 @@ TidalService::TidalService(Application *app, InternetModel *parent)
user_id_(0),
pending_search_id_(0),
next_pending_search_id_(1),
login_sent_(false)
search_id_(0),
albums_requested_(0),
albums_received_(0),
login_sent_(false),
login_attempts_(0)
{
timer_searchdelay_->setSingleShot(true);
@ -103,7 +107,7 @@ void TidalService::ReloadSettings() {
QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup);
username_ = s.value("username").toString();
password_ = s.value("password").toString();
password_ = QByteArray::fromBase64(s.value("password").toByteArray());
quality_ = s.value("quality").toString();
searchdelay_ = s.value("searchdelay", 1500).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 100).toInt();
@ -123,6 +127,7 @@ void TidalService::LoadSessionID() {
session_id_ = s.value("session_id").toString();
user_id_ = s.value("user_id").toInt();
country_code_ = s.value("country_code").toString();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12);
s.endGroup();
}
@ -144,7 +149,10 @@ void TidalService::SendLogin(const QString &username, const QString &password) {
typedef QPair<QByteArray, QByteArray> EncodedArg;
typedef QList<EncodedArg> EncodedArgList;
ArgList args = ArgList() <<Arg("token", kApiToken) << Arg("username", username) << Arg("password", password) << Arg("clientVersion", "2.2.1--7");
ArgList args = ArgList() << Arg("token", QByteArray::fromBase64(kApiTokenB64))
<< Arg("username", username)
<< Arg("password", password)
<< Arg("clientUniqueKey", clientuniquekey_);
QStringList query_items;
QUrlQuery url_query;
@ -158,6 +166,7 @@ void TidalService::SendLogin(const QString &username, const QString &password) {
QNetworkRequest req(url);
req.setRawHeader("Origin", "http://listen.tidal.com");
req.setRawHeader("X-Tidal-Token", QByteArray::fromBase64(kApiTokenB64));
QNetworkReply *reply = network_->post(req, url_query.toString(QUrl::FullyEncoded).toUtf8());
NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*)), reply);
@ -244,6 +253,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply) {
country_code_ = json_obj["countryCode"].toString();
session_id_ = json_obj["sessionId"].toString();
user_id_ = json_obj["userId"].toInt();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12);
QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup);
@ -307,9 +317,11 @@ QNetworkReply *TidalService::CreateRequest(const QString &ressource_name, const
QUrl url(kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query);
QNetworkRequest req(url);
req.setRawHeader("Origin", "http://listen.tidal.com");
req.setRawHeader("X-Tidal-SessionId", session_id_.toUtf8());
QNetworkReply *reply = network_->get(req);
//qLog(Debug) << "Tidal: Sending request" << url;
qLog(Debug) << "Tidal: Sending request" << url;
return reply;
@ -611,11 +623,7 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
void TidalService::GetAlbum(const int album_id) {
QList<Param> parameters;
parameters << Param("token", session_id_)
<< Param("soundQuality", quality_);
QNetworkReply *reply = CreateRequest(QString("albums/%1/tracks").arg(album_id), parameters);
NewClosure(reply, SIGNAL(finished()), this, SLOT(GetAlbumFinished(QNetworkReply*, int, int)), reply, search_id_, album_id);
}
@ -784,8 +792,7 @@ void TidalService::GetStreamURL(const QUrl &url) {
requests_song_.insert(song_id, url);
QList<Param> parameters;
parameters << Param("token", session_id_)
<< Param("soundQuality", quality_);
parameters << Param("soundQuality", quality_);
QNetworkReply *reply = CreateRequest(QString("tracks/%1/streamUrl").arg(song_id), parameters);

View File

@ -103,7 +103,7 @@ class TidalService : public InternetService {
static const char *kApiUrl;
static const char *kAuthUrl;
static const char *kResourcesUrl;
static const char *kApiToken;
static const char *kApiTokenB64;
NetworkAccessManager *network_;
TidalUrlHandler *url_handler_;
@ -121,6 +121,7 @@ class TidalService : public InternetService {
QString session_id_;
quint64 user_id_;
QString country_code_;
QString clientuniquekey_;
int pending_search_id_;
int next_pending_search_id_;