Remove deezer

This commit is contained in:
Jonas Kvinge
2019-03-09 17:43:20 +01:00
parent c0fb35f6b9
commit 380f2509ac
46 changed files with 19 additions and 2484 deletions

View File

@@ -64,14 +64,6 @@ if(HAVE_PHONON)
include_directories(${PHONON_INCLUDE_DIRS})
endif()
if(HAVE_LIBDEEZER)
include_directories(${DEEZER_INCLUDE_DIRS})
endif()
if(HAVE_LIBDZMEDIA)
include_directories(${DZMEDIA_INCLUDE_DIRS})
endif()
link_directories(${TAGLIB_LIBRARY_DIRS})
include_directories(${TAGLIB_INCLUDE_DIRS})
@@ -579,12 +571,6 @@ optional_source(HAVE_PHONON
HEADERS engine/phononengine.h
)
# Deezer
optional_source(HAVE_DEEZER
SOURCES engine/deezerengine.cpp
HEADERS engine/deezerengine.h
)
# DBUS and MPRIS - Unix specific
if(UNIX AND HAVE_DBUS)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
@@ -888,19 +874,6 @@ optional_source(HAVE_STREAM_TIDAL
settings/tidalsettingspage.ui
)
optional_source(HAVE_STREAM_DEEZER
SOURCES
deezer/deezerservice.cpp
deezer/deezerurlhandler.cpp
settings/deezersettingspage.cpp
HEADERS
deezer/deezerservice.h
deezer/deezerurlhandler.h
settings/deezersettingspage.h
UI
settings/deezersettingspage.ui
)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
@@ -994,14 +967,6 @@ if(HAVE_PHONON)
target_link_libraries(strawberry_lib ${PHONON_LIBRARIES})
endif()
if(HAVE_DEEZER)
target_link_libraries(strawberry_lib ${LIBDEEZER_LIBRARIES})
endif()
if(HAVE_DZMEDIA)
target_link_libraries(strawberry_lib ${LIBDZMEDIA_LIBRARIES})
endif()
if(HAVE_LIBGPOD)
target_link_libraries(strawberry_lib ${LIBGPOD_LIBRARIES})
endif(HAVE_LIBGPOD)

View File

@@ -39,7 +39,6 @@
#cmakedefine HAVE_SPARKLE
#cmakedefine HAVE_CHROMAPRINT
#cmakedefine HAVE_TAGLIB_DSFFILE
#cmakedefine HAVE_DZMEDIA
#cmakedefine HAVE_GLOBALSHORTCUTS
#cmakedefine IMOBILEDEVICE_USES_UDIDS
#cmakedefine USE_INSTALL_PREFIX
@@ -48,10 +47,8 @@
#cmakedefine HAVE_VLC
#cmakedefine HAVE_XINE
#cmakedefine HAVE_PHONON
#cmakedefine HAVE_DEEZER
#cmakedefine HAVE_STREAM_TIDAL
#cmakedefine HAVE_STREAM_DEEZER
#cmakedefine HAVE_KEYSYMDEF_H
#cmakedefine HAVE_XF86KEYSYM_H

View File

