1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 12:10:47 +02:00

Compare commits

...

85 Commits

Author SHA1 Message Date
Dominik Schmidt
8ddc270c8c Workaround crash from SpotifyParser 2016-01-13 03:28:14 +01:00
Dominik Schmidt
47e09fbaca Fix url lookup by adding LinkParser plugins 2016-01-13 02:53:28 +01:00
Dominik Schmidt
0585acc869 Merge pull request #389 from TheOneRing/snore_next
Use Snore 0.7
2016-01-11 14:50:35 +01:00
Dominik Schmidt
8c8cd88388 OSX: Try to fix HLS 2016-01-11 14:48:14 +01:00
Dominik Schmidt
9e91ad6549 Reenable a bunch of plugins in OSX nightlies 2016-01-11 01:19:41 +01:00
Hannah von Reth
192627a4ea Update nsis for snore 2016-01-08 15:09:28 +01:00
Dominik Schmidt
ee1f19c34e Merge pull request #337 from TheOneRing/window-preview
[Proof of Concept] Album art as Windows window preview
2016-01-08 01:02:34 +01:00
Hannah von Reth
a41af0ed7f Use snorenotify from git 2016-01-07 20:18:23 +01:00
Dominik Schmidt
feffda8339 Build libtomahawk instead of libTomahawk on OSX 2016-01-07 15:20:58 +01:00
Dominik Schmidt
4b7f8929fd Fix OSX deployment 2016-01-07 15:20:58 +01:00
Dominik Schmidt
acdd0e3b9f Add type (collection/resolver) to resolve / search javascript arguments 2016-01-07 02:14:51 +01:00
Dominik Schmidt
4ba32e0add Add resolvers to diagnostics dialog 2016-01-07 00:43:11 +01:00
Dominik Schmidt
9b1b7be207 Fix lastfm infoplugin for usage without account 2016-01-07 00:42:46 +01:00
Dominik Schmidt
d7e5ce6e4e Put crashreporter topLabel text into Tomahawk instead of libcrashreporter-qt 2016-01-06 05:05:52 +01:00
Dominik Schmidt
684e5f3a58 Update libcrashreporter-qt once more 2016-01-06 04:27:20 +01:00
Dominik Schmidt
3d0584a351 Add comment text edit to crash reporter 2016-01-06 03:42:09 +01:00
Dominik Schmidt
2a43e9aba5 Fix what's new menu on Qt 5 on OS X 2016-01-06 02:16:05 +01:00
Dominik Schmidt
cde395edf0 Differentiate between removable and deletable viewpages to allow showing whatsnew-page after removing it 2016-01-06 01:18:27 +01:00
Dominik Schmidt
5aa875c40a Stop JSResolverHelper on destruction and block any calls 2016-01-05 22:49:31 +01:00
Dominik Schmidt
e4146c98c7 ScriptEngine is already parented, don't track in a smart pointer 2016-01-05 22:49:31 +01:00
Dominik Schmidt
d2dc01e6a3 Update libcrashreporter to show crash id after upload 2016-01-04 22:49:16 +01:00
Jason Herskowitz
8fbd3b9872 Clean up nav icons in mac toolbar 2015-12-23 20:57:17 -05:00
Christian Muehlhaeuser
ac4debbe02 Merge pull request #358 from tomahawk-player/osx-fixes
[OSX] Various build adjustments
2015-12-22 19:12:48 +01:00
Dominik Schmidt
c4197374ca Fixed extracmakemodules instead of remove item hack 2015-12-22 19:02:37 +01:00
Dominik Schmidt
a36287c2ed Fix OSX bundle icon 2015-12-22 19:02:36 +01:00
Dominik Schmidt
054650743d No more Mr. Nice Guy, No more Mr. Cleaaaheean ... except today 2015-12-17 01:25:26 +01:00
Dominik Schmidt
ea55329c67 Add url click handler to push buttons in resolver configs 2015-12-17 01:07:12 +01:00
Dominik Schmidt
5a45bd0882 Small JS fixes 2015-12-15 01:11:04 +01:00
Dominik Schmidt
5395908cbe Merge pull request #339 from tomahawk-player/promisify-all-the-things
[WIP] Promisify JSResolver
2015-12-14 21:51:11 +01:00
Dominik Schmidt
213bf1cd08 Don't leak script jobs 2015-12-14 21:49:45 +01:00
Enno Gottschalk
efc31e6246 Fix Tomahawk.Collection.revision() stub function 2015-12-14 14:23:10 +01:00
Christian Muehlhaeuser
3d511b65e1 Better size for toolbar search-field on OS X. Not sure why the auto-sizing doesn't work as expected. 2015-12-13 10:18:44 +01:00
Enno Gottschalk
5a34bae757 Added stub Tomahawk.Collection.revision() function in tomahawk.js 2015-12-11 03:19:35 +01:00
Dominik Schmidt
0223fd5992 Ship hatchet account again. why was this disabled?! 2015-12-10 22:51:19 +01:00
Dominik Schmidt
66a2d3cb88 Fix snorenotify paths 2015-12-10 22:50:57 +01:00
Dominik Schmidt
75427a7af2 Fixup libs that were fixed up but aren't correct anymore (because they reference ../lib while we use ../Frameworks/ 2015-12-10 02:46:20 +01:00
Dominik Schmidt
92c66d7252 Fix snorenotify plugin lookup path 2015-12-10 02:45:47 +01:00
Dominik Schmidt
2afc578d7a Fix qt5 plugin lookup path 2015-12-10 02:45:36 +01:00
Dominik Schmidt
0791937032 Fixup last commit 2015-12-09 13:44:11 +01:00
Dominik Schmidt
45c96e7367 Update VLC plugin paths 2015-12-09 13:44:11 +01:00
Dominik Schmidt
4d351168da Don't compile 64x64 icon into OSX iconset 2015-12-09 13:44:11 +01:00
Dominik Schmidt
86f08b307e Fix typo 2015-12-09 13:44:11 +01:00
Dominik Schmidt
99fe5c8e8e Look for keg-only libvlc 2015-12-09 13:44:11 +01:00
Dominik Schmidt
daf3034d99 Only look for and link against libvlc, not libvlccore 2015-12-09 13:44:11 +01:00
Dominik Schmidt
e8d73fe0cc Merge pull request #317 from TheOneRing/snorenotify-0.6
Snorenotify 0.6
2015-12-09 13:20:42 +01:00
Christian Muehlhaeuser
de78768be6 Delay AudioControls repaint until we've updated all elements. Looks smoother. 2015-12-05 09:38:26 +01:00
Dominik Schmidt
d0c1d83f90 Remove more comp hacks 2015-11-21 16:36:10 +01:00
Hannah von Reth
ca379868dc Use qApp->applicationName() for default text 2015-11-21 01:18:09 +01:00
Hannah von Reth
cf7194407b Center text 2015-11-21 01:15:04 +01:00
Hannah von Reth
365a021a29 Remove linebreak 2015-11-21 01:04:37 +01:00
Hannah von Reth
c39750d814 Fix text 2015-11-21 01:01:36 +01:00
Hannah von Reth
6d12f03023 Outline elements and change font colour 2015-11-21 00:55:22 +01:00
Dominik Schmidt
03935f26a9 Use ordinary wrapper function for testConfig 2015-11-21 00:45:17 +01:00
Dominik Schmidt
e697303743 Properly log errors to javascript console 2015-11-21 00:45:03 +01:00
Hannah von Reth
e4f55da2b2 Update preview design 2015-11-21 00:18:49 +01:00
Dominik Schmidt
576c91eb19 Set *pretty* collection name as result friendly source 2015-11-21 00:13:36 +01:00
Dominik Schmidt
6823b57823 Show friendlySource in alternate sources box tooltips 2015-11-21 00:13:36 +01:00
Dominik Schmidt
88099eae0e Don't use collectionId from resolvers anymore 2015-11-21 00:13:35 +01:00
Dominik Schmidt
8679713dea Remove more leftovers from the old collection hack 2015-11-21 00:13:35 +01:00
Dominik Schmidt
a589e4f688 Expect javascript resolvers to return "tracks" instead of "results" 2015-11-21 00:13:31 +01:00
Hannah von Reth
f3021e4d71 Change preview look and update less often. 2015-11-20 22:36:52 +01:00
Hannah von Reth
c577073f57 Hacked together an album art preview for the thumb bar and alt+tab 2015-11-20 22:36:52 +01:00
Hannah von Reth
e746b4c3e9 Remove dead code 2015-11-20 22:36:52 +01:00
Dominik Schmidt
16946592b7 Allow script jobs to return object members directly if they are not functions 2015-11-20 22:15:26 +01:00
Dominik Schmidt
f3c8038c42 Remove legacy hack 2015-11-20 22:15:26 +01:00
Dominik Schmidt
4637f3ed23 Make collections able to resolve queries 2015-11-20 22:15:24 +01:00
Dominik Schmidt
dc32f7eeb1 Remove legacy hacks and add useful error 2015-11-19 13:38:23 +01:00
Dominik Schmidt
e997a194a4 Add nativeScriptJob errors 2015-11-19 06:20:28 +01:00
Dominik Schmidt
e8aa2e6de9 Make params for nativeScriptJobs optional 2015-11-19 06:20:09 +01:00
Dominik Schmidt
29aa9546a8 Allow instantaneous returns for nativeScriptJobs 2015-11-19 06:19:53 +01:00
Dominik Schmidt
17d71b413f Implement Tomahawk.ajax native implementation on behalf of NativeScriptJobs 2015-11-19 05:39:07 +01:00
Dominik Schmidt
3db8e91b51 Bye bye syncRequest 2015-11-19 05:36:05 +01:00
Dominik Schmidt
1b4efa8f4a Bye bye callOnResolver 2015-11-19 01:44:54 +01:00
Dominik Schmidt
90d6f0d4e5 Set collectionId on results from Tomahawk.Collection 2015-11-19 01:24:07 +01:00
Dominik Schmidt
dde7db616a Use getStreamUrl on ResultProviders instead of awkward custom iodevicefactories 2015-11-19 01:23:38 +01:00
Dominik Schmidt
30789bcb9b Add getStreamUrl translation step in AudioEngine 2015-11-17 03:12:12 +01:00
Dominik Schmidt
64f71fe453 Forward declare ResultProvider in Result 2015-11-17 02:50:00 +01:00
Dominik Schmidt
c1eadce374 Move lookupUrl stuff from JSResolverHelper to JSResolver, to be moved to own plugin type 2015-11-16 18:19:51 +01:00
Enno Gottschalk
73a1160ce3 Added empty default implementation of testConfig() to Tomahawk.Resolver in tomahawk.js 2015-11-13 16:37:56 +01:00
Dominik Schmidt
846699c03c Don't wait for timeouts on resolver errors 2015-11-13 14:12:12 +01:00
Dominik Schmidt
35d8945975 Port JSResolver::resolve() 2015-11-13 13:20:52 +01:00
Dominik Schmidt
186d98b263 Update shipped dependencies in NSIS 2015-11-05 16:29:35 +01:00
Dominik Schmidt
0b7d3846e5 There's no such thing MING 2015-11-05 16:28:00 +01:00
Hannah von Reth
84e0754bd7 Install snoresettings as "Notification Settings".
This enables the user to change the notification backend
and to configure the banckend.
Opening the shortcut will only work after Tomahawk was started
for a first time to initialize the settings.
An alternative would be to call snoresettings.exe from inside of
Tomahawk or to use the QWidget provided by libsnore.
2015-11-05 13:38:42 +01:00
Hannah von Reth
c838af6d17 Update to Snorenotify 0.6 2015-11-05 12:54:20 +01:00
86 changed files with 1914 additions and 1867 deletions

View File

@@ -404,12 +404,8 @@ if( WIN32 )
endif( WIN32 )
if( WIN32 OR APPLE )
if( TOMAHAWK_QT5 )
macro_optional_find_package(LibsnoreQt5 QUIET)
else()
macro_optional_find_package(Libsnore QUIET)
endif()
macro_log_feature(LIBSNORE_FOUND "Libsnore" "Library for notifications" "https://github.com/TheOneRing/Snorenotify" FALSE "" "")
macro_optional_find_package(LibsnoreQt5 0.5.70 QUIET)
macro_log_feature(LibsnoreQt5_FOUND "Libsnore" "Library for notifications" "https://projects.kde.org/projects/playground/libs/snorenotify" FALSE "" "")
endif()
find_package(LIBVLC REQUIRED 2.1.0)

View File

@@ -6,19 +6,16 @@ find_path(LIBVLC_INCLUDE_DIR vlc/vlc.h
HINTS
${PC_LIBVLC_INCLUDEDIR}
${PC_LIBVLC_INCLUDE_DIRS}
/usr/local/opt/vlc/include
)
find_library(LIBVLC_LIBRARY NAMES vlc libvlc
HINTS
${PC_LIBVLC_LIBDIR}
${PC_LIBVLC_LIBRARY_DIRS}
/usr/local/opt/vlc/lib
)
find_library(LIBVLCCORE_LIBRARY NAMES vlccore libvlccore
HINTS
${PC_LIBVLC_LIBDIR}
${PC_LIBVLC_LIBRARY_DIRS}
)
set(LIBVLC_VERSION ${PC_LIBVLC_VERSION})
@@ -32,8 +29,6 @@ int main(int argc, char *argv[]) {
HAVE_VLC_ALBUMARTIST)
find_package_handle_standard_args(LibVLC
REQUIRED_VARS LIBVLC_LIBRARY LIBVLCCORE_LIBRARY LIBVLC_INCLUDE_DIR
REQUIRED_VARS LIBVLC_LIBRARY LIBVLC_INCLUDE_DIR
VERSION_VAR LIBVLC_VERSION
)

View File

@@ -15,21 +15,25 @@
;-----------------------------------------------------------------------------
; Some paths.
;-----------------------------------------------------------------------------
!ifndef MING_PATH
!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw"
!ifndef MINGW_ROOT
!define MINGW_ROOT "/usr/i686-w64-mingw32/sys-root/mingw"
!endif
!define APPLICATION_NAME "Tomahawk"
!define TARGET_NAME "tomahawk"
;define app id needed for Windows 8 notifications
!define AppUserModelId @TOMAHAWK_APPLICATION_PACKAGE_NAME@
!define MING_BIN "${MING_PATH}/bin"
!define MING_LIB "${MING_PATH}/lib"
!define MINGW_BIN "${MINGW_ROOT}/bin"
!define MINGW_LIB "${MINGW_ROOT}/lib"
!define MINGW_SHARE "${MINGW_ROOT}/share"
!define BUILD_PATH "@CMAKE_BINARY_DIR@"
!define SOURCE_PATH "@CMAKE_SOURCE_DIR@"
!define QT_DLL_PATH "${MING_BIN}"
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
!define QT_DLL_PATH "${MINGW_BIN}"
!define QT_QML_PATH "${MINGW_SHARE}/qt5/qml"
!define SQLITE_DLL_PATH "${MINGW_LIB}/qt5/plugins/sqldrivers"
!define IMAGEFORMATS_DLL_PATH "${MINGW_LIB}/qt5/plugins/imageformats"
; We use official release plugins
; mingw32-vlc from obs misses a lot and has even broken ones probably
@@ -330,7 +334,7 @@ Section "${APPLICATION_NAME}" SEC_TOMAHAWK_PLAYER
File "${QT_DLL_PATH}\Qt5WebKitWidgets.dll"
File "${QT_DLL_PATH}\Qt5Multimedia.dll"
File "${QT_DLL_PATH}\Qt5MultimediaWidgets.dll"
File "${QT_DLL_PATH}\Qt5Positioning.dll"
;Qt deps
File "${QT_DLL_PATH}\libpcre16-0.dll"
@@ -339,21 +343,19 @@ Section "${APPLICATION_NAME}" SEC_TOMAHAWK_PLAYER
File "${QT_DLL_PATH}\libEGL.dll"
File "${QT_DLL_PATH}\libGLESv2.dll"
File "${QT_DLL_PATH}\libwebp-5.dll"
;Boost fnord
File "${QT_DLL_PATH}\icuuc53.dll"
File "${QT_DLL_PATH}\icudata53.dll"
File "${QT_DLL_PATH}\icui18n53.dll"
File "${QT_DLL_PATH}\icuuc56.dll"
File "${QT_DLL_PATH}\icudata56.dll"
File "${QT_DLL_PATH}\icui18n56.dll"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR"
File "${MING_BIN}\libsqlite3-0.dll"
File "${MINGW_BIN}\libsqlite3-0.dll"
;Qt platform plugins
SetOutPath "$INSTDIR\platforms"
File "${MING_LIB}/qt5/plugins/platforms/qwindows.dll"
File "${MINGW_LIB}/qt5/plugins/platforms/qwindows.dll"
SetOutPath "$INSTDIR"
;Image plugins
@@ -363,9 +365,16 @@ Section "${APPLICATION_NAME}" SEC_TOMAHAWK_PLAYER
File "${IMAGEFORMATS_DLL_PATH}\qsvg.dll"
SetOutPath "$INSTDIR"
;Qt qml plugins
SetOutPath "$INSTDIR\QtQuick.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick.2\*"
SetOutPath "$INSTDIR\QtQuick\Window.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick\Window.2\*"
SetOutPath "$INSTDIR"
;Cygwin/c++ stuff
File "${MING_BIN}\libgcc_s_sjlj-1.dll"
File "${MING_BIN}\libstdc++-6.dll"
File "${MINGW_BIN}\libgcc_s_sjlj-1.dll"
File "${MINGW_BIN}\libstdc++-6.dll"
;VLC
File "${VLC_BIN}\libvlc.dll"
@@ -375,60 +384,68 @@ Section "${APPLICATION_NAME}" SEC_TOMAHAWK_PLAYER
SetOutPath "$INSTDIR"
; Other
File "${MING_BIN}\libtag.dll"
File "${MING_BIN}\libpng16-16.dll"
File "${MING_BIN}\libjpeg-8.dll"
File "${MING_BIN}\zlib1.dll"
File "${MINGW_BIN}\libtag.dll"
File "${MINGW_BIN}\libpng16-16.dll"
File "${MINGW_BIN}\libjpeg-8.dll"
File "${MINGW_BIN}\zlib1.dll"
File "${MINGW_BIN}\libfreetype-6.dll"
File "${MINGW_BIN}\libglib-2.0-0.dll"
File "${MINGW_BIN}\libharfbuzz-0.dll"
File "${MING_BIN}\libechonest5.dll"
File "${MING_BIN}\liblastfm5.dll"
File "${MING_BIN}\libquazip5.dll"
File "${MING_BIN}\libqt5keychain.dll"
File "${MINGW_BIN}\libechonest5.dll"
File "${MINGW_BIN}\liblastfm5.dll"
File "${MINGW_BIN}\libquazip5.dll"
File "${MINGW_BIN}\libqt5keychain.dll"
; GnuTLS
File "${MING_BIN}\libgnutls-28.dll"
File "${MING_BIN}\libtasn1-6.dll"
File "${MING_BIN}\libgmp-10.dll"
File "${MING_BIN}\libhogweed-2-4.dll"
File "${MING_BIN}\libintl-8.dll"
File "${MING_BIN}\libnettle-4-6.dll"
File "${MING_BIN}\libp11-kit-0.dll"
File "${MING_BIN}\libffi-6.dll"
File "${MINGW_BIN}\libgnutls-28.dll"
File "${MINGW_BIN}\libtasn1-6.dll"
File "${MINGW_BIN}\libgmp-10.dll"
File "${MINGW_BIN}\libhogweed-2-4.dll"
File "${MINGW_BIN}\libintl-8.dll"
File "${MINGW_BIN}\libnettle-4-6.dll"
File "${MINGW_BIN}\libp11-kit-0.dll"
File "${MINGW_BIN}\libffi-6.dll"
; Snorenotify
File "${MING_BIN}\SnoreToast.exe"
File "${MING_BIN}\libsnore-qt5.dll"
File "${MING_LIB}\plugins\libsnore-qt5\libsnore_backend_growl.dll"
File "${MING_LIB}\plugins\libsnore-qt5\libsnore_backend_snarl.dll"
File "${MING_LIB}\plugins\libsnore-qt5\libsnore_backend_snore.dll"
File "${MING_LIB}\plugins\libsnore-qt5\libsnore_backend_snoretoast.dll"
File "${MINGW_BIN}\SnoreToast.exe"
File "${MINGW_BIN}\libsnore-qt5.dll"
File "${MINGW_BIN}\libsnoresettings-qt5.dll"
File "${MINGW_BIN}\snoresettings.exe"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_windowstoast.dll"
; Snoregrowl
File "${MING_BIN}\libsnoregrowl++.dll"
File "${MING_BIN}\libsnoregrowl.dll"
File "${MINGW_BIN}\libsnoregrowl++.dll"
File "${MINGW_BIN}\libsnoregrowl.dll"
; Jabber
File "${MING_BIN}\libjreen-qt5.dll"
File "${MING_BIN}\libidn-11.dll"
File "${MING_BIN}\libgsasl-7.dll"
File "${MING_BIN}\libqca-qt5.dll"
File "${MINGW_BIN}\libjreen-qt5.dll"
File "${MINGW_BIN}\libidn-11.dll"
File "${MINGW_BIN}\libgsasl-7.dll"
File "${MINGW_BIN}\libqca-qt5.dll"
SetOutPath "$INSTDIR\crypto"
File "${MING_LIB}\qca-qt5\crypto\libqca-ossl.dll"
File "${MINGW_LIB}\qca-qt5\crypto\libqca-ossl.dll"
SetOutPath "$INSTDIR"
File "${MING_BIN}\libssl-10.dll"
File "${MING_BIN}\libcrypto-10.dll"
File "${MINGW_BIN}\libssl-10.dll"
File "${MINGW_BIN}\libcrypto-10.dll"
; LucenePlusPlus
File "${MING_BIN}\liblucene++.dll"
File "${MING_BIN}\libboost_system-mt.dll"
File "${MING_BIN}\libboost_filesystem-mt.dll"
File "${MING_BIN}\libboost_iostreams-mt.dll"
File "${MING_BIN}\libboost_regex-mt.dll"
File "${MING_BIN}\libboost_thread-mt.dll"
File "${MING_BIN}\libbz2-1.dll"
File "${MINGW_BIN}\liblucene++.dll"
File "${MINGW_BIN}\libboost_system-mt.dll"
File "${MINGW_BIN}\libboost_filesystem-mt.dll"
File "${MINGW_BIN}\libboost_iostreams-mt.dll"
File "${MINGW_BIN}\libboost_regex-mt.dll"
File "${MINGW_BIN}\libboost_thread-mt.dll"
File "${MINGW_BIN}\libbz2-1.dll"
File "${MING_BIN}\libqtsparkle-qt5.dll"
File "${MING_BIN}\libKF5Attica.dll"
File "${MINGW_BIN}\libqtsparkle-qt5.dll"
File "${MINGW_BIN}\libKF5Attica.dll"
SectionEnd
SectionGroup "Shortcuts"
@@ -443,7 +460,7 @@ SectionGroup "Shortcuts"
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
CreateDirectory "$SMPROGRAMS\${APPLICATION_NAME}"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_NAME}.exe"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Notification Settings.lnk" "$INSTDIR\snoresettings.exe" "-a ${APPLICATION_NAME}"
!insertmacro SnoreShortcut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_NAME}.exe" "${AppUserModelId}"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Release notes.lnk" "$INSTDIR\NOTES.txt"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"

