Remove deezer
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,6 @@ signals:
|
||||
#endif
|
||||
|
||||
InternetSearchView *tidal_search_view_;
|
||||
InternetSearchView *deezer_search_view_;
|
||||
|
||||
QAction *collection_show_all_;
|
||||
QAction *collection_show_duplicates_;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@ class Song {
|
||||
Source_Device = 4,
|
||||
Source_Stream = 5,
|
||||
Source_Tidal = 6,
|
||||
Source_Deezer = 7,
|
||||
};
|
||||
|
||||
enum FileType {
|
||||
|
||||
@@ -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> ¶ms) {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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>> ¶ms);
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -32,8 +32,7 @@ enum EngineType {
|
||||
GStreamer,
|
||||
VLC,
|
||||
Xine,
|
||||
Phonon,
|
||||
Deezer
|
||||
Phonon
|
||||
};
|
||||
|
||||
Engine::EngineType EngineTypeFromName(QString enginename);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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*)));
|
||||
|
||||
@@ -83,7 +83,6 @@ public:
|
||||
Page_Proxy,
|
||||
Page_Scrobbler,
|
||||
Page_Tidal,
|
||||
Page_Deezer,
|
||||
};
|
||||
|
||||
enum Role {
|
||||
|
||||
Reference in New Issue
Block a user