@@ -66,9 +66,6 @@
#ifdef HAVE_STREAM_TIDAL
# include "tidal/tidalservice.h"
#endif
#ifdef HAVE_STREAM_DEEZER
# include "deezer/deezerservice.h"
#endif
#include "scrobbler/audioscrobbler.h"
@@ -127,17 +124,11 @@ class ApplicationImpl {
InternetServices *internet_services = new InternetServices(app);
#ifdef HAVE_STREAM_TIDAL
internet_services->AddService(new TidalService(app, internet_services));
#endif
#ifdef HAVE_STREAM_DEEZER
internet_services->AddService(new DeezerService(app, internet_services));
#endif
return internet_services;
}),
#ifdef HAVE_STREAM_TIDAL
tidal_search_([=]() { return new InternetSearch(app, Song::Source_Tidal, app); }),
#endif
#ifdef HAVE_STREAM_DEEZER
deezer_search_([=]() { return new InternetSearch(app, Song::Source_Deezer, app); }),
#endif
scrobbler_([=]() { return new AudioScrobbler(app, app); })
{}
@@ -161,9 +152,6 @@ class ApplicationImpl {
Lazy<InternetServices> internet_services_;
#ifdef HAVE_STREAM_TIDAL
Lazy<InternetSearch> tidal_search_;
#endif
#ifdef HAVE_STREAM_DEEZER
Lazy<InternetSearch> deezer_search_;
#endif
Lazy<AudioScrobbler> scrobbler_;
@@ -236,7 +224,4 @@ InternetServices *Application::internet_services() const { return p_->internet_s
#ifdef HAVE_STREAM_TIDAL
InternetSearch *Application::tidal_search() const { return p_->tidal_search_.get(); }
#endif
#ifdef HAVE_STREAM_DEEZER
InternetSearch *Application::deezer_search() const { return p_->deezer_search_.get(); }
#endif
AudioScrobbler *Application::scrobbler() const { return p_->scrobbler_.get(); }

View File

@@ -96,9 +96,6 @@ class Application : public QObject {
#ifdef HAVE_STREAM_TIDAL
InternetSearch *tidal_search() const;
#endif
#ifdef HAVE_STREAM_DEEZER
InternetSearch *deezer_search() const;
#endif
AudioScrobbler *scrobbler() const;

View File

@@ -136,9 +136,6 @@
#ifdef HAVE_STREAM_TIDAL
# include "settings/tidalsettingspage.h"
#endif
#ifdef HAVE_STREAM_DEEZER
# include "settings/deezersettingspage.h"
#endif
#include "internet/internetservices.h"
#include "internet/internetservice.h"
@@ -206,9 +203,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
}),
#ifdef HAVE_STREAM_TIDAL
tidal_search_view_(new InternetSearchView(app_, app_->tidal_search(), TidalSettingsPage::kSettingsGroup, SettingsDialog::Page_Tidal, this)),
#endif
#ifdef HAVE_STREAM_DEEZER
deezer_search_view_(new InternetSearchView(app_, app_->deezer_search(), DeezerSettingsPage::kSettingsGroup, SettingsDialog::Page_Deezer, this)),
#endif
playlist_menu_(new QMenu(this)),
playlist_add_to_another_(nullptr),
@@ -266,9 +260,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#ifdef HAVE_STREAM_TIDAL
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal"));
#endif
#ifdef HAVE_STREAM_DEEZER
ui_->tabs->addTab(deezer_search_view_, IconLoader::Load("deezer"), tr("Deezer"));
#endif
// Add the playing widget to the fancy tab widget
ui_->tabs->addBottomWidget(ui_->widget_playing);
@@ -547,9 +538,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#ifdef HAVE_STREAM_TIDAL
connect(tidal_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
#ifdef HAVE_STREAM_DEEZER
connect(deezer_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
// Playlist menu
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
@@ -859,16 +847,6 @@ void MainWindow::ReloadSettings() {
ui_->tabs->delTab("Tidal");
#endif
#ifdef HAVE_STREAM_DEEZER
settings.beginGroup(DeezerSettingsPage::kSettingsGroup);
bool enable_deezer = settings.value("enabled", false).toBool();
settings.endGroup();
if (enable_deezer)
ui_->tabs->addTab(deezer_search_view_, IconLoader::Load("deezer"), tr("Deezer"));
else
ui_->tabs->delTab("Deezer");
#endif
}
void MainWindow::ReloadAllSettings() {
@@ -885,9 +863,6 @@ void MainWindow::ReloadAllSettings() {
#ifdef HAVE_STREAM_TIDAL
tidal_search_view_->ReloadSettings();
#endif
#ifdef HAVE_STREAM_DEEZER
deezer_search_view_->ReloadSettings();
#endif
}

View File

@@ -332,7 +332,6 @@ signals:
#endif
InternetSearchView *tidal_search_view_;
InternetSearchView *deezer_search_view_;
QAction *collection_show_all_;
QAction *collection_show_duplicates_;

View File

@@ -59,9 +59,6 @@
#ifdef HAVE_VLC
# include "engine/vlcengine.h"
#endif
#ifdef HAVE_DEEZER
# include "engine/deezerengine.h"
#endif
#include "collection/collectionbackend.h"
#include "playlist/playlist.h"
@@ -140,15 +137,6 @@ Engine::EngineType Player::CreateEngine(Engine::EngineType enginetype) {
use_enginetype=Engine::Phonon;
engine_.reset(new PhononEngine(app_->task_manager()));
break;
#endif
#ifdef HAVE_DEEZER
case Engine::Deezer:{
use_enginetype=Engine::Deezer;
DeezerEngine *deezerengine = new DeezerEngine(app_->task_manager());
connect(this, SIGNAL(Authenticated()), deezerengine, SLOT(LoadAccessToken()));
engine_.reset(deezerengine);
break;
}
#endif
default:
if (i > 0) { qFatal("No engine available!"); }
@@ -321,7 +309,7 @@ void Player::NextInternal(Engine::TrackChangeFlags change) {
if (app_->playlist_manager()->active()->current_item()) {
const QUrl url = app_->playlist_manager()->active()->current_item()->Url();
if (url_handlers_.contains(url.scheme()) && !(engine_->type() == Engine::Deezer && url.scheme() == "dzmedia")) {
if (url_handlers_.contains(url.scheme())) {
// The next track is already being loaded
if (url == loading_async_) return;
@@ -531,7 +519,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
if (current_item_ && change == Engine::Manual && engine_->position_nanosec() != engine_->length_nanosec()) {
emit TrackSkipped(current_item_);
const QUrl &url = current_item_->Url();
if (url_handlers_.contains(url.scheme()) && !(engine_->type() == Engine::Deezer && url.scheme() == "dzmedia")) {
if (url_handlers_.contains(url.scheme())) {
url_handlers_[url.scheme()]->TrackSkipped();
}
}
@@ -550,7 +538,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
current_item_ = app_->playlist_manager()->active()->current_item();
const QUrl url = current_item_->Url();
if (url_handlers_.contains(url.scheme()) && !(engine_->type() == Engine::Deezer && url.scheme() == "dzmedia")) {
if (url_handlers_.contains(url.scheme())) {
// It's already loading
if (url == loading_async_) return;
@@ -697,7 +685,7 @@ void Player::TrackAboutToEnd() {
// We don't want to preload (and scrobble) the next item in the playlist if it's just going to be stopped again immediately after.
if (app_->playlist_manager()->active()->current_item()) {
const QUrl url = app_->playlist_manager()->active()->current_item()->Url();
if (url_handlers_.contains(url.scheme()) && !(engine_->type() == Engine::Deezer && url.scheme() == "dzmedia")) {
if (url_handlers_.contains(url.scheme())) {
url_handlers_[url.scheme()]->TrackAboutToEnd();
return;
}
@@ -730,7 +718,7 @@ void Player::TrackAboutToEnd() {
QUrl url = next_item->Url();
// Get the actual track URL rather than the stream URL.
if (url_handlers_.contains(url.scheme()) && !(engine_->type() == Engine::Deezer && url.scheme() == "dzmedia")) {
if (url_handlers_.contains(url.scheme())) {
UrlHandler::LoadResult result = url_handlers_[url.scheme()]->LoadNext(url);
switch (result.type_) {
case UrlHandler::LoadResult::Error:

View File

@@ -304,7 +304,7 @@ uint Song::mtime() const { return d->mtime_; }
uint Song::ctime() const { return d->ctime_; }
int Song::filesize() const { return d->filesize_; }
Song::FileType Song::filetype() const { return d->filetype_; }
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Deezer; }
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal; }
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
bool Song::is_collection_song() const {
return !is_cdda() && !is_stream() && id() != -1;
@@ -393,7 +393,6 @@ QString Song::TextForSource(Source source) {
case Song::Source_Device: return QObject::tr("Device");
case Song::Source_Stream: return QObject::tr("Stream");
case Song::Source_Tidal: return QObject::tr("Tidal");
case Song::Source_Deezer: return QObject::tr("Deezer");
default: return QObject::tr("Unknown");
}
@@ -408,7 +407,6 @@ QIcon Song::IconForSource(Source source) {
case Song::Source_Device: return IconLoader::Load("device");
case Song::Source_Stream: return IconLoader::Load("applications-internet");
case Song::Source_Tidal: return IconLoader::Load("tidal");
case Song::Source_Deezer: return IconLoader::Load("deezer");
default: return IconLoader::Load("edit-delete");
}

View File

@@ -101,7 +101,6 @@ class Song {
Source_Device = 4,
Source_Stream = 5,
Source_Tidal = 6,
Source_Deezer = 7,
};
enum FileType {

View File

@@ -1,827 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#ifdef HAVE_DZMEDIA
# include <dzmedia.h>
#endif
#include <QObject>
#include <QByteArray>
#include <QList>
#include <QVector>
#include <QPair>
#include <QString>
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QMenu>
#include <QDesktopServices>
#include <QSettings>
#include "core/application.h"
#include "core/player.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/mergedproxymodel.h"
#include "core/network.h"
#include "core/song.h"
#include "core/iconloader.h"
#include "core/taskmanager.h"
#include "core/timeconstants.h"
#include "core/utilities.h"
#include "internet/internetservices.h"
#include "internet/internetsearch.h"
#include "internet/localredirectserver.h"
#include "deezerservice.h"
#include "deezerurlhandler.h"
#include "settings/deezersettingspage.h"
const Song::Source DeezerService::kSource = Song::Source_Deezer;
const char *DeezerService::kApiUrl = "https://api.deezer.com";
const char *DeezerService::kOAuthUrl = "https://connect.deezer.com/oauth/auth.php";
const char *DeezerService::kOAuthAccessTokenUrl = "https://connect.deezer.com/oauth/access_token.php";
const char *DeezerService::kOAuthRedirectUrl = "https://oauth.strawbs.net";
const int DeezerService::kAppID = 303684;
const char *DeezerService::kSecretKey = "06911976010b9ddd7256769adf2b2e56";
typedef QPair<QString, QString> Param;
DeezerService::DeezerService(Application *app, QObject *parent)
: InternetService(Song::Source_Deezer, "Deezer", "dzmedia", app, parent),
network_(new NetworkAccessManager(this)),
url_handler_(new DeezerUrlHandler(app, this)),
#ifdef HAVE_DZMEDIA
dzmedia_(new DZMedia(this)),
#endif
timer_searchdelay_(new QTimer(this)),
searchdelay_(1500),
albumssearchlimit_(1),
songssearchlimit_(1),
fetchalbums_(false),
preview_(false),
pending_search_id_(0),
next_pending_search_id_(1),
search_id_(0),
albums_requested_(0),
albums_received_(0)
{
timer_searchdelay_->setSingleShot(true);
connect(timer_searchdelay_, SIGNAL(timeout()), SLOT(StartSearch()));
connect(this, SIGNAL(Authenticated()), app->player(), SLOT(HandleAuthentication()));
app->player()->RegisterUrlHandler(url_handler_);
ReloadSettings();
LoadAccessToken();
#ifdef HAVE_DZMEDIA
connect(dzmedia_, SIGNAL(StreamURLReceived(QUrl, QUrl, DZMedia::FileType)), this, SLOT(GetStreamURLFinished(QUrl, QUrl, DZMedia::FileType)));
#endif
}
DeezerService::~DeezerService() {}
void DeezerService::ShowConfig() {
app_->OpenSettingsDialogAtPage(SettingsDialog::Page_Deezer);
}
void DeezerService::ReloadSettings() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
searchdelay_ = s.value("searchdelay", 1500).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 100).toInt();
songssearchlimit_ = s.value("songssearchlimit", 100).toInt();
fetchalbums_ = s.value("fetchalbums", false).toBool();
coversize_ = s.value("coversize", "cover_big").toString();
#if defined(HAVE_DEEZER) || defined(HAVE_DZMEDIA)
bool preview(false);
#else
bool preview(true);
#endif
preview_ = s.value("preview", preview).toBool();
s.endGroup();
}
void DeezerService::LoadAccessToken() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
if (s.contains("access_token") && s.contains("expiry_time")) {
access_token_ = s.value("access_token").toString();
expiry_time_ = s.value("expiry_time").toDateTime();
}
s.endGroup();
}
void DeezerService::Logout() {
access_token_.clear();
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
s.remove("access_token");
s.remove("expiry_time");
s.endGroup();
}
void DeezerService::StartAuthorisation() {
LocalRedirectServer *server = new LocalRedirectServer(this);
server->Listen();
QUrl url = QUrl(kOAuthUrl);
QUrlQuery url_query;
//url_query.addQueryItem("response_type", "token");
url_query.addQueryItem("response_type", "code");
url_query.addQueryItem("app_id", QString::number(kAppID));
QUrl redirect_url;
QUrlQuery redirect_url_query;
const QString port = QString::number(server->url().port());
redirect_url = QUrl(kOAuthRedirectUrl);
redirect_url_query.addQueryItem("port", port);
redirect_url.setQuery(redirect_url_query);
url_query.addQueryItem("redirect_uri", redirect_url.toString());
url.setQuery(url_query);
NewClosure(server, SIGNAL(Finished()), this, &DeezerService::RedirectArrived, server, redirect_url);
QDesktopServices::openUrl(url);
}
void DeezerService::RedirectArrived(LocalRedirectServer *server, QUrl url) {
server->deleteLater();
QUrl request_url = server->request_url();
RequestAccessToken(QUrlQuery(request_url).queryItemValue("code").toUtf8());
}
void DeezerService::RequestAccessToken(const QByteArray &code) {
typedef QPair<QString, QString> Arg;
typedef QList<Arg> ArgList;
typedef QPair<QByteArray, QByteArray> EncodedArg;
typedef QList<EncodedArg> EncodedArgList;
ArgList args = ArgList() << Arg("app_id", QString::number(kAppID))
<< Arg("secret", kSecretKey)
<< Arg("code", code);
QUrlQuery url_query;
for (const Arg &arg : args) {
EncodedArg encoded_arg(QUrl::toPercentEncoding(arg.first), QUrl::toPercentEncoding(arg.second));
url_query.addQueryItem(encoded_arg.first, encoded_arg.second);
}
QUrl url(kOAuthAccessTokenUrl);
QNetworkRequest request = QNetworkRequest(url);
QNetworkReply *reply = network_->post(request, url_query.toString(QUrl::FullyEncoded).toUtf8());
NewClosure(reply, SIGNAL(finished()), this, SLOT(FetchAccessTokenFinished(QNetworkReply*)), reply);
}
void DeezerService::FetchAccessTokenFinished(QNetworkReply *reply) {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
return;
}
forever {
QByteArray line = reply->readLine();
QString str(line);
QStringList args = str.split("&");
for (QString arg : args) {
QStringList params = arg.split("=");
if (params.count() < 2) continue;
QString param1 = params.first();
QString param2 = params[1];
if (param1 == "access_token") access_token_ = param2;
else if (param1 == "expires") SetExpiryTime(param2.toInt());
}
if (reply->atEnd()) break;
}
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
s.setValue("access_token", access_token_);
s.setValue("expiry_time", expiry_time_);
s.endGroup();
emit Authenticated();
emit LoginSuccess();
}
void DeezerService::SetExpiryTime(int expires_in_seconds) {
// Set the expiry time with two minutes' grace.
expiry_time_ = QDateTime::currentDateTime().addSecs(expires_in_seconds - 120);
qLog(Debug) << "Current oauth access token expires at:" << expiry_time_;
}
QNetworkReply *DeezerService::CreateRequest(const QString &ressource_name, const QList<Param> &params) {
typedef QPair<QString, QString> Arg;
typedef QList<Arg> ArgList;
typedef QPair<QByteArray, QByteArray> EncodedArg;
typedef QList<EncodedArg> EncodedArgList;
ArgList args = ArgList() << Arg("access_token", access_token_)
<< Arg("output", "json")
<< params;
QUrlQuery url_query;
for (const Arg& arg : args) {
EncodedArg encoded_arg(QUrl::toPercentEncoding(arg.first), QUrl::toPercentEncoding(arg.second));
url_query.addQueryItem(encoded_arg.first, encoded_arg.second);
}
QUrl url(kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query);
QNetworkRequest req(url);
QNetworkReply *reply = network_->get(req);
//qLog(Debug) << "Deezer: Sending request" << url;
return reply;
}
QByteArray DeezerService::GetReplyData(QNetworkReply *reply) {
QByteArray data;
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
data = reply->readAll();
}
else {
if (reply->error() < 200) {
// This is a network error, there is nothing more to do.
QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
Error(failure_reason);
}
else {
// See if there is Json data containing "error" - then use that instead.
data = reply->readAll();
QJsonParseError error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &error);
QString failure_reason;
if (error.error == QJsonParseError::NoError && !json_doc.isNull() && !json_doc.isEmpty() && json_doc.isObject()) {
QJsonObject json_obj = json_doc.object();
if (json_obj.contains("error")) {
QJsonValue json_value_error = json_obj["error"];
if (json_value_error.isObject()) {
QJsonObject json_error = json_value_error.toObject();
int code = json_error["code"].toInt();
if (code == 300) Logout();
QString message = json_error["message"].toString();
QString type = json_error["type"].toString();
failure_reason = QString("%1 (%2)").arg(message).arg(code);
}
else { failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); }
}
else {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
}
}
else {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
}
if (reply->error() == QNetworkReply::ContentAccessDenied || reply->error() == QNetworkReply::ContentOperationNotPermittedError || reply->error() == QNetworkReply::AuthenticationRequiredError) {
// Session is probably expired
Logout();
Error(failure_reason);
}
else if (reply->error() == QNetworkReply::ContentNotFoundError) { // Ignore this error
Error(failure_reason);
}
else { // Fail
Error(failure_reason);
}
}
return QByteArray();
}
return data;
}
QJsonObject DeezerService::ExtractJsonObj(QByteArray &data) {
QJsonParseError error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
Error("Reply from server missing Json data.", data);
return QJsonObject();
}
if (json_doc.isNull() || json_doc.isEmpty()) {
Error("Received empty Json document.", json_doc);
return QJsonObject();
}
if (!json_doc.isObject()) {
Error("Json document is not an object.", json_doc);
return QJsonObject();
}
QJsonObject json_obj = json_doc.object();
if (json_obj.isEmpty()) {
Error("Received empty Json object.", json_doc);
return QJsonObject();
}
return json_obj;
}
QJsonValue DeezerService::ExtractData(QByteArray &data) {
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) return QJsonObject();
if (json_obj.contains("error")) {
QJsonValue json_value_error = json_obj["error"];
if (!json_value_error.isObject()) {
Error("Error missing object", json_obj);
return QJsonValue();
}
QJsonObject json_error = json_value_error.toObject();
int code = json_error["code"].toInt();
if (code == 300) Logout();
QString message = json_error["message"].toString();
QString type = json_error["type"].toString();
Error(QString("%1 (%2)").arg(message).arg(code));
return QJsonValue();
}
if (!json_obj.contains("data") && !json_obj.contains("DATA")) {
Error("Json reply is missing data.", json_obj);
return QJsonValue();
}
QJsonValue json_data;
if (json_obj.contains("data")) json_data = json_obj["data"];
else json_data = json_obj["DATA"];
return json_data;
}
int DeezerService::Search(const QString &text, InternetSearch::SearchType searchby) {
pending_search_id_ = next_pending_search_id_;
pending_search_text_ = text;
pending_search_type_ = searchby;
next_pending_search_id_++;
if (text.isEmpty()) {
timer_searchdelay_->stop();
return pending_search_id_;
}
timer_searchdelay_->setInterval(searchdelay_);
timer_searchdelay_->start();
return pending_search_id_;
}
void DeezerService::StartSearch() {
if (access_token_.isEmpty()) {
emit SearchError(pending_search_id_, "Not authenticated.");
next_pending_search_id_ = 1;
ShowConfig();
return;
}
ClearSearch();
search_id_ = pending_search_id_;
search_text_ = pending_search_text_;
SendSearch();
}
void DeezerService::CancelSearch() {
ClearSearch();
}
void DeezerService::ClearSearch() {
search_id_ = 0;
search_text_.clear();
search_error_.clear();
albums_requested_ = 0;
albums_received_ = 0;
requests_album_.clear();
requests_song_.clear();
songs_.clear();
}
void DeezerService::SendSearch() {
emit UpdateStatus(tr("Searching..."));
QList<Param> parameters;
parameters << Param("q", search_text_);
QString searchparam;
switch (pending_search_type_) {
case InternetSearch::SearchType_Songs:
searchparam = "search/track";
parameters << Param("limit", QString::number(songssearchlimit_));
break;
case InternetSearch::SearchType_Albums:
case InternetSearch::SearchType_Artists:
default:
searchparam = "search/album";
parameters << Param("limit", QString::number(albumssearchlimit_));
break;
}
QNetworkReply *reply = CreateRequest(searchparam, parameters);
NewClosure(reply, SIGNAL(finished()), this, SLOT(SearchFinished(QNetworkReply*, int)), reply, search_id_);
}
void DeezerService::SearchFinished(QNetworkReply *reply, int id) {
reply->deleteLater();
if (id != search_id_) return;
QByteArray data = GetReplyData(reply);
if (data.isEmpty()) {
CheckFinish();
return;
}
QJsonValue json_value = ExtractData(data);
if (!json_value.isArray()) {
CheckFinish();
return;
}
QJsonArray json_data = json_value.toArray();
if (json_data.isEmpty()) {
Error(tr("No match."));
CheckFinish();
return;
}
for (const QJsonValue &value : json_data) {
if (!value.isObject()) {
Error("Invalid Json reply, data is not an object.", value);
continue;
}
QJsonObject json_obj = value.toObject();
if (!json_obj.contains("id") || !json_obj.contains("type")) {
Error("Invalid Json reply, item is missing ID or type.", json_obj);
continue;
}
QString type = json_obj["type"].toString();
if (!json_obj.contains("artist")) {
Error("Invalid Json reply, item missing artist.", json_obj);
continue;
}
QJsonValue json_value_artist = json_obj["artist"];
if (!json_value_artist.isObject()) {
Error("Invalid Json reply, item artist is not a object.", json_value_artist);
continue;
}
QJsonObject json_artist = json_value_artist.toObject();
if (!json_artist.contains("name")) {
Error("Invalid Json reply, artist data missing name.", json_artist);
continue;
}
QString artist = json_artist["name"].toString();
int album_id(0);
QString album;
QString cover;
if (type == "album") {
album_id = json_obj["id"].toInt();
album = json_obj["title"].toString();
cover = json_obj[coversize_].toString();
}
else if (type == "track") {
if (!json_obj.contains("album")) {
Error("Invalid Json reply, missing album data.", json_obj);
continue;
}
QJsonValue json_value_album = json_obj["album"];
if (!json_value_album.isObject()) {
Error("Invalid Json reply, album data is not an object.", json_value_album);
continue;
}
QJsonObject json_album = json_value_album.toObject();
if (!json_album.contains("id") || !json_album.contains("title")) {
Error("Invalid Json reply, album data is missing ID or title.", json_album);
continue;
}
album_id = json_album["id"].toInt();
album = json_album["title"].toString();
cover = json_album[coversize_].toString();
if (!fetchalbums_) {
Song song = ParseSong(album_id, album, artist, cover, value);
songs_ << song;
continue;
}
}
DeezerAlbumContext *album_ctx;
if (requests_album_.contains(album_id)) {
album_ctx = requests_album_.value(album_id);
album_ctx->search_id = search_id_;
continue;
}
album_ctx = CreateAlbum(album_id, artist, album, cover);
GetAlbum(album_ctx);
albums_requested_++;
if (albums_requested_ >= albumssearchlimit_) break;
}
if (albums_requested_ > 0) {
if (albums_requested_ == 1) emit UpdateStatus(tr("Retrieving %1 album...").arg(albums_requested_));
else emit UpdateStatus(tr("Retrieving %1 albums...").arg(albums_requested_));
emit ProgressSetMaximum(albums_requested_);
emit UpdateProgress(0);
}
CheckFinish();
}
DeezerAlbumContext *DeezerService::CreateAlbum(const int album_id, const QString &artist, const QString &album, const QString &cover) {
DeezerAlbumContext *album_ctx = new DeezerAlbumContext;
album_ctx->id = album_id;
album_ctx->artist = artist;
album_ctx->album = album;
album_ctx->cover = cover;
album_ctx->cover_url.setUrl(cover);
requests_album_.insert(album_id, album_ctx);
return album_ctx;
}
void DeezerService::GetAlbum(const DeezerAlbumContext *album_ctx) {
QList<Param> parameters;
QNetworkReply *reply = CreateRequest(QString("album/%1/tracks").arg(album_ctx->id), parameters);
NewClosure(reply, SIGNAL(finished()), this, SLOT(GetAlbumFinished(QNetworkReply*, int, int)), reply, search_id_, album_ctx->id);
}
void DeezerService::GetAlbumFinished(QNetworkReply *reply, int search_id, int album_id) {
reply->deleteLater();
if (!requests_album_.contains(album_id)) {
qLog(Error) << "Deezer: Got reply for cancelled album request: " << album_id;
CheckFinish();
return;
}
DeezerAlbumContext *album_ctx = requests_album_.value(album_id);
if (search_id != search_id_) {
if (album_ctx->search_id == search_id) delete requests_album_.take(album_ctx->id);
return;
}
albums_received_++;
emit UpdateProgress(albums_received_);
QByteArray data = GetReplyData(reply);
if (data.isEmpty()) {
delete requests_album_.take(album_ctx->id);
CheckFinish();
return;
}
QJsonValue json_value = ExtractData(data);
if (!json_value.isArray()) {
delete requests_album_.take(album_ctx->id);
CheckFinish();
return;
}
QJsonArray json_data = json_value.toArray();
if (json_data.isEmpty()) {
delete requests_album_.take(album_ctx->id);
CheckFinish();
return;
}
bool compilation = false;
bool multidisc = false;
SongList songs;
for (const QJsonValue &value : json_data) {
Song song = ParseSong(album_ctx->id, album_ctx->album, album_ctx->artist, album_ctx->cover, value);
if (!song.is_valid()) continue;
if (song.disc() >= 2) multidisc = true;
if (song.is_compilation()) compilation = true;
songs << song;
}
for (Song &song : songs) {
if (compilation) song.set_compilation_detected(true);
if (multidisc) {
QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc()));
song.set_album(album_full);
}
songs_ << song;
}
delete requests_album_.take(album_ctx->id);
CheckFinish();
}
Song DeezerService::ParseSong(const int album_id, const QString &album, const QString &album_artist, const QString &album_cover, const QJsonValue &value) {
if (!value.isObject()) {
Error("Invalid Json reply, track is not an object.", value);
return Song();
}
QJsonObject json_obj = value.toObject();
if (
!json_obj.contains("id") ||
!json_obj.contains("title") ||
!json_obj.contains("artist") ||
!json_obj.contains("duration") ||
!json_obj.contains("preview")
) {
Error("Invalid Json reply, track is missing one or more values.", json_obj);
return Song();
}
int song_id = json_obj["id"].toInt();
QString title = json_obj["title"].toString();
QJsonValue json_value_artist = json_obj["artist"];
QVariant q_duration = json_obj["duration"].toVariant();
int track(0);
if (json_obj.contains("track_position")) track = json_obj["track_position"].toInt();
int disc(0);
if (json_obj.contains("disk_number")) disc = json_obj["disk_number"].toInt();
QString preview = json_obj["preview"].toString();
if (!json_value_artist.isObject()) {
Error("Invalid Json reply, track artist is not an object.", json_value_artist);
return Song();
}
QJsonObject json_artist = json_value_artist.toObject();
if (!json_artist.contains("name")) {
Error("Invalid Json reply, track artist is missing name.", json_artist);
return Song();
}
QString artist = json_artist["name"].toString();
Song song;
song.set_source(Song::Source_Deezer);
song.set_id(song_id);
song.set_album_id(album_id);
if (artist != album_artist) song.set_albumartist(album_artist);
song.set_artist(artist);
song.set_album(album);
song.set_title(title);
song.set_disc(disc);
song.set_track(track);
song.set_art_automatic(album_cover);
QUrl url;
if (preview_) {
url.setUrl(preview);
quint64 duration = (30 * kNsecPerSec);
song.set_length_nanosec(duration);
}
else {
url.setScheme(url_handler_->scheme());
url.setPath(QString("track/%1").arg(QString::number(song_id)));
if (q_duration.isValid()) {
quint64 duration = q_duration.toULongLong() * kNsecPerSec;
song.set_length_nanosec(duration);
}
}
song.set_url(url);
song.set_valid(true);
return song;
}
bool DeezerService::GetStreamURL(const QUrl &original_url) {
#ifdef HAVE_DZMEDIA
stream_request_url_ = original_url;
dzmedia_->GetStreamURL(original_url);
return true;
#else
stream_request_url_ = QUrl();
return false;
#endif
}
#ifdef HAVE_DZMEDIA
void DeezerService::GetStreamURLFinished(const QUrl original_url, const QUrl media_url, const DZMedia::FileType dzmedia_filetype) {
Song::FileType filetype(Song::FileType_Unknown);
switch (dzmedia_filetype) {
case DZMedia::FileType_FLAC:
filetype = Song::FileType_FLAC;
break;
case DZMedia::FileType_MPEG:
filetype = Song::FileType_MPEG;
break;
case DZMedia::FileType_Stream:
filetype = Song::FileType_Stream;
break;
default:
filetype = Song::FileType_Unknown;
break;
}
stream_request_url_ = QUrl();
emit StreamURLReceived(original_url, media_url, filetype);
}
#endif
void DeezerService::CheckFinish() {
if (search_id_ == 0) return;
if (albums_requested_ <= albums_received_) {
if (songs_.isEmpty()) {
if (search_error_.isEmpty()) emit SearchError(search_id_, "Unknown error");
else emit SearchError(search_id_, search_error_);
}
else emit SearchResults(search_id_, songs_);
ClearSearch();
}
}
void DeezerService::Error(QString error, QVariant debug) {
qLog(Error) << "Deezer:" << error;
if (debug.isValid()) qLog(Debug) << debug;
if (search_id_ != 0) {
if (!error.isEmpty()) {
search_error_ += error;
search_error_ += "<br />";
}
CheckFinish();
}
if (!stream_request_url_.isEmpty()) {
emit StreamURLReceived(stream_request_url_, stream_request_url_, Song::FileType_Stream);
stream_request_url_ = QUrl();
}
}

View File

@@ -1,162 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DEEZERSERVICE_H
#define DEEZERSERVICE_H
#include "config.h"
#ifdef HAVE_DZMEDIA
# include <dzmedia.h>
#endif
#include <QtGlobal>
#include <QObject>
#include <QHash>
#include <QString>
#include <QUrl>
#include <QNetworkReply>
#include <QTimer>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include "core/song.h"
#include "internet/internetservices.h"
#include "internet/internetservice.h"
#include "internet/internetsearch.h"
class NetworkAccessManager;
class LocalRedirectServer;
class DeezerUrlHandler;
struct DeezerAlbumContext {
int id;
int search_id;
QString artist;
QString album;
QString cover;
QUrl cover_url;
};
Q_DECLARE_METATYPE(DeezerAlbumContext);
class DeezerService : public InternetService {
Q_OBJECT
public:
DeezerService(Application *app, QObject *parent);
~DeezerService();
static const Song::Source kSource;
static const int kAppID;
void ReloadSettings();
void Logout();
int Search(const QString &query, InternetSearch::SearchType searchby);
void CancelSearch();
const bool app_id() { return kAppID; }
const bool authenticated() { return !access_token_.isEmpty(); }
bool GetStreamURL(const QUrl &url);
signals:
void Login();
void LoginSuccess();
void LoginFailure(QString failure_reason);
void Authenticated();
void SearchResults(int id, SongList songs);
void SearchError(int id, QString message);
void UpdateStatus(QString text);
void ProgressSetMaximum(int max);
void UpdateProgress(int max);
void StreamURLReceived(const QUrl original_url, const QUrl media_url, const Song::FileType filetype);
public slots:
void ShowConfig();
private slots:
void StartAuthorisation();
void FetchAccessTokenFinished(QNetworkReply *reply);
void StartSearch();
void SearchFinished(QNetworkReply *reply, int search_id);
void GetAlbumFinished(QNetworkReply *reply, int search_id, int album_id);
#ifdef HAVE_DZMEDIA
void GetStreamURLFinished(const QUrl original_url, const QUrl media_url, const DZMedia::FileType dzmedia_filetype);
#endif
private:
void LoadAccessToken();
void RedirectArrived(LocalRedirectServer *server, QUrl url);
void RequestAccessToken(const QByteArray &code);
void SetExpiryTime(int expires_in_seconds);
void ClearSearch();
QNetworkReply *CreateRequest(const QString &ressource_name, const QList<QPair<QString, QString>> &params);
QByteArray GetReplyData(QNetworkReply *reply);
QJsonObject ExtractJsonObj(QByteArray &data);
QJsonValue ExtractData(QByteArray &data);
void SendSearch();
DeezerAlbumContext *CreateAlbum(const int album_id, const QString &artist, const QString &album, const QString &cover);
void GetAlbum(const DeezerAlbumContext *album_ctx);
Song ParseSong(const int album_id, const QString &album, const QString &album_artist, const QString &album_cover, const QJsonValue &value);
void CheckFinish();
void Error(QString error, QVariant debug = QString());
static const char *kApiUrl;
static const char *kOAuthUrl;
static const char *kOAuthAccessTokenUrl;
static const char *kOAuthRedirectUrl;
static const char *kSecretKey;
NetworkAccessManager *network_;
DeezerUrlHandler *url_handler_;
#ifdef HAVE_DZMEDIA
DZMedia *dzmedia_;
#endif
QTimer *timer_searchdelay_;
int searchdelay_;
int albumssearchlimit_;
int songssearchlimit_;
bool fetchalbums_;
QString coversize_;
bool preview_;
QString access_token_;
QDateTime expiry_time_;
int pending_search_id_;
int next_pending_search_id_;
QString pending_search_text_;
InternetSearch::SearchType pending_search_type_;
int search_id_;
QString search_text_;
QHash<int, DeezerAlbumContext*> requests_album_;
QHash<int, QUrl> requests_song_;
int albums_requested_;
int albums_received_;
SongList songs_;
QString search_error_;
QUrl stream_request_url_;
};
#endif // DEEZERSERVICE_H

View File

@@ -1,72 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <QObject>
#include <QString>
#include <QUrl>
#include "core/application.h"
#include "core/taskmanager.h"
#include "core/iconloader.h"
#include "core/logging.h"
#include "core/song.h"
#include "deezer/deezerservice.h"
#include "deezerurlhandler.h"
DeezerUrlHandler::DeezerUrlHandler(
Application *app, DeezerService *service)
: UrlHandler(service), app_(app), service_(service), task_id_(-1) {
connect(service, SIGNAL(StreamURLReceived(QUrl, QUrl, Song::FileType)), this, SLOT(GetStreamURLFinished(QUrl, QUrl, Song::FileType)));
}
UrlHandler::LoadResult DeezerUrlHandler::StartLoading(const QUrl &url) {
LoadResult ret(url);
if (task_id_ != -1) return ret;
last_original_url_ = url;
task_id_ = app_->task_manager()->StartTask(QString("Loading %1 stream...").arg(url.scheme()));
bool wait_for_url = service_->GetStreamURL(url);
if (wait_for_url) {
ret.type_ = LoadResult::WillLoadAsynchronously;
}
else {
CancelTask();
ret.type_ = LoadResult::TrackAvailable;
ret.media_url_ = url;
}
return ret;
}
void DeezerUrlHandler::GetStreamURLFinished(QUrl original_url, QUrl media_url, Song::FileType filetype) {
if (task_id_ == -1) return;
CancelTask();
emit AsyncLoadComplete(LoadResult(original_url, LoadResult::TrackAvailable, media_url, filetype));
}
void DeezerUrlHandler::CancelTask() {
app_->task_manager()->SetTaskFinished(task_id_);
task_id_ = -1;
}

View File

@@ -1,56 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DEEZERURLHANDLER_H
#define DEEZERURLHANDLER_H
#include <QObject>
#include <QString>
#include <QUrl>
#include "core/urlhandler.h"
#include "core/song.h"
#include "deezer/deezerservice.h"
class Application;
class DeezerService;
class DeezerUrlHandler : public UrlHandler {
Q_OBJECT
public:
DeezerUrlHandler(Application *app, DeezerService *service);
QString scheme() const { return service_->url_scheme(); }
LoadResult StartLoading(const QUrl &url);
void CancelTask();
private slots:
void GetStreamURLFinished(QUrl original_url, QUrl media_url, Song::FileType filetype);
private:
Application *app_;
DeezerService *service_;
int task_id_;
QUrl last_original_url_;
};
#endif

View File

@@ -1,536 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "version.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <deezer/deezer-connect.h>
#include <deezer/deezer-player.h>
#include <deezer/deezer-object.h>
#include <deezer/deezer-track.h>
#include <QtGlobal>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QByteArray>
#include <QString>
#include <QUrl>
#include <QDateTime>
#include "core/timeconstants.h"
#include "core/taskmanager.h"
#include "core/logging.h"
#include "core/song.h"
#include "engine_fwd.h"
#include "enginebase.h"
#include "enginetype.h"
#include "deezerengine.h"
#include "deezer/deezerservice.h"
#include "settings/deezersettingspage.h"
const char *DeezerEngine::kAppID = "303684";
const char *DeezerEngine::kProductID = "strawberry";
const char *DeezerEngine::kProductVersion = STRAWBERRY_VERSION_DISPLAY;
DeezerEngine::DeezerEngine(TaskManager *task_manager)
: EngineBase(),
state_(Engine::Empty),
position_(0),
stopping_(false) {
type_ = Engine::Deezer;
}
DeezerEngine::~DeezerEngine() {
if (player_) {
dz_object_release((dz_object_handle) player_);
player_ = nullptr;
}
if (connect_) {
dz_object_release((dz_object_handle) connect_);
connect_ = nullptr;
}
}
bool DeezerEngine::Init() {
qLog(Debug) << "Deezer native SDK Version:" << dz_connect_get_build_id() << QCoreApplication::applicationName().toUtf8();
struct dz_connect_configuration config;
memset(&config, 0, sizeof(struct dz_connect_configuration));
config.app_id = kAppID;
config.product_id = kProductID;
config.product_build_id = kProductVersion;
config.connect_event_cb = ConnectEventCallback;
connect_ = dz_connect_new(&config);
if (!connect_) {
qLog(Error) << "Deezer: Failed to create connect.";
return false;
}
qLog(Debug) << "Device ID:" << dz_connect_get_device_id(connect_);
dz_error_t dzerr(DZ_ERROR_NO_ERROR);
dzerr = dz_connect_debug_log_disable(connect_);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to disable debug log.";
return false;
}
dzerr = dz_connect_activate(connect_, this);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to activate connect.";
return false;
}
dz_connect_cache_path_set(connect_, nullptr, nullptr, QStandardPaths::writableLocation(QStandardPaths::CacheLocation).toUtf8().constData());
player_ = dz_player_new(connect_);
if (!player_) {
qLog(Error) << "Deezer: Failed to create player.";
return false;
}
dzerr = dz_player_activate(player_, this);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to activate player.";
return false;
}
dzerr = dz_player_set_event_cb(player_, PlayerEventCallback);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set event callback.";
return false;
}
dzerr = dz_player_set_metadata_cb(player_, PlayerMetaDataCallback);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set metadata callback.";
return false;
}
dzerr = dz_player_set_render_progress_cb(player_, PlayerProgressCallback, 1000);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set progress callback.";
return false;
}
dzerr = dz_player_set_crossfading_duration(player_, nullptr, nullptr, 3000);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set crossfade duration.";
return false;
}
dzerr = dz_connect_offline_mode(connect_, nullptr, nullptr, false);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set offline mode.";
return false;
}
LoadAccessToken();
ReloadSettings();
return true;
}
void DeezerEngine::ReloadSettings() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
QString quality = s.value("quality", "FLAC").toString();
s.endGroup();
dz_error_t dzerr;
if (quality == "MP3_128")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_STANDARD);
else if (quality == "MP3_320")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_HIGHQUALITY);
else if (quality == "FLAC")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_CDQUALITY);
else if (quality == "DATA_EFFICIENT")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_DATA_EFFICIENT);
else
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_CDQUALITY);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set quality.";
}
}
bool DeezerEngine::Initialised() const {
if (connect_ && player_) return true;
return false;
}
void DeezerEngine::LoadAccessToken() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
if (!s.contains("access_token") || !s.contains("expiry_time")) return;
access_token_ = s.value("access_token").toString();
expiry_time_ = s.value("expiry_time").toDateTime();
s.endGroup();
dz_error_t dzerr = dz_connect_set_access_token(connect_, nullptr, nullptr, access_token_.toUtf8().constData());
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set access token.";
}
}
bool DeezerEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Initialised()) return false;
stopping_ = false;
Engine::Base::Load(media_url, original_url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
dz_error_t dzerr = dz_player_load(player_, nullptr, nullptr, media_url.toString().toUtf8().constData());
if (dzerr != DZ_ERROR_NO_ERROR) return false;
return true;
}
bool DeezerEngine::Play(quint64 offset_nanosec) {
if (!Initialised()) return false;
stopping_ = false;
dz_error_t dzerr(DZ_ERROR_NO_ERROR);
if (state() == Engine::Paused) dzerr = dz_player_resume(player_, nullptr, nullptr);
else dzerr = dz_player_play(player_, nullptr, nullptr, DZ_PLAYER_PLAY_CMD_START_TRACKLIST, DZ_INDEX_IN_QUEUELIST_CURRENT);
if (dzerr != DZ_ERROR_NO_ERROR) return false;
Seek(offset_nanosec);
return true;
}
void DeezerEngine::Stop(bool stop_after) {
if (!Initialised()) return;
stopping_ = true;
dz_error_t dzerr = dz_player_stop(player_, nullptr, nullptr);
if (dzerr != DZ_ERROR_NO_ERROR) return;
}
void DeezerEngine::Pause() {
if (!Initialised()) return;
dz_error_t dzerr = dz_player_pause(player_, nullptr, nullptr);
if (dzerr != DZ_ERROR_NO_ERROR) return;
state_ = Engine::Paused;
emit StateChanged(state_);
}
void DeezerEngine::Unpause() {
if (!Initialised()) return;
dz_error_t dzerr = dz_player_resume(player_, nullptr, nullptr);
if (dzerr != DZ_ERROR_NO_ERROR) return;
}
void DeezerEngine::Seek(quint64 offset_nanosec) {
if (!Initialised()) return;
stopping_ = false;
dz_useconds_t offset = (offset_nanosec / kNsecPerUsec);
dz_error_t dzerr = dz_player_seek(player_, nullptr, nullptr, offset);
if (dzerr != DZ_ERROR_NO_ERROR) return;
}
void DeezerEngine::SetVolumeSW(uint percent) {
if (!Initialised()) return;
dz_error_t dzerr = dz_player_set_output_volume(player_, nullptr, nullptr, percent);
if (dzerr != DZ_ERROR_NO_ERROR) qLog(Error) << "Deezer: Failed to set volume.";
}
qint64 DeezerEngine::position_nanosec() const {
if (state() == Engine::Empty) return 0;
const qint64 result = (position_ * kNsecPerUsec);
return qint64(qMax(0ll, result));
}
qint64 DeezerEngine::length_nanosec() const {
if (state() == Engine::Empty) return 0;
const qint64 result = (end_nanosec_ - beginning_nanosec_);
return result;
}
EngineBase::OutputDetailsList DeezerEngine::GetOutputsList() const {
OutputDetailsList ret;
OutputDetails output;
output.name = "default";
output.description = "Default";
output.iconname = "soundcard";
ret << output;
return ret;
}
bool DeezerEngine::ValidOutput(const QString &output) {
return(true);
}
bool DeezerEngine::CustomDeviceSupport(const QString &output) {
return false;
}
bool DeezerEngine::ALSADeviceSupport(const QString &output) {
return false;
}
bool DeezerEngine::CanDecode(const QUrl &url) {
if (url.scheme() == "dzmedia") return true;
else return false;
}
void DeezerEngine::ConnectEventCallback(dz_connect_handle handle, dz_connect_event_handle event, void *delegate) {
dz_connect_event_t type = dz_connect_event_get_type(event);
//DeezerEngine *engine = reinterpret_cast<DeezerEngine*>(delegate);
switch (type) {
case DZ_CONNECT_EVENT_USER_OFFLINE_AVAILABLE:
qLog(Debug) << "CONNECT_EVENT USER_OFFLINE_AVAILABLE";
break;
case DZ_CONNECT_EVENT_USER_ACCESS_TOKEN_OK: {
const char* szAccessToken;
szAccessToken = dz_connect_event_get_access_token(event);
qLog(Debug) << "CONNECT_EVENT USER_ACCESS_TOKEN_OK Access_token :" << szAccessToken;
}
break;
case DZ_CONNECT_EVENT_USER_ACCESS_TOKEN_FAILED:
qLog(Debug) << "CONNECT_EVENT USER_ACCESS_TOKEN_FAILED";
break;
case DZ_CONNECT_EVENT_USER_LOGIN_OK:
qLog(Debug) << "Deezer CONNECT_EVENT USER_LOGIN_OK";
break;
case DZ_CONNECT_EVENT_USER_NEW_OPTIONS:
qLog(Debug) << "Deezer: CONNECT_EVENT USER_NEW_OPTIONS";
break;
case DZ_CONNECT_EVENT_USER_LOGIN_FAIL_NETWORK_ERROR:
qLog(Debug) << "Deezer: CONNECT_EVENT USER_LOGIN_FAIL_NETWORK_ERROR";
break;
case DZ_CONNECT_EVENT_USER_LOGIN_FAIL_BAD_CREDENTIALS:
qLog(Debug) << "Deezer: CONNECT_EVENT USER_LOGIN_FAIL_BAD_CREDENTIALS";
break;
case DZ_CONNECT_EVENT_USER_LOGIN_FAIL_USER_INFO:
qLog(Debug) << "Deezer: CONNECT_EVENT USER_LOGIN_FAIL_USER_INFO";
break;
case DZ_CONNECT_EVENT_USER_LOGIN_FAIL_OFFLINE_MODE:
qLog(Debug) << "Deezer: CONNECT_EVENT USER_LOGIN_FAIL_OFFLINE_MODE";
break;
case DZ_CONNECT_EVENT_ADVERTISEMENT_START:
qLog(Debug) << "Deezer: CONNECT_EVENTADVERTISEMENT_START";
break;
case DZ_CONNECT_EVENT_ADVERTISEMENT_STOP:
qLog(Debug) << "Deezer: CONNECT_EVENTADVERTISEMENT_STOP";
break;
case DZ_CONNECT_EVENT_UNKNOWN:
default:
qLog(Debug) << "Deezer: CONNECT_EVENTUNKNOWN or default (type =" << type;
break;
}
}
void DeezerEngine::PlayerEventCallback(dz_player_handle handle, dz_player_event_handle event, void *supervisor) {
DeezerEngine *engine = reinterpret_cast<DeezerEngine*>(supervisor);
dz_streaming_mode_t streaming_mode;
dz_index_in_queuelist idx;
dz_player_event_t type = dz_player_event_get_type(event);
if (!dz_player_event_get_queuelist_context(event, &streaming_mode, &idx)) {
streaming_mode = DZ_STREAMING_MODE_ONDEMAND;
idx = DZ_INDEX_IN_QUEUELIST_INVALID;
}
switch (type) {
case DZ_PLAYER_EVENT_LIMITATION_FORCED_PAUSE:
break;
case DZ_PLAYER_EVENT_QUEUELIST_LOADED:
break;
case DZ_PLAYER_EVENT_QUEUELIST_NO_RIGHT:
break;
case DZ_PLAYER_EVENT_QUEUELIST_NEED_NATURAL_NEXT:
break;
case DZ_PLAYER_EVENT_QUEUELIST_TRACK_NOT_AVAILABLE_OFFLINE:
engine->state_ = Engine::Error;
emit engine->StateChanged(engine->state_);
emit engine->InvalidSongRequested(engine->media_url_);
emit engine->Error("Track not available offline.");
break;
case DZ_PLAYER_EVENT_QUEUELIST_TRACK_RIGHTS_AFTER_AUDIOADS:
break;
case DZ_PLAYER_EVENT_QUEUELIST_SKIP_NO_RIGHT:
break;
case DZ_PLAYER_EVENT_QUEUELIST_TRACK_SELECTED:
break;
case DZ_PLAYER_EVENT_MEDIASTREAM_DATA_READY:
break;
case DZ_PLAYER_EVENT_MEDIASTREAM_DATA_READY_AFTER_SEEK:
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_START_FAILURE:
engine->state_ = Engine::Error;
emit engine->StateChanged(engine->state_);
emit engine->InvalidSongRequested(engine->media_url_);
emit engine->Error("Track start failure.");
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_START:
engine->state_ = Engine::Playing;
engine->position_ = 0;
emit engine->StateChanged(engine->state_);
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_END:
engine->state_ = Engine::Idle;
engine->position_ = 0;
emit engine->TrackEnded();
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_PAUSED:
engine->state_ = Engine::Paused;
emit engine->StateChanged(engine->state_);
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_UNDERFLOW:
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_RESUMED:
engine->state_ = Engine::Playing;
emit engine->StateChanged(engine->state_);
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_SEEKING:
break;
case DZ_PLAYER_EVENT_RENDER_TRACK_REMOVED:
if (!engine->stopping_) return;
engine->state_ = Engine::Empty;
engine->position_ = 0;
emit engine->StateChanged(engine->state_);
break;
case DZ_PLAYER_EVENT_UNKNOWN:
default:
qLog(Error) << "Deezer: Unknown player event" << type;
break;
}
}
void DeezerEngine::PlayerProgressCallback(dz_player_handle handle, dz_useconds_t progress, void *userdata) {
DeezerEngine *engine = reinterpret_cast<DeezerEngine*>(userdata);
engine->position_ = progress;
}
void DeezerEngine::PlayerMetaDataCallback(dz_player_handle handle, dz_track_metadata_handle metadata, void *userdata) {
const dz_media_track_detailed_infos_t *track_metadata = dz_track_metadata_get_format_header(metadata);
DeezerEngine *engine = reinterpret_cast<DeezerEngine*>(userdata);
Engine::SimpleMetaBundle bundle;
switch (track_metadata->format) {
case DZ_MEDIA_FORMAT_AUDIO_MPEG:
bundle.filetype = Song::FileType_MPEG;
break;
case DZ_MEDIA_FORMAT_AUDIO_FLAC:
bundle.filetype = Song::FileType_FLAC;
break;
case DZ_MEDIA_FORMAT_AUDIO_PCM:
bundle.filetype = Song::FileType_PCM;
break;
default:
return;
}
bundle.url = engine->original_url_;
bundle.title = QString();
bundle.artist = QString();
bundle.comment = QString();
bundle.album = QString();
bundle.length = 0;
bundle.year = 0;
bundle.tracknr = 0;
bundle.samplerate = track_metadata->audio.samples.sample_rate;
bundle.bitdepth = 0;
bundle.bitrate = track_metadata->average_bitrate / 1000;
bundle.lyrics = QString();
emit engine->MetaData(bundle);
}