View File

@@ -5,23 +5,23 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Tomahawk</string>
<string>@TOMAHAWK_APPLICATION_NAME@</string>
<key>CFBundleIdentifier</key>
<string>org.tomahawk-player.Tomahawk</string>
<string>@TOMAHAWK_APPLICATION_PACKAGE_NAME@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>TOMAHAWK_VERSION</string>
<string>@TOMAHAWK_VERSION@</string>
<key>CFBundleShortVersionString</key>
<string>TOMAHAWK_VERSION</string>
<string>@TOMAHAWK_VERSION@</string>
<key>CFBundleSignature</key>
<string>tomahawk</string>
<string>@TOMAHAWK_BASE_TARGET_NAME@</string>
<key>CFBundleIconFile</key>
<string>Tomahawk.icns</string>
<string>@TOMAHAWK_APPLICATION_NAME@.icns</string>
<key>CFBundleName</key>
<string>Tomahawk</string>
<string>@TOMAHAWK_APPLICATION_NAME@</string>
<key>LSMinimumSystemVersion</key>
<string>10.7.0</string>
<key>NSPrincipalClass</key>
@@ -29,7 +29,7 @@
<key>NSHighResolutionCapable</key>
<true />
<key>SUFeedURL</key>
<string>http://download.tomahawk-player.org/sparkle/update.php</string>
<string>@TOMAHAWK_SPARKLE_UPDATE_URL@</string>
<key>SUPublicDSAKeyFile</key>
<string>sparkle_pub.pem</string>
<key>SUEnableSystemProfiling</key>

View File

@@ -4,8 +4,9 @@
#
################################################################################
set -e
TARGET_NAME="Tomahawk"
set -e
function header {
echo -e "\033[0;34m==>\033[0;0;1m $1 \033[0;0m"
@@ -31,30 +32,28 @@ CERT_SIGNER=$2
################################################################################
header "Fixing and copying libraries"
$ROOT/../admin/mac/macdeploy.py Tomahawk.app quiet
$ROOT/../admin/mac/macdeploy.py "${TARGET_NAME}.app" quiet
cd Tomahawk.app
cd "${TARGET_NAME}.app"
cp $ROOT/../admin/mac/qt.conf Contents/Resources/qt.conf
# header "Copying Sparkle framework"
# cp -R /Library/Frameworks/Sparkle.framework Contents/Frameworks
header "Creating DMG"
cd ..
header "Fixing fonts"
mkdir "${ROOT}/${TARGET_NAME}.app/Contents/Resources/Fonts"
cp -R $ROOT/../data/fonts/*.ttf "${ROOT}/${TARGET_NAME}.app/Contents/Resources/Fonts"
header "Signing bundle"
# codesign -s "Developer ID Application: $CERT_SIGNER" -f -v ./Tomahawk.app
cd ..
if [ -f ~/sign_step.sh ];
then
~/sign_step.sh "$CERT_SIGNER" "Tomahawk.app" || true
~/sign_step.sh "$CERT_SIGNER" "${TARGET_NAME}.app" || true
fi
$ROOT/../admin/mac/create-dmg.sh Tomahawk.app
mv Tomahawk.dmg Tomahawk-$VERSION.dmg
header "Creating DMG"
$ROOT/../admin/mac/create-dmg.sh "${TARGET_NAME}.app"
mv "${TARGET_NAME}.dmg" "${TARGET_NAME}-$VERSION.dmg"
header "Creating signed Sparkle update"
$ROOT/../admin/mac/sign_bundle.rb $VERSION ~/tomahawk_sparkle_privkey.pem
# $ROOT/../admin/mac/sign_bundle.rb "${TARGET_NAME}" $VERSION ~/tomahawk_sparkle_privkey.pem
header "Done!"

View File

@@ -43,7 +43,7 @@ ln -s /Applications "$TMP/Applications"
cp -R "$IN" "$TMP"
# create
hdiutil makehybrid -hfs -hfs-volume-name Tomahawk -hfs-openfolder "$TMP" "$TMP" -o tmp.dmg
hdiutil makehybrid -hfs -hfs-volume-name "$NAME" -hfs-openfolder "$TMP" "$TMP" -o tmp.dmg
hdiutil convert -format UDZO -imagekey zlib-level=9 tmp.dmg -o "$OUT"
# cleanup

View File

@@ -22,171 +22,170 @@ import commands
import sys
import glob
TARGET_NAME="tomahawk"
FRAMEWORK_SEARCH_PATH=[
'/Library/Frameworks',
os.path.join(os.environ['HOME'], 'Library/Frameworks')
]
LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Cellar/gettext/0.19.2/lib', '.']
LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/opt/vlc/lib', '/usr/local/Cellar/gettext/0.19.2/lib', '.']
VLC_PLUGINS=[
'access/libattachment_plugin.dylib',
#'access/libaccess_avio_plugin.dylib',
#'access/libaccess_fake_plugin.dylib',
'access/libftp_plugin.dylib',
'access/libhttp_plugin.dylib',
'access/libimem_plugin.dylib',
#'access/libaccess_mmap_plugin.dylib',
'access/libaccess_mms_plugin.dylib',
'access/libaccess_realrtsp_plugin.dylib',
'access/libtcp_plugin.dylib',
'access/libudp_plugin.dylib',
'access/libcdda_plugin.dylib',
'access/libfilesystem_plugin.dylib',
'access/libqtcapture_plugin.dylib',
'access/librtp_plugin.dylib',
'access/libzip_plugin.dylib',
'access_output/libaccess_output_dummy_plugin.dylib',
'access_output/libaccess_output_file_plugin.dylib',
'access_output/libaccess_output_http_plugin.dylib',
'access_output/libaccess_output_shout_plugin.dylib',
'access_output/libaccess_output_udp_plugin.dylib',
'audio_filter/liba52tofloat32_plugin.dylib',
'audio_filter/liba52tospdif_plugin.dylib',
'audio_filter/libaudio_format_plugin.dylib',
'audio_filter/libaudiobargraph_a_plugin.dylib',
'audio_filter/libchorus_flanger_plugin.dylib',
'libattachment_plugin.dylib',
#'libaccess_avio_plugin.dylib',
#'libaccess_fake_plugin.dylib',
'libftp_plugin.dylib',
'libhttp_plugin.dylib',
'libhttplive_plugin.dylib',
'libimem_plugin.dylib',
#'libaccess_mmap_plugin.dylib',
'libaccess_mms_plugin.dylib',
'libaccess_realrtsp_plugin.dylib',
'libtcp_plugin.dylib',
'libudp_plugin.dylib',
'libcdda_plugin.dylib',
'libfilesystem_plugin.dylib',
'libqtcapture_plugin.dylib',
'librtp_plugin.dylib',
'libzip_plugin.dylib',
'liba52tofloat32_plugin.dylib',
'liba52tospdif_plugin.dylib',
'libaudio_format_plugin.dylib',
'libaudiobargraph_a_plugin.dylib',
'libchorus_flanger_plugin.dylib',
#'libconverter_fixed_plugin.dylib',
'audio_filter/libdolby_surround_decoder_plugin.dylib',
'audio_filter/libdtstofloat32_plugin.dylib',
'audio_filter/libdtstospdif_plugin.dylib',
'audio_filter/libequalizer_plugin.dylib',
'audio_filter/libheadphone_channel_mixer_plugin.dylib',
'audio_filter/libmono_plugin.dylib',
'audio_filter/libmpgatofixed32_plugin.dylib',
'audio_filter/libnormvol_plugin.dylib',
'audio_filter/libparam_eq_plugin.dylib',
'audio_filter/libscaletempo_plugin.dylib',
'audio_filter/libsimple_channel_mixer_plugin.dylib',
'audio_filter/libspatializer_plugin.dylib',
'audio_filter/libtrivial_channel_mixer_plugin.dylib',
'audio_filter/libugly_resampler_plugin.dylib',
'audio_mixer/libfloat_mixer_plugin.dylib',
'libdolby_surround_decoder_plugin.dylib',
'libdtstofloat32_plugin.dylib',
'libdtstospdif_plugin.dylib',
'libequalizer_plugin.dylib',
'libheadphone_channel_mixer_plugin.dylib',
'libmono_plugin.dylib',
'libmpgatofixed32_plugin.dylib',
'libnormvol_plugin.dylib',
'libparam_eq_plugin.dylib',
'libscaletempo_plugin.dylib',
'libsimple_channel_mixer_plugin.dylib',
'libspatializer_plugin.dylib',
'libtrivial_channel_mixer_plugin.dylib',
'libugly_resampler_plugin.dylib',
'libfloat_mixer_plugin.dylib',
#'libspdif_mixer_plugin.dylib',
#'libtrivial_mixer_plugin.dylib',
#'libaout_file_plugin.dylib',
'audio_output/libauhal_plugin.dylib',
'codec/liba52_plugin.dylib',
'codec/libadpcm_plugin.dylib',
'codec/libaes3_plugin.dylib',
'codec/libaraw_plugin.dylib',
'codec/libavcodec_plugin.dylib',
'codec/libcc_plugin.dylib',
'codec/libcdg_plugin.dylib',
'codec/libdts_plugin.dylib',
'codec/libfaad_plugin.dylib',
'libauhal_plugin.dylib',
'liba52_plugin.dylib',
'libadpcm_plugin.dylib',
'libaes3_plugin.dylib',
'libaraw_plugin.dylib',
'libavcodec_plugin.dylib',
'libcc_plugin.dylib',
'libcdg_plugin.dylib',
'libdts_plugin.dylib',
'libfaad_plugin.dylib',
#'libfake_plugin.dylib',
'codec/libflac_plugin.dylib',
'libflac_plugin.dylib',
#'libfluidsynth_plugin.dylib',
#'libinvmem_plugin.dylib',
'codec/liblpcm_plugin.dylib',
'codec/libmpeg_audio_plugin.dylib',
'codec/libpng_plugin.dylib',
'codec/librawvideo_plugin.dylib',
'codec/libspeex_plugin.dylib',
'codec/libspudec_plugin.dylib',
'codec/libtheora_plugin.dylib',
'codec/libtwolame_plugin.dylib',
'codec/libvorbis_plugin.dylib',
#'control/libgestures_plugin.dylib',
'liblpcm_plugin.dylib',
'libmpeg_audio_plugin.dylib',
'libpng_plugin.dylib',
'librawvideo_plugin.dylib',
'libspeex_plugin.dylib',
'libspudec_plugin.dylib',
'libtheora_plugin.dylib',
'libtwolame_plugin.dylib',
'libvorbis_plugin.dylib',
#'libgestures_plugin.dylib',
#'libhotkeys_plugin.dylib',
#'libmotion_plugin.dylib',
#'libnetsync_plugin.dylib',
#'libsignals_plugin.dylib',
'demux/libaiff_plugin.dylib',
'demux/libasf_plugin.dylib',
'demux/libau_plugin.dylib',
'libaiff_plugin.dylib',
'libasf_plugin.dylib',
'libau_plugin.dylib',
#'libavformat_plugin.dylib',
'demux/libavi_plugin.dylib',
'demux/libdemux_cdg_plugin.dylib',
'demux/libdemuxdump_plugin.dylib',
'demux/libdiracsys_plugin.dylib',
'demux/libes_plugin.dylib',
'demux/libflacsys_plugin.dylib',
'access/liblive555_plugin.dylib',
'demux/libmkv_plugin.dylib',
'demux/libmod_plugin.dylib',
'demux/libmp4_plugin.dylib',
'demux/libmpc_plugin.dylib',
'demux/libmpgv_plugin.dylib',
'demux/libnsc_plugin.dylib',
'demux/libnsv_plugin.dylib',
'demux/libnuv_plugin.dylib',
'demux/libogg_plugin.dylib',
'demux/libplaylist_plugin.dylib',
'demux/libps_plugin.dylib',
'demux/libpva_plugin.dylib',
'demux/librawaud_plugin.dylib',
'demux/librawdv_plugin.dylib',
'demux/librawvid_plugin.dylib',
'demux/libreal_plugin.dylib',
'demux/libsmf_plugin.dylib',
'demux/libts_plugin.dylib',
'demux/libtta_plugin.dylib',
'demux/libty_plugin.dylib',
'demux/libvc1_plugin.dylib',
'demux/libvoc_plugin.dylib',
'demux/libwav_plugin.dylib',
'demux/libxa_plugin.dylib',
'meta_engine/libfolder_plugin.dylib',
'meta_engine/libtaglib_plugin.dylib',
'libavi_plugin.dylib',
'libdemux_cdg_plugin.dylib',
'libdemuxdump_plugin.dylib',
'libdiracsys_plugin.dylib',
'libes_plugin.dylib',
'libflacsys_plugin.dylib',
'liblive555_plugin.dylib',
'libmkv_plugin.dylib',
'libmod_plugin.dylib',
'libmp4_plugin.dylib',
'libmpc_plugin.dylib',
'libmpgv_plugin.dylib',
'libnsc_plugin.dylib',
'libnsv_plugin.dylib',
'libnuv_plugin.dylib',
'libogg_plugin.dylib',
'libplaylist_plugin.dylib',
'libps_plugin.dylib',
'libpva_plugin.dylib',
'librawaud_plugin.dylib',
'librawdv_plugin.dylib',
'librawvid_plugin.dylib',
'libreal_plugin.dylib',
'libsmf_plugin.dylib',
'libts_plugin.dylib',
'libtta_plugin.dylib',
'libty_plugin.dylib',
'libvc1_plugin.dylib',
'libvoc_plugin.dylib',
'libwav_plugin.dylib',
'libxa_plugin.dylib',
'libfolder_plugin.dylib',
'libtaglib_plugin.dylib',
#'libaudioscrobbler_plugin.dylib',
'control/libdummy_plugin.dylib',
'misc/libexport_plugin.dylib',
'libdummy_plugin.dylib',
'libexport_plugin.dylib',
#'libfreetype_plugin.dylib',
#'libgnutls_plugin.dylib',
'misc/liblogger_plugin.dylib',
'lua/liblua_plugin.dylib',
'liblogger_plugin.dylib',
'liblua_plugin.dylib',
#'libosd_parser_plugin.dylib',
#'libquartztext_plugin.dylib',
#'libstats_plugin.dylib',
'misc/libvod_rtsp_plugin.dylib',
'misc/libxml_plugin.dylib',
'libvod_rtsp_plugin.dylib',
'libxml_plugin.dylib',
#'libxtag_plugin.dylib',
'video_chroma/libi420_rgb_mmx_plugin.dylib',
'video_chroma/libi420_yuy2_mmx_plugin.dylib',
'video_chroma/libi422_yuy2_mmx_plugin.dylib',
'libi420_rgb_mmx_plugin.dylib',
'libi420_yuy2_mmx_plugin.dylib',
'libi422_yuy2_mmx_plugin.dylib',
#'libmemcpymmx_plugin.dylib',
#'libmemcpymmxext_plugin.dylib',
'mux/libmux_asf_plugin.dylib',
'mux/libmux_avi_plugin.dylib',
'mux/libmux_dummy_plugin.dylib',
'mux/libmux_mp4_plugin.dylib',
'mux/libmux_mpjpeg_plugin.dylib',
'mux/libmux_ogg_plugin.dylib',
'mux/libmux_ps_plugin.dylib',
'mux/libmux_ts_plugin.dylib',
'mux/libmux_wav_plugin.dylib',
'packetizer/libpacketizer_copy_plugin.dylib',
'packetizer/libpacketizer_dirac_plugin.dylib',
'packetizer/libpacketizer_flac_plugin.dylib',
'packetizer/libpacketizer_h264_plugin.dylib',
'packetizer/libpacketizer_mlp_plugin.dylib',
'packetizer/libpacketizer_mpeg4audio_plugin.dylib',
'packetizer/libpacketizer_mpeg4video_plugin.dylib',
'packetizer/libpacketizer_mpegvideo_plugin.dylib',
'packetizer/libpacketizer_vc1_plugin.dylib',
'video_chroma/libi420_rgb_sse2_plugin.dylib',
'video_chroma/libi420_yuy2_sse2_plugin.dylib',
'video_chroma/libi422_yuy2_sse2_plugin.dylib',
'stream_filter/libdecomp_plugin.dylib',
#'access/libstream_filter_rar_plugin.dylib',
'stream_filter/librecord_plugin.dylib',
'libmux_asf_plugin.dylib',
'libmux_avi_plugin.dylib',
'libmux_dummy_plugin.dylib',
'libmux_mp4_plugin.dylib',
'libmux_mpjpeg_plugin.dylib',
'libmux_ogg_plugin.dylib',
'libmux_ps_plugin.dylib',
'libmux_ts_plugin.dylib',
'libmux_wav_plugin.dylib',
'libpacketizer_copy_plugin.dylib',
'libpacketizer_dirac_plugin.dylib',
'libpacketizer_flac_plugin.dylib',
'libpacketizer_h264_plugin.dylib',
'libpacketizer_mlp_plugin.dylib',
'libpacketizer_mpeg4audio_plugin.dylib',
'libpacketizer_mpeg4video_plugin.dylib',
'libpacketizer_mpegvideo_plugin.dylib',
'libpacketizer_vc1_plugin.dylib',
'libi420_rgb_sse2_plugin.dylib',
'libi420_yuy2_sse2_plugin.dylib',
'libi422_yuy2_sse2_plugin.dylib',
'libdecomp_plugin.dylib',
#'libstream_filter_rar_plugin.dylib',
'librecord_plugin.dylib',
#'libvisual_plugin.dylib',
'libsecuretransport_plugin.dylib'
]
VLC_SEARCH_PATH=[
'/usr/local/lib/vlc/plugins/',
'/usr/local/opt/vlc/lib/vlc/plugins/',
]
QT_PLUGINS = [
@@ -201,39 +200,39 @@ QT_PLUGINS = [
]
SNORE_PLUGINS = [
# 'libsnore_backend_growl.so',
# 'libsnore_backend_osxnotificationcenter.so',
'libsnore_backend_growl.so',
'libsnore_backend_osxnotificationcenter.so',
]
TOMAHAWK_PLUGINS = [
'libtomahawk_account_xmpp.dylib',
'libtomahawk_account_google.so',
'libtomahawk_account_zeroconf.so',
# 'libtomahawk_account_hatchet.so',
'libtomahawk_infoplugin_adium.so',
'libtomahawk_infoplugin_charts.so',
'libtomahawk_infoplugin_discogs.so',
'libtomahawk_infoplugin_echonest.so',
'libtomahawk_infoplugin_hypem.so',
'libtomahawk_infoplugin_musicbrainz.so',
'libtomahawk_infoplugin_musixmatch.so',
'libtomahawk_infoplugin_newreleases.so',
'libtomahawk_infoplugin_rovi.so',
# 'libtomahawk_infoplugin_snorenotify.so',
'libtomahawk_infoplugin_spotify.so',
'libtomahawk_viewpage_dashboard.so',
# 'libtomahawk_viewpage_networkactivity.so',
'libtomahawk_viewpage_charts.so',
'libtomahawk_viewpage_newreleases.so',
'libtomahawk_viewpage_whatsnew_0_8.so',
'lib%s_account_xmpp.dylib' % TARGET_NAME,
'lib%s_account_google.so' % TARGET_NAME,
'lib%s_account_zeroconf.so' % TARGET_NAME,
'lib%s_account_hatchet.so' % TARGET_NAME,
'lib%s_infoplugin_adium.so' % TARGET_NAME,
'lib%s_infoplugin_charts.so' % TARGET_NAME,
# 'lib%s_infoplugin_discogs.so' % TARGET_NAME,
'lib%s_infoplugin_echonest.so' % TARGET_NAME,
'lib%s_infoplugin_hypem.so' % TARGET_NAME,
# 'lib%s_infoplugin_musicbrainz.so' % TARGET_NAME,
'lib%s_infoplugin_musixmatch.so' % TARGET_NAME,
'lib%s_infoplugin_newreleases.so' % TARGET_NAME,
# 'lib%s_infoplugin_rovi.so' % TARGET_NAME,
'lib%s_infoplugin_snorenotify.so' % TARGET_NAME,
'lib%s_infoplugin_spotify.so' % TARGET_NAME,
'lib%s_viewpage_dashboard.so' % TARGET_NAME,
# 'lib%s_viewpage_networkactivity.so' % TARGET_NAME,
'lib%s_viewpage_charts.so' % TARGET_NAME,
'lib%s_viewpage_newreleases.so' % TARGET_NAME,
'lib%s_viewpage_whatsnew_0_8.so' % TARGET_NAME,
]
QT_PLUGINS_SEARCH_PATH=[
'/usr/local/Cellar/qt5/5.4.0/plugins',
'/usr/local/opt/qt5/plugins',
]
SNORE_PLUGINS_SEARCH_PATH=[
'/usr/local/Cellar/snorenotify/HEAD/lib/libsnore',
'/usr/local/opt/snorenotify/lib/plugins/libsnore-qt5',
]
class Error(Exception):
@@ -247,6 +246,8 @@ class CouldNotFindQtPluginErrorFindFrameworkError(Error):
class InstallNameToolError(Error):
pass
class CouldNotFindFrameworkError(Error):
pass
class CouldNotFindQtPluginError(Error):
pass
@@ -308,7 +309,7 @@ def GetBrokenLibraries(binary):
continue # unix style system library
elif re.match(r'Breakpad', line):
continue # Manually added by cmake.
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line) and not re.match(r'^\s*@loader_path/../lib', line):
# Potentially already fixed library
if '.framework' in line:
relative_path = os.path.join(*line.split('/')[3:])
@@ -332,6 +333,11 @@ def FindFramework(path):
if os.path.exists(abs_path):
return abs_path
# replace rpath with /Library/Frameworks for Sparkle
abs_path = path.replace("@rpath/", "/Library/Frameworks/")
if os.path.exists(abs_path):
return abs_path
raise CouldNotFindFrameworkError(path)
def FindLibrary(path):
@@ -576,12 +582,12 @@ for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS')
for plugin in SNORE_PLUGINS:
FixPlugin(FindSnorePlugin(plugin), '../MacOS/libsnore')
FixPlugin(FindSnorePlugin(plugin), '../lib/plugins/libsnore-qt5')
try:
FixPlugin('tomahawk_crash_reporter', '../MacOS')
FixPlugin('%s_crash_reporter' % TARGET_NAME, '../MacOS')
except:
print 'Failed to find tomahawk_crash_reporter'
print 'Failed to find %s_crash_reporter' % TARGET_NAME
for plugin in QT_PLUGINS:
FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))

View File

@@ -6,9 +6,9 @@ if ARGV.length < 2
exit
end
tarball = "tomahawk-#{ARGV[0]}.tar.bz2"
tarball = "#{ARGV[0].downcase}-#{ARGV[1]}.tar.bz2"
puts "Zipping: #{tarball}..."
`tar jcvf "#{tarball}" Tomahawk.app`
`tar jcvf "#{tarball}" #{ARGV[0]}.app`
puts "Signing..."
puts `openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[1]}" | openssl enc -base64`
puts `openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[2]}" | openssl enc -base64`

55
data/images/nav-back.svg Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>nav-back</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="nav-back" sketch:type="MSLayerGroup" transform="translate(4.000000, 6.000000)">
<g id="Layer_2"></g>
<g id="Layer_3"></g>
<g id="Layer_4"></g>
<g id="Layer_5"></g>
<g id="Layer_6"></g>
<g id="Layer_8"></g>
<g id="Layer_9"></g>
<g id="Layer_10"></g>
<g id="Layer_11"></g>
<g id="Layer_12"></g>
<g id="Layer_13"></g>
<g id="Layer_14"></g>
<g id="Layer_15"></g>
<g id="Layer_16"></g>
<g id="Layer_17"></g>
<g id="Layer_18"></g>
<g id="Layer_19"></g>
<g id="Layer_20"></g>
<g id="Layer_21"></g>
<g id="Layer_24"></g>
<g id="Layer_25"></g>
<g id="Layer_26"></g>
<g id="Layer_27"></g>
<g id="Layer_28"></g>
<g id="Layer_29"></g>
<g id="Layer_47"></g>
<g id="Layer_30"></g>
<g id="Layer_31"></g>
<g id="Layer_32"></g>
<g id="Layer_33"></g>
<g id="Layer_34"></g>
<g id="Layer_35"></g>
<g id="Layer_36"></g>
<g id="Layer_37"></g>
<g id="Layer_38" transform="translate(2.000000, 0.000000)" fill="#000000" sketch:type="MSShapeGroup">
<path d="M9.7,13.9 L-0.3,7 L9.7,0.1 L10.3,0.9 L1.5,7 L10.3,13.1 L9.7,13.9 Z" id="Shape"></path>
</g>
<g id="Layer_39"></g>
<g id="Layer_40"></g>
<g id="Layer_41"></g>
<g id="Layer_42"></g>
<g id="Layer_43"></g>
<g id="Layer_44"></g>
<g id="Layer_45"></g>
<g id="Layer_46"></g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>nav-forward</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="nav-forward" sketch:type="MSLayerGroup" transform="translate(6.000000, 6.000000)">
<g id="Layer_2"></g>
<g id="Layer_3"></g>
<g id="Layer_4"></g>
<g id="Layer_5"></g>
<g id="Layer_6"></g>
<g id="Layer_8"></g>
<g id="Layer_9"></g>
<g id="Layer_10"></g>
<g id="Layer_11"></g>
<g id="Layer_12"></g>
<g id="Layer_13"></g>
<g id="Layer_14"></g>
<g id="Layer_15"></g>
<g id="Layer_16"></g>
<g id="Layer_17"></g>
<g id="Layer_18"></g>
<g id="Layer_19"></g>
<g id="Layer_20"></g>
<g id="Layer_21"></g>
<g id="Layer_24"></g>
<g id="Layer_25"></g>
<g id="Layer_26"></g>
<g id="Layer_27"></g>
<g id="Layer_28"></g>
<g id="Layer_29"></g>
<g id="Layer_47"></g>
<g id="Layer_30"></g>
<g id="Layer_31"></g>
<g id="Layer_32"></g>
<g id="Layer_33"></g>
<g id="Layer_34"></g>
<g id="Layer_35"></g>
<g id="Layer_36"></g>
<g id="Layer_37"></g>
<g id="Layer_38"></g>
<g id="Layer_39"></g>
<g id="Layer_40"></g>
<g id="Layer_41" transform="translate(2.000000, 0.000000)" fill="#000000" sketch:type="MSShapeGroup">
<path d="M0.5,13.7 L-0.1,12.9 L8.3,7 L-0.1,1.1 L0.5,0.3 L10.1,7 L0.5,13.7 Z" id="Shape"></path>
</g>
<g id="Layer_42"></g>
<g id="Layer_43"></g>
<g id="Layer_44"></g>
<g id="Layer_45"></g>
<g id="Layer_46"></g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -102,7 +102,7 @@ Tomahawk.InfoSystem.InfoPlugin = {
notInCache: function (infoType, criteria) {
var requestMethod = 'request' + this.infoTypeString(infoType);
return Promise.resolve(this[requestMethod](criteria));
return RSVP.Promise.resolve(this[requestMethod](criteria));
},
pushInfo: function (pushData) {
var pushMethod = 'push' + this.infoTypeString(pushData.type);
@@ -114,6 +114,6 @@ Tomahawk.InfoSystem.InfoPlugin = {
getInfo: function (type, infoHash) {
var getInfoMethod = 'get' + this.infoTypeString(type);
return Promise.resolve(this[getInfoMethod](infoHash));
return RSVP.Promise.resolve(this[getInfoMethod](infoHash));
}
};

View File

@@ -49,7 +49,7 @@ RSVP.on('error', function (reason) {
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
}
if (reason) {
console.error(resolverName + 'Uncaught error:' + JSON.stringify(reason));
console.error(resolverName + 'Uncaught error:', reason);
} else {
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
}
@@ -173,7 +173,8 @@ var TomahawkUrlType = {
Playlist: 1,
Track: 2,
Album: 4,
Artist: 8
Artist: 8,
Xspf: 16
};
//Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead.
@@ -236,7 +237,7 @@ var TomahawkResolver = {
collection: function () {
return {};
},
_testConfig: function (config) {
_adapter_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
});
@@ -267,81 +268,28 @@ Tomahawk.Resolver = {
},
newConfigSaved: function () {
},
testConfig: function () {
},
getStreamUrl: function (params) {
return params;
},
_convertUrls: function (results) {
var that = this;
return results.map(function (r) {
if (r && r.url) {
r.url = that._urlProtocol + '://' + r.url;
}
return r;
_adapter_resolve: function (params) {
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
return {
'tracks': results
};
});
},
_adapter_resolve: function (qid, artist, album, title) {
var that = this;
var collectionPromises = [];
Tomahawk.collections.forEach(function (col) {
if (col.resolve) {
collectionPromises.push(col.resolve({artist: artist, album: album, track: title}));
}
});
RSVP.Promise.all(collectionPromises).then(function (collectionResults) {
var merged = [];
return merged.concat.apply(merged, collectionResults);
}).then(function (collectionResults) {
RSVP.Promise.resolve(that.resolve({
artist: artist,
album: album,
track: title
})).then(function (results) {
Tomahawk.addTrackResults({
'qid': qid,
'results': that._convertUrls(results.concat(collectionResults))
});
});
_adapter_search: function (params) {
return RSVP.Promise.resolve(this.search(params)).then(function (results) {
return {
'tracks': results
};
});
},
_adapter_init: function () {
this._urlProtocol = this.settings.name.replace(/[^a-zA-Z]/g, '').toLowerCase();
Tomahawk.addCustomUrlHandler(this._urlProtocol, '_adapter_getStreamUrl', true);
Tomahawk.log('Registered custom url handler for protocol "' + this._urlProtocol + '"');
this.init();
},
_adapter_getStreamUrl: function (params) {
params.url = params.url.slice(this._urlProtocol.length + 3);
RSVP.Promise.resolve(this.getStreamUrl(params)).then(function (result) {
Tomahawk.reportStreamUrl(params.qid, result.url, result.headers);
});
},
_adapter_search: function (qid, query) {
var that = this;
var collectionPromises = [];
Tomahawk.collections.forEach(function (col) {
if (col.search) {
collectionPromises.push(col.search({query: query}));
}
});
RSVP.Promise.all(collectionPromises).then(function (collectionResults) {
var merged = [];
return merged.concat.apply(merged, collectionResults);
}).then(function (collectionResults) {
RSVP.Promise.resolve(that.search({query: query})).then(function (results) {
Tomahawk.addTrackResults({
'qid': qid,
'results': that._convertUrls(results.concat(collectionResults))
});
});
});
},
_testConfig: function (config) {
_adapter_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
});
@@ -364,34 +312,6 @@ Tomahawk.valueForSubNode = function (node, tag) {
return element.textContent;
};
/**
* Do a synchronous HTTP(S) request. For further options see
* Tomahawk.asyncRequest
*/
Tomahawk.syncRequest = function (url, extraHeaders, options) {
// unpack options
var opt = options || {};
var method = opt.method || 'GET';
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(method, url, false, opt.username, opt.password);
if (extraHeaders) {
for (var headerName in extraHeaders) {
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
}
}
xmlHttpRequest.send(null);
if (httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
return xmlHttpRequest.responseText;
} else {
Tomahawk.log("Failed to do GET request: to: " + url);
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
if (opt.hasOwnProperty('errorHandler')) {
opt.errorHandler.call(window, xmlHttpRequest);
}
}
};
/**
* Internal counter used to identify retrievedMetadata call back from native
* code.
@@ -487,6 +407,23 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
delete Tomahawk.asyncRequestCallbacks[reqId];
};
/**
* This method is externalized from Tomahawk.asyncRequest, so that other clients
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
* natively.
*
* @returns boolean indicating whether or not to do a request with the given parameters natively
*/
var shouldDoNativeRequest = function (options) {
var extraHeaders = options.headers;
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|| extraHeaders.hasOwnProperty("referer")
|| extraHeaders.hasOwnProperty("User-Agent")));
};
/**
* Possible options:
* - method: The HTTP request method (default: GET)
@@ -496,59 +433,45 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
* - data: body data included in POST requests
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
* "Set-Cookie" response header
* - headers: headers set on the request
*/
Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
// unpack options
var opt = options || {};
var method = opt.method || 'GET';
var doRequest = function(options) {
if (shouldDoNativeRequest(options)) {
return Tomahawk.NativeScriptJobManager.invoke('httpRequest', options).then(function(xhr) {
xhr.responseHeaders = xhr.responseHeaders || {};
xhr.getAllResponseHeaders = function() {
return this.responseHeaders;
};
xhr.getResponseHeader = function (header) {
return this.responseHeaders[header];
};
if (shouldDoNativeRequest(url, callback, extraHeaders, options)) {
// Assign a request Id to the callback so we can use it when we are
// returning from the native call.
var reqId = Tomahawk.asyncRequestIdCounter;
Tomahawk.asyncRequestIdCounter++;
Tomahawk.asyncRequestCallbacks[reqId] = {
callback: callback,
errorHandler: opt.errorHandler
};
Tomahawk.nativeAsyncRequest(reqId, url, extraHeaders, options);
return xhr;
});
} else {
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
if (extraHeaders) {
for (var headerName in extraHeaders) {
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
}
}
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState == 4
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
callback.call(window, xmlHttpRequest);
} else if (xmlHttpRequest.readyState === 4) {
Tomahawk.log("Failed to do " + method + " request: to: " + url);
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
if (opt.hasOwnProperty('errorHandler')) {
opt.errorHandler.call(window, xmlHttpRequest);
return new RSVP.Promise(function(resolve, reject) {
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(options.method, options.url, true, options.username, options.password);
if (options.headers) {
for (var headerName in options.headers) {
xmlHttpRequest.setRequestHeader(headerName, options.headers[headerName]);
}
}
};
xmlHttpRequest.send(opt.data || null);
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState == 4
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
resolve(xmlHttpRequest);
} else if (xmlHttpRequest.readyState === 4) {
Tomahawk.log("Failed to do " + options.method + " request: to: " + options.url);
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
reject(xmlHttpRequest);
}
};
xmlHttpRequest.send(options.data || null);
});
}
};
/**
* This method is externalized from Tomahawk.asyncRequest, so that other clients
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
* natively.
*
* @returns boolean indicating whether or not to do a request with the given parameters natively
*/
var shouldDoNativeRequest = function (url, callback, extraHeaders, options) {
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|| extraHeaders.hasOwnProperty("referer")
|| extraHeaders.hasOwnProperty("User-Agent")));
};
Tomahawk.ajax = function (url, settings) {
if (typeof url === "object") {
settings = url;
@@ -606,10 +529,7 @@ Tomahawk.ajax = function (url, settings) {
}
}
return new RSVP.Promise(function (resolve, reject) {
settings.errorHandler = reject;
Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings);
}).then(function (xhr) {
return doRequest(settings).then(function (xhr) {
if (settings.rawResponse) {
return xhr;
}
@@ -815,7 +735,9 @@ Tomahawk.base64Encode = function (b) {
return window.btoa(b);
};
Tomahawk.PluginManager = {
wrapperPrefix: '_adapter_',
objects: {},
objectCounter: 0,
identifyObject: function (object) {
@@ -827,10 +749,6 @@ Tomahawk.PluginManager = {
},
registerPlugin: function (type, object) {
this.objects[this.identifyObject(object)] = object;
if (type === 'collection') {
Tomahawk.collections.push(object);
}
Tomahawk.log("registerPlugin: " + type + " id: " + object.id);
Tomahawk.registerScriptPlugin(type, object.id);
},
@@ -844,14 +762,11 @@ Tomahawk.PluginManager = {
resolve: [],
invokeSync: function (requestId, objectId, methodName, params) {
if (!Tomahawk.resolver.instance.apiVersion || Tomahawk.resolver.instance.apiVersion < 0.9) {
if (methodName === 'artistAlbums') {
methodName = 'albums';
} else if (methodName === 'albumTracks') {
methodName = 'tracks';
}
if (this.objects[objectId][this.wrapperPrefix + methodName]) {
methodName = this.wrapperPrefix + methodName;
}
var pluginManager = this;
if (!this.objects[objectId]) {
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
@@ -861,52 +776,13 @@ Tomahawk.PluginManager = {
}
}
if (typeof this.objects[objectId][methodName] === 'function') {
if (!Tomahawk.resolver.instance.apiVersion
|| Tomahawk.resolver.instance.apiVersion < 0.9) {
if (methodName == 'artists') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.artists(requestId);
});
} else if (methodName == 'albums') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.albums(requestId, params.artist);
});
} else if (methodName == 'tracks') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.tracks(requestId, params.artist, params.album);
});
} else if (methodName == 'lookupUrl') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[params.url] = resolve;
Tomahawk.resolver.instance.lookupUrl(params.url);
});
} else if (methodName == 'getStreamUrl') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.getStreamUrl(requestId, params.url);
});
} else if (methodName == 'resolve') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.resolve(requestId, params.artist,
params.album, params.track);
});
} else if (methodName == 'search') {
return new RSVP.Promise(function (resolve, reject) {
pluginManager.resolve[requestId] = resolve;
Tomahawk.resolver.instance.search(requestId, params.query);
});
}
}
return this.objects[objectId][methodName](params);
if (typeof this.objects[objectId][methodName] !== 'function' && this.objects[objectId][methodName]) {
return this.objects[objectId][methodName];
} else if (typeof this.objects[objectId][methodName] !== 'function') {
throw new Error('\'' + methodName + '\' on ScriptObject ' + objectId + ' is not a function', typeof this.objects[objectId][methodName]);
}
return this.objects[objectId][methodName];
return this.objects[objectId][methodName](params);
},
invoke: function (requestId, objectId, methodName, params) {
@@ -925,21 +801,38 @@ Tomahawk.PluginManager = {
}
};
var encodeParamsToNativeFunctions = function(param) {
return param;
};
Tomahawk.NativeScriptJobManager = {
idCounter: 0,
deferreds: {},
invoke: function (methodName, params) {
params = params || {};
var requestId = this.idCounter++;
Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params));
this.deferreds[requestId] = RSVP.defer();
return this.deferreds[requestId].promise;
var deferred = RSVP.defer();
this.deferreds[requestId] = deferred;
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));;
return deferred.promise;
},
reportNativeScriptJobResult: function (requestId, result) {
reportNativeScriptJobResult: function(requestId, result) {
var deferred = this.deferreds[requestId];
if (!deferred) {
Tomahawk.log("Deferred object with the given requestId is not present!");
}
deferred.resolve(result);
delete this.deferreds[requestId];
},
reportNativeScriptJobError: function(requestId, error) {
var deferred = this.deferreds[requestId];
if (!deferred) {
console.log("Deferred object with the given requestId is not present!");
}
deferred.reject(error);
delete this.deferreds[requestId];
}
};
@@ -1213,8 +1106,6 @@ Tomahawk.Country = {
LatinAmericaAndTheCaribbean: 246
};
Tomahawk.collections = [];
Tomahawk.Collection = {
BrowseCapability: {
Artists: 1,
@@ -1607,6 +1498,10 @@ Tomahawk.Collection = {
});
},
revision: function (params) {
return RSVP.resolve();
},
_fuzzyIndexIdsToTracks: function (resultIds, id) {
if (typeof id === 'undefined') {
id = this.settings.id;
@@ -1658,6 +1553,14 @@ Tomahawk.Collection = {
});
},
_adapter_resolve: function (params) {
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
return {
'tracks': results
};
});
},
resolve: function (params) {
var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track);
return this._fuzzyIndexIdsToTracks(resultIds);
@@ -1665,7 +1568,12 @@ Tomahawk.Collection = {
search: function (params) {
var resultIds = Tomahawk.searchFuzzyIndex(params.query);
return this._fuzzyIndexIdsToTracks(resultIds);
return this._fuzzyIndexIdsToTracks(resultIds).then(function(tracks) {
return {
tracks: tracks
};
});
},
tracks: function (params, where) {
@@ -1709,7 +1617,7 @@ Tomahawk.Collection = {
);
return t.execDeferredStatements();
}).then(function (results) {
return {results: Tomahawk.resolver.instance._convertUrls(results[0])};
return {tracks: results[0]};
});
},
@@ -1883,71 +1791,13 @@ Tomahawk.Collection = {
Tomahawk.Collection.BrowseCapability.Albums,
Tomahawk.Collection.BrowseCapability.Tracks];
return this.settings;
},
getStreamUrl: function(params) {
if(this.resolver) {
return this.resolver.getStreamUrl(params);
}
return params;
}
};
// Legacy compability for 0.8 and before
Tomahawk.reportCapabilities = function (capabilities) {
if (capabilities & TomahawkResolverCapability.Browsable) {
Tomahawk.PluginManager.registerPlugin("collection", Tomahawk.resolver.instance);
}
Tomahawk.nativeReportCapabilities(capabilities);
};
Tomahawk.addArtistResults = Tomahawk.addAlbumResults = Tomahawk.addAlbumTrackResults
= function (result) {
Tomahawk.PluginManager.resolve[result.qid](result);
delete Tomahawk.PluginManager.resolve[result.qid];
};
Tomahawk.addTrackResults = function (result) {
Tomahawk.PluginManager.resolve[result.qid](result.results);
delete Tomahawk.PluginManager.resolve[result.qid];
};
Tomahawk.reportStreamUrl = function (qid, streamUrl, headers) {
Tomahawk.PluginManager.resolve[qid]({
url: streamUrl,
headers: headers
});
delete Tomahawk.PluginManager.resolve[qid];
};
Tomahawk.addUrlResult = function (url, result) {
/* Merge the whole mess into one consistent result which is independent of type
var cleanResult = {
type: result.type,
guid: result.guid,
info: result.info,
creator: result.creator,
linkUrl: result.url
};
if (cleanResult.type == "track") {
cleanResult.track = result.title;
cleanResult.artist = result.artist;
} else if (cleanResult.type == "artist") {
cleanResult.artist = result.name;
} else if (cleanResult.type == "album") {
cleanResult.album = result.name;
cleanResult.artist = result.artist;
} else if (cleanResult.type == "playlist") {
cleanResult.title = result.title;
} else if (cleanResult.type == "xspf-url") {
cleanResult.url = result.url;
}
if (result.tracks) {
cleanResult.tracks = [];
var i;
for (i=0;i<result.tracks.length;i++) {
var cleanTrack = {
track: result.tracks[i].title,
artist: result.tracks[i].artist
};
cleanResult.push(cleanTrack)
}
Tomahawk.PluginManager.resolve[url](cleanResult);
*/
Tomahawk.PluginManager.resolve[url](result);
delete Tomahawk.PluginManager.resolve[url];
};

