Remove deezer
@ -22,7 +22,7 @@ before_install:
|
||||
git pull;
|
||||
brew update;
|
||||
brew unlink python;
|
||||
brew install glib pkgconfig protobuf protobuf-c qt gettext;
|
||||
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext;
|
||||
brew install sqlite --with-fts;
|
||||
brew install gstreamer gst-plugins-base;
|
||||
brew install gst-plugins-good --with-flac;
|
||||
@ -35,8 +35,8 @@ before_install:
|
||||
ls /usr/local/lib/gstreamer-1.0;
|
||||
fi
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_STREAM_DEEZER=ON -DENABLE_TRANSLATIONS=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_STREAM_DEEZER=ON -DENABLE_TRANSLATIONS=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_TRANSLATIONS=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_TRANSLATIONS=ON ; fi
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
|
@ -112,8 +112,6 @@ pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
|
||||
pkg_check_modules(LIBUSBMUXD libusbmuxd)
|
||||
pkg_check_modules(LIBPLIST libplist)
|
||||
pkg_check_modules(LIBDEEZER libdeezer)
|
||||
pkg_check_modules(LIBDZMEDIA libdzmedia)
|
||||
find_package(Gettext)
|
||||
|
||||
if(WIN32)
|
||||
@ -291,18 +289,6 @@ optional_component(PHONON OFF "Engine: Phonon backend (UNSTABLE)"
|
||||
DEPENDS "phonon4qt5" PHONON_FOUND
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
optional_component(DEEZER ON "Engine: Deezer backend"
|
||||
DEPENDS "libdeezer" LIBDEEZER_FOUND
|
||||
)
|
||||
else ()
|
||||
optional_component(DEEZER ON "Engine: Deezer backend"
|
||||
DEPENDS "Linux" LINUX
|
||||
DEPENDS "libdeezer" LIBDEEZER_FOUND
|
||||
DEPENDS "libpulse" LIBPULSE_FOUND
|
||||
)
|
||||
endif()
|
||||
|
||||
optional_component(CHROMAPRINT ON "Chromaprint (Tag fetching from Musicbrainz)"
|
||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||
)
|
||||
@ -359,21 +345,6 @@ optional_component(TRANSLATIONS OFF "Translations (No languages included yet)"
|
||||
|
||||
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
|
||||
|
||||
if (LIBDZMEDIA_FOUND OR LIBDEEZER_FOUND)
|
||||
optional_component(STREAM_DEEZER ON "Streaming: Deezer support")
|
||||
else()
|
||||
optional_component(STREAM_DEEZER OFF "Streaming: Deezer support")
|
||||
endif()
|
||||
|
||||
optional_component(DZMEDIA ON "DZMedia"
|
||||
DEPENDS "libdzmedia" LIBDZMEDIA_FOUND
|
||||
DEPENDS "Deezer support" HAVE_STREAM_DEEZER
|
||||
)
|
||||
|
||||
if (HAVE_STREAM_DEEZER AND NOT HAVE_DZMEDIA AND NOT HAVE_DEEZER)
|
||||
message(STATUS "Deezer is enabled, but not DZMedia or Deezer engine, only preview streams will be available.")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
option(USE_BUNDLE "Bundle macOS dependencies" OFF)
|
||||
elseif(WIN32)
|
||||
@ -418,8 +389,8 @@ add_custom_target(uninstall
|
||||
|
||||
# Show a summary of what we have enabled
|
||||
summary_show()
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON AND NOT HAVE_DEEZER)
|
||||
message(FATAL_ERROR "You need to have either GStreamer, Xine, VLC, Phonon or Deezer to compile!")
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON)
|
||||
message(FATAL_ERROR "You need to have either GStreamer, Xine, VLC or Phonon to compile!")
|
||||
elseif(NOT HAVE_GSTREAMER)
|
||||
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
||||
endif()
|
||||
|
@ -25,7 +25,7 @@ Strawberry is a audio player and music collection organizer. It is a fork of Cle
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Streaming support for Tidal and Deezer [*]
|
||||
* Streaming support for Tidal
|
||||
* Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
|
||||
It has so far been tested to work on Linux, OpenBSD, macOS and Windows.
|
||||
@ -48,7 +48,7 @@ To build Strawberry from source you need the following installed on your system
|
||||
* [ALSA library (linux)](https://www.alsa-project.org/)
|
||||
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org), [Deezer](https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip) or [Phonon](https://techbase.kde.org/Phonon)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org) or [Phonon](https://techbase.kde.org/Phonon)
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
@ -57,11 +57,9 @@ Optional dependencies:
|
||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
|
||||
|
||||
Either GStreamer, Xine, VLC, Deezer or Phonon engine is required, but only GStreamer is fully implemented so far.
|
||||
Either GStreamer, Xine, VLC or Phonon engine is required, but only GStreamer is fully implemented so far.
|
||||
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
||||
|
||||
Deezer support require deezer's own engine, and usually only works on Windows. It is not available on Linux unless you specifically compile with the deezer library, which currently only works on Ubuntu Xenial. The Deezer SDK can be found here: https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip
|
||||
|
||||
### :wrench: Compiling from source
|
||||
|
||||
### Get the code:
|
||||
|
@ -29,7 +29,6 @@
|
||||
<file>pictures/osd_background.png</file>
|
||||
<file>pictures/osd_shadow_corner.png</file>
|
||||
<file>pictures/osd_shadow_edge.png</file>
|
||||
<file>pictures/deezer.png</file>
|
||||
<file>pictures/nyancat.png</file>
|
||||
<file>pictures/rainbowdash.png</file>
|
||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||
|
@ -84,7 +84,6 @@
|
||||
<file>icons/128x128/zoom-in.png</file>
|
||||
<file>icons/128x128/zoom-out.png</file>
|
||||
<file>icons/128x128/tidal.png</file>
|
||||
<file>icons/128x128/deezer.png</file>
|
||||
<file>icons/128x128/scrobble.png</file>
|
||||
<file>icons/128x128/scrobble-disabled.png</file>
|
||||
<file>icons/64x64/albums.png</file>
|
||||
@ -171,7 +170,6 @@
|
||||
<file>icons/64x64/zoom-in.png</file>
|
||||
<file>icons/64x64/zoom-out.png</file>
|
||||
<file>icons/64x64/tidal.png</file>
|
||||
<file>icons/64x64/deezer.png</file>
|
||||
<file>icons/64x64/scrobble.png</file>
|
||||
<file>icons/64x64/scrobble-disabled.png</file>
|
||||
<file>icons/48x48/albums.png</file>
|
||||
@ -351,7 +349,6 @@
|
||||
<file>icons/32x32/zoom-in.png</file>
|
||||
<file>icons/32x32/zoom-out.png</file>
|
||||
<file>icons/32x32/tidal.png</file>
|
||||
<file>icons/32x32/deezer.png</file>
|
||||
<file>icons/32x32/scrobble.png</file>
|
||||
<file>icons/32x32/scrobble-disabled.png</file>
|
||||
<file>icons/22x22/albums.png</file>
|
||||
@ -442,7 +439,6 @@
|
||||
<file>icons/22x22/zoom-in.png</file>
|
||||
<file>icons/22x22/zoom-out.png</file>
|
||||
<file>icons/22x22/tidal.png</file>
|
||||
<file>icons/22x22/deezer.png</file>
|
||||
<file>icons/22x22/scrobble.png</file>
|
||||
<file>icons/22x22/scrobble-disabled.png</file>
|
||||
</qresource>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 28 KiB |
5
dist/debian/copyright
vendored
@ -25,8 +25,6 @@ Files: src/core/main.h
|
||||
src/engine/enginetype.h
|
||||
src/engine/alsadevicefinder.cpp
|
||||
src/engine/alsadevicefinder.h
|
||||
src/engine/deezerengine.cpp
|
||||
src/engine/deezerengine.h
|
||||
src/engine/devicefinder.cpp
|
||||
src/engine/devicefinder.h
|
||||
src/engine/enginedevice.cpp
|
||||
@ -39,8 +37,6 @@ Files: src/core/main.h
|
||||
src/settings/backendsettingspage.h
|
||||
src/settings/scrobblersettingspage.cpp
|
||||
src/settings/scrobblersettingspage.h
|
||||
src/settings/deezersettingspage.cpp
|
||||
src/settings/deezersettingspage.h
|
||||
src/settings/tidalsettingspage.cpp
|
||||
src/settings/tidalsettingspage.h
|
||||
src/covermanager/lastfmcoverprovider.cpp
|
||||
@ -58,7 +54,6 @@ Files: src/core/main.h
|
||||
src/lyrics/*
|
||||
src/scrobbler/*
|
||||
src/tidal/*
|
||||
src/deezer/*
|
||||
src/transcoder/transcoderoptionswavpack.cpp
|
||||
src/transcoder/transcoderoptionswavpack.h
|
||||
Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
|
3
dist/debian/rules
vendored
@ -12,8 +12,7 @@ configure-stamp:
|
||||
dh_testdir
|
||||
cmake .. \
|
||||
-DCMAKE_INSTALL_PREFIX=$(CURDIR)/debian/strawberry/usr \
|
||||
-DENABLE_TRANSLATIONS=ON \
|
||||
-DENABLE_STREAM_DEEZER=ON
|
||||
-DENABLE_TRANSLATIONS=ON
|
||||
touch configure-stamp
|
||||
|
||||
build: build-stamp
|
||||
|
1
dist/pacman/PKGBUILD.in
vendored
@ -52,7 +52,6 @@ build() {
|
||||
cmake ../${pkgname}-@STRAWBERRY_VERSION_PACKAGE@ \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DUSE_SYSTEM_TAGLIB=ON \
|
||||
-DENABLE_STREAM_DEEZER=ON \
|
||||
-DENABLE_PHONON=ON \
|
||||
-DENABLE_TRANSLATIONS=ON
|
||||
make -j$(nproc)
|
||||
|
2
dist/unix/org.strawbs.strawberry.appdata.xml
vendored
@ -34,7 +34,7 @@
|
||||
<li>Audio analyzer</li>
|
||||
<li>Audio equalizer</li>
|
||||
<li>Transfer music to iPod, iPhone, MTP or mass-storage USB player</li>
|
||||
<li>Streaming support for Tidal and Deezer</li>
|
||||
<li>Streaming support for Tidal</li>
|
||||
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
|
||||
</ul>
|
||||
</description>
|
||||
|
2
dist/windows/strawberry-debug-x64.nsi.in
vendored
@ -162,7 +162,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x64.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@ -403,7 +402,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x64.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
2
dist/windows/strawberry-debug-x86.nsi.in
vendored
@ -162,7 +162,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x86.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@ -403,7 +402,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x86.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
2
dist/windows/strawberry-x64.nsi.in
vendored
@ -161,7 +161,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x64.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@ -370,7 +369,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x64.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
2
dist/windows/strawberry-x86.nsi.in
vendored
@ -161,7 +161,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x86.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@ -370,7 +369,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x86.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
@ -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() |