View File

@@ -1,94 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DEEZERENGINE_H
#define DEEZERENGINE_H
#include "config.h"
#include <stdbool.h>
#include <deezer/deezer-connect.h>
#include <deezer/deezer-player.h>
#include <QtGlobal>
#include <QObject>
#include <QString>
#include <QUrl>
#include <QDateTime>
#include "engine_fwd.h"
#include "enginebase.h"
class TaskManager;
class DeezerEngine : public Engine::Base {
Q_OBJECT
public:
DeezerEngine(TaskManager *task_manager);
~DeezerEngine();
bool Init();
void ReloadSettings();
Engine::State state() const { return state_; }
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false);
void Pause();
void Unpause();
void Seek(quint64 offset_nanosec);
protected:
void SetVolumeSW(uint percent);
public:
virtual qint64 position_nanosec() const;
virtual qint64 length_nanosec() const;
OutputDetailsList GetOutputsList() const;
bool ValidOutput(const QString &output);
QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
private:
static const char *kAppID;
static const char *kProductVersion;
static const char *kProductID;
static const char *kPath;
Engine::State state_;
dz_connect_handle connect_;
dz_player_handle player_;
QString access_token_;
QDateTime expiry_time_;
qint64 position_;
bool stopping_;
bool Initialised() const;
bool CanDecode(const QUrl &url);
static void ConnectEventCallback(dz_connect_handle handle, dz_connect_event_handle event, void *delegate);
static void PlayerEventCallback(dz_player_handle handle, dz_player_event_handle event, void *supervisor);
static void PlayerMetaDataCallback(dz_player_handle handle, dz_track_metadata_handle metadata, void *userdata);
static void PlayerProgressCallback(dz_player_handle handle, dz_useconds_t progress, void *userdata);
public slots:
void LoadAccessToken();
};
#endif