View File

@@ -167,5 +167,7 @@
<file>data/images/repeat-one.svg</file>
<file>data/images/downloads.svg</file>
<file>data/images/downloadbutton.svg</file>
<file>data/images/nav-back.svg</file>
<file>data/images/nav-forward.svg</file>
</qresource>
</RCC>

View File

@@ -174,7 +174,7 @@ int main( int argc, char* argv[] )
reporter.setLogo( QPixmap( CRASHREPORTER_ICON ) );
#endif
reporter.setWindowTitle( CRASHREPORTER_PRODUCT_NAME );
reporter.setText("<html><head/><body><p><span style=\" font-weight:600;\">Sorry!</span> " CRASHREPORTER_PRODUCT_NAME " crashed. Please tell us about it! " CRASHREPORTER_PRODUCT_NAME " has created an error report for you that can help improve the stability in the future. You can now send this report directly to the " CRASHREPORTER_PRODUCT_NAME " developers.</p></body></html>");
reporter.setText("<html><head/><body><p><span style=\"font-weight:600;\">Sorry!</span> " CRASHREPORTER_PRODUCT_NAME " crashed. Please tell us about it! " CRASHREPORTER_PRODUCT_NAME " has created an error report for you that can help improve the stability in the future. You can now send this report directly to the " CRASHREPORTER_PRODUCT_NAME " developers.</p><p>Can you tell us what you were doing when this happened?</p></body></html>");
reporter.setReportData( "BuildID", CRASHREPORTER_BUILD_ID );
reporter.setReportData( "ProductName", CRASHREPORTER_PRODUCT_NAME );

View File

