diff --git a/Changelog b/Changelog index 9953ff53d..89b010d6e 100644 --- a/Changelog +++ b/Changelog @@ -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: diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp index 8bbdbf51a..05e3f94ea 100644 --- a/src/core/utilities.cpp +++ b/src/core/utilities.cpp @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2018, Jonas Kvinge * * 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) diff --git a/src/core/utilities.h b/src/core/utilities.h index 0753103f3..b8ab67c8e 100644 --- a/src/core/utilities.h +++ b/src/core/utilities.h @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2018, Jonas Kvinge * * 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 { diff --git a/src/engine/enginetype.cpp b/src/engine/enginetype.cpp index 04b412d51..6d60547e9 100644 --- a/src/engine/enginetype.cpp +++ b/src/engine/enginetype.cpp @@ -25,7 +25,7 @@ #include "enginetype.h" namespace Engine { - + Engine::EngineType EngineTypeFromName(QString enginename) { QString lower = enginename.toLower(); if (lower == "xine") return Engine::Xine; diff --git a/src/settings/tidalsettingspage.cpp b/src/settings/tidalsettingspage.cpp index 0acd42470..dca8c517d 100644 --- a/src/settings/tidalsettingspage.cpp +++ b/src/settings/tidalsettingspage.cpp @@ -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()); diff --git a/src/settings/tidalsettingspage.h b/src/settings/tidalsettingspage.h index 949047e1a..9526e03fe 100644 --- a/src/settings/tidalsettingspage.h +++ b/src/settings/tidalsettingspage.h @@ -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(); diff --git a/src/tidal/tidalservice.cpp b/src/tidal/tidalservice.cpp index e925343d6..a1d6d0eb9 100644 --- a/src/tidal/tidalservice.cpp +++ b/src/tidal/tidalservice.cpp @@ -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 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 EncodedArg; typedef QList EncodedArgList; - ArgList args = ArgList() <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 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 parameters; - parameters << Param("token", session_id_) - << Param("soundQuality", quality_); + parameters << Param("soundQuality", quality_); QNetworkReply *reply = CreateRequest(QString("tracks/%1/streamUrl").arg(song_id), parameters); diff --git a/src/tidal/tidalservice.h b/src/tidal/tidalservice.h index 4669dd063..c4cc33ab4 100644 --- a/src/tidal/tidalservice.h +++ b/src/tidal/tidalservice.h @@ -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_;