View File

@@ -32,7 +32,6 @@ Engine::EngineType EngineTypeFromName(QString enginename) {
else if (lower == "xine") return Engine::Xine;
else if (lower == "vlc") return Engine::VLC;
else if (lower == "phonon") return Engine::Phonon;
else if (lower == "deezer") return Engine::Deezer;
else return Engine::None;
}
@@ -42,7 +41,6 @@ QString EngineName(Engine::EngineType enginetype) {
case Engine::Xine: return QString("xine");
case Engine::VLC: return QString("vlc");
case Engine::Phonon: return QString("phonon");
case Engine::Deezer: return QString("deezer");
case Engine::None:
default: return QString("None");
}
@@ -54,7 +52,6 @@ QString EngineDescription(Engine::EngineType enginetype) {
case Engine::Xine: return QString("Xine");
case Engine::VLC: return QString("VLC");
case Engine::Phonon: return QString("Phonon");
case Engine::Deezer: return QString("Deezer");
case Engine::None:
default: return QString("None");

View File

@@ -32,8 +32,7 @@ enum EngineType {
GStreamer,
VLC,
Xine,
Phonon,
Deezer
Phonon
};
Engine::EngineType EngineTypeFromName(QString enginename);

View File

@@ -75,7 +75,7 @@ class PlaylistHeader;
// that uses Gtk to paint row backgrounds, ignoring any custom brush or palette the caller set in the QStyleOption.
// That breaks our currently playing track animation, which relies on the background painted by Qt to be transparent.
// This proxy style uses QCommonStyle to paint the affected elements.
// This class is used by tidal and deezer search view as well.
// This class is used by internet search view as well.
class PlaylistProxyStyle : public QProxyStyle {
public:
PlaylistProxyStyle(QStyle *base);

View File

@@ -94,9 +94,6 @@ void BackendSettingsPage::Load() {
#ifdef HAVE_PHONON
ui_->combobox_engine->addItem(IconLoader::Load("speaker"), EngineDescription(Engine::Phonon), QVariant::fromValue(Engine::Phonon));
#endif
#ifdef HAVE_DEEZER
ui_->combobox_engine->addItem(IconLoader::Load("deezer"), EngineDescription(Engine::Deezer), QVariant::fromValue(Engine::Deezer));
#endif
enginetype_current_ = enginetype;
output_current_ = s_.value("output", QString()).toString();

View File

@@ -1,142 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QObject>
#include <QString>
#include <QSettings>
#include <QMessageBox>
#include <QEvent>
#include "deezersettingspage.h"
#include "ui_deezersettingspage.h"
#include "core/application.h"
#include "core/iconloader.h"
#include "internet/internetservices.h"
#include "deezer/deezerservice.h"
const char *DeezerSettingsPage::kSettingsGroup = "Deezer";
DeezerSettingsPage::DeezerSettingsPage(SettingsDialog *parent)
: SettingsPage(parent),
ui_(new Ui::DeezerSettingsPage),
service_(dialog()->app()->internet_services()->Service<DeezerService>()) {
ui_->setupUi(this);
setWindowIcon(IconLoader::Load("deezer"));
connect(ui_->button_login, SIGNAL(clicked()), SLOT(LoginClicked()));
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked()));
connect(this, SIGNAL(Login()), service_, SLOT(StartAuthorisation()));
connect(service_, SIGNAL(LoginFailure(QString)), SLOT(LoginFailure(QString)));
connect(service_, SIGNAL(LoginSuccess()), SLOT(LoginSuccess()));
dialog()->installEventFilter(this);
ui_->combobox_quality->addItem("MP3 128kbps \"Standard\"", "MP3_128");
ui_->combobox_quality->addItem("MP3 320kbps \"High Quality\"", "MP3_320");
ui_->combobox_quality->addItem("FLAC \"CD Quality\"", "FLAC");
ui_->combobox_quality->addItem("\"Data Efficient\"", "DATA_EFFICIENT");
ui_->combobox_coversize->addItem("Small", "cover_small");
ui_->combobox_coversize->addItem("Medium", "cover_medium");
ui_->combobox_coversize->addItem("Big", "cover_big");
ui_->combobox_coversize->addItem("XL", "cover_xl");
}
DeezerSettingsPage::~DeezerSettingsPage() { delete ui_; }
void DeezerSettingsPage::Load() {
QSettings s;
s.beginGroup(kSettingsGroup);
ui_->checkbox_enable->setChecked(s.value("enabled", false).toBool());
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_quality, "quality", "FLAC");
ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 100).toInt());
ui_->spinbox_songssearchlimit->setValue(s.value("songssearchlimit", 100).toInt());
ui_->checkbox_fetchalbums->setChecked(s.value("fetchalbums", false).toBool());
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_coversize, "coversize", "cover_big");
#if defined(HAVE_DEEZER) || defined(HAVE_DZMEDIA)
bool preview(false);
#else
bool preview(true);
#endif
ui_->checkbox_preview->setChecked(s.value("preview", preview).toBool());
s.endGroup();
if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
else ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
}
void DeezerSettingsPage::Save() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("enabled", ui_->checkbox_enable->isChecked());
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());
s.setValue("songssearchlimit", ui_->spinbox_songssearchlimit->value());
s.setValue("fetchalbums", ui_->checkbox_fetchalbums->isChecked());
s.setValue("coversize", ui_->combobox_coversize->itemData(ui_->combobox_coversize->currentIndex()));
s.setValue("preview", ui_->checkbox_preview->isChecked());
s.endGroup();
service_->ReloadSettings();
}
void DeezerSettingsPage::LoginClicked() {
emit Login();
ui_->button_login->setEnabled(false);
}
bool DeezerSettingsPage::eventFilter(QObject *object, QEvent *event) {
if (object == dialog() && event->type() == QEvent::Enter) {
ui_->button_login->setEnabled(true);
return false;
}
return SettingsPage::eventFilter(object, event);
}
void DeezerSettingsPage::LogoutClicked() {
service_->Logout();
ui_->button_login->setEnabled(true);
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
}
void DeezerSettingsPage::LoginSuccess() {
if (!this->isVisible()) return;
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
ui_->button_login->setEnabled(false);
}
void DeezerSettingsPage::LoginFailure(QString failure_reason) {
if (!this->isVisible()) return;
QMessageBox::warning(this, tr("Authentication failed"), failure_reason);
}