@@ -3,17 +3,15 @@ include_directories(
${Boost_INCLUDE_DIR}
)
if(WIN32 OR APPLE)
if(BUILD_GUI AND LIBSNORE_FOUND)
if(BUILD_GUI AND LibsnoreQt5_FOUND)
SET(snore_srcs
snorenotify/SnoreNotifyPlugin.cpp
)
SET(SNORE_LINK_LIBRARIES ${LINK_LIBRARIES} ${LIBSNORE_LIBRARIES} )
tomahawk_add_plugin(snorenotify
TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO
SOURCES "${snore_srcs}" LINK_LIBRARIES "${SNORE_LINK_LIBRARIES}"
SOURCES "${snore_srcs}" LINK_LIBRARIES Snore::Libsnore
)
endif(BUILD_GUI AND LIBSNORE_FOUND)
endif()
endif(WIN32 OR APPLE)
list(APPEND simple_plugins

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013-2014, Patrick von Reth <vonreth@kde.org>
* Copyright 2013-2015, Hannah von Reth <vonreth@kde.org>
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
@@ -27,27 +27,15 @@
#include "TomahawkVersion.h"
#include <snore/core/application.h>
#include <snore/core/notification/icon.h>
#include <libsnore/application.h>
#include <libsnore/notification/icon.h>
#include <QApplication>
#include <QImage>
#include <QPixmap>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
namespace Qt
{
inline QString escape( const QString &x )
{
return x.toHtmlEscaped();
}
}
#else
// QTextDocument provides Qt::escape()
#include <QTextDocument>
#endif
namespace Tomahawk
{
@@ -61,27 +49,13 @@ SnoreNotifyPlugin::SnoreNotifyPlugin()
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped << InfoInboxReceived;
m_snore = new Snore::SnoreCore();
m_snore->loadPlugins( Snore::SnorePlugin::BACKEND );
QString backend = qgetenv( "SNORE_BACKEND" ).constData();
if( backend.isEmpty() )
{
m_snore->setPrimaryNotificationBackend();
}
else
{
if( !m_snore->setPrimaryNotificationBackend( backend ) )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Ivalid or unavailible Snore backend: " << backend << " availible backens: " << m_snore->notificationBackends();
m_snore->setPrimaryNotificationBackend();
}
}
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << m_snore->primaryNotificationBackend();
Snore::SnoreCore &snore = Snore::SnoreCore::instance();
snore.loadPlugins( Snore::SnorePlugin::Backend | Snore::SnorePlugin::SecondaryBackend );
snore.setDefaultSettingsValue("Silent", true, Snore::LocalSetting );
m_application = Snore::Application( qApp->applicationName(), m_defaultIcon );
m_application.hints().setValue( "windows_app_id", TOMAHAWK_APPLICATION_PACKAGE_NAME );
m_application.hints().setValue( "use-markup", true );
m_application.hints().setValue( "windows-app-id", TOMAHAWK_APPLICATION_PACKAGE_NAME );
m_application.hints().setValue( "desktop-entry", TOMAHAWK_APPLICATION_NAME );
addAlert( InfoNotifyUser, tr( "Notify User" ) );
@@ -90,9 +64,10 @@ SnoreNotifyPlugin::SnoreNotifyPlugin()
addAlert( InfoNowStopped, tr( "Playback Stopped" ) );
addAlert( InfoInboxReceived, tr( "You received a Song recommendation" ) );
m_snore->registerApplication( m_application );
snore.registerApplication( m_application );
snore.setDefaultApplication( m_application );
connect( m_snore, SIGNAL( actionInvoked( Snore::Notification ) ), this, SLOT( slotActionInvoked( Snore::Notification ) ) );
connect( &snore, SIGNAL( actionInvoked( Snore::Notification ) ), this, SLOT( slotActionInvoked( Snore::Notification ) ) );
}
@@ -100,8 +75,7 @@ SnoreNotifyPlugin::~SnoreNotifyPlugin()
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
m_snore->deregisterApplication( m_application );
m_snore->deleteLater();
Snore::SnoreCore::instance().deregisterApplication( m_application );
}
void
@@ -111,25 +85,19 @@ SnoreNotifyPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
if ( !TomahawkSettings::instance()->songChangeNotificationEnabled() )
return;
if( m_snore->primaryNotificationBackend().isNull() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "no notification backend set";
return;
}
switch ( pushData.type )
{
case Tomahawk::InfoSystem::InfoTrackUnresolved:
notifyUser( Tomahawk::InfoSystem::InfoTrackUnresolved, tr( "The current track could not be resolved. %applicationName will pick back up with the next resolvable track from this source." ) );
notifyUser( Tomahawk::InfoSystem::InfoTrackUnresolved, tr( "The current track could not be resolved. %applicationName will pick back up with the next resolvable track from this source." ), m_defaultIcon );
return;
case Tomahawk::InfoSystem::InfoNotifyUser:
notifyUser( Tomahawk::InfoSystem::InfoNotifyUser,pushData.infoPair.second.toString() );
notifyUser( Tomahawk::InfoSystem::InfoNotifyUser,pushData.infoPair.second.toString(), m_defaultIcon );
return;
case Tomahawk::InfoSystem::InfoNowStopped:
notifyUser( Tomahawk::InfoSystem::InfoNowStopped, tr( "%applicationName stopped playback." ) );
notifyUser( Tomahawk::InfoSystem::InfoNowStopped, tr( "%applicationName stopped playback." ), m_defaultIcon );
return;
case Tomahawk::InfoSystem::InfoNowPlaying:
@@ -157,15 +125,10 @@ SnoreNotifyPlugin::slotActionInvoked( Snore::Notification n )
void
SnoreNotifyPlugin::notifyUser( Tomahawk::InfoSystem::InfoType type, const QString& messageText, Snore::Icon icon )
{
if(!icon.isValid())
{
icon = m_defaultIcon;
}
const Snore::Alert &alert = m_alerts[ type ];
Snore::Notification n( m_application , alert, alert.name(), messageText, icon );
m_snore->broadcastNotification( n );
Snore::SnoreCore::instance().broadcastNotification( n );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "showing notification:" << messageText;
}
void
@@ -194,41 +157,28 @@ SnoreNotifyPlugin::nowPlaying( const QVariant& input )
QString messageText;
// If the window manager supports notification styling then use it.
if ( m_snore->primaryBackendSupportsRichtext() )
{
// Remark: If using xml-based markup in notifications, the supplied strings need to be escaped.
QString album;
if ( !hash[ "album" ].isEmpty() )
album = QString( "<br><i>%1</i> %2" ).arg( tr( "on", "'on' is followed by an album name" ) ).arg( Qt::escape( hash[ "album" ] ) );
messageText = tr( "%1%4 %2%3.", "%1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english)" )
.arg( Qt::escape( hash[ "title" ] ) )
.arg( Qt::escape( hash[ "artist" ] ) )
.arg( album )
.arg( QString( "<br><i>%1</i>" ).arg( tr( "by", "preposition to link track and artist" ) ) );
// Remark: If using xml-based markup in notifications, the supplied strings need to be escaped.
QString album;
if ( !hash[ "album" ].isEmpty() )
album = QString( "<br><i>%1</i> %2" ).arg( tr( "on", "'on' is followed by an album name" ) ).arg( hash[ "album" ].toHtmlEscaped() );
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
}
else
{
QString album;
if ( !hash[ "album" ].isEmpty() )
album = QString( " %1" ).arg( tr( "on \"%1\"", "%1 is an album name" ).arg( hash[ "album" ] ) );
messageText = tr( "%1%4 %2%3.", "%1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english)" )
.arg( hash[ "title" ].toHtmlEscaped() )
.arg( hash[ "artist" ].toHtmlEscaped() )
.arg( album )
.arg( QString( "<br><i>%1</i>" ).arg( tr( "by", "preposition to link track and artist" ) ) );
messageText = tr( "\"%1\" by %2%3.", "%1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing" )
.arg( hash[ "title" ] )
.arg( hash[ "artist" ] )
.arg( album );
}
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText;
// If there is a cover availble use it, else use Tomahawk logo as default.
Snore::Icon image;
Snore::Icon image = m_defaultIcon;
if ( map.contains( "cover" ) && map[ "cover" ].canConvert< QImage >() )
{
image = Snore::Icon( map[ "cover" ].value< QImage >() );
image = Snore::Icon( QPixmap::fromImage( map[ "cover" ].value< QImage >() ) );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << image;
}
notifyUser( InfoNowPlaying, messageText, image );
@@ -259,26 +209,15 @@ SnoreNotifyPlugin::inboxReceived( const QVariant& input )
return;
QString messageText;
// If the window manager supports notification styling then use it.
if ( m_snore->primaryBackendSupportsRichtext() )
{
// Remark: If using xml-based markup in notifications, the supplied strings need to be escaped.
messageText = tr( "%1 sent you\n%2%4 %3.", "%1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english)" )
.arg( Qt::escape( src["friendlyname"] ) )
.arg( Qt::escape( hash[ "title" ] ) )
.arg( Qt::escape( hash[ "artist" ] ) )
.arg( QString( "\n<i>%1</i>" ).arg( tr( "by", "preposition to link track and artist" ) ) );
// Remark: If using xml-based markup in notifications, the supplied strings need to be escaped.
messageText = tr( "%1 sent you\n%2%4 %3.", "%1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english)" )
.arg( src["friendlyname"].toHtmlEscaped() )
.arg( hash[ "title" ].toHtmlEscaped() )
.arg( hash[ "artist" ].toHtmlEscaped() )
.arg( QString( "\n<i>%1</i>" ).arg( tr( "by", "preposition to link track and artist" ) ) );
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
}
else
{
messageText = tr( "%1 sent you \"%2\" by %3.", "%1 is a nickname, %2 is a title, %3 is an artist" )
.arg( src["friendlyname"] )
.arg( hash[ "title" ] )
.arg( hash[ "artist" ] );
}
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
Snore::Icon icon( RESPATH "images/inbox-512x512.png" );
notifyUser( Tomahawk::InfoSystem::InfoInboxReceived, messageText, icon );

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013-2014, Patrick von Reth <vonreth@kde.org>
* Copyright 2013-2015, Hannah von Reth <vonreth@kde.org>
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
*
@@ -24,7 +24,7 @@
#include "../../InfoPluginDllMacro.h"
#include "infosystem/InfoSystem.h"
#include <snore/core/snore.h>
#include <libsnore/snore.h>
namespace Tomahawk
{
@@ -63,9 +63,8 @@ protected slots:
void slotActionInvoked(Snore::Notification n);
private:
void notifyUser( InfoType type, const QString &messageText, Snore::Icon icon = Snore::Icon() );
void notifyUser( InfoType type, const QString &messageText, Snore::Icon icon );
void addAlert( Tomahawk::InfoSystem::InfoType type, const QString &title );
Snore::SnoreCore *m_snore;
Snore::Application m_application;
Snore::Icon m_defaultIcon;
QHash< Tomahawk::InfoSystem::InfoType, Snore::Alert > m_alerts;

View File

@@ -62,6 +62,8 @@ ActionCollection::~ActionCollection()
void
ActionCollection::initActions()
{
// ATTENTION: Don't set ApplicationSpecificRole for submenu actions: they won't show up on OS X (Qt 5.5)
QAction *latchOn = new QAction( tr( "&Listen Along" ), this );
latchOn->setIcon( ImageRegistry::instance()->icon( RESPATH "images/headphones.svg" ) );
m_actionCollection[ "latchOn" ] = latchOn;
@@ -147,7 +149,6 @@ ActionCollection::initActions()
#endif
m_actionCollection[ "crashNow" ] = new QAction( "Crash now...", this );
m_actionCollection[ "whatsnew_0_8" ] = new QAction( tr( "%applicationName 0.8" ) , this );
m_actionCollection[ "whatsnew_0_8" ]->setMenuRole( QAction::ApplicationSpecificRole );
m_actionCollection[ "reportBug" ] = new QAction( tr( "Report a Bug" ) , this );
m_actionCollection[ "getSupport" ] = new QAction( tr( "Get Support" ) , this );
m_actionCollection[ "helpTranslate" ] = new QAction( tr( "Help Us Translate" ) , this );

View File

@@ -126,6 +126,8 @@ set( libGuiSources
utils/LinkGenerator.cpp
utils/LinkGeneratorPlugin.cpp
utils/TomaHkLinkGeneratorPlugin.cpp
utils/LinkParser.cpp
utils/LinkParserPlugin.cpp
viewpages/SearchViewPage.cpp
viewpages/SourceViewPage.cpp
@@ -199,7 +201,6 @@ list(APPEND libSources
MetaPlaylistInterface.cpp
Query.cpp
Result.cpp
ResultProvider.cpp
Source.cpp
Track.cpp
TrackData.cpp
@@ -351,8 +352,6 @@ list(APPEND libSources
resolvers/ScriptCommand_AllArtists.cpp
resolvers/ScriptCommand_AllAlbums.cpp
resolvers/ScriptCommand_AllTracks.cpp
resolvers/ScriptCommand_LookupUrl.cpp
resolvers/ScriptCommandQueue.cpp
resolvers/ScriptPluginFactory.cpp
# ScriptPlugins
@@ -360,6 +359,8 @@ list(APPEND libSources
resolvers/plugins/ScriptCollectionFactory.cpp
resolvers/ScriptInfoPlugin.cpp
resolvers/plugins/ScriptInfoPluginFactory.cpp
resolvers/plugins/ScriptLinkParserPlugin.cpp
resolvers/plugins/ScriptLinkParserPluginFactory.cpp
sip/SipPlugin.cpp
@@ -367,6 +368,7 @@ list(APPEND libSources
sip/PeerInfo.cpp
sip/SipStatusMessage.cpp
utils/UrlType.cpp
utils/Cloudstream.cpp
utils/Json.cpp
utils/TomahawkUtils.cpp
@@ -501,7 +503,7 @@ set_target_properties(
AUTOMOC TRUE
VERSION ${TOMAHAWK_VERSION_SHORT}
SOVERSION ${TOMAHAWK_VERSION_SHORT}
OUTPUT_NAME ${TOMAHAWK_TARGET_NAME}
OUTPUT_NAME ${TOMAHAWK_BASE_TARGET_NAME}
)
@@ -524,7 +526,6 @@ ENDIF( UNIX AND NOT APPLE )
TARGET_LINK_LIBRARIES( ${TOMAHAWK_LIBRARY}
LINK_PRIVATE
${LIBVLC_LIBRARY}
${LIBVLCCORE_LIBRARY}
# Thirdparty shipped with tomahawk
${LIBPORTFWD_LIBRARIES}

View File

@@ -217,7 +217,7 @@ DownloadJob::download()
arguments[ "url" ] = m_format.url;
// HACK: *shrug* WIP.
Tomahawk::ScriptJob* job = collection->scriptObject()->invoke( "getStreamUrlPromise", arguments );
Tomahawk::ScriptJob* job = collection->scriptObject()->invoke( "getStreamUrl", arguments );
connect( job, SIGNAL( done(QVariantMap) ), SLOT( onUrlRetrieved(QVariantMap) ) );
job->start();
}

View File

@@ -4,6 +4,7 @@
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2011-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2011-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,6 +36,7 @@
#include "utils/Logger.h"
#include "utils/TomahawkUtils.h"
#include "utils/XspfLoader.h"
#include "utils/LinkParser.h"
#include "Artist.h"
#include "Album.h"
@@ -172,12 +174,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
return true;
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
return true;
}
return Tomahawk::Utils::LinkParser::instance()->canParseUrl( url, Tomahawk::Utils::UrlTypePlaylist );
}
if ( acceptedType.testFlag( Track ) )
@@ -198,12 +195,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|| url.contains( "playlists" ) ) ) )
return true;
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Track ) )
return true;
}
return Tomahawk::Utils::LinkParser::instance()->canParseUrl( url, Tomahawk::Utils::UrlTypeTrack );
}
if ( acceptedType.testFlag( Album ) )
@@ -215,12 +207,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && url.contains( "album" ) && !url.contains( "track" ) ) )
return true;
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Album ) )
return true;
}
return Tomahawk::Utils::LinkParser::instance()->canParseUrl( url, Tomahawk::Utils::UrlTypeAlbum );
}
if ( acceptedType.testFlag( Artist ) )
@@ -232,12 +219,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && !url.contains( "album" ) && !url.contains( "track" ) ) )
return true;
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Artist ) )
return true;
}
return Tomahawk::Utils::LinkParser::instance()->canParseUrl( url, Tomahawk::Utils::UrlTypeArtist );
}
// We whitelist certain url-shorteners since they do some link checking. Often playable (e.g. spotify) links hide behind them,
@@ -303,15 +285,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
if ( ShortenedLinkParser::handlesUrl( url ) )
return true;
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Accepting current drop as a playlist" << resolver->name();
return true;
}
}
return Tomahawk::Utils::LinkParser::instance()->canParseUrl( url, Tomahawk::Utils::UrlTypePlaylist );
}
@@ -761,16 +735,12 @@ DropJob::handleTrackUrls( const QString& urls )
foreach ( QString track, tracks )
{
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
QList< QSharedPointer< Utils::LinkParserPlugin > > parserPlugins = Utils::LinkParser::instance()->parserPluginsForUrl( track, Utils::UrlTypeAny );
if( !parserPlugins.isEmpty() )
{
if ( resolver->canParseUrl( track, ExternalResolver::Any ) )
{
ScriptCommand_LookupUrl* cmd = new ScriptCommand_LookupUrl( resolver, track );
connect( cmd, SIGNAL( information( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ) );
cmd->enqueue();
m_queryCount++;
break;
}
Tomahawk::Utils::LinkParser::instance()->lookupUrl( track, parserPlugins );
connect( Tomahawk::Utils::LinkParser::instance(), SIGNAL( informationFound( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ) );
m_queryCount++;
}
}
}

View File

@@ -5,6 +5,7 @@
* Copyright (C) 2011-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright (C) 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright (C) 2013, Teo Mrnjavac <teo@kde.org>
* Copyright (C) 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,13 +36,13 @@
#include "playlist/TrackView.h"
#include "playlist/PlayableModel.h"
#include "resolvers/ExternalResolver.h"
#include "resolvers/ScriptCommand_LookupUrl.h"
#include "utils/JspfLoader.h"
#include "utils/Logger.h"
#include "utils/SpotifyParser.h"
#include "utils/XspfLoader.h"
#include "utils/XspfGenerator.h"
#include "viewpages/SearchViewPage.h"
#include "utils/LinkParser.h"
#include "Pipeline.h"
#include "TomahawkSettings.h"
@@ -162,27 +163,12 @@ GlobalActionManager::openUrl( const QString& url )
else if ( url.contains( "open.spotify.com" ) || url.startsWith( "spotify:" ) )
return openSpotifyLink( url );
// Can we parse the Url using a ScriptResolver?
bool canParse = false;
QList< QPointer< ExternalResolver > > possibleResolvers;
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
// Can we parse the Url using LinkParser?
QList< QSharedPointer< Utils::LinkParserPlugin > > parserPlugins = Utils::LinkParser::instance()->parserPluginsForUrl( url, Utils::UrlTypeAny );
if( !parserPlugins.isEmpty() )
{
if ( resolver->canParseUrl( url, ExternalResolver::Any ) )
{
canParse = true;
possibleResolvers << resolver;
}
}
if ( canParse )
{
m_queuedUrl = url;
foreach ( QPointer<ExternalResolver> resolver, possibleResolvers )
{
ScriptCommand_LookupUrl* cmd = new ScriptCommand_LookupUrl( resolver, url );
connect( cmd, SIGNAL( information( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ) );
cmd->enqueue();
}
Tomahawk::Utils::LinkParser::instance()->lookupUrl( url, parserPlugins );
connect( Tomahawk::Utils::LinkParser::instance(), SIGNAL( informationFound( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ), Qt::UniqueConnection );
return true;
}

View File

@@ -149,6 +149,15 @@ Pipeline::removeResolver( Resolver* r )
}
QList< Tomahawk::Resolver* >
Pipeline::resolvers() const
{
Q_D( const Pipeline );
return d->resolvers;
}
void
Pipeline::addResolver( Resolver* r )
{
@@ -323,6 +332,13 @@ Pipeline::resolve( QID qid, bool prioritized, bool temporaryQuery )
}
void
Pipeline::reportError( QID qid, Tomahawk::Resolver* r )
{
reportResults( qid, r, QList< result_ptr>() );
}
void
Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results )
{
@@ -333,7 +349,7 @@ Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr
{
if ( !results.isEmpty() )
{
ResultProvider* resolvedBy = results[0]->resolvedBy();
Resolver* resolvedBy = results[0]->resolvedBy();
if ( resolvedBy )
{
tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name();

View File

@@ -54,6 +54,7 @@ public:
unsigned int pendingQueryCount() const;
unsigned int activeQueryCount() const;
void reportError( QID qid, Tomahawk::Resolver* r );
void reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results );
void reportAlbums( QID qid, const QList< album_ptr >& albums );
void reportArtists( QID qid, const QList< artist_ptr >& artists );
@@ -65,6 +66,7 @@ public:
QList< QPointer< ExternalResolver > > scriptResolvers() const;
Tomahawk::ExternalResolver* resolverForPath( const QString& scriptPath );
QList< Resolver* > resolvers() const;
void addResolver( Resolver* r );
void removeResolver( Resolver* r );

View File

@@ -491,7 +491,7 @@ Result::setFileId( unsigned int id )
}
Tomahawk::ResultProvider*
Tomahawk::Resolver*
Result::resolvedBy() const
{
if ( !m_collection.isNull() )

View File

@@ -21,7 +21,6 @@
#ifndef RESULT_H
#define RESULT_H
#include "ResultProvider.h"
#include "DownloadJob.h"
#include "utils/TomahawkUtils.h"
#include "Typedefs.h"
@@ -86,9 +85,9 @@ public:
void setResolvedByResolver( Tomahawk::Resolver* resolver );
/**
* This is very bad. ResultProvider is not a QObject and thus can not be tracked by a qt smart pointer ... :-(
* TODO: Make this a smart pointer
*/
ResultProvider* resolvedBy() const;
Resolver* resolvedBy() const;
RID id() const;
bool isOnline() const;

View File

@@ -76,6 +76,8 @@ public:
*/
virtual bool addPageItem() const;
virtual bool isRemovable() const { return false; }
/**
* This page is actually a constant page that will be shown on every
* restart of Tomahawk until the user selects it to be removed.

View File

@@ -524,7 +524,7 @@ void ResolverAccount::testConfig()
if ( resolver )
{
QVariantMap data = resolver->loadDataFromWidgets();
ScriptJob* job = resolver->scriptObject()->invoke( "_testConfig", data );
ScriptJob* job = resolver->scriptObject()->invoke( "testConfig", data );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTestConfig( QVariantMap ) ) );
job->start();
}

View File

@@ -65,13 +65,6 @@ LastFmInfoPlugin::init()
return;
}
lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a";
lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f";
lastfm::ws::Username = m_account.data()->username();
lastfm::setNetworkAccessManager( Tomahawk::Utils::nam() );
m_pw = m_account.data()->password();
//HACK work around a bug in liblastfm---it doesn't create its config dir, so when it
// tries to write the track cache, it fails silently. until we have a fixed version, do this
// code taken from Amarok (src/services/lastfm/ScrobblerAdapter.cpp)
@@ -86,6 +79,17 @@ LastFmInfoPlugin::init()
m_badUrls << QUrl( "http://cdn.last.fm/flatness/catalogue/noimage" );
lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a";
lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f";
lastfm::setNetworkAccessManager( Tomahawk::Utils::nam() );
if ( !m_account.isNull() )
{
lastfm::ws::Username = m_account->username();
m_pw = m_account->password();
}
QTimer::singleShot( 0, this, SLOT( settingsChanged() ) );
}

View File

@@ -842,15 +842,28 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg
const QString qid = msg.value( "qid" ).toString();
if ( m_qidToSlotMap.contains( qid ) )
{
QObject* receiver = m_qidToSlotMap[ qid ].first;
QPointer< QObject > receiver = m_qidToSlotMap[ qid ].first;
QString slot = m_qidToSlotMap[ qid ].second;
m_qidToSlotMap.remove( qid );
QVariant extraData;
if ( m_qidToExtraData.contains( qid ) )
extraData = m_qidToExtraData.take( qid );
QMetaObject::invokeMethod( receiver, slot.toLatin1(), Q_ARG( QString, msgType ), Q_ARG( QVariantMap, msg ), Q_ARG( QVariant, extraData ) );
// FIXME: SpotifyParser is sometimes a dangling pointer, haven't found a real way to reproduce: happens sometimes when dropping a playlist url onto the sidebar
//Q_ASSERT( !receiver.isNull() );
if ( !receiver.isNull() )
{
QMetaObject::invokeMethod( receiver, slot.toLatin1(), Q_ARG( QString, msgType ), Q_ARG( QVariantMap, msg ), Q_ARG( QVariant, extraData ) );
}
else
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Spotify account could not finish action. Try again." )
) );
}
}
else if ( msgType == "allPlaylists" )
{

View File

@@ -172,7 +172,7 @@ private:
QPointer<ScriptResolver> m_spotifyResolver;
QPointer< InfoSystem::SpotifyInfoPlugin > m_infoPlugin;
QMap<QString, QPair<QObject*, QString> > m_qidToSlotMap;
QMap<QString, QPair< QPointer< QObject >, QString> > m_qidToSlotMap;
QMap<QString, QVariant > m_qidToExtraData;
// List of synced spotify playlists in config UI

View File

@@ -32,6 +32,8 @@
#include "playlist/SingleTrackPlaylistInterface.h"
#include "utils/Closure.h"
#include "utils/Logger.h"
#include "utils/NetworkReply.h"
#include "utils/NetworkAccessManager.h"
#include "Album.h"
#include "Artist.h"
@@ -40,6 +42,7 @@
#include "SourceList.h"
#include "TomahawkSettings.h"
#include "UrlHandler.h"
#include "resolvers/ScriptJob.h"
#include <QDir>
@@ -574,16 +577,64 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
setCurrentTrack( result );
if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && !TomahawkUtils::isHttpResult( d->currentTrack->url() )
&& !TomahawkUtils::isRtmpResult( d->currentTrack->url() ) )
ScriptJob* job = result->resolvedBy()->getStreamUrl( result );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( gotStreamUrl( QVariantMap ) ) );
job->setProperty( "result", QVariant::fromValue( result ) );
job->start();
}
void
AudioEngine::gotStreamUrl( const QVariantMap& data )
{
QString streamUrl = data[ "url" ].toString();
QVariantMap headers = data[ "headers" ].toMap();
Tomahawk::result_ptr result = sender()->property( "result" ).value<result_ptr>();
if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) || TomahawkUtils::isRtmpResult( streamUrl ) ) )
{
performLoadIODevice( d->currentTrack, d->currentTrack->url() );
// Not an http(s) or RTMP URL, get IO device
QSharedPointer< QIODevice > sp;
performLoadIODevice( result, streamUrl );
}
else
{
QSharedPointer< QIODevice > io;
performLoadTrack( result, result->url(), io );
// TODO: just make this part of the http(s) IoDeviceFactory (?)
QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() );
QNetworkRequest req( url );
QMap<QString, QString> parsedHeaders;
foreach ( const QString& key, headers.keys() )
{
Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" );
if ( headers[key].canConvert( QVariant::String ) )
{
parsedHeaders.insert( key, headers[key].toString() );
}
}
foreach ( const QString& key, parsedHeaders.keys() )
{
req.setRawHeader( key.toLatin1(), parsedHeaders[key].toLatin1() );
}
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* )), result, reply );
}
sender()->deleteLater();
}
void
AudioEngine::gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply )
{
// std::functions cannot accept temporaries as parameters
QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater );
QString url = reply->reply()->url().toString();
reply->disconnectFromReply();
reply->deleteLater();
performLoadTrack( result, url, sp );
}

View File

@@ -28,6 +28,7 @@
#include "DllMacro.h"
class NetworkReply;
class AudioEnginePrivate;
class DLLEXPORT AudioEngine : public QObject
@@ -180,6 +181,10 @@ signals:
private slots:
void loadTrack( const Tomahawk::result_ptr& result ); //async!
void gotStreamUrl( const QVariantMap& data );
void gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply );
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi
void performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
void loadPreviousTrack();

View File

@@ -35,7 +35,7 @@ using namespace Tomahawk;
Collection::Collection( const source_ptr& source, const QString& name, QObject* parent )
: QObject( parent )
: Resolver( parent )
, m_name( name )
, m_lastmodified( 0 )
, m_changed( false )

View File

@@ -33,7 +33,7 @@
#include "collection/ArtistsRequest.h"
#include "collection/AlbumsRequest.h"
#include "collection/TracksRequest.h"
#include "../ResultProvider.h"
#include "../resolvers/Resolver.h"
#include "DllMacro.h"
@@ -46,7 +46,7 @@
namespace Tomahawk
{
class DLLEXPORT Collection : public QObject, public ResultProvider
class DLLEXPORT Collection : public Resolver
{
Q_OBJECT

View File

@@ -251,3 +251,32 @@ DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList
}
/*
* Resolver interface
*
* We implement searching the database in the DatabaseResolver which avoids a n+1 query here.
* We can't simply let ScriptCollection inherit Collection and Resolver because both are QObjects,
* although Resolver doesn't need to be a QObject atm, blocking adding signals/slots to Resolver
* in future seems to me worse than violating Liskov's law here. ~ domme
*/
unsigned int
DatabaseCollection::timeout() const
{
return 0;
}
unsigned int
DatabaseCollection::weight() const
{
return 0;
}
void
DatabaseCollection::resolve( const Tomahawk::query_ptr& query )
{
Q_UNUSED( query );
Q_ASSERT(false);
}

View File

@@ -65,6 +65,11 @@ public:
int trackCount() const override;
QPixmap icon( const QSize& size ) const override;
// Resolver interface
unsigned int weight() const override;
unsigned int timeout() const override;
void resolve( const Tomahawk::query_ptr& query ) override;
public slots:
virtual void addTracks( const QList<QVariant>& newitems );
virtual void removeTracks( const QDir& dir );

View File

@@ -262,8 +262,15 @@ TrackDetailView::onResultsChanged()
resolverLabel->setFont( f );
resolverLabel->setStyleSheet( "QLabel { color: rgba( 0, 0, 0, 50% ) }" );
resolverLabel->setText( QString( "%1 - %2" ).arg( result->track()->track() ).arg( result->track()->artist() ) );
resolverLabel->setToolTip( QString( "%1 by %2%3" ).arg( result->track()->track() ).arg( result->track()->artist() )
.arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() ) );
resolverLabel->setToolTip(
QString( "%1 by %2%3 (%4)" )
.arg( result->track()->track() )
.arg( result->track()->artist() )
.arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() )
.arg( result->friendlySource() )
);
;
resolverLabel->setFixedWidth( width() - 32 - 4 );
NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ),

View File

@@ -3,6 +3,7 @@
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,8 +25,6 @@
#include "Source.h"
#include "DllMacro.h"
#include "Resolver.h"
#include "ScriptCommandQueue.h"
#include "ScriptCommand_LookupUrl.h"
#include "Typedefs.h"
#include <QObject>
@@ -45,7 +44,6 @@ class DLLEXPORT ExternalResolver : public Resolver
{
Q_OBJECT
friend class ScriptCommandQueue;
friend class ScriptCommand_LookupUrl;
public:
@@ -61,25 +59,13 @@ public:
Browsable = 0x1, // can be represented in one or more collection tree views
PlaylistSync = 0x2, // can sync playlists
AccountFactory = 0x4, // can configure multiple accounts at the same time
UrlLookup = 0x8 // can be queried for information on an Url
};
Q_DECLARE_FLAGS( Capabilities, Capability )
Q_FLAGS( Capabilities )
enum UrlType
{
Any = 0x00,
Playlist = 0x01,
Track = 0x02,
Album = 0x04,
Artist = 0x08
};
Q_DECLARE_FLAGS( UrlTypes, UrlType )
Q_FLAGS( UrlTypes )
ExternalResolver( const QString& filePath )
: m_commandQueue( new ScriptCommandQueue( this ) )
{ m_filePath = filePath; }
: m_filePath( filePath )
{}
QString filePath() const { return m_filePath; }
virtual void setIcon( const QPixmap& ) {}
@@ -91,12 +77,6 @@ public:
virtual bool running() const = 0;
virtual Capabilities capabilities() const = 0;
// UrlLookup, sync call
virtual bool canParseUrl( const QString& url, UrlType type ) = 0;
virtual void enqueue( const QSharedPointer< ScriptCommand >& req )
{ m_commandQueue->enqueue( req ); }
public slots:
virtual void start() = 0;
virtual void stop() = 0;
@@ -111,11 +91,6 @@ signals:
protected:
void setFilePath( const QString& path ) { m_filePath = path; }
ScriptCommandQueue* m_commandQueue;
// Should only be called by ScriptCommands
// UrlLookup
virtual void lookupUrl( const QString& url ) = 0;
private:
QString m_filePath;

View File

@@ -30,6 +30,8 @@
#include <QWidget>
#include <QUiLoader>
#include <QBoxLayout>
#include <QPushButton>
#include <QDesktopServices>
Tomahawk::ExternalResolverGui::ExternalResolverGui(const QString& filePath)
: Tomahawk::ExternalResolver(filePath)
@@ -82,6 +84,30 @@ Tomahawk::ExternalResolverGui::addChildProperties( QObject* widget, QVariantMap&
}
void
Tomahawk::ExternalResolverGui::setupClickHandlerOnUrlButtons( QObject* widget )
{
if( !widget || !widget->isWidgetType() )
return;
if( qstrcmp( widget->metaObject()->className(), "QPushButton" ) == 0 && !widget->property( "url" ).isNull() )
{
QPushButton* button = qobject_cast< QPushButton* >( widget );
Q_ASSERT( button );
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
connect( button, &QPushButton::clicked, [=]() {
QDesktopServices::openUrl( widget->property( "url" ).toUrl() );
});
#endif
}
// and recurse
foreach( QObject* child, widget->children() )
setupClickHandlerOnUrlButtons( child );
}
AccountConfigWidget*
Tomahawk::ExternalResolverGui::widgetFromData( QByteArray& data, QWidget* parent )
{
@@ -94,6 +120,8 @@ Tomahawk::ExternalResolverGui::widgetFromData( QByteArray& data, QWidget* parent
QBuffer b( &data );
QWidget* w = l.load( &b, configWidget );
setupClickHandlerOnUrlButtons( w );
// HACK: proper way would be to create a designer plugin for this widget type
configWidget->setLayout( new QBoxLayout( QBoxLayout::TopToBottom ) );
configWidget->layout()->addWidget( w );

View File

@@ -50,7 +50,8 @@ protected:
QByteArray fixDataImagePaths( const QByteArray& data, bool compressed, const QVariantMap& images );
private:
void addChildProperties( QObject* parent, QVariantMap& m );
void addChildProperties( QObject* widget, QVariantMap& m );
void setupClickHandlerOnUrlButtons( QObject* widget );
};
}; //ns

View File

@@ -170,6 +170,41 @@ JSAccount::syncInvoke( const scriptobject_ptr& scriptObject, const QString& meth
return evaluateJavaScriptWithResult( eval );
}
void
JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result )
{
QString eval = QString(
"Tomahawk.NativeScriptJobManager.reportNativeScriptJobResult("
"%1," // requestId
"%2" // results
");"
).arg( resultId )
.arg( serializeQVariantMap( result ) );
// Remove when new scripting api turned out to work reliably
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
evaluateJavaScript( eval );
}
void
JSAccount::reportNativeScriptJobError( int resultId, const QVariantMap& error )
{
QString eval = QString(
"Tomahawk.NativeScriptJobManager.reportNativeScriptJobError("
"%1," // requestId
"%2" // results
");"
).arg( resultId )
.arg( serializeQVariantMap( error ) );
// Remove when new scripting api turned out to work reliably
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
evaluateJavaScript( eval );
}
QVariant
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )

View File

@@ -71,13 +71,16 @@ public:
static QString serializeQVariantMap(const QVariantMap& map);
void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override;
void reportNativeScriptJobError( int resultId, const QVariantMap& error ) override;
private:
/**
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
*/
QVariant evaluateJavaScriptInternal( const QString& scriptSource );
std::unique_ptr<ScriptEngine> m_engine;
ScriptEngine* m_engine;
// HACK: the order of initializen is flawed, tbr
JSResolver* m_resolver;
};

View File

@@ -4,6 +4,7 @@
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,6 +45,9 @@
#include "Track.h"
#include "ScriptInfoPlugin.h"
#include "JSAccount.h"
#include "ScriptJob.h"
#include "database/Database.h"
#include "database/DatabaseImpl.h"
#include <QDir>
#include <QFile>
@@ -250,12 +254,11 @@ JSResolver::init()
d->scriptAccount->loadScript( filePath() );
// HACK: register resolver object
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" )
;
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" );
// init resolver
resolverInit();
scriptObject()->syncInvoke( "init" );
QVariantMap m = resolverSettings();
QVariantMap m = scriptObject()->syncInvoke( "settings" ).toMap();
d->name = m.value( "name" ).toString();
d->weight = m.value( "weight", 0 ).toUInt();
d->timeout = m.value( "timeout", 25 ).toUInt() * 1000;
@@ -305,6 +308,8 @@ JSResolver::start()
Q_D( JSResolver );
d->stopped = false;
d->resolverHelper->start();
if ( d->ready )
Tomahawk::Pipeline::instance()->addResolver( this );
else
@@ -314,65 +319,16 @@ JSResolver::start()
}
bool
JSResolver::canParseUrl( const QString& url, UrlType type )
{
Q_D( const JSResolver );
// FIXME: How can we do this?
/*if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "canParseUrl", Qt::QueuedConnection,
Q_ARG( QString, url ) );
return;
}*/
if ( d->capabilities.testFlag( UrlLookup ) )
{
QString eval = QString( "canParseUrl( '%1', %2 )" )
.arg( JSAccount::escape( QString( url ) ) )
.arg( (int) type );
return callOnResolver( eval ).toBool();
}
else
{
// We cannot do URL lookup.
return false;
}
}
void
JSResolver::lookupUrl( const QString& url )
JSResolver::tracksAdded( const QList<query_ptr>&, const ModelMode, const collection_ptr&)
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "lookupUrl", Qt::QueuedConnection,
Q_ARG( QString, url ) );
// Check if we still are actively waiting
if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() )
return;
}
Q_D( const JSResolver );
if ( !d->capabilities.testFlag( UrlLookup ) )
{
emit informationFound( url, QSharedPointer<QObject>() );
return;
}
QString eval = QString( "lookupUrl( '%1' )" )
.arg( JSAccount::escape( QString( url ) ) );
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
// if the resolver doesn't return anything, async api is used
return;
}
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
tDebug() << errorMessage << m;
emit informationFound( m_pendingUrl, m_pendingAlbum.objectCast<QObject>() );
m_pendingAlbum = album_ptr();
m_pendingUrl = QString();
}
@@ -388,38 +344,51 @@ JSResolver::error() const
void
JSResolver::resolve( const Tomahawk::query_ptr& query )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) );
return;
}
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query, "resolver" );
QString eval;
if ( !query->isFullTextQuery() )
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
job->start();
}
void
JSResolver::onResolveRequestDone( const QVariantMap& data )
{
Q_ASSERT( QThread::currentThread() == thread() );
Q_D( JSResolver );
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
QID qid = job->property( "qid" ).toString();
if ( job->error() )
{
eval = QString( "resolve( '%1', '%2', '%3', '%4' )" )
.arg( JSAccount::escape( query->id() ) )
.arg( JSAccount::escape( query->queryTrack()->artist() ) )
.arg( JSAccount::escape( query->queryTrack()->album() ) )
.arg( JSAccount::escape( query->queryTrack()->track() ) );
Tomahawk::Pipeline::instance()->reportError( qid, this );
}
else
{
eval = QString( "search( '%1', '%2' )" )
.arg( JSAccount::escape( query->id() ) )
.arg( JSAccount::escape( query->fullTextQuery() ) );
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
foreach( const result_ptr& result, results )
{
result->setResolvedByResolver( this );
result->setFriendlySource( name() );
}
Tomahawk::Pipeline::instance()->reportResults( qid, this, results );
}
QVariantMap m = callOnResolver( eval ).toMap();
sender()->deleteLater();
}
void
JSResolver::stop()
{
Q_D( JSResolver );
d->stopped = true;
d->resolverHelper->stop();
scriptAccount()->stop();
@@ -433,7 +402,7 @@ JSResolver::loadUi()
{
Q_D( JSResolver );
QVariantMap m = callOnResolver( "getConfigUi()" ).toMap();
QVariantMap m = scriptObject()->syncInvoke( "getConfigUi" ).toMap();
bool compressed = m.value( "compressed", "false" ).toBool();
qDebug() << "Resolver has a preferences widget! compressed?" << compressed;
@@ -485,7 +454,7 @@ JSResolver::saveConfig()
// qDebug() << Q_FUNC_INFO << saveData;
d->resolverHelper->setResolverConfig( saveData.toMap() );
callOnResolver( "saveUserConfig()" );
scriptObject()->syncInvoke( "saveUserConfig" );
}
@@ -516,39 +485,18 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa
}
QVariantMap
JSResolver::resolverSettings()
{
return callOnResolver( "settings" ).toMap();
}
QVariantMap
JSResolver::resolverUserConfig()
{
return callOnResolver( "getUserConfig()" ).toMap();
return scriptObject()->syncInvoke( "getUserConfig" ).toMap();
}
QVariantMap
JSResolver::resolverInit()
ScriptJob*
JSResolver::getStreamUrl( const result_ptr& result )
{
return callOnResolver( "init()" ).toMap();
}
QVariant
JSResolver::callOnResolver( const QString& scriptSource )
{
Q_D( JSResolver );
QString propertyName = scriptSource.split('(').first();
return d->scriptAccount->evaluateJavaScriptWithResult( QString(
"if(Tomahawk.resolver.instance['_adapter_%1']) {"
" Tomahawk.resolver.instance._adapter_%2;"
"} else {"
" Tomahawk.resolver.instance.%2"
"}"
).arg( propertyName ).arg( scriptSource ) );
QVariantMap arguments;
arguments["url"] = result->url();
return scriptObject()->invoke( "getStreamUrl", arguments );
}

View File

@@ -4,6 +4,7 @@
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -69,26 +70,26 @@ public:
void setIcon( const QPixmap& icon ) override;
bool canParseUrl( const QString& url, UrlType type ) override;
QVariantMap loadDataFromWidgets();
ScriptAccount* scriptAccount() const;
ScriptJob* getStreamUrl( const result_ptr& result ) override;
public slots:
void resolve( const Tomahawk::query_ptr& query ) override;
void stop() override;
void start() override;
// For UrlLookup
void lookupUrl( const QString& url ) override;
signals:
void stopped();
protected:
QVariant callOnResolver( const QString& scriptSource );
private slots:
void onResolveRequestDone(const QVariantMap& data);
private:
void init();
@@ -96,12 +97,16 @@ private:
void onCapabilitiesChanged( Capabilities capabilities );
// encapsulate javascript calls
QVariantMap resolverSettings();
QVariantMap resolverUserConfig();
QVariantMap resolverInit();
Q_DECLARE_PRIVATE( JSResolver )
QScopedPointer<JSResolverPrivate> d_ptr;
// TODO: collection stuff, get rid of collection scriptcommands
QString m_pendingUrl;
Tomahawk::album_ptr m_pendingAlbum;
private slots:
void tracksAdded( const QList<Tomahawk::query_ptr>& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection );
};
} // ns: Tomahawk

View File