View File

@@ -1,60 +0,0 @@
/*
* Strawberry Music Player
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DEEZERSETTINGSPAGE_H
#define DEEZERSETTINGSPAGE_H
#include <QObject>
#include <QString>
#include <QEvent>
#include "settings/settingspage.h"
class DeezerService;
class Ui_DeezerSettingsPage;
class DeezerSettingsPage : public SettingsPage {
Q_OBJECT
public:
explicit DeezerSettingsPage(SettingsDialog* parent = nullptr);
~DeezerSettingsPage();
static const char *kSettingsGroup;
void Load();
void Save();
bool eventFilter(QObject *object, QEvent *event);
signals:
void Login();
private slots:
void LoginClicked();
void LogoutClicked();
void LoginSuccess();
void LoginFailure(QString failure_reason);
private:
Ui_DeezerSettingsPage* ui_;
DeezerService *service_;
};
#endif

View File

@@ -1,354 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DeezerSettingsPage</class>
<widget class="QWidget" name="DeezerSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>575</height>
</rect>
</property>
<property name="windowTitle">
<string>Deezer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkbox_enable">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="credential_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Authentication</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="button_login">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer_auth">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupbox_preferences">
<property name="title">
<string>Preferences</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_quality">
<item>
<widget class="QLabel" name="label_quality">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Audio quality</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_quality"/>
</item>
<item>
<spacer name="spacer_quality">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_searchdelay">
<item>
<widget class="QLabel" name="label_searchdelay">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Search delay</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinbox_searchdelay">
<property name="suffix">
<string>ms</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="singleStep">
<number>50</number>
</property>
<property name="value">
<number>1500</number>
</property>
</widget>
</item>
<item>
<spacer name="spacer_searchdelay">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_albumssearchlimit">
<item>
<widget class="QLabel" name="label_albumssearchlimit">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Albums search limit</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinbox_albumssearchlimit">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<spacer name="spacer_albumssearchlimit">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_songssearchlimit">
<item>
<widget class="QLabel" name="label_songssearchlimit">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Songs search limit</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinbox_songssearchlimit">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<spacer name="spacer_songssearchlimit">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkbox_fetchalbums">
<property name="text">
<string>Fetch entire albums when searching songs</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_coversize">
<item>
<widget class="QLabel" name="label_coversize">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Album cover size</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_coversize"/>
</item>
<item>
<spacer name="spacer_coversize">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkbox_preview">
<property name="text">
<string>Use 30 seconds preview streams</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer_middle">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="spacer_bottom">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_deezer">
<property name="minimumSize">
<size>
<width>200</width>
<height>62</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>62</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../../data/data.qrc">:/pictures/deezer.png</pixmap>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LoginStateWidget</class>
<extends>QWidget</extends>
<header>widgets/loginstatewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../data/data.qrc"/>
<include location="../../data/icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -65,9 +65,6 @@
#ifdef HAVE_STREAM_TIDAL
# include "tidalsettingspage.h"
#endif
#ifdef HAVE_STREAM_DEEZER
# include "deezersettingspage.h"
#endif
#include "ui_settingsdialog.h"
@@ -136,15 +133,12 @@ SettingsDialog::SettingsDialog(Application *app, QWidget *parent)
AddPage(Page_GlobalShortcuts, new GlobalShortcutsSettingsPage(this), iface);
#endif
#if defined(HAVE_STREAM_TIDAL) || defined(HAVE_STREAM_DEEZER)
#if defined(HAVE_STREAM_TIDAL)
QTreeWidgetItem *streaming = AddCategory(tr("Streaming"));
#endif
#ifdef HAVE_STREAM_TIDAL
AddPage(Page_Tidal, new TidalSettingsPage(this), streaming);
#endif
#ifdef HAVE_STREAM_DEEZER
AddPage(Page_Deezer, new DeezerSettingsPage(this), streaming);
#endif
// List box
connect(ui_->list, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(CurrentItemChanged(QTreeWidgetItem*)));

View File

@@ -83,7 +83,6 @@ public:
Page_Proxy,
Page_Scrobbler,
Page_Tidal,
Page_Deezer,
};
enum Role {