@@ -21,10 +21,6 @@
#include "JSResolverHelper.h"
#include "database/Database.h"
#include "database/DatabaseImpl.h"
#include "playlist/PlaylistTemplate.h"
#include "playlist/XspfPlaylistTemplate.h"
#include "resolvers/ScriptEngine.h"
#include "network/Servent.h"
#include "utils/Closure.h"
@@ -51,6 +47,8 @@
#include <QMap>
#include <QWebFrame>
#include <QLocale>
#include <QNetworkReply>
#include <taglib/asffile.h>
#include <taglib/flacfile.h>
#include <taglib/id3v2framefactory.h>
@@ -76,11 +74,25 @@ JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* paren
: QObject( parent )
, m_resolver( parent )
, m_scriptPath( scriptPath )
, m_urlCallbackIsAsync( false )
, m_stopped( false )
{
}
void
JSResolverHelper::start()
{
m_stopped = false;
}
void
JSResolverHelper::stop()
{
m_stopped = true;
}
QByteArray
JSResolverHelper::readRaw( const QString& fileName )
{
@@ -140,55 +152,6 @@ JSResolverHelper::log( const QString& message )
}
void
JSResolverHelper::addTrackResults( const QVariantMap& results )
{
Q_ASSERT( results["results"].toMap().isEmpty() );
QList< Tomahawk::result_ptr > tracks = m_resolver->scriptAccount()->parseResultVariantList( results.value( "results" ).toList() );
foreach( const result_ptr& track, tracks )
{
track->setResolvedByResolver( m_resolver );
track->setFriendlySource( m_resolver->name() );
}
QString qid = results.value("qid").toString();
Tomahawk::Pipeline::instance()->reportResults( qid, m_resolver, tracks );
}
query_ptr
JSResolverHelper::parseTrack( const QVariantMap& track )
{
QString title = track.value( "title" ).toString();
QString artist = track.value( "artist" ).toString();
QString album = track.value( "album" ).toString();
if ( title.isEmpty() || artist.isEmpty() )
{
return query_ptr();
}
Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album );
QString resultHint = track.value( "hint" ).toString();
if ( !resultHint.isEmpty() )
{
query->setResultHint( resultHint );
query->setSaveHTTPResultHint( true );
}
return query;
}
QString
JSResolverHelper::instanceUUID()
{
return Tomahawk::Database::instance()->impl()->dbid();
}
QString
JSResolverHelper::uuid() const
{
@@ -483,123 +446,13 @@ JSResolverHelper::currentCountry() const
}
void
JSResolverHelper::addUrlResult( const QString& url, const QVariantMap& result )
{
// It may seem a bit weird, but currently no slot should do anything
// more as we starting on a new URL and not task are waiting for it yet.
m_pendingUrl = QString();
m_pendingAlbum = album_ptr();
QString type = result.value( "type" ).toString();
if ( type == "artist" )
{
QString name = result.value( "name" ).toString();
Q_ASSERT( !name.isEmpty() );
emit m_resolver->informationFound( url, Artist::get( name, true ).objectCast<QObject>() );
}
else if ( type == "album" )
{
QString name = result.value( "name" ).toString();
QString artist = result.value( "artist" ).toString();
album_ptr album = Album::get( Artist::get( artist, true ), name );
m_pendingUrl = url;
m_pendingAlbum = album;
connect( album.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ),
SLOT( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) );
if ( !album->tracks().isEmpty() )
{
emit m_resolver->informationFound( url, album.objectCast<QObject>() );
}
}
else if ( type == "track" )
{
Tomahawk::query_ptr query = parseTrack( result );
if ( query.isNull() )
{
// A valid track result shoud have non-empty title and artist.
tLog() << Q_FUNC_INFO << m_resolver->name() << "Got empty track information for " << url;
emit m_resolver->informationFound( url, QSharedPointer<QObject>() );
}
else
{
emit m_resolver->informationFound( url, query.objectCast<QObject>() );
}
}
else if ( type == "playlist" )
{
QString guid = result.value( "guid" ).toString();
Q_ASSERT( !guid.isEmpty() );
// Append nodeid to guid to make it globally unique.
guid += instanceUUID();
// Do we already have this playlist loaded?
{
playlist_ptr playlist = Playlist::get( guid );
if ( !playlist.isNull() )
{
emit m_resolver->informationFound( url, playlist.objectCast<QObject>() );
return;
}
}
// Get all information to build a new playlist but do not build it until we know,
// if it is really handled as a playlist and not as a set of tracks.
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
const QString title = result.value( "title" ).toString();
const QString info = result.value( "info" ).toString();
const QString creator = result.value( "creator" ).toString();
QList<query_ptr> queries;
foreach( QVariant track, result.value( "tracks" ).toList() )
{
query_ptr query = parseTrack( track.toMap() );
if ( !query.isNull() )
{
queries << query;
}
}
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url;
playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) );
emit m_resolver->informationFound( url, pltemplate.objectCast<QObject>() );
}
else if ( type == "xspf-url" )
{
QString xspfUrl = result.value( "url" ).toString();
Q_ASSERT( !xspfUrl.isEmpty() );
QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() );
// Do we already have this playlist loaded?
{
playlist_ptr playlist = Playlist::get( guid );
if ( !playlist.isNull() )
{
emit m_resolver->informationFound( url, playlist.objectCast<QObject>() );
return;
}
}
// Get all information to build a new playlist but do not build it until we know,
// if it is really handled as a playlist and not as a set of tracks.
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
QSharedPointer<XspfPlaylistTemplate> pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) );
NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ),
this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ),
url, pltemplate.objectCast<Tomahawk::PlaylistTemplate>() );
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url;
pltemplate->load();
}
else
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "No usable information found for " << url;
emit m_resolver->informationFound( url, QSharedPointer<QObject>() );
}
}
void
JSResolverHelper::nativeReportCapabilities( const QVariant& v )
{
if( m_stopped )
return;
bool ok;
int intCap = v.toInt( &ok );
Tomahawk::ExternalResolver::Capabilities capabilities;
@@ -615,6 +468,9 @@ JSResolverHelper::nativeReportCapabilities( const QVariant& v )
void
JSResolverHelper::reportScriptJobResults( const QVariantMap& result )
{
if( m_stopped )
return;
m_resolver->d_func()->scriptAccount->reportScriptJobResult( result );
}
@@ -622,6 +478,9 @@ JSResolverHelper::reportScriptJobResults( const QVariantMap& result )
void
JSResolverHelper::registerScriptPlugin( const QString& type, const QString& objectId )
{
if( m_stopped )
return;
m_resolver->d_func()->scriptAccount->registerScriptPlugin( type, objectId );
}
@@ -629,28 +488,10 @@ JSResolverHelper::registerScriptPlugin( const QString& type, const QString& obje
void
JSResolverHelper::unregisterScriptPlugin( const QString& type, const QString& objectId )
{
m_resolver->d_func()->scriptAccount->unregisterScriptPlugin( type, objectId );
}
void
JSResolverHelper::tracksAdded( const QList<query_ptr>&, const ModelMode, const collection_ptr&)
{
// Check if we still are actively waiting
if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() )
if( m_stopped )
return;
emit m_resolver->informationFound( m_pendingUrl, m_pendingAlbum.objectCast<QObject>() );
m_pendingAlbum = album_ptr();
m_pendingUrl = QString();
}
void
JSResolverHelper::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate )
{
tLog() << Q_FUNC_INFO;
emit m_resolver->informationFound( url, pltemplate.objectCast<QObject>() );
m_resolver->d_func()->scriptAccount->unregisterScriptPlugin( type, objectId );
}
@@ -668,31 +509,6 @@ JSResolverHelper::accountId()
}
void
JSResolverHelper::addCustomUrlHandler( const QString& protocol,
const QString& callbackFuncName,
const QString& isAsynchronous )
{
m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" );
std::function< void( const Tomahawk::result_ptr&, const QString&,
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > )> fac =
std::bind( &JSResolverHelper::customIODeviceFactory, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3 );
Tomahawk::UrlHandler::registerIODeviceFactory( protocol, fac );
m_urlCallback = callbackFuncName;
}
void
JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl )
{
reportStreamUrl( qid, streamUrl, QVariantMap() );
}
void JSResolverHelper::nativeAssert( bool assertion, const QString& message )
{
if ( !assertion )
@@ -703,61 +519,6 @@ void JSResolverHelper::nativeAssert( bool assertion, const QString& message )
}
void
JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url,
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback )
{
//can be sync or async
if ( m_urlCallbackIsAsync )
{
QString qid = uuid();
QString getUrl = QString(
"if(Tomahawk.resolver.instance['_adapter_%1']) {"
" Tomahawk.resolver.instance._adapter_%1( {qid: '%2', url: '%3'} );"
"} else {"
" Tomahawk.resolver.instance.%1( {qid: '%2', url: '%3'} );"
"}"
).arg( m_urlCallback )
.arg( qid )
.arg( url );
m_streamCallbacks.insert( qid, callback );
m_resolver->d_func()->scriptAccount->evaluateJavaScript( getUrl );
}
else
{
QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback )
.arg( url );
QString urlStr = m_resolver->d_func()->scriptAccount->evaluateJavaScriptWithResult( getUrl ).toString();
returnStreamUrl( urlStr, QMap<QString, QString>(), callback );
}
}
void
JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers )
{
if ( !m_streamCallbacks.contains( qid ) )
return;
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid );
QMap<QString, QString> parsedHeaders;
foreach ( const QString& key, headers.keys() )
{
Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" );
if ( headers[key].canConvert( QVariant::String ) )
{
parsedHeaders.insert( key, headers[key].toString() );
}
}
returnStreamUrl( streamUrl, parsedHeaders, callback );
}
void
JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
const QString& mime_type, int sizehint,
@@ -896,12 +657,27 @@ JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
}
}
void
JSResolverHelper::invokeNativeScriptJob( int requestId, const QString& methodName, const QVariantMap& params )
{
if ( methodName == "httpRequest" ) {
nativeAsyncRequest( requestId, params );
} else {
QVariantMap error;
error["message"] = "NativeScriptJob methodName was not found";
error["name"] = "method_was_not_found";
m_resolver->d_func()->scriptAccount->reportNativeScriptJobError( requestId, error );
}
}
void
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
const QVariantMap& headers,
const QVariantMap& options )
JSResolverHelper::nativeAsyncRequest( const int requestId, const QVariantMap& options )
{
QString url = options[ "url" ].toString();
QVariantMap headers = options[ "headers" ].toMap();
QNetworkRequest req( url );
foreach ( const QString& key, headers.keys() )
{
@@ -957,17 +733,16 @@ JSResolverHelper::nativeAsyncRequestDone( int requestId, NetworkReply* reply )
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
if (reply->reply()->hasRawHeader( "Content-Type" ))
map["contentType"] = reply->reply()->rawHeader( "Content-Type" );
bool ok = false;
QString json = QString::fromUtf8( TomahawkUtils::toJson( map, &ok ) );
Q_ASSERT( ok );
QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" )
.arg( QString::number( requestId ) )
.arg( json );
m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript );
QVariantMap responseHeaders;
foreach( const QNetworkReply::RawHeaderPair& pair, reply->reply()->rawHeaderPairs() )
{
responseHeaders[ pair.first ] = pair.second;
}
map["responseHeaders"] = responseHeaders;
m_resolver->d_func()->scriptAccount->reportNativeScriptJobResult( requestId, map );
}
@@ -1143,43 +918,3 @@ JSResolverHelper::readdResolver()
Pipeline::instance()->addResolver( m_resolver );
}
void
JSResolverHelper::returnStreamUrl( const QString& streamUrl, const QMap<QString, QString>& headers,
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback )
{
if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) ) )
{
// Not an https? URL, so let Phonon handle it
QSharedPointer< QIODevice > sp;
callback( streamUrl, sp );
}
else
{
QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() );
QNetworkRequest req( url );
foreach ( const QString& key, headers.keys() )
{
req.setRawHeader( key.toLatin1(), headers[key].toLatin1() );
}
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotStreamUrl( IODeviceCallback, NetworkReply* )), callback, reply );
}
}
Q_DECLARE_METATYPE( IODeviceCallback )
void
JSResolverHelper::gotStreamUrl( std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback, NetworkReply* reply )
{
// std::functions cannot accept temporaries as parameters
QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater );
QString url = reply->reply()->url().toString();
reply->disconnectFromReply();
reply->deleteLater();
callback( url, sp );
}

View File

@@ -54,6 +54,9 @@ public:
*/
void setResolverConfig( const QVariantMap& config );
void start();
void stop();
/**
* Get the instance unique account id for this resolver.
*
@@ -61,9 +64,6 @@ public:
*/
Q_INVOKABLE QString accountId();
Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" );
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl );
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers );
/**
* Make Tomahawk assert the assertion is true, probably not to be used by resolvers directly
@@ -89,20 +89,9 @@ public:
int sizehint,
const QVariantMap& options );
/**
* Native handler for asynchronous HTTP requests.
*
* This handler shall only be used if we cannot achieve the request with
* XMLHttpRequest as that would be more efficient.
* Use cases are:
* * Referer header: Stripped on MacOS and the specification says it
* should be stripped
*
* INTERNAL USE ONLY!
*/
Q_INVOKABLE void nativeAsyncRequest( int requestId, const QString& url,
const QVariantMap& headers,
const QVariantMap& options );
Q_INVOKABLE void invokeNativeScriptJob( int requestId,
const QString& methodName,
const QVariantMap& params );
/**
* Lucene++ indices for JS resolvers
@@ -122,18 +111,10 @@ public:
Q_INVOKABLE void readdResolver();
/**
* INTERNAL USE ONLY!
*/
void customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url,
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ); // async
public slots:
QByteArray readRaw( const QString& fileName );
QString readBase64( const QString& fileName );
QString readCompressed( const QString& fileName );
QString instanceUUID();
QString uuid() const;
int currentCountry() const;
QString compress( const QString& data );
@@ -142,10 +123,6 @@ public slots:
void log( const QString& message );
bool fakeEnv() { return false; }
void addTrackResults( const QVariantMap& results );
void addUrlResult( const QString& url, const QVariantMap& result );
void nativeReportCapabilities( const QVariant& capabilities );
void reportScriptJobResults( const QVariantMap& result );
@@ -154,27 +131,20 @@ public slots:
void unregisterScriptPlugin( const QString& type, const QString& objectId );
private slots:
void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply );
void tracksAdded( const QList<Tomahawk::query_ptr>& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection );
void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate );
void nativeAsyncRequestDone( int requestId, NetworkReply* reply );
private:
Tomahawk::query_ptr parseTrack( const QVariantMap& track );
void returnStreamUrl( const QString& streamUrl, const QMap<QString, QString>& headers,
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback );
bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData );
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
// native script jobs
void nativeAsyncRequest( int requestId, const QVariantMap& options );
QVariantMap m_resolverConfig;
JSResolver* m_resolver;
QString m_scriptPath, m_urlCallback, m_urlTranslator;
QHash< QString, std::function< void( const QString&, QSharedPointer< QIODevice >& ) > > m_streamCallbacks;
QHash< QString, std::function< void( const QString& ) > > m_translatorCallbacks;
bool m_urlCallbackIsAsync;
QString m_pendingUrl;
Tomahawk::album_ptr m_pendingAlbum;
QString m_scriptPath;
bool m_stopped;
};
} // ns: Tomahawk

View File

@@ -17,10 +17,31 @@
*/
#include "Resolver.h"
#include "../resolvers/SyncScriptJob.h"
#include "../Result.h"
#include <QPixmap>
Tomahawk::Resolver::Resolver( QObject* parent )
: QObject( parent )
{
}
QPixmap
Tomahawk::Resolver::icon( const QSize& ) const
{
Q_ASSERT(false);
return QPixmap();
}
Tomahawk::ScriptJob*
Tomahawk::Resolver::getStreamUrl( const result_ptr& result )
{
QVariantMap data;
data[ "url" ] = result->url();
return new SyncScriptJob( data );
}

View File

@@ -19,7 +19,6 @@
#ifndef RESOLVER_H
#define RESOLVER_H
#include "../ResultProvider.h"
#include "Typedefs.h"
#include "DllMacro.h"
@@ -36,21 +35,23 @@
namespace Tomahawk
{
class ScriptJob;
class DLLEXPORT Resolver : public QObject, public ResultProvider
class DLLEXPORT Resolver : public QObject
{
Q_OBJECT
public:
Resolver() {}
Resolver( QObject* parent = nullptr );
virtual QString name() const = 0;
virtual QPixmap icon( const QSize& size ) const;
virtual unsigned int weight() const = 0;
virtual unsigned int timeout() const = 0;
virtual QPixmap icon( const QSize& size ) const override;
public slots:
virtual void resolve( const Tomahawk::query_ptr& query ) = 0;
virtual ScriptJob* getStreamUrl( const result_ptr& result );
};
} //ns

View File

@@ -1,7 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
* Copyright (C) 2014 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2014-2016, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "../utils/Logger.h"
#include "../Typedefs.h"
#include "plugins/ScriptLinkParserPluginFactory.h"
#include "plugins/ScriptCollectionFactory.h"
#include "plugins/ScriptInfoPluginFactory.h"
@@ -44,6 +45,7 @@ ScriptAccount::ScriptAccount( const QString& name )
: QObject()
, m_name( name )
, m_stopped( true )
, m_linkParserPluginFactory( new ScriptLinkParserPluginFactory() )
, m_collectionFactory( new ScriptCollectionFactory() )
, m_infoPluginFactory( new ScriptInfoPluginFactory() )
{
@@ -52,6 +54,7 @@ ScriptAccount::ScriptAccount( const QString& name )
ScriptAccount::~ScriptAccount()
{
delete m_linkParserPluginFactory;
delete m_collectionFactory;
delete m_infoPluginFactory;
}
@@ -62,6 +65,7 @@ ScriptAccount::start()
{
m_stopped = false;
m_linkParserPluginFactory->addAllPlugins();
m_collectionFactory->addAllPlugins();
m_infoPluginFactory->addAllPlugins();
}
@@ -72,6 +76,7 @@ ScriptAccount::stop()
{
m_stopped = true;
m_linkParserPluginFactory->removeAllPlugins();
m_collectionFactory->removeAllPlugins();
m_infoPluginFactory->removeAllPlugins();
}
@@ -193,7 +198,11 @@ ScriptAccount::unregisterScriptPlugin( const QString& type, const QString& objec
return;
}
if ( type == "collection" )
if( type == "linkParser" )
{
m_linkParserPluginFactory->unregisterPlugin( object );
}
else if ( type == "collection" )
{
m_collectionFactory->unregisterPlugin( object );
}
@@ -231,6 +240,10 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
ScriptLinkGeneratorPlugin* lgp = new ScriptLinkGeneratorPlugin( object );
Utils::LinkGenerator::instance()->addPlugin( lgp );
}
else if( type == "linkParser" )
{
m_linkParserPluginFactory->registerPlugin( object, this );
}
else if ( type == "infoPlugin" )
{
m_infoPluginFactory->registerPlugin( object, this );
@@ -326,21 +339,6 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
}
rp->setDownloadFormats( fl );
// find collection
const QString collectionId = m.value( "collectionId" ).toString();
if ( !collectionId.isEmpty() )
{
if ( scriptCollection( collectionId ).isNull() )
{
tLog() << "Resolver returned invalid collection id";
Q_ASSERT( false );
}
else
{
rp->setResolvedByCollection( scriptCollection( collectionId ) );
}
}
results << rp;
}
@@ -348,8 +346,29 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
}
QSharedPointer< ScriptCollection >
ScriptAccount::scriptCollection( const QString& id ) const
ScriptJob*
ScriptAccount::resolve( const scriptobject_ptr& scriptObject, const query_ptr& query, const QString& resolveType )
{
return m_collectionFactory->scriptPlugins().value( id );
ScriptJob* job = nullptr;
if ( !query->isFullTextQuery() )
{
QVariantMap arguments;
arguments["artist"] = query->queryTrack()->artist();
arguments["album"] = query->queryTrack()->album();
arguments["track"] = query->queryTrack()->track();
arguments["type"] = resolveType;
job = scriptObject->invoke( "resolve", arguments );
}
else
{
QVariantMap arguments;
arguments["query"] = query->fullTextQuery();
arguments["type"] = resolveType;
job = scriptObject->invoke( "search", arguments );
}
job->setProperty( "qid", query->id() );
return job;
}

View File

@@ -1,7 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
* Copyright (C) 2014 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2014-2016, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -39,6 +39,7 @@ namespace Tomahawk {
class ScriptObject;
class ScriptJob;
class ScriptLinkParserPluginFactory;
class ScriptCollectionFactory;
class ScriptInfoPluginFactory;
@@ -65,18 +66,20 @@ public:
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
virtual void startJob( ScriptJob* scriptJob ) = 0;
void reportScriptJobResult( const QVariantMap& result );
void registerScriptPlugin( const QString& type, const QString& objectId );
void unregisterScriptPlugin( const QString& type, const QString& objectId );
virtual void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) = 0;
virtual void reportNativeScriptJobError( int resultId, const QVariantMap& error ) = 0;
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
// helpers
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const;
ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query, const QString& resolveType );
private slots:
void onJobDeleted( const QString& jobId );
@@ -92,6 +95,7 @@ private: // TODO: pimple, might be renamed before tho
QHash< QString, scriptobject_ptr > m_objects;
// port to QScopedPointer when pimple'd
ScriptLinkParserPluginFactory* m_linkParserPluginFactory;
ScriptCollectionFactory* m_collectionFactory;
ScriptInfoPluginFactory* m_infoPluginFactory;
};

View File

@@ -26,7 +26,10 @@
#include "resolvers/ScriptCommand_AllArtists.h"
#include "resolvers/ScriptCommand_AllAlbums.h"
#include "resolvers/ScriptCommand_AllTracks.h"
#include "resolvers/ScriptJob.h"
#include "ScriptAccount.h"
#include "Result.h"
#include "Pipeline.h"
#include <QImageReader>
#include <QPainter>
@@ -223,6 +226,15 @@ void ScriptCollection::parseMetaData()
return parseMetaData( readMetaData() );
}
ScriptJob*
ScriptCollection::getStreamUrl( const result_ptr& result )
{
QVariantMap arguments;
arguments["url"] = result->url();
return scriptObject()->invoke( "getStreamUrl", arguments );
}
void
ScriptCollection::parseMetaData( const QVariantMap& metadata )
@@ -326,3 +338,58 @@ ScriptCollection::onIconFetched()
reply->deleteLater();
}
}
unsigned int
ScriptCollection::timeout() const
{
return 0;
}
unsigned int
ScriptCollection::weight() const
{
return 0;
}
void
ScriptCollection::resolve( const Tomahawk::query_ptr& query )
{
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query, "collection" );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
job->start();
}
void
ScriptCollection::onResolveRequestDone( const QVariantMap& data )
{
Q_ASSERT( QThread::currentThread() == thread() );
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
QID qid = job->property( "qid" ).toString();
if ( job->error() )
{
Tomahawk::Pipeline::instance()->reportError( qid, this );
}
else
{
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
foreach( const result_ptr& result, results )
{
result->setResolvedByCollection( weakRef().toStrongRef() );
result->setFriendlySource( prettyName() );
}
Tomahawk::Pipeline::instance()->reportResults( qid, this, results );
}
sender()->deleteLater();
}

View File

@@ -91,8 +91,15 @@ public:
void parseMetaData();
void parseMetaData( const QVariantMap& metadata );
// Resolver interface
unsigned int weight() const override;
unsigned int timeout() const override;
void resolve( const Tomahawk::query_ptr& query ) override;
ScriptJob* getStreamUrl( const result_ptr& result ) override;
private slots:
void onIconFetched();
void onResolveRequestDone( const QVariantMap& data );
private:
ScriptAccount* m_scriptAccount;

View File

@@ -34,7 +34,6 @@ signals:
virtual void done() = 0;
protected:
friend class ScriptCommandQueue;
virtual void exec() = 0;
virtual void reportFailure() = 0;
};

View File

@@ -1,107 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptCommandQueue.h"
#include <QMetaType>
#include <QMutex>
using namespace Tomahawk;
ScriptCommandQueue::ScriptCommandQueue( QObject* parent )
: QObject( parent )
, m_timer( new QTimer( this ) )
{
m_timer->setSingleShot( true );
}
void
ScriptCommandQueue::enqueue( const QSharedPointer< ScriptCommand >& req )
{
QMutexLocker locker( &m_mutex );
m_queue.append( req );
locker.unlock();
if ( m_queue.count() == 1 )
nextCommand();
}
void
ScriptCommandQueue::nextCommand()
{
if ( m_queue.isEmpty() )
return;
QSharedPointer< ScriptCommand > req = m_queue.first();
connect( req.data(), SIGNAL( done() ),
this, SLOT( onCommandDone() ) );
connect( m_timer, SIGNAL( timeout() ),
this, SLOT( onTimeout() ) );
m_timer->start( 20000 );
req->exec();
}
void
ScriptCommandQueue::onCommandDone()
{
if ( m_queue.isEmpty() || !m_timer->isActive() ) //the timeout already happened or some other weird thing
return; //nothing to do here
m_timer->stop();
QMutexLocker locker( &m_mutex );
const QSharedPointer< ScriptCommand > req = m_queue.first();
m_queue.removeAll( req );
locker.unlock();
disconnect( req.data(), SIGNAL( done() ),
this, SLOT( onCommandDone() ) );
disconnect( m_timer, SIGNAL( timeout() ),
this, SLOT( onTimeout() ) );
if ( !m_queue.isEmpty() )
nextCommand();
}
void
ScriptCommandQueue::onTimeout()
{
m_timer->stop();
QMutexLocker locker( &m_mutex );
const QSharedPointer< ScriptCommand > req = m_queue.first();
m_queue.removeAll( req );
locker.unlock();
req->reportFailure();
disconnect( req.data(), SIGNAL( done() ),
this, SLOT( onCommandDone() ) );
disconnect( m_timer, SIGNAL( timeout() ),
this, SLOT( onTimeout() ) );
if ( !m_queue.isEmpty() )
nextCommand();
}

View File

@@ -1,57 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTCOMMANDQUEUE_H
#define SCRIPTCOMMANDQUEUE_H
#include "ScriptCommand.h"
#include <QQueue>
#include <QSharedPointer>
#include <QTimer>
#include <QMetaType>
#include <QMutex>
namespace Tomahawk
{
class ScriptCommandQueue : public QObject
{
Q_OBJECT
public:
explicit ScriptCommandQueue( QObject* parent = 0 );
virtual ~ScriptCommandQueue() {}
void enqueue( const QSharedPointer< ScriptCommand >& req );
private slots:
void nextCommand();
void onCommandDone();
void onTimeout();
private:
QQueue< QSharedPointer< ScriptCommand > > m_queue;
QTimer* m_timer;
QMutex m_mutex;
};
} // ns: Tomahawk
Q_DECLARE_METATYPE( QSharedPointer< Tomahawk::ScriptCommand > )
#endif // SCRIPTCOMMANDQUEUE_H

View File

@@ -117,7 +117,7 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
Q_ASSERT( !collection.isNull() );
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "results"].toList() );
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() );
QList< Tomahawk::query_ptr > queries;

View File

@@ -1,78 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptCommand_LookupUrl_p.h"
#include "PlaylistEntry.h"
using namespace Tomahawk;
ScriptCommand_LookupUrl::ScriptCommand_LookupUrl( Tomahawk::ExternalResolver* resolver, const QString& url, QObject* parent )
: ScriptCommand( parent )
, d_ptr( new ScriptCommand_LookupUrlPrivate( this, resolver, url ) )
{
}
ScriptCommand_LookupUrl::~ScriptCommand_LookupUrl()
{
delete d_ptr;
}
void
ScriptCommand_LookupUrl::enqueue()
{
Q_D( ScriptCommand_LookupUrl );
d->resolver->enqueue( QSharedPointer< ScriptCommand >( this ) );
}
void
ScriptCommand_LookupUrl::exec()
{
Q_D( ScriptCommand_LookupUrl );
connect( d->resolver, SIGNAL( informationFound( QString , QSharedPointer<QObject> ) ),
this, SLOT( onResolverDone( QString, QSharedPointer<QObject> ) ) );
d->resolver->lookupUrl( d->url );
}
void
ScriptCommand_LookupUrl::reportFailure()
{
Q_D( ScriptCommand_LookupUrl );
emit information( d->url, QSharedPointer<QObject>() );
emit done();
}
void
ScriptCommand_LookupUrl::onResolverDone( const QString& url, const QSharedPointer<QObject>& _information )
{
Q_D( ScriptCommand_LookupUrl );
qDebug() << Q_FUNC_INFO << url << _information.isNull();
if ( url != d->url )
{
// This data is not for us, skip.
return;
}
emit information( d->url, _information );
emit done();
}

View File

@@ -1,64 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTCOMMAND_LOOKUPURL_H
#define SCRIPTCOMMAND_LOOKUPURL_H
#include "ScriptCommand.h"
#include "DllMacro.h"
#include "Typedefs.h"
#include <QVariant>
namespace Tomahawk
{
class ScriptCommand_LookupUrlPrivate;
class ExternalResolver;
class DLLEXPORT ScriptCommand_LookupUrl : public ScriptCommand
{
Q_OBJECT
public:
explicit ScriptCommand_LookupUrl( Tomahawk::ExternalResolver* resolver,
const QString& url,
QObject* parent = nullptr );
virtual ~ScriptCommand_LookupUrl();
void enqueue();
signals:
void information( const QString& url, const QSharedPointer<QObject>& variant );
void done();
protected:
void exec() override;
void reportFailure() override;
private slots:
void onResolverDone( const QString& url, const QSharedPointer<QObject>& information );
private:
Q_DECLARE_PRIVATE( ScriptCommand_LookupUrl )
ScriptCommand_LookupUrlPrivate* d_ptr;
};
} // ns: Tomahawk
#endif // SCRIPTCOMMAND_LOOKUPURL_H

View File

@@ -3,6 +3,7 @@
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,6 +40,7 @@
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkProxy>
#include <QTimer>
#ifdef Q_OS_WIN
#include <shlwapi.h>

View File

@@ -3,6 +3,7 @@
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -63,8 +64,6 @@ public:
void sendMessage( const QVariantMap& map );
bool canParseUrl( const QString&, UrlType ) Q_DECL_OVERRIDE { return false; }
signals:
void terminated();
void customMessage( const QString& msgType, const QVariantMap& msg );
@@ -74,9 +73,6 @@ public slots:
void resolve( const Tomahawk::query_ptr& query ) Q_DECL_OVERRIDE;
void start() Q_DECL_OVERRIDE;
void lookupUrl( const QString& ) Q_DECL_OVERRIDE {}
private slots:
void readStderr();
void readStdout();

View File

@@ -19,6 +19,7 @@
#include "SourceList.h"
#include "../ScriptAccount.h"
#include "../../Pipeline.h"
using namespace Tomahawk;
@@ -29,12 +30,14 @@ void ScriptCollectionFactory::addPlugin( const QSharedPointer<ScriptCollection>&
collection->setOnline( true );
SourceList::instance()->addScriptCollection( collection );
Pipeline::instance()->addResolver( collection.data() );
}
void ScriptCollectionFactory::removePlugin( const QSharedPointer<ScriptCollection>& collection ) const
{
collection->setOnline( false );
SourceList::instance()->removeScriptCollection( collection );
Pipeline::instance()->removeResolver( collection.data() );
}
QSharedPointer< ScriptCollection > ScriptCollectionFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount )

View File

@@ -0,0 +1,228 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptLinkParserPlugin_p.h"
#include "../ScriptJob.h"
#include "../ScriptObject.h"
#include "../../utils/Logger.h"
#include "../ScriptAccount.h"
#include "../../database/Database.h"
#include "../../database/DatabaseImpl.h"
#include "../../SourceList.h"
#include "../../Artist.h"
#include "../../Album.h"
#include "../../playlist/PlaylistTemplate.h"
#include "../../playlist/XspfPlaylistTemplate.h"
using namespace Tomahawk;
ScriptLinkParserPlugin::ScriptLinkParserPlugin( const scriptobject_ptr& scriptObject, ScriptAccount* account )
: Utils::LinkParserPlugin()
, ScriptPlugin( scriptObject )
, d_ptr( new ScriptLinkParserPluginPrivate( this, scriptObject, account ) )
{
}
ScriptLinkParserPlugin::~ScriptLinkParserPlugin()
{
}
bool
ScriptLinkParserPlugin::canParseUrl( const QString& url, Tomahawk::Utils::UrlType type ) const
{
QVariantMap arguments;
arguments["url"] = url;
arguments["type"] = (int) type;
return scriptObject()->syncInvoke( "canParseUrl", arguments ).toBool();
}
void
ScriptLinkParserPlugin::lookupUrl( const QString& url ) const
{
QVariantMap arguments;
arguments["url"] = url;
Tomahawk::ScriptJob* job = scriptObject()->invoke( "lookupUrl", arguments );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onLookupUrlRequestDone( QVariantMap ) ) );
job->setProperty( "url", url );
job->start();
}
void
ScriptLinkParserPlugin::onLookupUrlRequestDone( const QVariantMap& result )
{
Q_D( ScriptLinkParserPlugin );
sender()->deleteLater();
QString url = sender()->property( "url" ).toString();
tLog() << "ON LOOKUP URL REQUEST DONE" << url << result;
// It may seem a bit weird, but currently no slot should do anything
// more as we starting on a new URL and not task are waiting for it yet.
d->pendingUrl = QString();
d->pendingAlbum = album_ptr();
Utils::UrlType type = (Utils::UrlType) result.value( "type" ).toInt();
if ( type == Utils::UrlTypeArtist )
{
QString name = result.value( "name" ).toString();
Q_ASSERT( !name.isEmpty() );
emit informationFound( url, Artist::get( name, true ).objectCast<QObject>() );
}
else if ( type == Utils::UrlTypeAlbum )
{
QString name = result.value( "name" ).toString();
QString artist = result.value( "artist" ).toString();
album_ptr album = Album::get( Artist::get( artist, true ), name );
d->pendingUrl = url;
d->pendingAlbum = album;
connect( album.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ),
SLOT( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) );
if ( !album->tracks().isEmpty() )
{
emit informationFound( url, album.objectCast<QObject>() );
}
}
else if ( type == Utils::UrlTypeTrack )
{
Tomahawk::query_ptr query = parseTrack( result );
if ( query.isNull() )
{
// A valid track result shoud have non-empty title and artist.
tLog() << Q_FUNC_INFO << d->scriptAccount->name() << "Got empty track information for " << url;
emit informationFound( url, QSharedPointer<QObject>() );
}
else
{
emit informationFound( url, query.objectCast<QObject>() );
}
}
else if ( type == Utils::UrlTypePlaylist )
{
QString guid = result.value( "guid" ).toString();
Q_ASSERT( !guid.isEmpty() );
// Append nodeid to guid to make it globally unique.
guid += instanceUUID();
// Do we already have this playlist loaded?
{
playlist_ptr playlist = Playlist::get( guid );
if ( !playlist.isNull() )
{
emit informationFound( url, playlist.objectCast<QObject>() );
return;
}
}
// Get all information to build a new playlist but do not build it until we know,
// if it is really handled as a playlist and not as a set of tracks.
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
const QString title = result.value( "title" ).toString();
const QString info = result.value( "info" ).toString();
const QString creator = result.value( "creator" ).toString();
QList<query_ptr> queries;
foreach( QVariant track, result.value( "tracks" ).toList() )
{
query_ptr query = parseTrack( track.toMap() );
if ( !query.isNull() )
{
queries << query;
}
}
tLog( LOGVERBOSE ) << Q_FUNC_INFO << d->scriptAccount->name() << "Got playlist for " << url;
playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) );
emit informationFound( url, pltemplate.objectCast<QObject>() );
}
else if ( type == Utils::UrlTypeXspf )
{
QString xspfUrl = result.value( "url" ).toString();
Q_ASSERT( !xspfUrl.isEmpty() );
QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() );
// Do we already have this playlist loaded?
{
playlist_ptr playlist = Playlist::get( guid );
if ( !playlist.isNull() )
{
emit informationFound( url, playlist.objectCast<QObject>() );
return;
}
}
// Get all information to build a new playlist but do not build it until we know,
// if it is really handled as a playlist and not as a set of tracks.
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
QSharedPointer<XspfPlaylistTemplate> pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) );
NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ),
this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ),
url, pltemplate.objectCast<Tomahawk::PlaylistTemplate>() );
tLog( LOGVERBOSE ) << Q_FUNC_INFO << d->scriptAccount->name() << "Got playlist for " << url;
pltemplate->load();
}
else
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << d->scriptAccount->name() << "No usable information found for " << url;
emit informationFound( url, QSharedPointer<QObject>() );
}
}
void
ScriptLinkParserPlugin::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate )
{
tLog() << Q_FUNC_INFO;
emit informationFound( url, pltemplate.objectCast<QObject>() );
}
QString
ScriptLinkParserPlugin::instanceUUID()
{
return Tomahawk::Database::instance()->impl()->dbid();
}
Tomahawk::query_ptr
ScriptLinkParserPlugin::parseTrack( const QVariantMap& track )
{
QString title = track.value( "track" ).toString();
QString artist = track.value( "artist" ).toString();
QString album = track.value( "album" ).toString();
if ( title.isEmpty() || artist.isEmpty() )
{
return query_ptr();
}
Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album );
QString resultHint = track.value( "hint" ).toString();
if ( !resultHint.isEmpty() )
{
query->setResultHint( resultHint );
query->setSaveHTTPResultHint( true );
}
return query;
}

View File

@@ -0,0 +1,61 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOMAHAWK_SCRIPTLINKPARSERPLUGIN_H
#define TOMAHAWK_SCRIPTLINKPARSERPLUGIN_H
#include "../../resolvers/ScriptPlugin.h"
#include "../../utils/LinkParserPlugin.h"
#include <QObject>
#include "DllMacro.h"
namespace Tomahawk
{
class ScriptObject;
class ScriptLinkParserPluginPrivate;
class DLLEXPORT ScriptLinkParserPlugin : public Utils::LinkParserPlugin, public ScriptPlugin
{
Q_OBJECT
public:
ScriptLinkParserPlugin( const scriptobject_ptr& scriptObject, ScriptAccount* account );
virtual ~ScriptLinkParserPlugin();
bool canParseUrl( const QString& url, Tomahawk::Utils::UrlType type ) const override;
void lookupUrl( const QString& url ) const override;
private slots:
void onLookupUrlRequestDone( const QVariantMap& result );
void pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate );
private:
QString instanceUUID();
static Tomahawk::query_ptr parseTrack( const QVariantMap& track );
private:
Q_DECLARE_PRIVATE( ScriptLinkParserPlugin )
QScopedPointer<ScriptLinkParserPluginPrivate> d_ptr;
};
}; // ns: Tomahawk
#endif // TOMAHAWK_SCRIPTLINKPARSERPLUGIN_H

View File

@@ -0,0 +1,39 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptLinkParserPluginFactory.h"
#include "../ScriptAccount.h"
#include "../../utils/LinkParser.h"
#include "../../utils/LinkParserPlugin.h"
using namespace Tomahawk;
void ScriptLinkParserPluginFactory::addPlugin( const QSharedPointer <ScriptLinkParserPlugin >& plugin ) const
{
Tomahawk::Utils::LinkParser::instance()->addPlugin( plugin );
}
void ScriptLinkParserPluginFactory::removePlugin( const QSharedPointer< ScriptLinkParserPlugin >& plugin ) const
{
Tomahawk::Utils::LinkParser::instance()->removePlugin( plugin );
}
QSharedPointer< ScriptLinkParserPlugin > ScriptLinkParserPluginFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount )
{
return QSharedPointer< ScriptLinkParserPlugin >( new ScriptLinkParserPlugin( object, scriptAccount ) );
}

View File

@@ -0,0 +1,41 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TOMAHAWK_SCRIPTLINKPARSERPLUGINFACTORY_H
#define TOMAHAWK_SCRIPTLINKPARSERPLUGINFACTORY_H
#include "Typedefs.h"
#include "ScriptLinkParserPlugin.h"
#include "../ScriptPluginFactory.h"
namespace Tomahawk
{
class ScriptAccount;
class DLLEXPORT ScriptLinkParserPluginFactory : public ScriptPluginFactory< ScriptLinkParserPlugin >
{
QSharedPointer< ScriptLinkParserPlugin > createPlugin( const scriptobject_ptr&, ScriptAccount* ) override;
void addPlugin( const QSharedPointer< ScriptLinkParserPlugin >& scriptPlugin ) const override;
void removePlugin( const QSharedPointer< ScriptLinkParserPlugin >& scriptPlugin ) const override;
};
} // ns: Tomahawk
#endif // TOMAHAWK_SCRIPTLINKPARSERPLUGINFACTORY_H

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,33 +17,33 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTCOMMAND_LOOKUPURL_P_H
#define SCRIPTCOMMAND_LOOKUPURL_P_H
#ifndef TOMAHAWK_SCRIPTLINKGENERATORPLUGIN_P_H
#define TOMAHAWK_SCRIPTLINKGENERATORPLUGIN_P_H
#include "ScriptCommand_LookupUrl.h"
#include "ScriptLinkParserPlugin.h"
#include "ExternalResolver.h"
namespace Tomahawk
namespace Tomahawk
{
class ScriptCommand_LookupUrlPrivate
class ScriptLinkParserPluginPrivate
{
public:
ScriptCommand_LookupUrlPrivate( ScriptCommand_LookupUrl* q, Tomahawk::ExternalResolver* _resolver, const QString& _url )
ScriptLinkParserPluginPrivate( ScriptLinkParserPlugin* q, const scriptobject_ptr& scriptObject, ScriptAccount* scriptAccount )
: q_ptr ( q )
, url( _url )
, resolver( _resolver )
, scriptObject( scriptObject )
, scriptAccount( scriptAccount )
{
}
ScriptCommand_LookupUrl* q_ptr;
Q_DECLARE_PUBLIC ( ScriptCommand_LookupUrl )
ScriptLinkParserPlugin* q_ptr;
Q_DECLARE_PUBLIC ( ScriptLinkParserPlugin )
private:
QString url;
Tomahawk::ExternalResolver* resolver;
scriptobject_ptr scriptObject;
ScriptAccount* scriptAccount;
QString pendingUrl;
album_ptr pendingAlbum;
};
} // ns: Tomahawk
#endif // SCRIPTCOMMAND_LOOKUPURL_P_H
#endif // TOMAHAWK_SCRIPTLINKGENERATORPLUGIN_P_H

View File

@@ -0,0 +1,122 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2016, Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LinkParser.h"
#include "TomahawkUtils.h"
#include "Logger.h"
#include "../resolvers/SyncScriptJob.h"
using namespace Tomahawk;
using namespace Tomahawk::Utils;
LinkParser* LinkParser::s_instance = 0;
LinkParser*
LinkParser::instance()
{
if ( !s_instance )
s_instance = new LinkParser;
return s_instance;
}
LinkParser::LinkParser( QObject* parent )
: QObject( parent )
{
}
LinkParser::~LinkParser()
{
}
void LinkParser::addPlugin( const QSharedPointer< LinkParserPlugin >& plugin )
{
m_plugins.append( plugin );
connect( plugin.data(), SIGNAL( informationFound( QString, QSharedPointer<QObject> ) ), SLOT( onInformationFound( QString, QSharedPointer<QObject> ) ) );
}
void
LinkParser::removePlugin( const QSharedPointer< LinkParserPlugin >& plugin )
{
if ( !plugin.isNull() )
{
disconnect( plugin.data(), 0, this, 0);
}
QMutableListIterator< QSharedPointer< LinkParserPlugin > > iter( m_plugins );
while ( iter.hasNext() )
{
QSharedPointer< LinkParserPlugin > ptr = iter.next();
if ( ptr.data() == plugin.data() || ptr.isNull() )
{
iter.remove();
}
}
}
bool
LinkParser::canParseUrl( const QString& url, UrlType type ) const
{
return !parserPluginsForUrl( url, type).isEmpty();
}
QList< QSharedPointer< LinkParserPlugin > >
LinkParser::parserPluginsForUrl( const QString& url, Tomahawk::Utils::UrlType type ) const
{
QList< QSharedPointer< LinkParserPlugin > > plugins;
foreach ( const QSharedPointer< LinkParserPlugin >& plugin, m_plugins )
{
if ( plugin->canParseUrl( url, type ) )
{
plugins.append( plugin );
}
}
return plugins;
}
void
LinkParser::lookupUrl( const QString& url, const QList< QSharedPointer < LinkParserPlugin > >& parserPlugins ) const
{
foreach ( const QSharedPointer< LinkParserPlugin >& plugin, parserPlugins )
{
if ( !plugin.isNull() )
{
plugin->lookupUrl( url );
}
}
}
void
LinkParser::onInformationFound( const QString& url, const QSharedPointer<QObject>& information )
{
tLog() << Q_FUNC_INFO << url;
emit informationFound( url, information );
}

View File

@@ -0,0 +1,69 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2014 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TOMAHAWK_UTILS_LINKPARSER_H
#define TOMAHAWK_UTILS_LINKPARSER_H
#include "../resolvers/ScriptJob.h"
#include "LinkParserPlugin.h"
#include "UrlType.h"
#include "../DllMacro.h"
#include "../Typedefs.h"
#include <memory>
namespace Tomahawk {
namespace Utils {
class LinkParserPlugin;
class DLLEXPORT LinkParser : public QObject
{
Q_OBJECT
public:
static LinkParser* instance();
virtual ~LinkParser();
void addPlugin( const QSharedPointer< LinkParserPlugin >& plugin );
void removePlugin( const QSharedPointer< LinkParserPlugin >& plugin );
bool canParseUrl( const QString& url, UrlType type ) const;
QList< QSharedPointer < LinkParserPlugin > > parserPluginsForUrl( const QString& url, UrlType type ) const;
void lookupUrl( const QString& url, const QList< QSharedPointer < LinkParserPlugin > >& parserPlugins = QList< QSharedPointer < LinkParserPlugin > >() ) const;
signals:
void informationFound( const QString& url, const QSharedPointer<QObject>& information );
private slots:
void onInformationFound( const QString& url, const QSharedPointer<QObject>& information );
private:
explicit LinkParser( QObject* parent = 0 );
QList< QSharedPointer < LinkParserPlugin > > m_plugins;
static LinkParser* s_instance;
};
} // namespace Utils
} // namespace Tomahawk
#endif // TOMAHAWK_UTILS_LINKPARSER_H

View File

@@ -0,0 +1,24 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LinkParserPlugin.h"
#include "LinkParser.h"
Tomahawk::Utils::LinkParserPlugin::~LinkParserPlugin()
{
}

View File

@@ -0,0 +1,50 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk 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 2 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TOMAHAWK_UTILS_LINKPARSERPLUGIN_H
#define TOMAHAWK_UTILS_LINKPARSERPLUGIN_H
#include "../DllMacro.h"
#include "../Typedefs.h"
#include "UrlType.h"
namespace Tomahawk {
class ScriptJob;
namespace Utils {
class DLLEXPORT LinkParserPlugin : public QObject
{
Q_OBJECT
public:
virtual ~LinkParserPlugin();
virtual bool canParseUrl( const QString& url, Tomahawk::Utils::UrlType type ) const = 0;
virtual void lookupUrl( const QString& url ) const = 0;
signals:
void informationFound( const QString&, const QSharedPointer<QObject>& );
};
} // namespace Utils
} // namespace Tomahawk
#endif // TOMAHAWK_UTILS_LINKPARSERPLUGIN_H

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2015 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,10 +15,5 @@
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ResultProvider.h"
using namespace Tomahawk;
ResultProvider::~ResultProvider()
{
}
#include "UrlType.h"

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2015 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2016 Dominik Schmidt <domme@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,28 +15,26 @@
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TOMAHAWK_RESULTPROVIDER_H
#define TOMAHAWK_RESULTPROVIDER_H
#ifndef TOMAHAWK_UTILS_URLTYPE_H
#define TOMAHAWK_UTILS_URLTYPE_H
#include "DllMacro.h"
namespace Tomahawk {
class QPixmap;
class QString;
class QSize;
namespace Utils {
namespace Tomahawk
enum UrlType
{
class DLLEXPORT ResultProvider
{
public:
virtual ~ResultProvider();
virtual QString name() const = 0;
virtual QPixmap icon( const QSize& size ) const = 0;
UrlTypeAny = 0x00,
UrlTypePlaylist = 0x01,
UrlTypeTrack = 0x02,
UrlTypeAlbum = 0x04,
UrlTypeArtist = 0x08,
UrlTypeXspf = 0x10
};
}
} // namespace Utils
} // namespace Tomahawk
#endif // TOMAHAWK_RESULTPROVIDER_H
#endif // TOMAHAWK_UTILS_URLTYPE_H

View File

@@ -310,8 +310,9 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr result )
connect( m_currentTrack->track().data(), SIGNAL( coverChanged() ), SLOT( onCoverUpdated() ) );
connect( m_currentTrack->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
ui->artistLabel->setResult( result );
setUpdatesEnabled( false );
ui->trackLabel->setResult( result );
ui->artistLabel->setResult( result );
const QString duration = TomahawkUtils::timeToString( result.data()->track()->duration() );
ui->timeLabel->setFixedWidth( ui->timeLabel->fontMetrics().width( QString( duration.length(), QChar( '0' ) ) ) );
@@ -370,6 +371,7 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr result )
setCover();
setSocialActions();
setUpdatesEnabled( true );
}

View File

@@ -1,4 +1,4 @@
set(TOMAHAWK_APPLICATION_TARGET "tomahahawk-bin")
set(TOMAHAWK_APPLICATION_TARGET "tomahawk-bin")
include( ECMAddAppIcon )
@@ -146,8 +146,14 @@ IF( BUILD_GUI )
ENDIF()
file( GLOB_RECURSE TOMAHAWK_ICONS "${CMAKE_SOURCE_DIR}/data/icons/*-tomahawk-icon.png" )
if(APPLE)
# OUTFILE is currently only supported in my branch, see https://git.reviewboard.kde.org/r/126303/
# once merged (and released), we can remove this conditional usage
ecm_add_app_icon(tomahawkSources ICONS ${TOMAHAWK_ICONS} OUTFILE "${TOMAHAWK_APPLICATION_NAME}")
else()
ecm_add_app_icon(tomahawkSources ICONS ${TOMAHAWK_ICONS})
endif()
ecm_add_app_icon( tomahawkSources ICONS ${TOMAHAWK_ICONS} )
qt_add_resources( RC_SRCS "../../resources.qrc" )

View File

@@ -96,12 +96,6 @@
#endif
#endif
#include <shellapi.h>
#if QT_VERSION < QT_VERSION_CHECK(5,2,0)
#include <windows.h>
#ifndef THBN_CLICKED
#define THBN_CLICKED 0x1800
#endif
#endif
#endif
using namespace Tomahawk;
@@ -111,10 +105,6 @@ using namespace Accounts;
TomahawkWindow::TomahawkWindow( QWidget* parent )
: QMainWindow( parent )
, TomahawkUtils::DpiScaler( this )
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
, m_buttonCreatedID( RegisterWindowMessage( L"TaskbarButtonCreated" ) )
, m_taskbarList( 0 )
#endif
, ui( new Ui::TomahawkWindow )
, m_searchWidget( 0 )
, m_trayIcon( 0 )
@@ -154,9 +144,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
#ifdef Q_OS_WIN
connect( AudioEngine::instance(), SIGNAL( stateChanged( AudioState, AudioState ) ), SLOT( audioStateChanged( AudioState, AudioState ) ) );
#if QT_VERSION >= QT_VERSION_CHECK( 5, 2, 0 )
setupWindowsButtons();
#endif
#endif
if ( qApp->arguments().contains( "--debug" ) )
@@ -537,61 +525,12 @@ TomahawkWindow::setupUpdateCheck()
#ifdef Q_OS_WIN
bool
void
TomahawkWindow::setupWindowsButtons()
{
#if QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
const GUID IID_ITaskbarList3 = { 0xea1afb91,0x9e28,0x4b86, { 0x90,0xe9,0x9e,0x9f,0x8a,0x5e,0xef,0xaf } };
HRESULT hr = S_OK;
THUMBBUTTONMASK dwMask = THUMBBUTTONMASK( THB_ICON | THB_TOOLTIP | THB_FLAGS );
m_thumbButtons[TP_PREVIOUS].dwMask = dwMask;
m_thumbButtons[TP_PREVIOUS].iId = TP_PREVIOUS;
m_thumbButtons[TP_PREVIOUS].hIcon = thumbIcon(TomahawkUtils::PrevButton);
m_thumbButtons[TP_PREVIOUS].dwFlags = THBF_ENABLED;
m_thumbButtons[TP_PREVIOUS].szTip[ tr( "Back" ).toWCharArray( m_thumbButtons[TP_PREVIOUS].szTip ) ] = 0;
m_thumbButtons[TP_PLAY_PAUSE].dwMask = dwMask;
m_thumbButtons[TP_PLAY_PAUSE].iId = TP_PLAY_PAUSE;
m_thumbButtons[TP_PLAY_PAUSE].hIcon = thumbIcon(TomahawkUtils::PlayButton);
m_thumbButtons[TP_PLAY_PAUSE].dwFlags = THBF_ENABLED;
m_thumbButtons[TP_PLAY_PAUSE].szTip[ tr( "Play" ).toWCharArray( m_thumbButtons[TP_PLAY_PAUSE].szTip ) ] = 0;
m_thumbButtons[TP_NEXT].dwMask = dwMask;
m_thumbButtons[TP_NEXT].iId = TP_NEXT;
m_thumbButtons[TP_NEXT].hIcon = thumbIcon(TomahawkUtils::NextButton);
m_thumbButtons[TP_NEXT].dwFlags = THBF_ENABLED;
m_thumbButtons[TP_NEXT].szTip[ tr( "Next" ).toWCharArray( m_thumbButtons[TP_NEXT].szTip ) ] = 0;
m_thumbButtons[3].dwMask = dwMask;
m_thumbButtons[3].iId = -1;
m_thumbButtons[3].hIcon = 0;
m_thumbButtons[3].dwFlags = THBF_NOBACKGROUND | THBF_DISABLED;
m_thumbButtons[3].szTip[0] = 0;
m_thumbButtons[TP_LOVE].dwMask = dwMask;
m_thumbButtons[TP_LOVE].iId = TP_LOVE;
m_thumbButtons[TP_LOVE].hIcon = thumbIcon(TomahawkUtils::NotLoved);
m_thumbButtons[TP_LOVE].dwFlags = THBF_DISABLED;
m_thumbButtons[TP_LOVE].szTip[ tr( "Love" ).toWCharArray( m_thumbButtons[TP_LOVE].szTip ) ] = 0;
if ( S_OK == CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void **)&m_taskbarList ) )
{
hr = m_taskbarList->HrInit();
if ( SUCCEEDED( hr ) )
{
hr = m_taskbarList->ThumbBarAddButtons( (HWND)winId(), ARRAYSIZE( m_thumbButtons ), m_thumbButtons );
}
else
{
m_taskbarList->Release();
m_taskbarList = 0;
}
}
return SUCCEEDED( hr );
#else
m_taskbarList = new QWinThumbnailToolBar( this );
m_taskbarList->setWindow( this->windowHandle() );
updatePreview();
QWinThumbnailToolButton *back = new QWinThumbnailToolButton( m_taskbarList );
back->setToolTip( tr( "Back" ) );
@@ -625,26 +564,84 @@ TomahawkWindow::setupWindowsButtons()
love->setInteractive( false );
connect( love , SIGNAL( clicked() ) , this , SLOT( toggleLoved() ) );
m_taskbarList->addButton(love);
return true;
#endif//QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
}
#if QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
HICON
TomahawkWindow::thumbIcon( TomahawkUtils::ImageType type )
void
TomahawkWindow::updatePreview()
{
static QMap<TomahawkUtils::ImageType,HICON> thumbIcons;
if ( !thumbIcons.contains( type ) )
{
QPixmap pix ( TomahawkUtils::defaultPixmap(type , TomahawkUtils::Original, QSize( 40, 40 ) ) );
thumbIcons[type] = pix.toWinHICON();
const QSize size = QDesktopWidget().availableGeometry().size();
const qreal margin = size.height() * 0.05;
const QSize coverSize( size.height() - 2 * margin , size.height() - 2 * margin);
QPixmap cover;
QString title( qApp->applicationName() );
if ( !m_currentTrack.isNull() ) {
cover = m_currentTrack->track()->albumPtr()->cover( coverSize , false );
title = tr( "%1<br><br><b>%2</b>", "track, artist name" ).arg( m_currentTrack->track()->track(), m_currentTrack->track()->artist() );
}
return thumbIcons[type];
if ( cover.isNull() ) {
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover , TomahawkUtils::Original, coverSize );
}
QPixmap thumb( size );
thumb.fill( QColor( "#FF004C" ) );
QPainter paint( &thumb );
QPen pen = paint.pen();
pen.setColor( Qt::white );
pen.setWidth( size.height() * 0.01 );
paint.setPen( pen );
paint.drawPixmap(margin , margin , coverSize.width() , coverSize.height() , cover );
paint.drawRect( margin , margin , coverSize.width() , coverSize.height() );
paint.drawRect( 0 , 0 , size.width() , size.height() );
QTextDocument doc;
QFont font = paint.font();
font.setPixelSize( size.height() * 0.1 );
doc.setDefaultFont( font );
doc.setPageSize( QSize( size.width() - 2 * margin - coverSize.width() , size.height() - 2 * margin ) );
doc.setHtml( QString( "<center><font color=\"white\">%1</font></center>" ).arg( title ));
paint.save();
paint.translate( coverSize.width() + 2 * margin , ( size.height() - doc.size().height() ) / 2);
doc.drawContents( &paint );
paint.restore();
m_taskbarList->setIconicThumbnailPixmap( thumb );
m_taskbarList->setIconicLivePreviewPixmap( thumb );
}
#else
void
TomahawkWindow::updateWindowsLoveButton()
{
QWinThumbnailToolButton *love = m_taskbarList->buttons()[ TP_LOVE ];
if ( !AudioEngine::instance()->currentTrack().isNull() )
{
love->setInteractive(true);
if ( AudioEngine::instance()->currentTrack()->track()->loved() )
{
love->setIcon(thumbIcon(TomahawkUtils::Loved));
love->setToolTip( tr( "Unlove" ) );
}
else
{
love->setIcon( thumbIcon(TomahawkUtils::NotLoved) );
love->setToolTip( tr( "Love" ) );
}
}
else
{
love->setInteractive(false);
love->setIcon( thumbIcon(TomahawkUtils::NotLoved) );
love->setToolTip( tr( "Love" ) );
}
}
QIcon
TomahawkWindow::thumbIcon(TomahawkUtils::ImageType type)
@@ -652,8 +649,6 @@ TomahawkWindow::thumbIcon(TomahawkUtils::ImageType type)
return TomahawkUtils::defaultPixmap( type , TomahawkUtils::Original, QSize( 40, 40 ) );
}
#endif//QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
#endif
@@ -844,51 +839,6 @@ TomahawkWindow::keyPressEvent( QKeyEvent* e )
QMainWindow::keyPressEvent( e );
}
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
bool
TomahawkWindow::winEvent( MSG* msg, long* result )
{
Q_UNUSED(result);
#define TB_PRESSED Q_FUNC_INFO << "Taskbar Button Pressed:"
switch ( msg->message )
{
case WM_COMMAND:
if ( HIWORD( msg->wParam ) == THBN_CLICKED )
{
switch ( TB_STATES( LOWORD( msg->wParam ) ) )
{
case TP_PREVIOUS:
tLog() << TB_PRESSED << "Previous";
AudioEngine::instance()->previous();
break;
case TP_PLAY_PAUSE:
tLog() << TB_PRESSED << "Play/Pause";
AudioEngine::instance()->playPause();
break;
case TP_NEXT:
tLog() << TB_PRESSED << "Next";
AudioEngine::instance()->next();
break;
case TP_LOVE:
tLog() << TB_PRESSED << "Love";
toggleLoved();
break;
}
return true;
}
break;
}
if ( msg->message == m_buttonCreatedID )
return setupWindowsButtons();
return false;
}
#endif//defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
void
TomahawkWindow::audioStateChanged( AudioState newState, AudioState oldState )
{
@@ -896,48 +846,8 @@ TomahawkWindow::audioStateChanged( AudioState newState, AudioState oldState )
#ifndef Q_OS_WIN
Q_UNUSED(newState);
#else
#if QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
if ( m_taskbarList == 0 )
return;
switch ( newState )
{
case AudioEngine::Playing:
{
m_thumbButtons[TP_PLAY_PAUSE].hIcon = thumbIcon(TomahawkUtils::PauseButton);
m_thumbButtons[TP_PLAY_PAUSE].szTip[ tr( "Pause" ).toWCharArray( m_thumbButtons[TP_PLAY_PAUSE].szTip ) ] = 0;
updateWindowsLoveButton();
}
break;
case AudioEngine::Paused:
{
m_thumbButtons[TP_PLAY_PAUSE].hIcon = thumbIcon(TomahawkUtils::PlayButton);
m_thumbButtons[TP_PLAY_PAUSE].szTip[ tr( "Play" ).toWCharArray( m_thumbButtons[TP_PLAY_PAUSE].szTip ) ] = 0;
}
break;
case AudioEngine::Stopped:
{
if ( !AudioEngine::instance()->currentTrack().isNull() )
{
disconnect( AudioEngine::instance()->currentTrack()->track().data(), SIGNAL( socialActionsLoaded() ), this, SLOT( updateWindowsLoveButton() ) );
}
m_thumbButtons[TP_PLAY_PAUSE].hIcon = thumbIcon(TomahawkUtils::PlayButton);
m_thumbButtons[TP_PLAY_PAUSE].szTip[ tr( "Play" ).toWCharArray( m_thumbButtons[TP_PLAY_PAUSE].szTip ) ] = 0;
m_thumbButtons[TP_LOVE].hIcon = thumbIcon(TomahawkUtils::NotLoved);
m_thumbButtons[TP_LOVE].dwFlags = THBF_DISABLED;
}
break;
default:
return;
}
m_taskbarList->ThumbBarUpdateButtons( (HWND)winId(), ARRAYSIZE( m_thumbButtons ), m_thumbButtons );
#else
updatePreview();
QWinThumbnailToolButton *play = m_taskbarList->buttons()[ TP_PLAY_PAUSE ];
switch ( newState )
{
@@ -946,7 +856,6 @@ TomahawkWindow::audioStateChanged( AudioState newState, AudioState oldState )
play->setIcon( thumbIcon(TomahawkUtils::PauseButton) );
play->setToolTip( tr( "Pause" ) );
updateWindowsLoveButton();
}
break;
@@ -976,57 +885,9 @@ TomahawkWindow::audioStateChanged( AudioState newState, AudioState oldState )
default:
return;
}
#endif//QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
#endif//Q_OS_WIN
}
void
TomahawkWindow::updateWindowsLoveButton()
{
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
if ( m_taskbarList == 0 )
return;
if ( !AudioEngine::instance()->currentTrack().isNull() && AudioEngine::instance()->currentTrack()->track()->loved() )
{
m_thumbButtons[TP_LOVE].hIcon = thumbIcon(TomahawkUtils::Loved);
m_thumbButtons[TP_LOVE].szTip[ tr( "Unlove" ).toWCharArray( m_thumbButtons[TP_LOVE].szTip ) ] = 0;
}
else
{
m_thumbButtons[TP_LOVE].hIcon = thumbIcon(TomahawkUtils::NotLoved);
m_thumbButtons[TP_LOVE].szTip[ tr( "Love" ).toWCharArray( m_thumbButtons[TP_LOVE].szTip ) ] = 0;
}
m_thumbButtons[TP_LOVE].dwFlags = THBF_ENABLED;
m_taskbarList->ThumbBarUpdateButtons( (HWND)winId(), ARRAYSIZE( m_thumbButtons ), m_thumbButtons );
#elif defined(Q_OS_WIN)
QWinThumbnailToolButton *love = m_taskbarList->buttons()[ TP_LOVE ];
if ( !AudioEngine::instance()->currentTrack().isNull() )
{
love->setInteractive(true);
if ( AudioEngine::instance()->currentTrack()->track()->loved() )
{
love->setIcon(thumbIcon(TomahawkUtils::Loved));
love->setToolTip( tr( "Unlove" ) );
}
else
{
love->setIcon( thumbIcon(TomahawkUtils::NotLoved) );
love->setToolTip( tr( "Love" ) );
}
}
else
{
love->setInteractive(false);
love->setIcon( thumbIcon(TomahawkUtils::NotLoved) );
love->setToolTip( tr( "Love" ) );
}
#endif//defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
}
void
TomahawkWindow::onHistoryBackAvailable( bool avail )
{

View File

@@ -37,11 +37,9 @@
#include <QToolButton>
#ifdef Q_OS_WIN
#include <shobjidl.h>
#if QT_VERSION >= QT_VERSION_CHECK( 5, 2, 0 )
#include <QWinThumbnailToolBar>
#include <QWinThumbnailToolButton>
#endif
#endif
namespace Tomahawk
{
@@ -92,10 +90,6 @@ protected:
bool eventFilter( QObject* obj, QEvent* event );
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
bool winEvent( MSG* message, long* result );
#endif
public slots:
void createStation();
void createPlaylist();
@@ -155,7 +149,11 @@ private slots:
void toggleLoved();
void audioStateChanged( AudioState newState, AudioState oldState );
#ifdef Q_OS_WIN
void updateWindowsLoveButton();
void updatePreview();
#endif
private:
void loadSettings();
@@ -173,16 +171,9 @@ private:
void importPlaylist( const QString& url, bool autoUpdate );
#ifdef Q_OS_WIN
bool setupWindowsButtons();
#if QT_VERSION < QT_VERSION_CHECK( 5, 2, 0 )
const unsigned int m_buttonCreatedID;
HICON thumbIcon(TomahawkUtils::ImageType type);
ITaskbarList3* m_taskbarList;
THUMBBUTTON m_thumbButtons[5];
#else
void setupWindowsButtons();
QIcon thumbIcon(TomahawkUtils::ImageType type);
QWinThumbnailToolBar *m_taskbarList;
#endif
enum TB_STATES{ TP_PREVIOUS = 0,TP_PLAY_PAUSE = 1,TP_NEXT = 2, TP_SPACE = 3, TP_LOVE = 4 };
#endif

View File

@@ -37,6 +37,7 @@
#include "sip/SipPlugin.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include "Pipeline.h"
#include <QApplication>
#include <QClipboard>
@@ -123,6 +124,20 @@ DiagnosticsDialog::updateLogView()
log.append( accountLog( account ) + "\n" );
}
log.append( "RESOLVERS:\n" );
connect( Tomahawk::Pipeline::instance(), SIGNAL( resolverAdded( Tomahawk::Resolver* ) ), SLOT( updateLogView() ), Qt::UniqueConnection );
connect( Tomahawk::Pipeline::instance(), SIGNAL( resolverRemoved( Tomahawk::Resolver* ) ), SLOT( updateLogView() ), Qt::UniqueConnection );
const QList< Tomahawk::Resolver* > resolvers = Tomahawk::Pipeline::instance()->resolvers();
foreach ( Tomahawk::Resolver* resolver, resolvers )
{
log.append( resolver->name() + "\n" );
}
ui->text->setText( log );
}

View File

@@ -366,8 +366,8 @@ Tomahawk::setupToolBarMac( TomahawkWindow* parent )
[toolbar->nativeToolbar() setDisplayMode: NSToolbarDisplayModeIconOnly];
[toolbar->nativeToolbar() setSizeMode: NSToolbarSizeModeSmall];
QMacToolBarItem* backItem = toolbar->addItem( ImageRegistry::instance()->pixmap( RESPATH "images/back.svg", QSize( 32, 32 ) ), QString( "Back" ) );
QMacToolBarItem* forwardItem = toolbar->addItem( ImageRegistry::instance()->pixmap( RESPATH "images/forward.svg", QSize( 32, 32 ) ), QString( "Forward" ) );
QMacToolBarItem* backItem = toolbar->addItem( ImageRegistry::instance()->pixmap( RESPATH "images/nav-back.svg", QSize( 32, 32 ) ), QString( "Back" ) );
QMacToolBarItem* forwardItem = toolbar->addItem( ImageRegistry::instance()->pixmap( RESPATH "images/nav-forward.svg", QSize( 32, 32 ) ), QString( "Forward" ) );
QObject::connect( backItem, SIGNAL( activated() ), ViewManager::instance(), SLOT( historyBack() ) );
QObject::connect( forwardItem, SIGNAL( activated() ), ViewManager::instance(), SLOT( historyForward() ) );
@@ -381,6 +381,12 @@ Tomahawk::setupToolBarMac( TomahawkWindow* parent )
searchField->parent = parent;
[searchItem->nativeToolBarItem() setView: searchField];
NSSize nsirect = [searchField frame].size;
nsirect.width = 250;
[searchItem->nativeToolBarItem() setMinSize: nsirect];
nsirect.width = 450;
[searchItem->nativeToolBarItem() setMaxSize: nsirect];
QMacToolBarItem* spacerRightItem = toolbar->addItem( QIcon(), QString() );
spacerRightItem->setStandardItem( QMacToolBarItem::FlexibleSpace );

View File

@@ -538,7 +538,7 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
paintStandardItem( painter, optIndentation, index, count );
}
else if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage )
else if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage || type == SourcesModel::RemovablePage )
{
if ( opt.state & QStyle::State_MouseOver )
{
@@ -668,7 +668,7 @@ SourceDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt
if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonPress )
{
SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage )
if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage || type == SourcesModel::RemovablePage )
{
SourceTreeItem* gpi = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
Q_ASSERT( gpi );

View File

@@ -334,7 +334,15 @@ SourcesModel::appendPageItem( const QString& name, ViewPage* page, int sortValue
page->pixmap(),
std::bind( &ViewManager::showDynamicPage, ViewManager::instance(), name ),
std::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), name ) );
pageItem->setDeletable( page->isDeletable() );
if ( page->isDeletable() )
{
pageItem->setDeletable( true );
}
else
{
pageItem->setRemovable( page->isRemovable() );
}
if ( sortValue )
{

View File

@@ -63,6 +63,7 @@ public:
TemporaryPage = 7,
LovedTracksPage = 10,
DeletablePage = 15,
RemovablePage = 16,
Collection = 14,
ScriptCollection = 11,

View File

@@ -132,6 +132,18 @@ GenericPageItem::setDeletable( bool deletable )
}
}
void
GenericPageItem::setRemovable( bool removable )
{
if ( removable )
{
setRowType( SourcesModel::RemovablePage );
}
else
{
setRowType( SourcesModel::GenericPage );
}
}
int
GenericPageItem::peerSortValue() const

View File

@@ -43,6 +43,7 @@ public:
virtual int peerSortValue() const; // How to sort relative to peers in the tree.
virtual bool isBeingPlayed() const;
void setRemovable( bool removable );
void setDeletable( bool deletable );
void setText( const QString& text );
void setSortValue( int value );

View File

@@ -91,7 +91,8 @@ public:
const QString pixmapPath() const { return ( RESPATH "images/whatsnew.svg" ); }
bool addPageItem() const;
bool isDeletable() const { return true; }
bool isRemovable() const override { return true; }
bool isDeletable() const override { return false; }
void onItemDeleted();
int sortValue() { return 1; }