1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-16 08:52:28 +02:00

Compare commits

..

3 Commits

Author SHA1 Message Date
Dominik Schmidt
365c7b0e31 Get woah resolvers from synchrotron 2015-11-13 01:55:15 +01:00
Dominik Schmidt
9ae97e50ad Move moar stuff over to es6 foo 2015-11-13 01:55:15 +01:00
Dominik Schmidt
72dab1aae6 Move to new es6 based standalone resolvers 2015-11-13 01:54:32 +01:00
143 changed files with 1616 additions and 3330 deletions

View File

@@ -307,10 +307,11 @@ else()
message(STATUS "${CMAKE_CXX_COMPILER} does not support C++11, please use a
different compiler")
endif()
if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR APPLE) AND LIBCPP_FOUND)
if(LIBCPP_FOUND AND APPLE)
tomahawk_add_cxx_flags( "-stdlib=libc++" )
endif()
macro_optional_find_package(Echonest 2.3.0)
macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.3.0 is needed for dynamic playlists and the infosystem")
@@ -403,8 +404,12 @@ if( WIN32 )
endif( WIN32 )
if( WIN32 OR APPLE )
macro_optional_find_package(LibsnoreQt5 0.6.0 QUIET)
macro_log_feature(LibsnoreQt5_FOUND "Libsnore" "Library for notifications" "https://projects.kde.org/projects/playground/libs/snorenotify" FALSE "" "")
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 "" "")
endif()
find_package(LIBVLC REQUIRED 2.1.0)

View File

@@ -6,16 +6,19 @@ 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})
@@ -29,6 +32,8 @@ int main(int argc, char *argv[]) {
HAVE_VLC_ALBUMARTIST)
find_package_handle_standard_args(LibVLC
REQUIRED_VARS LIBVLC_LIBRARY LIBVLC_INCLUDE_DIR
REQUIRED_VARS LIBVLC_LIBRARY LIBVLCCORE_LIBRARY LIBVLC_INCLUDE_DIR
VERSION_VAR LIBVLC_VERSION
)

View File

@@ -15,24 +15,21 @@
;-----------------------------------------------------------------------------
; Some paths.
;-----------------------------------------------------------------------------
!ifndef MINGW_ROOT
!define MINGW_ROOT "/usr/i686-w64-mingw32/sys-root/mingw"
!ifndef MING_PATH
!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw"
!endif
!define APPLICATION_NAME "@CPACK_PACKAGE_NAME@"
!define TARGET_NAME "@CPACK_PACKAGE_TARGET_NAME@"
!define APPLICATION_NAME "Tomahawk"
!define TARGET_NAME "tomahawk"
;define app id needed for Windows 8 notifications
!define AppUserModelId "@TOMAHAWK_APPLICATION_PACKAGE_NAME@"
!define MINGW_BIN "${MINGW_ROOT}/bin"
!define MINGW_LIB "${MINGW_ROOT}/lib"
!define MINGW_SHARE "${MINGW_ROOT}/share"
!define AppUserModelId @TOMAHAWK_APPLICATION_PACKAGE_NAME@
!define MING_BIN "${MING_PATH}/bin"
!define MING_LIB "${MING_PATH}/lib"
!define BUILD_PATH "@CMAKE_BINARY_DIR@"
!define SOURCE_PATH "@CMAKE_SOURCE_DIR@"
!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"
!define QT_DLL_PATH "${MING_BIN}"
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
; We use official release plugins
; mingw32-vlc from obs misses a lot and has even broken ones probably
@@ -333,7 +330,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"
@@ -342,112 +339,96 @@ 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"
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 "${MINGW_BIN}\libsqlite3-0.dll"
;Boost fnord
File "${QT_DLL_PATH}\icuuc53.dll"
File "${QT_DLL_PATH}\icudata53.dll"
File "${QT_DLL_PATH}\icui18n53.dll"
;Qt platform plugins
SetOutPath "$INSTDIR\platforms"
File "${MINGW_LIB}/qt5/plugins/platforms/qwindows.dll"
SetOutPath "$INSTDIR"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR"
File "${MING_BIN}\libsqlite3-0.dll"
;Image plugins
SetOutPath "$INSTDIR\imageformats"
File "${IMAGEFORMATS_DLL_PATH}\qgif.dll"
File "${IMAGEFORMATS_DLL_PATH}\qjpeg.dll"
File "${IMAGEFORMATS_DLL_PATH}\qsvg.dll"
SetOutPath "$INSTDIR"
;Qt platform plugins
SetOutPath "$INSTDIR\platforms"
File "${MING_LIB}/qt5/plugins/platforms/qwindows.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"
;Image plugins
SetOutPath "$INSTDIR\imageformats"
File "${IMAGEFORMATS_DLL_PATH}\qgif.dll"
File "${IMAGEFORMATS_DLL_PATH}\qjpeg.dll"
File "${IMAGEFORMATS_DLL_PATH}\qsvg.dll"
SetOutPath "$INSTDIR"
;Cygwin/c++ stuff
File "${MINGW_BIN}\libgcc_s_sjlj-1.dll"
File "${MINGW_BIN}\libstdc++-6.dll"
;Cygwin/c++ stuff
File "${MING_BIN}\libgcc_s_sjlj-1.dll"
File "${MING_BIN}\libstdc++-6.dll"
;VLC
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
File /r "${VLC_PLUGIN_PATH}\*.dll"
SetOutPath "$INSTDIR"
;VLC
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
File /r "${VLC_PLUGIN_PATH}\*.dll"
SetOutPath "$INSTDIR"
; Other
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"
; Other
File "${MING_BIN}\libtag.dll"
File "${MING_BIN}\libpng16-16.dll"
File "${MING_BIN}\libjpeg-8.dll"
File "${MING_BIN}\zlib1.dll"
; ANGLE
File "${MINGW_BIN}\D3DCompiler_43.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"
; GnuTLS
File "${MINGW_BIN}\libgnutls-28.dll"
File "${MINGW_BIN}\libtasn1-6.dll"
File "${MINGW_BIN}\libgmp-10.dll"
File "${MINGW_BIN}\libhogweed-4-1.dll"
File "${MINGW_BIN}\libintl-8.dll"
File "${MINGW_BIN}\libnettle-6-1.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"
; Snorenotify
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 "${MINGW_BIN}\libsnoregrowl++.dll"
File "${MINGW_BIN}\libsnoregrowl.dll"
; Snoregrowl
File "${MING_BIN}\libsnoregrowl++.dll"
File "${MING_BIN}\libsnoregrowl.dll"
; Jabber
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 "${MINGW_LIB}\qca-qt5\crypto\libqca-ossl.dll"
SetOutPath "$INSTDIR"
File "${MINGW_BIN}\libssl-10.dll"
File "${MINGW_BIN}\libcrypto-10.dll"
File "${MING_BIN}\libjreen-qt5.dll"
File "${MING_BIN}\libidn-11.dll"
File "${MING_BIN}\libgsasl-7.dll"
File "${MING_BIN}\libqca-qt5.dll"
SetOutPath "$INSTDIR\crypto"
File "${MING_LIB}\qca-qt5\crypto\libqca-ossl.dll"
SetOutPath "$INSTDIR"
File "${MING_BIN}\libssl-10.dll"
File "${MING_BIN}\libcrypto-10.dll"
; LucenePlusPlus
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"
; 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}\libqtsparkle-qt5.dll"
File "${MINGW_BIN}\libKF5Attica.dll"
File "${MING_BIN}\libqtsparkle-qt5.dll"
File "${MING_BIN}\libKF5Attica.dll"
SectionEnd
SectionGroup "Shortcuts"
@@ -462,7 +443,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}\Notification Settings.lnk" "$INSTDIR\snoresettings.exe" "-a ${APPLICATION_NAME}"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_NAME}.exe"
!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

@@ -2,7 +2,7 @@ INCLUDE( InstallRequiredSystemLibraries )
SET( CPACK_PACKAGE_CONTACT "Dominik Schmidt <domme@tomahawk-player.org>" )
SET( CPACK_PACKAGE_FILE_NAME "${TOMAHAWK_TARGET_NAME}-${TOMAHAWK_VERSION}" ) # Package file name without extension. Also a directory of installer cmake-2.5.0-Linux-i686
SET( CPACK_PACKAGE_FILE_NAME "${TOMAHAWK_APPLICATION_NAME}-${TOMAHAWK_VERSION}" ) # Package file name without extension. Also a directory of installer cmake-2.5.0-Linux-i686
# CPACK_GENERATOR CPack generator to be used STGZ;TGZ;TZ
# CPACK_INCLUDE_TOPLEVEL_DIRECTORY Controls whether CPack adds a top-level directory, usually of the form ProjectName-Version-OS, to the top of package tree. 0 to disable, 1 to enable
@@ -13,9 +13,7 @@ SET( CPACK_PACKAGE_DESCRIPTION_SUMMARY ${TOMAHAWK_DESCRIPTION_SUMMARY} ) # Des
SET( CPACK_PACKAGE_INSTALL_DIRECTORY ${TOMAHAWK_APPLICATION_NAME} ) # Installation directory on the target system -> C:\Program Files\fellody
SET( CPACK_PACKAGE_INSTALL_REGISTRY_KEY ${TOMAHAWK_APPLICATION_NAME} ) # Registry key used when installing this project CMake 2.5.0
SET( CPACK_PACKAGE_NAME ${TOMAHAWK_APPLICATION_NAME} ) # Package name, defaults to the project name
SET( CPACK_PACKAGE_TARGET_NAME ${TOMAHAWK_TARGET_NAME} ) # Used to build library and executable names
SET( CPACK_PACKAGE_VENDOR ${TOMAHAWK_ORGANIZATION_NAME} ) # Package vendor name
SET( TOMAHAWK_APPLICATION_PACKAGE_NAME ${TOMAHAWK_APPLICATION_PACKAGE_NAME} )
SET( CPACK_PACKAGE_VERSION_MAJOR ${TOMAHAWK_VERSION_MAJOR} )
SET( CPACK_PACKAGE_VERSION_MINOR ${TOMAHAWK_VERSION_MINOR} )
SET( CPACK_PACKAGE_VERSION_PATCH ${TOMAHAWK_VERSION_PATCH} )

View File

@@ -5,23 +5,23 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>@TOMAHAWK_APPLICATION_NAME@</string>
<string>Tomahawk</string>
<key>CFBundleIdentifier</key>
<string>@TOMAHAWK_APPLICATION_PACKAGE_NAME@</string>
<string>org.tomahawk-player.Tomahawk</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_BASE_TARGET_NAME@</string>
<string>tomahawk</string>
<key>CFBundleIconFile</key>
<string>@TOMAHAWK_APPLICATION_NAME@.icns</string>
<string>Tomahawk.icns</string>
<key>CFBundleName</key>
<string>@TOMAHAWK_APPLICATION_NAME@</string>
<string>Tomahawk</string>
<key>LSMinimumSystemVersion</key>
<string>10.7.0</string>
<key>NSPrincipalClass</key>
@@ -29,7 +29,7 @@
<key>NSHighResolutionCapable</key>
<true />
<key>SUFeedURL</key>
<string>@TOMAHAWK_SPARKLE_UPDATE_URL@</string>
<string>http://download.tomahawk-player.org/sparkle/update.php</string>
<key>SUPublicDSAKeyFile</key>
<string>sparkle_pub.pem</string>
<key>SUEnableSystemProfiling</key>

View File

@@ -4,10 +4,9 @@
#
################################################################################
TARGET_NAME="Tomahawk"
set -e
function header {
echo -e "\033[0;34m==>\033[0;0;1m $1 \033[0;0m"
}
@@ -32,28 +31,30 @@ CERT_SIGNER=$2
################################################################################
header "Fixing and copying libraries"
$ROOT/../admin/mac/macdeploy.py "${TARGET_NAME}.app" quiet
$ROOT/../admin/mac/macdeploy.py Tomahawk.app quiet
cd "${TARGET_NAME}.app"
cd Tomahawk.app
cp $ROOT/../admin/mac/qt.conf Contents/Resources/qt.conf
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"
cd ..
if [ -f ~/sign_step.sh ];
then
~/sign_step.sh "$CERT_SIGNER" "${TARGET_NAME}.app" || true
fi
# header "Copying Sparkle framework"
# cp -R /Library/Frameworks/Sparkle.framework Contents/Frameworks
header "Creating DMG"
$ROOT/../admin/mac/create-dmg.sh "${TARGET_NAME}.app"
mv "${TARGET_NAME}.dmg" "${TARGET_NAME}-$VERSION.dmg"
cd ..
header "Signing bundle"
# codesign -s "Developer ID Application: $CERT_SIGNER" -f -v ./Tomahawk.app
if [ -f ~/sign_step.sh ];
then
~/sign_step.sh "$CERT_SIGNER" "Tomahawk.app" || true
fi
$ROOT/../admin/mac/create-dmg.sh Tomahawk.app
mv Tomahawk.dmg Tomahawk-$VERSION.dmg
header "Creating signed Sparkle update"
# $ROOT/../admin/mac/sign_bundle.rb "${TARGET_NAME}" $VERSION ~/tomahawk_sparkle_privkey.pem
$ROOT/../admin/mac/sign_bundle.rb $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 "$NAME" -hfs-openfolder "$TMP" "$TMP" -o tmp.dmg
hdiutil makehybrid -hfs -hfs-volume-name Tomahawk -hfs-openfolder "$TMP" "$TMP" -o tmp.dmg
hdiutil convert -format UDZO -imagekey zlib-level=9 tmp.dmg -o "$OUT"
# cleanup

View File

@@ -22,170 +22,171 @@ 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/opt/vlc/lib', '/usr/local/Cellar/gettext/0.19.2/lib', '.']
LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Cellar/gettext/0.19.2/lib', '.']
VLC_PLUGINS=[
'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',
'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',
#'libconverter_fixed_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',
'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',
#'libspdif_mixer_plugin.dylib',
#'libtrivial_mixer_plugin.dylib',
#'libaout_file_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',
'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',
#'libfake_plugin.dylib',
'libflac_plugin.dylib',
'codec/libflac_plugin.dylib',
#'libfluidsynth_plugin.dylib',
#'libinvmem_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',
'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',
#'libhotkeys_plugin.dylib',
#'libmotion_plugin.dylib',
#'libnetsync_plugin.dylib',
#'libsignals_plugin.dylib',
'libaiff_plugin.dylib',
'libasf_plugin.dylib',
'libau_plugin.dylib',
'demux/libaiff_plugin.dylib',
'demux/libasf_plugin.dylib',
'demux/libau_plugin.dylib',
#'libavformat_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',
'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',
#'libaudioscrobbler_plugin.dylib',
'libdummy_plugin.dylib',
'libexport_plugin.dylib',
'control/libdummy_plugin.dylib',
'misc/libexport_plugin.dylib',
#'libfreetype_plugin.dylib',
#'libgnutls_plugin.dylib',
'liblogger_plugin.dylib',
'liblua_plugin.dylib',
'misc/liblogger_plugin.dylib',
'lua/liblua_plugin.dylib',
#'libosd_parser_plugin.dylib',
#'libquartztext_plugin.dylib',
#'libstats_plugin.dylib',
'libvod_rtsp_plugin.dylib',
'libxml_plugin.dylib',
'misc/libvod_rtsp_plugin.dylib',
'misc/libxml_plugin.dylib',
#'libxtag_plugin.dylib',
'libi420_rgb_mmx_plugin.dylib',
'libi420_yuy2_mmx_plugin.dylib',
'libi422_yuy2_mmx_plugin.dylib',
'video_chroma/libi420_rgb_mmx_plugin.dylib',
'video_chroma/libi420_yuy2_mmx_plugin.dylib',
'video_chroma/libi422_yuy2_mmx_plugin.dylib',
#'libmemcpymmx_plugin.dylib',
#'libmemcpymmxext_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',
'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',
#'libvisual_plugin.dylib',
'libsecuretransport_plugin.dylib'
]
VLC_SEARCH_PATH=[
'/usr/local/opt/vlc/lib/vlc/plugins/',
'/usr/local/lib/vlc/plugins/',
]
QT_PLUGINS = [
@@ -200,39 +201,39 @@ QT_PLUGINS = [
]
SNORE_PLUGINS = [
'libsnore_backend_growl.so',
'libsnore_backend_osxnotificationcenter.so',
# 'libsnore_backend_growl.so',
# 'libsnore_backend_osxnotificationcenter.so',
]
TOMAHAWK_PLUGINS = [
'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,
'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',
]
QT_PLUGINS_SEARCH_PATH=[
'/usr/local/opt/qt5/plugins',
'/usr/local/Cellar/qt5/5.4.0/plugins',
]
SNORE_PLUGINS_SEARCH_PATH=[
'/usr/local/opt/snorenotify/lib/plugins/libsnore-qt5',
'/usr/local/Cellar/snorenotify/HEAD/lib/libsnore',
]
class Error(Exception):
@@ -246,8 +247,6 @@ class CouldNotFindQtPluginErrorFindFrameworkError(Error):
class InstallNameToolError(Error):
pass
class CouldNotFindFrameworkError(Error):
pass
class CouldNotFindQtPluginError(Error):
pass
@@ -309,7 +308,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) and not re.match(r'^\s*@loader_path/../lib', line):
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
# Potentially already fixed library
if '.framework' in line:
relative_path = os.path.join(*line.split('/')[3:])
@@ -333,11 +332,6 @@ 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):
@@ -582,12 +576,12 @@ for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS')
for plugin in SNORE_PLUGINS:
FixPlugin(FindSnorePlugin(plugin), '../lib/plugins/libsnore-qt5')
FixPlugin(FindSnorePlugin(plugin), '../MacOS/libsnore')
try:
FixPlugin('%s_crash_reporter' % TARGET_NAME, '../MacOS')
FixPlugin('tomahawk_crash_reporter', '../MacOS')
except:
print 'Failed to find %s_crash_reporter' % TARGET_NAME
print 'Failed to find tomahawk_crash_reporter'
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 = "#{ARGV[0].downcase}-#{ARGV[1]}.tar.bz2"
tarball = "tomahawk-#{ARGV[0]}.tar.bz2"
puts "Zipping: #{tarball}..."
`tar jcvf "#{tarball}" #{ARGV[0]}.app`
`tar jcvf "#{tarball}" Tomahawk.app`
puts "Signing..."
puts `openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[2]}" | openssl enc -base64`
puts `openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[1]}" | openssl enc -base64`

View File

@@ -1,55 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,55 +0,0 @@
<?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>

Before

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 RSVP.Promise.resolve(this[requestMethod](criteria));
return 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 RSVP.Promise.resolve(this[getInfoMethod](infoHash));
return Promise.resolve(this[getInfoMethod](infoHash));
}
};

View File

@@ -39,101 +39,7 @@ if ((typeof Tomahawk === "undefined") || (Tomahawk === null)) {
Tomahawk.apiVersion = "0.2.2";
//Statuses considered a success for HTTP request
var httpSuccessStatuses = [200, 201];
// install RSVP error handler for uncaught(!) errors
RSVP.on('error', function (reason) {
var resolverName = "";
if (Tomahawk.resolver.instance) {
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
}
if (reason) {
console.error(resolverName + 'Uncaught error:', reason);
} else {
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
}
});
/**
* Compares versions strings
* (version1 < version2) == -1
* (version1 = version2) == 0
* (version1 > version2) == 1
*/
Tomahawk.versionCompare = function (version1, version2) {
var v1 = version1.split('.').map(function (item) {
return parseInt(item);
});
var v2 = version2.split('.').map(function (item) {
return parseInt(item);
});
var length = Math.max(v1.length, v2.length);
var i = 0;
for (; i < length; i++) {
if (typeof v1[i] == "undefined" || v1[i] === null) {
if (typeof v2[i] == "undefined" || v2[i] === null) {
// v1 == v2
return 0;
} else if (v2[i] === 0) {
continue;
} else {
// v1 < v2
return -1;
}
} else if (typeof v2[i] == "undefined" || v2[i] === null) {
if (v1[i] === 0) {
continue;
} else {
// v1 > v2
return 1;
}
} else if (v2[i] > v1[i]) {
// v1 < v2
return -1;
} else if (v2[i] < v1[i]) {
// v1 > v2
return 1;
}
}
// v1 == v2
return 0;
};
/**
* Check if this is at least specified tomahawk-api-version.
*/
Tomahawk.atLeastVersion = function (version) {
return (Tomahawk.versionCompare(Tomahawk.apiVersion, version) >= 0);
};
Tomahawk.resolver = {
scriptPath: Tomahawk.resolverData().scriptPath
};
Tomahawk.timestamp = function () {
return Math.round(new Date() / 1000);
};
Tomahawk.htmlDecode = (function () {
// this prevents any overhead from creating the object each time
var element = document.createElement('textarea');
function decodeHTMLEntities(str) {
if (str && typeof str === 'string') {
str = str.replace(/</g, "&lt;");
str = str.replace(/>/g, "&gt;");
element.innerHTML = str;
str = element.textContent;
element.textContent = '';
}
return str;
}
return decodeHTMLEntities;
})();
Tomahawk.dumpResult = function (result) {
var results = result.results;
@@ -145,168 +51,6 @@ Tomahawk.dumpResult = function (result) {
Tomahawk.log("Done.");
};
// javascript part of Tomahawk-Object API
Tomahawk.extend = function (object, members) {
var F = function () {};
F.prototype = object;
var newObject = new F();
for (var key in members) {
newObject[key] = members[key];
}
return newObject;
};
//Deprecated for 0.9 resolvers. Reporting resolver capabilities is no longer necessary.
var TomahawkResolverCapability = {
NullCapability: 0,
Browsable: 1,
PlaylistSync: 2,
AccountFactory: 4,
UrlLookup: 8
};
//Deprecated for 0.9 resolvers. Use Tomahawk.UrlType instead.
var TomahawkUrlType = {
Any: 0,
Playlist: 1,
Track: 2,
Album: 4,
Artist: 8,
Xspf: 16
};
//Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead.
var TomahawkConfigTestResultType = {
Other: 0,
Success: 1,
Logout: 2,
CommunicationError: 3,
InvalidCredentials: 4,
InvalidAccount: 5,
PlayingElsewhere: 6,
AccountExpired: 7
};
/**
* Resolver BaseObject, inherit it to implement your own resolver.
*/
var TomahawkResolver = {
init: function () {
},
scriptPath: function () {
return Tomahawk.resolverData().scriptPath;
},
getConfigUi: function () {
return {};
},
getUserConfig: function () {
return JSON.parse(window.localStorage[this.scriptPath()] || "{}");
},
saveUserConfig: function () {
var configJson = JSON.stringify(Tomahawk.resolverData().config);
window.localStorage[this.scriptPath()] = configJson;
this.newConfigSaved();
},
newConfigSaved: function () {
},
resolve: function (qid, artist, album, title) {
return {
qid: qid
};
},
search: function (qid, searchString) {
return this.resolve(qid, "", "", searchString);
},
artists: function (qid) {
return {
qid: qid
};
},
albums: function (qid, artist) {
return {
qid: qid
};
},
tracks: function (qid, artist, album) {
return {
qid: qid
};
},
collection: function () {
return {};
},
_adapter_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
});
},
testConfig: function () {
this.configTest();
},
getStreamUrl: function (qid, url) {
Tomahawk.reportStreamUrl(qid, url);
}
};
Tomahawk.Resolver = {
init: function () {
},
scriptPath: function () {
return Tomahawk.resolverData().scriptPath;
},
getConfigUi: function () {
return {};
},
getUserConfig: function () {
return JSON.parse(window.localStorage[this.scriptPath()] || "{}");
},
saveUserConfig: function () {
window.localStorage[this.scriptPath()] = JSON.stringify(Tomahawk.resolverData().config);
this.newConfigSaved(Tomahawk.resolverData().config);
},
newConfigSaved: function () {
},
testConfig: function () {
},
getStreamUrl: function (params) {
return params;
},
resolve: function() {
},
_adapter_resolve: function (params) {
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
if(Array.isArray(results)) {
return {
'tracks': results
};
}
return results;
});
},
_adapter_search: function (params) {
return RSVP.Promise.resolve(this.search(params)).then(function (results) {
if(Array.isArray(results)) {
return {
'tracks': results
};
}
return results;
});
},
_adapter_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
});
}
};
// help functions
Tomahawk.valueForSubNode = function (node, tag) {
@@ -322,6 +66,7 @@ Tomahawk.valueForSubNode = function (node, tag) {
return element.textContent;
};
/**
* Internal counter used to identify retrievedMetadata call back from native
* code.
@@ -419,174 +164,6 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
/**
* 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)
* - username: The username for HTTP Basic Auth
* - password: The password for HTTP Basic Auth
* - errorHandler: callback called if the request was not completed
* - 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
*/
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];
};
return xhr;
});
} else {
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.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);
});
}
};
Tomahawk.ajax = function (url, settings) {
if (typeof url === "object") {
settings = url;
} else {
settings = settings || {};
settings.url = url;
}
settings.type = settings.type || settings.method || 'get';
settings.method = settings.type;
settings.dataFormat = settings.dataFormat || 'form';
if (settings.data) {
var formEncode = function (obj) {
var str = [];
for (var p in obj) {
if (obj[p] !== undefined) {
if (Array.isArray(obj[p])) {
for (var i = 0; i < obj[p].length; i++) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]));
}
} else {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
}
str.sort();
return str.join("&");
};
if (typeof settings.data === 'object') {
if (settings.dataFormat == 'form') {
settings.data = formEncode(settings.data);
settings.contentType = settings.contentType || 'application/x-www-form-urlencoded';
} else if (settings.dataFormat == 'json') {
settings.data = JSON.stringify(settings.data);
settings.contentType = settings.contentType || 'application/json';
} else {
throw new Error("Tomahawk.ajax: unknown dataFormat requested: "
+ settings.dataFormat);
}
} else {
throw new Error("Tomahawk.ajax: data should be either object or string");
}
if (settings.type.toLowerCase() === 'get') {
settings.url += '?' + settings.data;
delete settings.data;
} else {
settings.headers = settings.headers || {};
if (!settings.headers.hasOwnProperty('Content-Type')) {
settings.headers['Content-Type'] = settings.contentType;
}
}
}
return doRequest(settings).then(function (xhr) {
if (settings.rawResponse) {
return xhr;
}
var responseText = xhr.responseText;
var contentType;
if (settings.dataType === 'json') {
contentType = 'application/json';
} else if (settings.dataType === 'xml') {
contentType = 'text/xml';
} else if (typeof xhr.getResponseHeader !== 'undefined') {
contentType = xhr.getResponseHeader('Content-Type');
} else if (xhr.hasOwnProperty('contentType')) {
contentType = xhr['contentType'];
} else {
contentType = 'text/html';
}
if (~contentType.indexOf('application/json')) {
return JSON.parse(responseText);
}
if (~contentType.indexOf('text/xml')) {
var domParser = new DOMParser();
return domParser.parseFromString(responseText, "text/xml");
}
return xhr.responseText;
});
};
Tomahawk.post = function (url, settings) {
if (typeof url === "object") {
settings = url;
} else {
settings = settings || {};
settings.url = url;
}
settings.method = 'POST';
return Tomahawk.ajax(settings);
};
Tomahawk.get = function (url, settings) {
return Tomahawk.ajax(url, settings);
};
Tomahawk.assert = function (assertion, message) {
Tomahawk.nativeAssert(assertion, message);
};
@@ -602,127 +179,6 @@ Tomahawk.hmac = function (key, message) {
return CryptoJS.HmacMD5(message, key).toString(CryptoJS.enc.Hex);
};
// Extracted from https://github.com/andrewrk/diacritics version 1.2.0
// Thanks to Andrew Kelley for this MIT-licensed diacritic removal code
// Initialisation / precomputation
(function() {
var replacementList = [
{base: ' ', chars: "\u00A0"},
{base: '0', chars: "\u07C0"},
{base: 'A', chars: "\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F"},
{base: 'AA', chars: "\uA732"},
{base: 'AE', chars: "\u00C6\u01FC\u01E2"},
{base: 'AO', chars: "\uA734"},
{base: 'AU', chars: "\uA736"},
{base: 'AV', chars: "\uA738\uA73A"},
{base: 'AY', chars: "\uA73C"},
{base: 'B', chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"},
{base: 'C', chars: "\uFF43\u24b8\uff23\uA73E\u1E08"},
{base: 'D', chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"},
{base: 'Dh', chars: "\u00D0"},
{base: 'DZ', chars: "\u01F1\u01C4"},
{base: 'Dz', chars: "\u01F2\u01C5"},
{base: 'E', chars: "\u025B\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07"},
{base: 'F', chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"},
{base: 'G', chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"},
{base: 'H', chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"},
{base: 'I', chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"},
{base: 'J', chars: "\u24BF\uFF2A\u0134\u0248\u0237"},
{base: 'K', chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"},
{base: 'L', chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"},
{base: 'LJ', chars: "\u01C7"},
{base: 'Lj', chars: "\u01C8"},
{base: 'M', chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"},
{base: 'N', chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"},
{base: 'NJ', chars: "\u01CA"},
{base: 'Nj', chars: "\u01CB"},
{base: 'O', chars: "\u24C4\uFF2F\xD2\xD3\xD4\u1ED2\u1ED0\u1ED6\u1ED4\xD5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\xD6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\xD8\u01FE\u0186\u019F\uA74A\uA74C"},
{base: 'OE', chars: "\u0152"},
{base: 'OI', chars: "\u01A2"},
{base: 'OO', chars: "\uA74E"},
{base: 'OU', chars: "\u0222"},
{base: 'P', chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"},
{base: 'Q', chars: "\u24C6\uFF31\uA756\uA758\u024A"},
{base: 'R', chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"},
{base: 'S', chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"},
{base: 'T', chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"},
{base: 'Th', chars: "\u00DE"},
{base: 'TZ', chars: "\uA728"},
{base: 'U', chars: "\u24CA\uFF35\xD9\xDA\xDB\u0168\u1E78\u016A\u1E7A\u016C\xDC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244"},
{base: 'V', chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"},
{base: 'VY', chars: "\uA760"},
{base: 'W', chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"},
{base: 'X', chars: "\u24CD\uFF38\u1E8A\u1E8C"},
{base: 'Y', chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"},
{base: 'Z', chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"},
{base: 'a', chars: "\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251"},
{base: 'aa', chars: "\uA733"},
{base: 'ae', chars: "\u00E6\u01FD\u01E3"},
{base: 'ao', chars: "\uA735"},
{base: 'au', chars: "\uA737"},
{base: 'av', chars: "\uA739\uA73B"},
{base: 'ay', chars: "\uA73D"},
{base: 'b', chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"},
{base: 'c', chars: "\u24D2\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184\u0043\u0106\u0108\u010A\u010C\u00C7\u0187\u023B"},
{base: 'd', chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"},
{base: 'dh', chars: "\u00F0"},
{base: 'dz', chars: "\u01F3\u01C6"},
{base: 'e', chars: "\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD"},
{base: 'f', chars: "\u24D5\uFF46\u1E1F\u0192"},
{base: 'ff', chars: "\uFB00"},
{base: 'fi', chars: "\uFB01"},
{base: 'fl', chars: "\uFB02"},
{base: 'ffi', chars: "\uFB03"},
{base: 'ffl', chars: "\uFB04"},
{base: 'g', chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"},
{base: 'h', chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"},
{base: 'hv', chars: "\u0195"},
{base: 'i', chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"},
{base: 'j', chars: "\u24D9\uFF4A\u0135\u01F0\u0249"},
{base: 'k', chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"},
{base: 'l', chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"},
{base: 'lj', chars: "\u01C9"},
{base: 'm', chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"},
{base: 'n', chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"},
{base: 'nj', chars: "\u01CC"},
{base: 'o', chars: "\u24DE\uFF4F\xF2\xF3\xF4\u1ED3\u1ED1\u1ED7\u1ED5\xF5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\xF6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\xF8\u01FF\uA74B\uA74D\u0275\u0254\u1D11"},
{base: 'oe', chars: "\u0153"},
{base: 'oi', chars: "\u01A3"},
{base: 'oo', chars: "\uA74F"},
{base: 'ou', chars: "\u0223"},
{base: 'p', chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"},
{base: 'q', chars: "\u24E0\uFF51\u024B\uA757\uA759"},
{base: 'r', chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"},
{base: 's', chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"},
{base: 'ss', chars: "\xDF"},
{base: 't', chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"},
{base: 'th', chars: "\u00FE"},
{base: 'tz', chars: "\uA729"},
{base: 'u', chars: "\u24E4\uFF55\xF9\xFA\xFB\u0169\u1E79\u016B\u1E7B\u016D\xFC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289"},
{base: 'v', chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"},
{base: 'vy', chars: "\uA761"},
{base: 'w', chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"},
{base: 'x', chars: "\u24E7\uFF58\u1E8B\u1E8D"},
{base: 'y', chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"},
{base: 'z', chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"}
];
Tomahawk.diacriticsMap = {};
var i, j, chars;
for (i = 0; i < replacementList.length; i += 1) {
chars = replacementList[i].chars;
for (j = 0; j < chars.length; j += 1) {
Tomahawk.diacriticsMap[chars[j]] = replacementList[i].base;
}
}
})();
Tomahawk.removeDiacritics = function (str) {
return str.replace(/[^\u0000-\u007E]/g, function (c) {
return Tomahawk.diacriticsMap[c] || c;
});
};
Tomahawk.localStorage = Tomahawk.localStorage || {
setItem: function (key, value) {
window.localStorage[key] = value;
@@ -745,115 +201,24 @@ Tomahawk.base64Encode = function (b) {
return window.btoa(b);
};
Tomahawk.PluginManager = {
wrapperPrefix: '_adapter_',
objects: {},
objectCounter: 0,
identifyObject: function (object) {
if (!object.hasOwnProperty('id')) {
object.id = this.objectCounter++;
}
return object.id;
},
registerPlugin: function (type, object) {
this.objects[this.identifyObject(object)] = object;
Tomahawk.log("registerPlugin: " + type + " id: " + object.id);
Tomahawk.registerScriptPlugin(type, object.id);
},
unregisterPlugin: function (type, object) {
this.objects[this.identifyObject(object)] = object;
Tomahawk.log("unregisterPlugin: " + type + " id: " + object.id);
Tomahawk.unregisterScriptPlugin(type, object.id);
},
resolve: [],
invokeSync: function (requestId, objectId, methodName, params) {
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);
} else {
if (!this.objects[objectId][methodName]) {
Tomahawk.log("Function not found: " + methodName);
}
}
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](params);
},
invoke: function (requestId, objectId, methodName, params) {
RSVP.Promise.resolve(this.invokeSync(requestId, objectId, methodName, params))
.then(function (result) {
Tomahawk.reportScriptJobResults({
requestId: requestId,
data: result
});
}, function (error) {
Tomahawk.reportScriptJobResults({
requestId: requestId,
error: error
});
});
}
};
var encodeParamsToNativeFunctions = function(param) {
return param;
};
Tomahawk.NativeScriptJobManager = {
idCounter: 0,
deferreds: {},
invoke: function (methodName, params) {
params = params || {};
var requestId = this.idCounter++;
var deferred = RSVP.defer();
this.deferreds[requestId] = deferred;
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));;
return deferred.promise;
Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params));
this.deferreds[requestId] = RSVP.defer();
return this.deferreds[requestId].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];
}
};
Tomahawk.UrlType = {
Any: 0,
Playlist: 1,
Track: 2,
Album: 3,
Artist: 4,
XspfPlaylist: 5
};
<<<<<<< HEAD
Tomahawk.ConfigTestResultType = {
Other: 0,
@@ -1508,10 +873,6 @@ Tomahawk.Collection = {
});
},
revision: function (params) {
return RSVP.resolve();
},
_fuzzyIndexIdsToTracks: function (resultIds, id) {
if (typeof id === 'undefined') {
id = this.settings.id;
@@ -1563,14 +924,6 @@ 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);
@@ -1578,12 +931,7 @@ Tomahawk.Collection = {
search: function (params) {
var resultIds = Tomahawk.searchFuzzyIndex(params.query);
return this._fuzzyIndexIdsToTracks(resultIds).then(function(tracks) {
return {
tracks: tracks
};
});
return this._fuzzyIndexIdsToTracks(resultIds);
},
tracks: function (params, where) {
@@ -1627,7 +975,7 @@ Tomahawk.Collection = {
);
return t.execDeferredStatements();
}).then(function (results) {
return {tracks: results[0]};
return {results: resolverInstance._convertUrls(results[0])};
});
},
@@ -1801,13 +1149,73 @@ 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];
};
=======
>>>>>>> Move moar stuff over to es6 foo

View File

@@ -167,7 +167,5 @@
<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

@@ -24,6 +24,13 @@ include_directories(
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
if(APPLE)
# http://stackoverflow.com/questions/7226753/osx-lion-xcode-4-1-how-do-i-setup-a-c0x-project/7236451#7236451
add_definitions(-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__)
set(PLATFORM_SPECIFIC_LINK_LIBRARIES "/usr/lib/libc++.dylib")
else()
add_definitions(-std=c++0x)
endif()
tomahawk_add_plugin(hatchet
TYPE account

View File

@@ -69,13 +69,6 @@ Tomahawk::InfoSystem::XmppInfoPlugin::init()
}
const QString
Tomahawk::InfoSystem::XmppInfoPlugin::friendlyName() const
{
return "xmpp";
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
{

View File

@@ -38,8 +38,6 @@ namespace Tomahawk {
XmppInfoPlugin(XmppSipPlugin* parent);
virtual ~XmppInfoPlugin();
const QString friendlyName() const override;
signals:
void publishTune( QUrl url, Tomahawk::InfoSystem::InfoStringHash trackInfo );

View File

@@ -194,9 +194,9 @@ InfoSystem::InfoPluginPtr
XmppSipPlugin::infoPlugin()
{
if ( m_infoPlugin.isNull() )
m_infoPlugin = QSharedPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) );
m_infoPlugin = QPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) );
return m_infoPlugin;
return InfoSystem::InfoPluginPtr( m_infoPlugin.data() );
}
@@ -285,7 +285,7 @@ XmppSipPlugin::onConnect()
// load XmppInfoPlugin
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
@@ -359,6 +359,7 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason )
if ( !m_infoPlugin.isNull() )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_infoPlugin;
}
}

View File

@@ -128,7 +128,7 @@ private:
int m_currentPort;
QString m_currentResource;
QSharedPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin;
QPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin;
Tomahawk::Accounts::Account::ConnectionState m_state;
// sort out

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><p>Can you tell us what you were doing when this happened?</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></body></html>");
reporter.setReportData( "BuildID", CRASHREPORTER_BUILD_ID );
reporter.setReportData( "ProductName", CRASHREPORTER_PRODUCT_NAME );

View File

@@ -3,15 +3,17 @@ include_directories(
${Boost_INCLUDE_DIR}
)
if(WIN32 OR APPLE)
if(BUILD_GUI AND LibsnoreQt5_FOUND)
if(BUILD_GUI AND LIBSNORE_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::Libsnore
SOURCES "${snore_srcs}" LINK_LIBRARIES "${SNORE_LINK_LIBRARIES}"
)
endif()
endif(BUILD_GUI AND LIBSNORE_FOUND)
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-2015, Hannah von Reth <vonreth@kde.org>
* Copyright 2013-2014, Patrick von Reth <vonreth@kde.org>
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
@@ -27,15 +27,27 @@
#include "TomahawkVersion.h"
#include <libsnore/application.h>
#include <libsnore/notification/icon.h>
#include <snore/core/application.h>
#include <snore/core/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
{
@@ -49,13 +61,27 @@ SnoreNotifyPlugin::SnoreNotifyPlugin()
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped << InfoInboxReceived;
Snore::SnoreCore &snore = Snore::SnoreCore::instance();
snore.loadPlugins( Snore::SnorePlugin::Backend | Snore::SnorePlugin::SecondaryBackend );
snore.setDefaultSettingsValue("Silent", true, Snore::LocalSetting );
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();
m_application = Snore::Application( qApp->applicationName(), m_defaultIcon );
m_application.hints().setValue( "use-markup", true );
m_application.hints().setValue( "windows-app-id", TOMAHAWK_APPLICATION_PACKAGE_NAME );
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" ) );
@@ -64,10 +90,9 @@ SnoreNotifyPlugin::SnoreNotifyPlugin()
addAlert( InfoNowStopped, tr( "Playback Stopped" ) );
addAlert( InfoInboxReceived, tr( "You received a Song recommendation" ) );
snore.registerApplication( m_application );
snore.setDefaultApplication( m_application );
m_snore->registerApplication( m_application );
connect( &snore, SIGNAL( actionInvoked( Snore::Notification ) ), this, SLOT( slotActionInvoked( Snore::Notification ) ) );
connect( m_snore, SIGNAL( actionInvoked( Snore::Notification ) ), this, SLOT( slotActionInvoked( Snore::Notification ) ) );
}
@@ -75,7 +100,8 @@ SnoreNotifyPlugin::~SnoreNotifyPlugin()
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
Snore::SnoreCore::instance().deregisterApplication( m_application );
m_snore->deregisterApplication( m_application );
m_snore->deleteLater();
}
void
@@ -85,19 +111,25 @@ 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." ), m_defaultIcon );
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." ) );
return;
case Tomahawk::InfoSystem::InfoNotifyUser:
notifyUser( Tomahawk::InfoSystem::InfoNotifyUser,pushData.infoPair.second.toString(), m_defaultIcon );
notifyUser( Tomahawk::InfoSystem::InfoNotifyUser,pushData.infoPair.second.toString() );
return;
case Tomahawk::InfoSystem::InfoNowStopped:
notifyUser( Tomahawk::InfoSystem::InfoNowStopped, tr( "%applicationName stopped playback." ), m_defaultIcon );
notifyUser( Tomahawk::InfoSystem::InfoNowStopped, tr( "%applicationName stopped playback." ) );
return;
case Tomahawk::InfoSystem::InfoNowPlaying:
@@ -125,10 +157,15 @@ 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 );
Snore::SnoreCore::instance().broadcastNotification( n );
m_snore->broadcastNotification( n );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "showing notification:" << messageText;
}
void
@@ -157,28 +194,41 @@ 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" ] ) );
// 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() );
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" ) ) );
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" ) ) );
// 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" ] ) );
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
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 );
}
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 = m_defaultIcon;
Snore::Icon image;
if ( map.contains( "cover" ) && map[ "cover" ].canConvert< QImage >() )
{
image = Snore::Icon( QPixmap::fromImage( map[ "cover" ].value< QImage >() ) );
image = Snore::Icon( map[ "cover" ].value< QImage >() );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << image;
}
notifyUser( InfoNowPlaying, messageText, image );
@@ -209,15 +259,26 @@ SnoreNotifyPlugin::inboxReceived( const QVariant& input )
return;
QString messageText;
// 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" ) ) );
// 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" ) ) );
// Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text
messageText = QString( "<i></i>%1" ).arg( messageText );
// 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" ] );
}
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-2015, Hannah von Reth <vonreth@kde.org>
* Copyright 2013-2014, Patrick 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 <libsnore/snore.h>
#include <snore/core/snore.h>
namespace Tomahawk
{
@@ -63,8 +63,9 @@ protected slots:
void slotActionInvoked(Snore::Notification n);
private:
void notifyUser( InfoType type, const QString &messageText, Snore::Icon icon );
void notifyUser( InfoType type, const QString &messageText, Snore::Icon icon = Snore::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

@@ -34,7 +34,7 @@ public:
* Creates a Playdar HTTP interface
* @param ha Address to listen on
* @param port Port to listen on with HTTP
* @param sport Port to listen on with HTTPS
* @param sport Pot to listen on with HTTPS
* @param parent
*/
explicit PlaydarApi( QHostAddress ha, qint16 port, qint16 sport, QObject *parent = 0 );

View File

@@ -62,8 +62,6 @@ 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;
@@ -149,6 +147,7 @@ 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

@@ -33,7 +33,6 @@
#include <QReadWriteLock>
#include <QPixmapCache>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -76,7 +75,6 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr
}
album_ptr album = album_ptr( new Album( name, artist ), &Album::deleteLater );
album->moveToThread( QCoreApplication::instance()->thread() );
album->setWeakRef( album.toWeakRef() );
album->loadId( autoCreate );
s_albumsByName.insert( key, album );

View File

@@ -36,7 +36,6 @@
#include <QReadWriteLock>
#include <QPixmapCache>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -110,7 +109,6 @@ Artist::get( unsigned int id, const QString& name )
}
artist_ptr a = artist_ptr( new Artist( id, name ), &Artist::deleteLater );
a->moveToThread( QCoreApplication::instance()->thread() );
a->setWeakRef( a.toWeakRef() );
s_artistsByName.insert( key, a );

View File

@@ -67,7 +67,7 @@ AtticaManager::AtticaManager( QObject* parent )
connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) );
// resolvers
// m_manager.addProviderFile( QUrl( "http://v09.bakery.tomahawk-player.org/resolvers/providers.xml" ) );
// m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) );
const QString url = QString( "%1/resolvers/providers.xml?version=%2" ).arg( hostname() ).arg( TomahawkUtils::appFriendlyVersion() );
QNetworkReply* reply = Tomahawk::Utils::nam()->get( QNetworkRequest( QUrl( url ) ) );

View File

@@ -37,7 +37,6 @@ set( libGuiSources
jobview/ErrorStatusMessage.cpp
jobview/IndexingJobItem.cpp
jobview/InboxJobItem.cpp
jobview/ScriptErrorStatusMessage.cpp
playlist/InboxModel.cpp
playlist/InboxView.cpp
@@ -200,6 +199,7 @@ list(APPEND libSources
MetaPlaylistInterface.cpp
Query.cpp
Result.cpp
ResultProvider.cpp
Source.cpp
Track.cpp
TrackData.cpp
@@ -418,7 +418,7 @@ include_directories(
${Boost_INCLUDE_DIR}
${LIBPORTFWD_INCLUDE_DIR}
${QUAZIP_INCLUDE_DIRS}
${QUAZIP_INCLUDE_DIR}
${QTKEYCHAIN_INCLUDE_DIRS}
)
@@ -501,7 +501,7 @@ set_target_properties(
AUTOMOC TRUE
VERSION ${TOMAHAWK_VERSION_SHORT}
SOVERSION ${TOMAHAWK_VERSION_SHORT}
OUTPUT_NAME ${TOMAHAWK_BASE_TARGET_NAME}
OUTPUT_NAME ${TOMAHAWK_TARGET_NAME}
)
@@ -524,6 +524,7 @@ 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( "getStreamUrl", arguments );
Tomahawk::ScriptJob* job = collection->scriptObject()->invoke( "getStreamUrlPromise", arguments );
connect( job, SIGNAL( done(QVariantMap) ), SLOT( onUrlRetrieved(QVariantMap) ) );
job->start();
}

View File

@@ -175,7 +175,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) )
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
return true;
}
}
@@ -201,7 +201,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeTrack ) )
if ( resolver->canParseUrl( url, ExternalResolver::Track ) )
return true;
}
}
@@ -218,7 +218,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAlbum ) )
if ( resolver->canParseUrl( url, ExternalResolver::Album ) )
return true;
}
}
@@ -235,7 +235,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeArtist ) )
if ( resolver->canParseUrl( url, ExternalResolver::Artist ) )
return true;
}
}
@@ -263,14 +263,9 @@ bool
DropJob::validateLocalFiles(const QString &paths, const QString &suffix)
{
QStringList filePaths = paths.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
QStringList::iterator it = filePaths.begin();
while ( it != filePaths.end() )
{
for ( QStringList::iterator it = filePaths.begin(); it != filePaths.end(); ++it )
if ( !validateLocalFile( *it, suffix ) )
it = filePaths.erase( it );
else
++it;
}
filePaths.erase( it );
return !filePaths.isEmpty();
}
@@ -311,7 +306,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
// Check Scriptresolvers
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) )
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Accepting current drop as a playlist" << resolver->name();
return true;
@@ -768,7 +763,7 @@ DropJob::handleTrackUrls( const QString& urls )
{
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( track, ExternalResolver::UrlTypeAny ) )
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> ) ) );

View File

@@ -167,7 +167,7 @@ GlobalActionManager::openUrl( const QString& url )
QList< QPointer< ExternalResolver > > possibleResolvers;
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
{
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAny ) )
if ( resolver->canParseUrl( url, ExternalResolver::Any ) )
{
canParse = true;
possibleResolvers << resolver;

View File

@@ -149,15 +149,6 @@ Pipeline::removeResolver( Resolver* r )
}
QList< Tomahawk::Resolver* >
Pipeline::resolvers() const
{
Q_D( const Pipeline );
return d->resolvers;
}
void
Pipeline::addResolver( Resolver* r )
{
@@ -332,13 +323,6 @@ 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 )
{
@@ -349,7 +333,7 @@ Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr
{
if ( !results.isEmpty() )
{
Resolver* resolvedBy = results[0]->resolvedBy();
ResultProvider* resolvedBy = results[0]->resolvedBy();
if ( resolvedBy )
{
tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name();
@@ -595,8 +579,8 @@ Pipeline::shunt( const query_ptr& q )
// we get here if we disable a resolver while a query is resolving
// OR we are just out of resolvers while query is still resolving
// since we seem to at least tried to kick off all of the resolvers,
// remove the '.keep' entry
//since we seem to at least tried to kick off all of the resolvers,
//remove the '.keep' entry
decQIDState( q, nullptr );
return;
}
@@ -636,7 +620,7 @@ Pipeline::checkQIDState( const Tomahawk::query_ptr& query )
Q_D( Pipeline );
QMutexLocker lock( &d->mut );
tDebug() << Q_FUNC_INFO << query->id() << d->qidsState.count( query->id() );
tDebug() << Q_FUNC_INFO << " " << query->id() << " " << d->qidsState.count( query->id() );
if ( d->qidsState.contains( query->id() ) )
{

View File

@@ -54,7 +54,6 @@ 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 );
@@ -66,7 +65,6 @@ 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

@@ -33,7 +33,6 @@
#include <QtAlgorithms>
#include <QDebug>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -48,7 +47,6 @@ Query::get( const QString& artist, const QString& track, const QString& album, c
autoResolve = false;
query_ptr q = query_ptr( new Query( Track::get( artist, track, album ), qid, autoResolve ), &QObject::deleteLater );
q->moveToThread( QCoreApplication::instance()->thread() );
q->setWeakRef( q.toWeakRef() );
if ( autoResolve )
@@ -405,17 +403,12 @@ Query::resultSorter( const result_ptr& left, const result_ptr& right )
{
return false;
}
if ( left->isPreview() != right->isPreview() )
if ( !right->isPreview() )
{
return !left->isPreview();
return false;
}
if ( left->resolvedBy() != nullptr && right->resolvedBy() != nullptr )
{
return left->resolvedBy()->weight() > right->resolvedBy()->weight();
}
return left->id() > right->id();
return true;
}
if ( left->isPreview() != right->isPreview() )
@@ -643,11 +636,6 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
qTrackname = queryTrack()->trackSortname();
}
//Cleanup symbols for minor naming differences
qArtistname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
qTrackname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
qAlbumname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
// normal edit distance
const int artdist = TomahawkUtils::levenshtein( qArtistname, rArtistname );
const int trkdist = TomahawkUtils::levenshtein( qTrackname, rTrackname );

View File

@@ -35,8 +35,6 @@
#include "Track.h"
#include "Typedefs.h"
#include <QCoreApplication>
using namespace Tomahawk;
static QHash< QString, result_wptr > s_results;
@@ -71,7 +69,6 @@ Result::get( const QString& url, const track_ptr& track )
}
result_ptr r = result_ptr( new Result( url, track ), &Result::deleteLater );
r->moveToThread( QCoreApplication::instance()->thread() );
r->setWeakRef( r.toWeakRef() );
s_results.insert( url, r );
@@ -135,19 +132,11 @@ Result::deleteLater()
void
Result::onResolverRemoved( Tomahawk::Resolver* resolver )
{
m_mutex.lock();
if ( m_resolver.data() == resolver )
{
m_resolver = 0;
m_mutex.unlock();
emit statusChanged();
}
else
{
m_mutex.unlock();
}
}
@@ -161,8 +150,6 @@ Result::resolvedByCollection() const
QString
Result::url() const
{
QMutexLocker lock( &m_mutex );
return m_url;
}
@@ -170,8 +157,6 @@ Result::url() const
bool
Result::checked() const
{
QMutexLocker lock( &m_mutex );
return m_checked;
}
@@ -179,8 +164,6 @@ Result::checked() const
bool
Result::isPreview() const
{
QMutexLocker lock( &m_mutex );
return m_isPreview;
}
@@ -188,8 +171,6 @@ Result::isPreview() const
QString
Result::mimetype() const
{
QMutexLocker lock( &m_mutex );
return m_mimetype;
}
@@ -197,8 +178,6 @@ Result::mimetype() const
RID
Result::id() const
{
QMutexLocker lock( &m_mutex );
if ( m_rid.isEmpty() )
m_rid = uuid();
@@ -215,8 +194,6 @@ Result::isOnline() const
}
else
{
QMutexLocker lock( &m_mutex );
return !m_resolver.isNull();
}
}
@@ -237,26 +214,24 @@ Result::playable() const
QVariant
Result::toVariant() const
{
track_ptr t = track();
QVariantMap m;
m.insert( "artist", t->artist() );
m.insert( "album", t->album() );
m.insert( "track", t->track() );
m.insert( "artist", m_track->artist() );
m.insert( "album", m_track->album() );
m.insert( "track", m_track->track() );
m.insert( "source", friendlySource() );
m.insert( "mimetype", mimetype() );
m.insert( "size", size() );
m.insert( "bitrate", bitrate() );
m.insert( "duration", t->duration() );
m.insert( "duration", m_track->duration() );
// m.insert( "score", score() );
m.insert( "sid", id() );
m.insert( "discnumber", t->discnumber() );
m.insert( "albumpos", t->albumpos() );
m.insert( "discnumber", m_track->discnumber() );
m.insert( "albumpos", m_track->albumpos() );
m.insert( "preview", isPreview() );
m.insert( "purchaseUrl", purchaseUrl() );
if ( !t->composer().isEmpty() )
m.insert( "composer", t->composer() );
if ( !m_track->composer().isEmpty() )
m.insert( "composer", m_track->composer() );
return m;
}
@@ -265,25 +240,20 @@ Result::toVariant() const
QString
Result::toString() const
{
m_mutex.lock();
track_ptr track = m_track;
QString url = m_url;
m_mutex.unlock();
if ( track )
if ( m_track )
{
return QString( "Result(%1) %2 - %3%4 (%5)" )
.arg( id() )
.arg( track->artist() )
.arg( track->track() )
.arg( track->album().isEmpty() ? QString() : QString( " on %1" ).arg( track->album() ) )
.arg( url );
.arg( m_track->artist() )
.arg( m_track->track() )
.arg( m_track->album().isEmpty() ? QString() : QString( " on %1" ).arg( m_track->album() ) )
.arg( m_url );
}
else
{
return QString( "Result(%1) (%2)" )
.arg( id() )
.arg( url );
.arg( m_url );
}
}
@@ -291,8 +261,6 @@ Result::toString() const
Tomahawk::query_ptr
Result::toQuery()
{
QMutexLocker l( &m_mutex );
if ( m_query.isNull() )
{
query_ptr query = Tomahawk::Query::get( m_track );
@@ -302,15 +270,12 @@ Result::toQuery()
m_query = query->weakRef();
QList<Tomahawk::result_ptr> rl;
rl << m_ownRef.toStrongRef();
m_mutex.unlock();
rl << weakRef().toStrongRef();
query->addResults( rl );
m_mutex.lock();
query->setResolveFinished( true );
return query;
}
return m_query.toStrongRef();
}
@@ -330,10 +295,9 @@ Result::onOffline()
void
Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection, bool emitOnlineEvents )
Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection , bool emitOnlineEvents )
{
m_collection = collection;
if ( emitOnlineEvents )
{
Q_ASSERT( !collection.isNull() );
@@ -347,8 +311,6 @@ Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection, boo
void
Result::setFriendlySource( const QString& s )
{
QMutexLocker lock( &m_mutex );
m_friendlySource = s;
}
@@ -356,8 +318,6 @@ Result::setFriendlySource( const QString& s )
void
Result::setPreview( bool isPreview )
{
QMutexLocker lock( &m_mutex );
m_isPreview = isPreview;
}
@@ -365,8 +325,6 @@ Result::setPreview( bool isPreview )
void
Result::setPurchaseUrl( const QString& u )
{
QMutexLocker lock( &m_mutex );
m_purchaseUrl = u;
}
@@ -374,8 +332,6 @@ Result::setPurchaseUrl( const QString& u )
void
Result::setLinkUrl( const QString& u )
{
QMutexLocker lock( &m_mutex );
m_linkUrl = u;
}
@@ -383,8 +339,6 @@ Result::setLinkUrl( const QString& u )
void
Result::setChecked( bool checked )
{
QMutexLocker lock( &m_mutex );
m_checked = checked;
}
@@ -392,8 +346,6 @@ Result::setChecked( bool checked )
void
Result::setMimetype( const QString& mimetype )
{
QMutexLocker lock( &m_mutex );
m_mimetype = mimetype;
}
@@ -401,8 +353,6 @@ Result::setMimetype( const QString& mimetype )
void
Result::setBitrate( unsigned int bitrate )
{
QMutexLocker lock( &m_mutex );
m_bitrate = bitrate;
}
@@ -410,8 +360,6 @@ Result::setBitrate( unsigned int bitrate )
void
Result::setSize( unsigned int size )
{
QMutexLocker lock( &m_mutex );
m_size = size;
}
@@ -419,8 +367,6 @@ Result::setSize( unsigned int size )
void
Result::setModificationTime( unsigned int modtime )
{
QMutexLocker lock( &m_mutex );
m_modtime = modtime;
}
@@ -428,8 +374,6 @@ Result::setModificationTime( unsigned int modtime )
void
Result::setTrack( const track_ptr& track )
{
QMutexLocker lock( &m_mutex );
m_track = track;
}
@@ -437,8 +381,6 @@ Result::setTrack( const track_ptr& track )
unsigned int
Result::fileId() const
{
QMutexLocker lock( &m_mutex );
return m_fileId;
}
@@ -448,8 +390,6 @@ Result::friendlySource() const
{
if ( resolvedByCollection().isNull() )
{
QMutexLocker lock( &m_mutex );
return m_friendlySource;
}
else
@@ -460,8 +400,6 @@ Result::friendlySource() const
QString
Result::purchaseUrl() const
{
QMutexLocker lock( &m_mutex );
return m_purchaseUrl;
}
@@ -469,8 +407,6 @@ Result::purchaseUrl() const
QString
Result::linkUrl() const
{
QMutexLocker lock( &m_mutex );
return m_linkUrl;
}
@@ -480,8 +416,6 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c
{
if ( resolvedByCollection().isNull() )
{
//QMutexLocker lock( &m_mutex );
const ExternalResolver* resolver = qobject_cast< ExternalResolver* >( m_resolver.data() );
if ( !resolver )
{
@@ -532,8 +466,6 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c
unsigned int
Result::bitrate() const
{
QMutexLocker lock( &m_mutex );
return m_bitrate;
}
@@ -541,8 +473,6 @@ Result::bitrate() const
unsigned int
Result::size() const
{
QMutexLocker lock( &m_mutex );
return m_size;
}
@@ -550,8 +480,6 @@ Result::size() const
unsigned int
Result::modificationTime() const
{
QMutexLocker lock( &m_mutex );
return m_modtime;
}
@@ -559,17 +487,13 @@ Result::modificationTime() const
void
Result::setFileId( unsigned int id )
{
QMutexLocker lock( &m_mutex );
m_fileId = id;
}
Tomahawk::Resolver*
Tomahawk::ResultProvider*
Result::resolvedBy() const
{
QMutexLocker lock( &m_mutex );
if ( !m_collection.isNull() )
return m_collection.data();
@@ -580,16 +504,12 @@ Result::resolvedBy() const
void
Result::setResolvedByResolver( Tomahawk::Resolver* resolver )
{
QMutexLocker lock( &m_mutex );
m_resolver = QPointer< Tomahawk::Resolver >( resolver );
}
QPointer< Resolver > Result::resolvedByResolver() const
{
QMutexLocker lock( &m_mutex );
return m_resolver;
}
@@ -605,29 +525,16 @@ Result::doneEditing()
track_ptr
Result::track() const
{
QMutexLocker lock( &m_mutex );
return m_track;
}
QList< DownloadFormat >
Result::downloadFormats() const
{
QMutexLocker lock( &m_mutex );
return m_formats;
}
void
Result::setDownloadFormats( const QList<DownloadFormat>& formats )
{
if ( formats.isEmpty() )
return;
QMutexLocker lock( &m_mutex );
m_formats.clear();
foreach ( const DownloadFormat& format, formats )
{
@@ -655,7 +562,7 @@ Result::setDownloadFormats( const QList<DownloadFormat>& formats )
void
Result::onSettingsChanged()
{
if ( TomahawkSettings::instance()->downloadsPreferredFormat().toLower() != downloadFormats().first().extension.toLower() )
if ( TomahawkSettings::instance()->downloadsPreferredFormat().toLower() != m_formats.first().extension.toLower() )
{
setDownloadFormats( downloadFormats() );
emit updated();
@@ -692,8 +599,6 @@ Result::onDownloadJobStateChanged( DownloadJob::TrackState newState, DownloadJob
QWeakPointer<Result>
Result::weakRef()
{
QMutexLocker lock( &m_mutex );
return m_ownRef;
}
@@ -701,7 +606,5 @@ Result::weakRef()
void
Result::setWeakRef( QWeakPointer<Result> weakRef )
{
QMutexLocker lock( &m_mutex );
m_ownRef = weakRef;
}

View File

@@ -21,6 +21,7 @@
#ifndef RESULT_H
#define RESULT_H
#include "ResultProvider.h"
#include "DownloadJob.h"
#include "utils/TomahawkUtils.h"
#include "Typedefs.h"
@@ -31,7 +32,6 @@
#include <QPixmap>
#include <QPointer>
#include <QVariant>
#include <QMutex>
class MetadataEditor;
@@ -86,9 +86,9 @@ public:
void setResolvedByResolver( Tomahawk::Resolver* resolver );
/**
* TODO: Make this a smart pointer
* This is very bad. ResultProvider is not a QObject and thus can not be tracked by a qt smart pointer ... :-(
*/
Resolver* resolvedBy() const;
ResultProvider* resolvedBy() const;
RID id() const;
bool isOnline() const;
@@ -132,7 +132,7 @@ public:
track_ptr track() const;
QList< DownloadFormat > downloadFormats() const;
QList<DownloadFormat> downloadFormats() const { return m_formats; }
void setDownloadFormats( const QList<DownloadFormat>& formats );
downloadjob_ptr downloadJob() const { return m_downloadJob; }
@@ -162,8 +162,6 @@ private:
explicit Result( const QString& url, const Tomahawk::track_ptr& track );
explicit Result();
mutable QMutex m_mutex;
mutable RID m_rid;
collection_wptr m_collection;
QPointer< Tomahawk::Resolver > m_resolver;

View File

@@ -1,10 +1,10 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015 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
* 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,
@@ -15,23 +15,10 @@
* 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"
#include "ScriptErrorStatusMessage.h"
#include "../utils/Logger.h"
using namespace Tomahawk;
ScriptErrorStatusMessage::ScriptErrorStatusMessage( const QString& message, Tomahawk::ScriptAccount* account )
: ErrorStatusMessage( tr( "Script Error: %1" ).arg( message ) )
, m_account( account )
ResultProvider::~ResultProvider()
{
}
void
ScriptErrorStatusMessage::activated()
{
if ( m_account.isNull() )
return;
tDebug() << "ScriptErrorStatusMessage clicked: " << mainText() << m_account->name();
m_account->showDebugger();
}

View File

@@ -1,10 +1,10 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015 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
* 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,
@@ -15,25 +15,28 @@
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTERRORSTATUSMESSAGE_H
#define SCRIPTERRORSTATUSMESSAGE_H
#include "ErrorStatusMessage.h"
#include "../resolvers/ScriptAccount.h"
#pragma once
#ifndef TOMAHAWK_RESULTPROVIDER_H
#define TOMAHAWK_RESULTPROVIDER_H
#include "DllMacro.h"
class DLLEXPORT ScriptErrorStatusMessage : public ErrorStatusMessage
class QPixmap;
class QString;
class QSize;
namespace Tomahawk
{
class DLLEXPORT ResultProvider
{
Q_OBJECT
public:
explicit ScriptErrorStatusMessage( const QString& scriptErrorMessage, Tomahawk::ScriptAccount* );
virtual ~ResultProvider();
void activated() override;
private:
QPointer< Tomahawk::ScriptAccount > m_account;
virtual QString name() const = 0;
virtual QPixmap icon( const QSize& size ) const = 0;
};
#endif // SCRIPTERRORSTATUSMESSAGE_H
}
#endif // TOMAHAWK_RESULTPROVIDER_H

View File

@@ -37,7 +37,6 @@
#include <QtAlgorithms>
#include <QDateTime>
#include <QReadWriteLock>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -93,7 +92,6 @@ Track::get( const QString& artist, const QString& track, const QString& album, c
}
track_ptr t = track_ptr( new Track( artist, track, album, albumArtist, duration, composer, albumpos, discnumber ), &Track::deleteLater );
t->moveToThread( QCoreApplication::instance()->thread() );
t->setWeakRef( t.toWeakRef() );
s_tracksByName.insert( key, t );

View File

@@ -19,7 +19,8 @@
#include "TrackData.h"
#include <QtAlgorithms>
#include <QReadWriteLock>
#include "audio/AudioEngine.h"
#include "collection/Collection.h"
@@ -40,10 +41,6 @@
#include "PlaylistEntry.h"
#include "SourceList.h"
#include <QtAlgorithms>
#include <QReadWriteLock>
#include <QCoreApplication>
using namespace Tomahawk;
QHash< QString, trackdata_wptr > TrackData::s_trackDatasByName = QHash< QString, trackdata_wptr >();
@@ -87,7 +84,6 @@ TrackData::get( unsigned int id, const QString& artist, const QString& track )
}
trackdata_ptr t = trackdata_ptr( new TrackData( id, artist, track ), &TrackData::deleteLater );
t->moveToThread( QCoreApplication::instance()->thread() );
t->setWeakRef( t.toWeakRef() );
s_trackDatasByName.insert( key, t );

View File

@@ -253,7 +253,7 @@ namespace Tomahawk
typedef QHash< QString, QString > InfoStringHash;
typedef QPair< QVariantMap, QVariant > PushInfoPair;
typedef QSharedPointer< InfoPlugin > InfoPluginPtr;
typedef QPointer< InfoPlugin > InfoPluginPtr;
}
namespace Network

View File

@@ -76,8 +76,6 @@ 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

@@ -79,7 +79,7 @@ ResolverAccountFactory::createFromPath( const QString& path )
}
ResolverAccount*
Account*
ResolverAccountFactory::createFromPath( const QString& path, const QString& factory, bool isAttica )
{
qDebug() << "Creating ResolverAccount from path:" << path << "is attica" << isAttica;
@@ -110,8 +110,57 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact
if ( pathInfo.suffix() == "axe" )
{
if ( !installAxe( realPath, configuration ) )
QString uniqueName = uuid();
QDir dir( TomahawkUtils::extractScriptPayload( pathInfo.filePath(),
uniqueName,
MANUALRESOLVERS_DIR ) );
if ( !( dir.exists() && dir.isReadable() ) ) //decompression fubar
{
displayError( tr( "Resolver installation error: cannot open bundle." ) );
return 0;
}
if ( !dir.cd( "content" ) ) //more fubar
{
displayError( tr( "Resolver installation error: incomplete bundle." ) );
return 0;
}
QString metadataFilePath = dir.absoluteFilePath( "metadata.json" );
configuration = metadataFromJsonFile( metadataFilePath );
configuration[ "bundleDir" ] = uniqueName;
if ( !configuration[ "pluginName" ].isNull() && !configuration[ "pluginName" ].toString().isEmpty() )
{
dir.cdUp();
if ( !dir.cdUp() ) //we're in MANUALRESOLVERS_DIR
return 0;
QString name = configuration[ "pluginName" ].toString();
QString namePath = dir.absoluteFilePath( name );
QFileInfo npI( namePath );
if ( npI.exists() && npI.isDir() )
{
TomahawkUtils::removeDirectory( namePath );
}
dir.rename( uniqueName, name );
configuration[ "bundleDir" ] = name;
if ( !dir.cd( QString( "%1/content" ).arg( name ) ) ) //should work if it worked once
return 0;
}
expandPaths( dir, configuration );
realPath = configuration[ "path" ].toString();
if ( realPath.isEmpty() )
{
displayError( tr( "Resolver installation error: bad metadata in bundle." ) );
return 0;
}
}
@@ -172,71 +221,6 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact
}
bool
ResolverAccountFactory::installAxe( QString& realPath, QVariantHash& configuration )
{
const QFileInfo pathInfo( realPath );
QString uniqueName = uuid();
QDir dir( TomahawkUtils::extractScriptPayload( pathInfo.filePath(),
uniqueName,
MANUALRESOLVERS_DIR ) );
if ( !( dir.exists() && dir.isReadable() ) ) //decompression fubar
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: cannot open bundle." ) ) );
return 0;
}
if ( !dir.cd( "content" ) ) //more fubar
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: incomplete bundle." ) ) );
return 0;
}
QString metadataFilePath = dir.absoluteFilePath( "metadata.json" );
configuration = metadataFromJsonFile( metadataFilePath );
configuration[ "bundleDir" ] = uniqueName;
if ( !configuration[ "pluginName" ].isNull() && !configuration[ "pluginName" ].toString().isEmpty() )
{
dir.cdUp();
if ( !dir.cdUp() ) //we're in MANUALRESOLVERS_DIR
return 0;
QString name = configuration[ "pluginName" ].toString();
QString namePath = dir.absoluteFilePath( name );
QFileInfo npI( namePath );
if ( npI.exists() && npI.isDir() )
{
TomahawkUtils::removeDirectory( namePath );
}
dir.rename( uniqueName, name );
configuration[ "bundleDir" ] = name;
if ( !dir.cd( QString( "%1/content" ).arg( name ) ) ) //should work if it worked once
return 0;
}
expandPaths( dir, configuration );
realPath = configuration[ "path" ].toString();
if ( realPath.isEmpty() )
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: bad metadata in bundle." ) ) );
return 0;
}
return true;
}
QVariantHash
ResolverAccountFactory::metadataFromJsonFile( const QString& path )
{
@@ -533,15 +517,14 @@ ResolverAccount::removeBundle()
}
void
ResolverAccount::testConfig()
void ResolverAccount::testConfig()
{
// HACK: move to JSAccount once we have that properly
JSResolver* resolver = qobject_cast< Tomahawk::JSResolver* >( m_resolver );
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();
}
@@ -552,13 +535,6 @@ ResolverAccount::testConfig()
}
ExternalResolverGui*
ResolverAccount::resolver() const
{
return m_resolver;
}
void
ResolverAccount::onTestConfig( const QVariantMap& result )
{

View File

@@ -33,8 +33,6 @@ class ExternalResolverGui;
namespace Accounts {
class ResolverAccount;
class DLLEXPORT ResolverAccountFactory : public AccountFactory
{
Q_OBJECT
@@ -55,10 +53,7 @@ public:
Account* createFromPath( const QString& path ) override;
// Internal use
static ResolverAccount* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
// YES, non const parameters!
static bool installAxe( QString& realPath, QVariantHash& configuration );
static Account* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
private:
static void displayError( const QString& error );
@@ -106,8 +101,6 @@ public:
void testConfig() override;
ExternalResolverGui* resolver() const;
private slots:
void resolverChanged();
void onTestConfig( const QVariantMap& result );

View File

@@ -71,7 +71,7 @@ LastFmAccount::LastFmAccount( const QString& accountId )
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
}
@@ -82,6 +82,7 @@ LastFmAccount::~LastFmAccount()
if ( m_infoPlugin )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_infoPlugin;
}
delete m_resolver.data();
@@ -167,11 +168,9 @@ InfoPluginPtr
LastFmAccount::infoPlugin()
{
if ( m_infoPlugin.isNull() )
{
m_infoPlugin = QSharedPointer< LastFmInfoPlugin>( new LastFmInfoPlugin( this ) );
}
m_infoPlugin = QPointer< LastFmInfoPlugin >( new LastFmInfoPlugin( this ) );
return m_infoPlugin;
return InfoPluginPtr( m_infoPlugin.data() );
}
bool

View File

@@ -105,7 +105,7 @@ private:
void hookupResolver();
QPointer<Tomahawk::ExternalResolverGui> m_resolver;
QSharedPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QPointer<LastFmConfig> m_configWidget;
};

View File

@@ -65,6 +65,13 @@ 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)
@@ -79,17 +86,6 @@ 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

@@ -116,12 +116,6 @@ SpotifyAccount::~SpotifyAccount()
{
clearUser();
if ( m_infoPlugin )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
}
if ( m_spotifyResolver.isNull() )
return;
@@ -141,7 +135,7 @@ SpotifyAccount::init()
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
@@ -372,10 +366,10 @@ SpotifyAccount::infoPlugin()
{
if ( m_infoPlugin.isNull() )
{
m_infoPlugin = QSharedPointer< InfoSystem::SpotifyInfoPlugin >( new InfoSystem::SpotifyInfoPlugin( this ) );
m_infoPlugin = QPointer< InfoSystem::SpotifyInfoPlugin >( new InfoSystem::SpotifyInfoPlugin( this ) );
}
return m_infoPlugin;
return InfoSystem::InfoPluginPtr( m_infoPlugin.data() );
}

View File

@@ -170,7 +170,7 @@ private:
QPointer<SpotifyAccountConfig> m_configWidget;
QPointer<QWidget> m_aboutWidget;
QPointer<ScriptResolver> m_spotifyResolver;
QSharedPointer< InfoSystem::SpotifyInfoPlugin > m_infoPlugin;
QPointer< InfoSystem::SpotifyInfoPlugin > m_infoPlugin;
QMap<QString, QPair<QObject*, QString> > m_qidToSlotMap;
QMap<QString, QVariant > m_qidToExtraData;

View File

@@ -277,7 +277,7 @@ SpotifyAccountConfig::showLoggedIn()
m_ui->verticalLayout->insertWidget( 1, m_loggedInUser, 0, Qt::AlignCenter );
}
qDebug() << "Showing logged in with username:" << m_verifiedUsername;
qDebug() << "Showing logged in withuserame:" << m_verifiedUsername;
m_loggedInUser->show();
m_loggedInUser->setText( tr( "Logged in as %1" ).arg( m_verifiedUsername ) );

View File

@@ -32,8 +32,6 @@
#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"
@@ -42,7 +40,6 @@
#include "SourceList.h"
#include "TomahawkSettings.h"
#include "UrlHandler.h"
#include "resolvers/ScriptJob.h"
#include <QDir>
@@ -577,64 +574,16 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
setCurrentTrack( result );
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 ) ) )
if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && !TomahawkUtils::isHttpResult( d->currentTrack->url() )
&& !TomahawkUtils::isRtmpResult( d->currentTrack->url() ) )
{
// Not an http(s) or RTMP URL, get IO device
QSharedPointer< QIODevice > sp;
performLoadIODevice( result, streamUrl );
performLoadIODevice( d->currentTrack, d->currentTrack->url() );
}
else
{
// 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 );
QSharedPointer< QIODevice > io;
performLoadTrack( result, result->url(), io );
}
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 );
}
@@ -669,7 +618,7 @@ AudioEngine::performLoadIODevice( const result_ptr& result, const QString& url )
void
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io )
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io )
{
if ( QThread::currentThread() != thread() )
{

View File

@@ -28,7 +28,6 @@
#include "DllMacro.h"
class NetworkReply;
class AudioEnginePrivate;
class DLLEXPORT AudioEngine : public QObject
@@ -181,12 +180,8 @@ 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 performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
void loadPreviousTrack();
void loadNextTrack();

View File

@@ -35,7 +35,7 @@ using namespace Tomahawk;
Collection::Collection( const source_ptr& source, const QString& name, QObject* parent )
: Resolver( parent )
: QObject( 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 "../resolvers/Resolver.h"
#include "../ResultProvider.h"
#include "DllMacro.h"
@@ -46,7 +46,7 @@
namespace Tomahawk
{
class DLLEXPORT Collection : public Resolver
class DLLEXPORT Collection : public QObject, public ResultProvider
{
Q_OBJECT

View File

@@ -251,32 +251,3 @@ 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,11 +65,6 @@ 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

@@ -277,8 +277,8 @@ InfoSystem::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() )
{
tLog() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread(): " << plugin->friendlyName();
Q_ASSERT( false );
tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()";
return;
}

View File

@@ -58,7 +58,13 @@ InfoSystemWorker::InfoSystemWorker()
InfoSystemWorker::~InfoSystemWorker()
{
tDebug() << Q_FUNC_INFO;
tDebug() << Q_FUNC_INFO << " beginning";
Q_FOREACH( InfoPluginPtr plugin, m_plugins )
{
if( plugin )
delete plugin.data();
}
tDebug() << Q_FUNC_INFO << " finished";
}
@@ -130,6 +136,29 @@ InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
emit updatedSupportedGetTypes( QSet< InfoType >::fromList( m_infoGetMap.keys() ) );
emit updatedSupportedPushTypes( QSet< InfoType >::fromList( m_infoPushMap.keys() ) );
connect( plugin.data(), SIGNAL( destroyed( QObject* ) ), SLOT( onInfoPluginDeleted() ) );
}
void
InfoSystemWorker::onInfoPluginDeleted()
{
foreach( const InfoPluginPtr& plugin, m_plugins )
{
if ( plugin.isNull() )
{
m_plugins.removeOne( plugin );
foreach( InfoType type, m_infoGetMap.keys() )
{
m_infoGetMap[type].removeOne( plugin );
}
foreach( InfoType type, m_infoPushMap.keys() )
{
m_infoPushMap[type].removeOne( plugin );
}
}
}
}

View File

@@ -72,6 +72,7 @@ public slots:
private slots:
void checkTimeoutsTimerFired();
void onInfoPluginDeleted();
private:
void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes );

View File

@@ -33,12 +33,6 @@ JobStatusItem::~JobStatusItem()
}
void
JobStatusItem::activated()
{
}
bool
JobStatusItem::allowMultiLine() const
{

View File

@@ -53,8 +53,6 @@ public:
virtual QString mainText() const = 0;
virtual QString rightColumnText() const { return QString(); };
virtual void activated();
/**
* If collapse item is true, sending multiple items of the same type will "collapse" them into one
* instead of showing each individually. In this case, the right column from the item will be ignored

View File

@@ -118,7 +118,6 @@ JobStatusView::setModel( JobStatusSortModel* m )
connect( m_view->model(), SIGNAL( customDelegateJobInserted( int, JobStatusItem* ) ), this, SLOT( customDelegateJobInserted( int, JobStatusItem* ) ) );
connect( m_view->model(), SIGNAL( customDelegateJobRemoved( int ) ), this, SLOT( customDelegateJobRemoved( int ) ) );
connect( m_view->model(), SIGNAL( refreshDelegates() ), this, SLOT( refreshDelegates() ) );
connect( m_view, SIGNAL( activated( QModelIndex ) ), this, SLOT( onItemActivated( QModelIndex ) ) );
foreach ( const QPointer<JobStatusItem> item, s_jobItems )
{
@@ -185,21 +184,6 @@ JobStatusView::refreshDelegates()
}
void
JobStatusView::onItemActivated( const QModelIndex& index )
{
QVariant itemVar = index.data( JobStatusModel::JobDataRole );
if ( !itemVar.canConvert< JobStatusItem* >() || !itemVar.value< JobStatusItem* >() )
{
tLog() << Q_FUNC_INFO << "unable to fetch JobStatusItem*";
return;
}
JobStatusItem* item = itemVar.value< JobStatusItem* >();
item->activated();
}
void
JobStatusView::checkCount()
{

View File

@@ -59,7 +59,6 @@ private slots:
void customDelegateJobInserted( int row, JobStatusItem* item );
void customDelegateJobRemoved( int row );
void refreshDelegates();
void onItemActivated( const QModelIndex& index );
private:
QListView* m_view;

View File

@@ -118,10 +118,6 @@ ColumnView::setModel( QAbstractItemModel* model )
void
ColumnView::setTreeModel( TreeModel* model )
{
// HACK: without setting a new preview widget, the old preview widget is shown in the first column while loading the artists.
m_previewWidget = new ColumnViewPreviewWidget( this );
setPreviewWidget( m_previewWidget );
m_model = model;
if ( m_proxyModel )

View File

@@ -96,10 +96,8 @@ ContextView::ContextView( QWidget* parent, const QString& caption )
connect( m_captionLabel, SIGNAL( clicked() ), SIGNAL( closeClicked() ) );
connect( m_trackView, SIGNAL( querySelected( Tomahawk::query_ptr ) ), SLOT( onQuerySelected( Tomahawk::query_ptr ) ) );
connect( m_trackView, SIGNAL( modelChanged() ), SLOT( onModelChanged() ) );
connect( m_trackView, SIGNAL( querySelected( Tomahawk::query_ptr ) ), m_detailView, SLOT( setQuery( Tomahawk::query_ptr ) ) );
connect( m_detailView, SIGNAL( downloadAll() ), SLOT( onDownloadAll() ) );
connect( m_detailView, SIGNAL( downloadCancel() ), SLOT( onDownloadCancel() ) );
connect( m_trackView, SIGNAL( modelChanged() ), SLOT( onModelChanged() ) );
TomahawkUtils::fixMargins( this );
}

View File

@@ -20,15 +20,11 @@
#include "GridItemDelegate.h"
#include <QApplication>
#include <QDesktopServices>
#include <QPainter>
#include <QAbstractItemView>
#include <QMouseEvent>
#include <QTimeLine>
#include "DownloadManager.h"
#include "DownloadJob.h"
#include "Artist.h"
#include "Query.h"
#include "Result.h"
@@ -39,14 +35,12 @@
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "widgets/HoverControls.h"
#include "widgets/DropDownButton.h"
#include "widgets/ImageButton.h"
#include "utils/TomahawkStyle.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/PixmapDelegateFader.h"
#include "utils/Closure.h"
#include "utils/AnimatedSpinner.h"
#include "utils/WebPopup.h"
#include "utils/DpiScaler.h"
#include "utils/Logger.h"
@@ -61,7 +55,6 @@ GridItemDelegate::GridItemDelegate( QAbstractItemView* parent, PlayableProxyMode
, m_model( proxy )
, m_itemWidth( 0 )
, m_showPosition( false )
, m_showBuyButtons( false )
, m_wordWrapping( false )
, m_margin( TomahawkUtils::DpiScaler::scaledY( parent, 32 ) )
{
@@ -100,8 +93,7 @@ GridItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelInde
if ( !m_wordWrapping )
return QSize( m_itemWidth, m_itemWidth + fm.height() + m_margin * 0.8 );
const int buyButtonHeight = m_showBuyButtons ? 40 : 0;
return QSize( m_itemWidth, m_itemWidth + fm.height() + fms.height() + buyButtonHeight + m_margin * 0.8 );
return QSize( m_itemWidth, m_itemWidth + fm.height() + fms.height() + m_margin * 0.8 );
}
}
@@ -283,11 +275,6 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->setFont( f );
}
if ( m_showBuyButtons && !item->query().isNull() )
{
textRect.adjust( 0, 0, 0, -40 );
}
to.setAlignment( Qt::AlignLeft | Qt::AlignTop );
text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - m_margin / 4 );
painter->drawText( textRect, text, to );
@@ -299,7 +286,6 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
}
painter->restore();
painter->save();
painter->setOpacity( 0.6 );
painter->setFont( m_smallFont );
@@ -318,48 +304,6 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
// Calculate rect of artist on-hover button click area
m_artistNameRects[ index ] = painter->fontMetrics().boundingRect( textRect, Qt::AlignLeft | Qt::AlignBottom, text );
painter->restore();
if ( m_showBuyButtons && !item->query().isNull() )
{
QRect r = textRect;
r.setY( textRect.y() + textRect.height() + 8 );
r.setHeight( 32 );
m_buyButtonRects[ index ] = r;
QString text;
if ( item->result() &&
( ( !item->result()->downloadFormats().isEmpty() && !DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ).isEmpty() ) ||
( item->result()->downloadJob() && item->result()->downloadJob()->state() == DownloadJob::Finished ) ) )
{
text = tr( "View in Finder" );
}
else if ( item->query() && item->query()->numResults( true ) && !item->query()->results().first()->downloadFormats().isEmpty() )
{
text = tr( "Download %1" ).arg( item->query()->results().first()->downloadFormats().first().extension.toUpper() );
}
else if ( item->query()->numResults( true ) && !item->query()->results().first()->purchaseUrl().isEmpty() )
{
text = tr( "Buy" );
}
if ( !item->result() || !item->result()->downloadJob() )
{
if ( !text.isEmpty() )
DropDownButton::drawPrimitive( painter, r, text, m_hoveringOverBuyButton == index, false );
}
else
{
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND.darker() );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_BACKGROUND );
painter->drawRect( r.adjusted( 2, 2, -2, -2 ) );
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
QRect fillp = r.adjusted( 3, 3, -3, -3 );
fillp.setWidth( float(fillp.width()) * ( float(item->result()->downloadJob()->progressPercentage()) / 100.0 ) );
painter->drawRect( fillp );
}
}
}
painter->restore();
@@ -418,7 +362,6 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
bool hoveringArtist = false;
bool hoveringAlbum = false;
bool hoveringBuyButton = false;
if ( m_artistNameRects.contains( index ) )
{
const QRect artistNameRect = m_artistNameRects[ index ];
@@ -429,11 +372,6 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
const QRect albumNameRect = m_albumNameRects[ index ];
hoveringAlbum = albumNameRect.contains( ev->pos() );
}
if ( m_buyButtonRects.contains( index ) )
{
const QRect buyButtonRect = m_buyButtonRects[ index ];
hoveringBuyButton = buyButtonRect.contains( ev->pos() );
}
QRect coverRect = m_view->visualRect( index );
coverRect.setHeight( coverRect.width() );
@@ -441,7 +379,7 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringArtist || hoveringAlbum || hoveringBuyButton )
if ( hoveringArtist || hoveringAlbum )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
@@ -497,17 +435,6 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
emit updateIndex( index );
}
if ( m_hoveringOverBuyButton != index || ( !hoveringBuyButton && m_hoveringOverBuyButton.isValid() ) )
{
emit updateIndex( m_hoveringOverBuyButton );
if ( hoveringBuyButton )
m_hoveringOverBuyButton = index;
else
m_hoveringOverBuyButton = QPersistentModelIndex();
emit updateIndex( index );
}
if ( m_hoverIndex != index || !hoveringCover )
{
@@ -617,27 +544,6 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
}
}
if ( hoveringBuyButton )
{
if ( event->type() == QEvent::MouseButtonRelease )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
if ( !item )
return false;
if ( item->query() && item->query()->numResults( true ) && !item->query()->results().first()->downloadFormats().isEmpty() )
{
m_view->edit( index );
return true;
}
else
{
WebPopup* popup = new WebPopup( item->query()->results().first()->purchaseUrl(), QSize( 400, 800 ) );
connect( item->query()->results().first().data(), SIGNAL( destroyed() ), popup, SLOT( close() ) );
}
}
}
return false;
}
@@ -649,7 +555,6 @@ GridItemDelegate::modelChanged()
m_albumNameRects.clear();
m_hoveringOverArtist = QPersistentModelIndex();
m_hoveringOverAlbum = QPersistentModelIndex();
m_hoveringOverBuyButton = QPersistentModelIndex();
m_hoverIndex = QPersistentModelIndex();
clearButtons();
@@ -782,10 +687,6 @@ GridItemDelegate::resetHoverIndex()
idx = m_hoveringOverAlbum;
m_hoveringOverAlbum = QPersistentModelIndex();
doUpdateIndex( idx );
idx = m_hoveringOverBuyButton;
m_hoveringOverBuyButton = QPersistentModelIndex();
doUpdateIndex( idx );
}
@@ -857,97 +758,3 @@ GridItemDelegate::eventFilter( QObject* obj, QEvent* event )
else
return QObject::eventFilter( obj, event );
}
QWidget*
GridItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
if ( item->result() && !item->result()->downloadFormats().isEmpty() &&
!DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ).isEmpty() )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ) ).absolutePath() ) );
}
else if ( item->result() && item->result()->downloadJob() && item->result()->downloadJob()->state() == DownloadJob::Finished )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( item->result()->downloadJob()->localFile() ).absolutePath() ) );
}
else if ( item->result() &&
!item->result()->downloadFormats().isEmpty() && !item->result()->downloadJob() )
{
QStringList formats;
foreach ( const DownloadFormat& format, item->result()->downloadFormats() )
{
formats << tr( "Download %1" ).arg( format.extension.toUpper() );
}
DropDownButton* editor = new DropDownButton( parent );
editor->addItems( formats );
NewClosure( editor, SIGNAL( clicked() ),
const_cast<GridItemDelegate*>(this), SLOT( addDownloadJob( const QModelIndex&, QWidget* ) ), index, (QWidget*)editor );
NewClosure( editor, SIGNAL( activated( int ) ),
const_cast<GridItemDelegate*>(this), SLOT( addDownloadJob( const QModelIndex&, QWidget* ) ), index, (QWidget*)editor );
return editor;
}
return 0;
}
void
GridItemDelegate::addDownloadJob( const QModelIndex& index, QWidget* editor )
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
m_view->closePersistentEditor( index );
DropDownButton* cb = static_cast< DropDownButton* >(editor);
if ( !item->result()->downloadFormats().isEmpty() )
DownloadManager::instance()->addJob( item->result()->toDownloadJob( item->result()->downloadFormats().at( cb->currentIndex() ) ) );
}
void
GridItemDelegate::closeEditor( const QModelIndex& index, QWidget* editor )
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
m_view->closePersistentEditor( index );
DropDownButton* cb = static_cast< DropDownButton* >(editor);
editor->deleteLater();
}
void
GridItemDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyledItemDelegate::updateEditorGeometry( editor, option, index );
DropDownButton* comboBox = static_cast<DropDownButton*>(editor);
comboBox->resize( option.rect.size() - QSize( 8, 0 ) );
comboBox->move( option.rect.x() + 4, option.rect.y() );
if ( m_downloadDropDownRects.contains( index ) )
{
editor->setGeometry( m_downloadDropDownRects.value( index ) );
}
if ( !comboBox->property( "shownPopup" ).toBool() )
{
comboBox->showPopup();
comboBox->setProperty( "shownPopup", true );
}
}
void
GridItemDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
{
}

View File

@@ -48,7 +48,6 @@ public:
QSize itemSize() const;
void setItemWidth( int width ) { m_itemWidth = width; }
void setShowBuyButtons( bool enabled ) { m_showBuyButtons = enabled; }
public slots:
void resetHoverIndex();
@@ -62,10 +61,6 @@ protected:
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
bool eventFilter( QObject* obj, QEvent* event );
QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const;
signals:
void updateIndex( const QModelIndex& idx );
@@ -87,9 +82,6 @@ private slots:
void fadingFrameChanged( const QPersistentModelIndex& );
void fadingFrameFinished( const QPersistentModelIndex& );
void closeEditor( const QModelIndex& index, QWidget* editor );
void addDownloadJob( const QModelIndex& index, QWidget* editor );
private:
QTimeLine* createTimeline( QTimeLine::Direction direction, int startFrame = 0 );
void clearButtons();
@@ -98,19 +90,15 @@ private:
PlayableProxyModel* m_model;
int m_itemWidth;
bool m_showPosition;
bool m_showBuyButtons;
bool m_wordWrapping;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
mutable QHash< QPersistentModelIndex, QRect > m_albumNameRects;
mutable QHash< QPersistentModelIndex, QRect > m_buyButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_downloadDropDownRects;
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_covers;
QPersistentModelIndex m_hoverIndex;
QPersistentModelIndex m_hoveringOverArtist;
QPersistentModelIndex m_hoveringOverAlbum;
QPersistentModelIndex m_hoveringOverBuyButton;
mutable QHash< QPersistentModelIndex, QWidget* > m_spinner;
mutable QHash< QPersistentModelIndex, HoverControls* > m_hoverControls;

View File

@@ -255,8 +255,6 @@ void GridView::wheelEvent( QWheelEvent* e )
// ^ scroll step is 1/8 of the estimated row height
QListView::wheelEvent( e );
m_delegate->resetHoverIndex();
}

View File

@@ -119,9 +119,8 @@ int
PlayableModel::columnCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
Q_D( const PlayableModel );
return d->header.length();
return 13;
}
@@ -445,21 +444,14 @@ PlayableModel::flags( const QModelIndex& index ) const
if ( index.isValid() )
{
Qt::ItemFlags returnFlags = defaultFlags;
if ( index.column() == 0 )
{
returnFlags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}
else if ( index.column() == PlayableModel::Download )
{
returnFlags |= Qt::ItemIsEditable | defaultFlags;
return Qt::ItemIsEditable | defaultFlags;
}
if ( areAllColumnsEditable() ) {
returnFlags |= Qt::ItemIsEditable;
}
return returnFlags;
}
return Qt::ItemIsDropEnabled | defaultFlags;
@@ -1027,7 +1019,7 @@ PlayableModel::appendAlbums( const QList< Tomahawk::album_ptr >& albums )
void
PlayableModel::appendAlbums( const Tomahawk::collection_ptr& collection )
{
startLoading();
emit loadingStarted();
insertAlbums( collection, rowCount( QModelIndex() ) );
}
@@ -1042,7 +1034,7 @@ PlayableModel::appendQueries( const QList< Tomahawk::query_ptr >& queries )
void
PlayableModel::appendTracks( const QList< Tomahawk::track_ptr >& tracks, const QList< Tomahawk::PlaybackLog >& logs )
{
startLoading();
emit loadingStarted();
QList< Tomahawk::query_ptr > queries;
foreach ( const track_ptr& track, tracks )
{
@@ -1056,7 +1048,7 @@ PlayableModel::appendTracks( const QList< Tomahawk::track_ptr >& tracks, const Q
void
PlayableModel::appendTracks( const Tomahawk::collection_ptr& collection )
{
startLoading();
emit loadingStarted();
insertTracks( collection, rowCount( QModelIndex() ) );
}
@@ -1179,22 +1171,6 @@ PlayableModel::setIcon( const QPixmap& pixmap )
}
void
PlayableModel::setAllColumnsEditable( bool editable )
{
Q_D( PlayableModel );
d->areAllColumnsEditable = editable;
}
bool
PlayableModel::areAllColumnsEditable() const
{
Q_D( const PlayableModel );
return d->areAllColumnsEditable;
}
int
PlayableModel::trackCount() const
{

View File

@@ -96,11 +96,6 @@ public:
virtual QPixmap icon() const;
virtual void setIcon( const QPixmap& pixmap );
// HACK: we need to set column 0 editable for DropDownButton in TrackView
void setAllColumnsEditable( bool editable );
bool areAllColumnsEditable() const;
virtual int trackCount() const;
virtual int itemCount() const;

View File

@@ -38,7 +38,6 @@ public:
, rootItem( new PlayableItem( 0 ) )
, readOnly( true )
, loading( _loading )
, areAllColumnsEditable( false )
{
}
@@ -59,7 +58,6 @@ private:
QStringList header;
bool loading;
bool areAllColumnsEditable;
};
#endif // PLAYABLEMODEL_P_H

View File

@@ -607,7 +607,25 @@ PlayableProxyModel::columnCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
return m_headerStyle[ m_style ].length();
switch ( m_style )
{
case SingleColumn:
return 1;
break;
case Collection:
return 10;
break;
case Locker:
return 11;
break;
case Detailed:
default:
return 12;
break;
}
}
@@ -715,13 +733,6 @@ PlayableProxyModel::setFilter( const QString& pattern )
}
int
PlayableProxyModel::mapSourceColumnToColumn( PlayableModel::Columns column )
{
return m_headerStyle[ m_style ].indexOf( column );
}
void
PlayableProxyModel::setCurrentIndex( const QModelIndex& index )
{

View File

@@ -98,8 +98,6 @@ public:
virtual void setFilter( const QString& pattern );
virtual void updateDetailedInfo( const QModelIndex& index );
int mapSourceColumnToColumn( PlayableModel::Columns column );
signals:
void filterChanged( const QString& filter );

View File

@@ -23,7 +23,6 @@
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QDateTime>
#include <QDesktopServices>
#include <QMouseEvent>
#include <QPainter>
#include <QDesktopServices>
@@ -52,7 +51,6 @@
#include "utils/Closure.h"
#include "utils/TomahawkStyle.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/WebPopup.h"
#include "utils/Logger.h"
using namespace Tomahawk;
@@ -125,16 +123,16 @@ PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem&
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
if ( /*index.column() == PlayableModel::Download &&*/ item->result() && !item->result()->downloadFormats().isEmpty() &&
if ( index.column() == PlayableModel::Download && item->result() && !item->result()->downloadFormats().isEmpty() &&
!DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ).isEmpty() )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ) ).absolutePath() ) );
}
else if ( /*index.column() == PlayableModel::Download &&*/ item->result() && item->result()->downloadJob() && item->result()->downloadJob()->state() == DownloadJob::Finished )
else if ( index.column() == PlayableModel::Download && item->result() && item->result()->downloadJob() && item->result()->downloadJob()->state() == DownloadJob::Finished )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( item->result()->downloadJob()->localFile() ).absolutePath() ) );
}
else if ( /*index.column() == PlayableModel::Download &&*/ item->result() &&
else if ( index.column() == PlayableModel::Download && item->result() &&
!item->result()->downloadFormats().isEmpty() && !item->result()->downloadJob() )
{
QStringList formats;
@@ -194,14 +192,9 @@ PlaylistItemDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionV
comboBox->resize( option.rect.size() - QSize( 8, 0 ) );
comboBox->move( option.rect.x() + 4, option.rect.y() );
if ( m_downloadDropDownRects.contains( index ) )
{
editor->setGeometry( m_downloadDropDownRects.value( index ) );
}
if ( !comboBox->property( "shownPopup" ).toBool() )
{
comboBox->showPopup();
// comboBox->showPopup();
comboBox->setProperty( "shownPopup", true );
}
}
@@ -298,8 +291,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt
}
else if ( !item->result()->downloadJob() )
{
DropDownButton::drawPrimitive( painter, optc.rect, optc.currentText, hoveringOver() == index, true );
DropDownButton::drawPrimitive( painter, optc.rect, optc.currentText );
/* QApplication::style()->drawComplexControl( QStyle::CC_ComboBox, &optc, painter, 0 );
optc.rect.adjust( 4, 0, 0, 0 );
QApplication::style()->drawControl( QStyle::CE_ComboBoxLabel, &optc, painter, 0 );*/
@@ -612,7 +604,6 @@ QRect
PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QRect& rect, PlayableItem* item ) const
{
const track_ptr track = item->query()->track();
const bool hasOnlineResults = ( item->query()->numResults( true ) > 0 );
painter->save();
painter->setRenderHint( QPainter::TextAntialiasing );
@@ -639,87 +630,21 @@ PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem&
QRect numberRect = QRect( r.x(), r.y(), numberWidth, r.height() );
QRect extraRect = QRect( r.x() + r.width() - durationWidth, r.y(), durationWidth, r.height() );
QRect stateRect = extraRect.adjusted( 0, 0, 0, 0 );
if ( option.state & QStyle::State_Selected || hoveringOver() == index )
QRect stateRect;
if ( option.state & QStyle::State_Selected || hoveringOver() == index )
{
int h = extraRect.height() / 3;
if ( track->loved() )
{
painter->save();
painter->setOpacity( 0.5 );
QRect r = stateRect.adjusted( -16, extraRect.height() / 2 - h / 2, 0, 0 );
r.setHeight( h );
r.setWidth( r.height() );
painter->drawPixmap( r, ImageRegistry::instance()->pixmap( RESPATH "images/love.svg", r.size() ) );
painter->restore();
int h = extraRect.height() / 3;
stateRect = extraRect.adjusted( -16, extraRect.height() / 2 - h / 2, 0, 0 );
stateRect.setHeight( h );
stateRect.setWidth( stateRect.height() );
painter->drawPixmap( stateRect, ImageRegistry::instance()->pixmap( RESPATH "images/love.svg", stateRect.size() ) );
stateWidth += r.width() + 16;
}
if ( hasOnlineResults && !item->query()->results().first()->purchaseUrl().isEmpty() )
{
QRect r = stateRect.adjusted( -stateWidth -144, 6, 0, -6 );
r.setWidth( 144 );
DropDownButton::drawPrimitive( painter, r, tr( "Buy" ), m_hoveringOverBuyButton == index, false );
m_buyButtonRects[ index ] = r;
stateWidth += r.width() + 16;
stateWidth = stateRect.width() + 16;
}
}
if ( hasOnlineResults && !item->query()->results().first()->downloadFormats().isEmpty() )
{
painter->save();
QStyleOptionComboBox optc;
optc.rect = stateRect.adjusted( -stateWidth -144, 6, 0, -6 );
optc.rect.setWidth( 144 );
m_downloadDropDownRects[ index ] = optc.rect;
stateWidth += optc.rect.width() + 16;
optc.editable = false;
optc.currentText = tr( "Download %1" ).arg( item->query()->results().first()->downloadFormats().first().extension.toUpper() );
optc.palette = m_view->palette();
if ( option.state & QStyle::State_Selected && option.state & QStyle::State_Active )
optc.state = QStyle::State_Active | QStyle::State_Selected | QStyle::State_Enabled;
else
optc.state = QStyle::State_Active | QStyle::State_Enabled;
if ( !DownloadManager::instance()->localFileForDownload( item->query()->results().first()->downloadFormats().first().url.toString() ).isEmpty() )
{
painter->setPen( optc.palette.text().color() );
const QString text = painter->fontMetrics().elidedText( tr( "View in Finder" ), Qt::ElideRight, optc.rect.width() - 3 );
painter->drawText( optc.rect, text, QTextOption( Qt::AlignCenter ) );
}
else if ( !item->query()->results().first()->downloadJob() )
{
DropDownButton::drawPrimitive( painter, optc.rect, optc.currentText, hoveringOver() == index, true );
}
else
{
if ( item->query()->results().first()->downloadJob()->state() == DownloadJob::Finished )
{
painter->setPen( optc.palette.text().color() );
const QString text = painter->fontMetrics().elidedText( tr( "View in Finder" ), Qt::ElideRight, optc.rect.width() - 3 );
painter->drawText( optc.rect, text, QTextOption( Qt::AlignCenter ) );
}
else
{
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND.darker() );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_BACKGROUND );
painter->drawRect( optc.rect.adjusted( 2, 2, -2, -2 ) );
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
QRect fillp = optc.rect.adjusted( 3, 3, -3, -3 );
fillp.setWidth( float(fillp.width()) * ( float(item->query()->results().first()->downloadJob()->progressPercentage()) / 100.0 ) );
painter->drawRect( fillp );
}
}
painter->restore();
}
const int remWidth = r.width() - numberWidth - durationWidth;
QRect titleRect = QRect( numberRect.x() + numberRect.width(), r.y(), (double)remWidth * 0.5, r.height() );
@@ -783,7 +708,6 @@ PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem&
int h = extraRect.height() / 2;
QRect playIconRect = extraRect.adjusted( extraRect.width() - h - 8, h / 2, -8, -h / 2 );
playIconRect.setWidth( playIconRect.height() );
painter->drawPixmap( playIconRect, ImageRegistry::instance()->pixmap( RESPATH "images/play.svg", playIconRect.size() ) );
double duration = (double)AudioEngine::instance()->currentTrackTotalTime();
@@ -835,11 +759,8 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
bool hoveringArtist = false;
bool hoveringInfo = false;
bool hoveringLove = false;
bool hoveringBuy = false;
bool hoveringDownloadDropDown = false;
Tomahawk::source_ptr hoveredAvatar;
QRect hoveredAvatarRect;
if ( m_infoButtonRects.contains( index ) )
{
const QRect infoRect = m_infoButtonRects[ index ];
@@ -858,18 +779,6 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringLove = loveRect.contains( ev->pos() );
}
if ( m_buyButtonRects.contains( index ) )
{
const QRect buyRect = m_buyButtonRects[ index ];
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringBuy = buyRect.contains( ev->pos() );
}
if ( m_downloadDropDownRects.contains( index ) )
{
const QRect downloadDropDownRect = m_downloadDropDownRects[ index ];
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringDownloadDropDown = downloadDropDownRect.contains( ev->pos() );
}
if ( m_avatarBoxRects.contains( index ) )
{
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
@@ -887,7 +796,7 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringInfo || hoveringLove || hoveringArtist || hoveringBuy )
if ( hoveringInfo || hoveringLove || hoveringArtist )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
@@ -911,23 +820,6 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
emit updateIndex( m_hoveringOverArtist );
m_hoveringOverArtist = QModelIndex();
}
if ( hoveringBuy && m_hoveringOverBuyButton != index )
{
QPersistentModelIndex ti = m_hoveringOverBuyButton;
m_hoveringOverBuyButton = index;
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( ti ) );
item->requestRepaint();
emit updateIndex( m_hoveringOverBuyButton );
}
if ( !hoveringBuy && m_hoveringOverBuyButton.isValid() )
{
QPersistentModelIndex ti = m_hoveringOverBuyButton;
m_hoveringOverBuyButton = QModelIndex();
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( ti ) );
item->requestRepaint();
}
if ( m_hoveringOver != index )
{
@@ -957,11 +849,6 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
{
item->query()->queryTrack()->setLoved( !item->query()->queryTrack()->loved() );
}
else if ( hoveringBuy )
{
WebPopup* popup = new WebPopup( item->query()->results().first()->purchaseUrl(), QSize( 400, 800 ) );
connect( item->query()->results().first().data(), SIGNAL( destroyed() ), popup, SLOT( close() ) );
}
else if ( hoveringInfo )
{
if ( m_model->style() == PlayableProxyModel::SingleColumn )
@@ -996,11 +883,9 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
}
}
}
else if ( ( m_view->proxyModel()->style() == PlayableProxyModel::Locker && index.column() == PlayableModel::Download ) || hoveringDownloadDropDown )
else if ( m_view->proxyModel()->style() == PlayableProxyModel::Locker && index.column() == PlayableModel::Download )
{
m_model->sourceModel()->setAllColumnsEditable( true );
m_view->edit( index );
m_model->sourceModel()->setAllColumnsEditable( false );
return true;
}
@@ -1015,17 +900,15 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
void
PlaylistItemDelegate::resetHoverIndex()
{
if ( !m_model || !m_hoveringOver.isValid() )
if ( !m_model )
return;
QPersistentModelIndex idx = m_hoveringOver;
m_hoveringOver = QModelIndex();
m_hoveringOverArtist = QModelIndex();
m_hoveringOverBuyButton = QModelIndex();
m_infoButtonRects.clear();
m_loveButtonRects.clear();
m_buyButtonRects.clear();
m_artistNameRects.clear();
QModelIndex itemIdx = m_model->mapToSource( idx );
@@ -1037,6 +920,7 @@ PlaylistItemDelegate::resetHoverIndex()
}
emit updateIndex( idx );
m_view->repaint();
}

View File

@@ -116,13 +116,10 @@ private:
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps;
mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_loveButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_buyButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_downloadDropDownRects;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
mutable QHash< QPersistentModelIndex, QHash< Tomahawk::source_ptr, QRect > > m_avatarBoxRects;
QPersistentModelIndex m_hoveringOver;
QPersistentModelIndex m_hoveringOverArtist;
QPersistentModelIndex m_hoveringOverBuyButton;
mutable QPersistentModelIndex m_nowPlaying;
TrackView* m_view;

View File

@@ -19,7 +19,6 @@
#include "TrackDetailView.h"
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QSizePolicy>
#include <QVBoxLayout>
@@ -36,7 +35,6 @@
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Closure.h"
#include "utils/WebPopup.h"
#include "utils/Logger.h"
using namespace Tomahawk;
@@ -44,7 +42,6 @@ using namespace Tomahawk;
TrackDetailView::TrackDetailView( QWidget* parent )
: QWidget( parent )
, DpiScaler( this )
, m_buyButtonVisible( false )
{
setFixedWidth( scaledX( 200 ) );
setContentsMargins( 0, 0, 0, 0 );
@@ -121,21 +118,11 @@ TrackDetailView::TrackDetailView( QWidget* parent )
TomahawkStyle::styleScrollBar( m_resultsScrollArea->verticalScrollBar() );
m_resultsScrollArea->hide();
m_buyButton = new QPushButton;
m_buyButton->setStyleSheet( "QPushButton:hover { font-size: 12px; color: #ffffff; background: #000000; border-style: solid; border-radius: 0px; border-width: 2px; border-color: #2b2b2b; }"
"QPushButton { font-size: 12px; color: #ffffff; background-color: #000000; border-style: solid; border-radius: 0px; border-width: 0px; }" );
m_buyButton->setMinimumHeight( 30 );
m_buyButton->setText( tr( "Buy Album" ) );
m_buyButton->setVisible( false );
connect( m_buyButton, SIGNAL( clicked() ), SLOT( onBuyButtonClicked() ) );
QVBoxLayout* layout = new QVBoxLayout;
TomahawkUtils::unmarginLayout( layout );
layout->addWidget( m_playableCover );
layout->addSpacerItem( new QSpacerItem( 0, 8, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
layout->addWidget( m_nameLabel );
layout->addSpacerItem( new QSpacerItem( 0, 4, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
layout->addWidget( m_buyButton );
layout->addWidget( m_dateLabel );
layout->addWidget( m_infoBox );
layout->addSpacerItem( new QSpacerItem( 0, 32, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
@@ -147,9 +134,6 @@ TrackDetailView::TrackDetailView( QWidget* parent )
setLayout( layout );
setQuery( query_ptr() );
connect( DownloadManager::instance(), SIGNAL( stateChanged( DownloadManager::DownloadManagerState, DownloadManager::DownloadManagerState ) ),
SLOT( onDownloadManagerStateChanged( DownloadManager::DownloadManagerState, DownloadManager::DownloadManagerState ) ) );
}
@@ -170,10 +154,6 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
{
if ( m_query )
{
if ( m_query->track()->albumPtr() && !m_query->track()->albumPtr()->name().isEmpty() )
{
disconnect( m_query->track()->albumPtr().data(), SIGNAL( updated() ), this, SLOT( onAlbumUpdated() ) );
}
disconnect( m_query->track().data(), SIGNAL( updated() ), this, SLOT( onCoverUpdated() ) );
disconnect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), this, SLOT( onSocialActionsLoaded() ) );
disconnect( m_query.data(), SIGNAL( resultsChanged() ), this, SLOT( onResultsChanged() ) );
@@ -184,7 +164,6 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
onResultsChanged();
setSocialActions();
onCoverUpdated();
onAlbumUpdated();
if ( !query )
{
@@ -195,85 +174,20 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
m_dateLabel->setText( tr( "Unknown Release-Date" ) );
connect( m_query->track().data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) );
connect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onResultsChanged() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onAlbumUpdated() ) );
}
void
TrackDetailView::onAlbumUpdated()
{
if ( !m_query )
return;
if ( m_query->track()->albumPtr() && !m_query->track()->albumPtr()->name().isEmpty() )
{
m_nameLabel->setType( QueryLabel::Album );
m_nameLabel->setAlbum( m_query->track()->albumPtr() );
connect( m_query->track()->albumPtr().data(), SIGNAL( updated() ), SLOT( onAlbumUpdated() ), Qt::UniqueConnection );
if ( m_buyButtonVisible )
{
if ( m_query->track()->albumPtr()->purchased() )
{
m_buyButton->setText( tr( "Download Album" ) );
m_buyButton->setVisible( true );
}
else
{
m_buyButton->setText( tr( "Buy Album" ) );
m_buyButton->setVisible( !m_query->track()->albumPtr()->purchaseUrl().isEmpty() );
}
}
}
else
{
m_nameLabel->setType( QueryLabel::Artist );
m_nameLabel->setArtist( m_query->track()->artistPtr() );
m_buyButton->setVisible( false );
}
}
void
TrackDetailView::onBuyButtonClicked()
{
if ( DownloadManager::instance()->state() == DownloadManager::Running )
{
emit downloadCancel();
return;
}
if ( m_query && m_query->track()->albumPtr() )
{
if ( m_query->track()->albumPtr()->purchased() )
{
emit downloadAll();
}
else
{
WebPopup* popup = new WebPopup( m_query->track()->albumPtr()->purchaseUrl(), QSize( 400, 800 ) );
connect( m_query->track()->albumPtr().data(), SIGNAL( destroyed() ), popup, SLOT( close() ) );
}
}
}
void
TrackDetailView::onDownloadManagerStateChanged( DownloadManager::DownloadManagerState newState, DownloadManager::DownloadManagerState oldState )
{
tDebug() << Q_FUNC_INFO;
if ( newState == DownloadManager::Running )
{
m_buyButton->setText( tr( "Cancel Download" ) );
}
else
{
onAlbumUpdated();
}
connect( m_query->track().data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) );
connect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onResultsChanged() ) );
}
@@ -348,15 +262,8 @@ 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 (%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->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->setFixedWidth( width() - 32 - 4 );
NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ),
@@ -390,10 +297,3 @@ TrackDetailView::onResultsChanged()
m_resultsScrollArea->hide();
}
}
void
TrackDetailView::setBuyButtonVisible( bool visible )
{
m_buyButtonVisible = visible;
}

View File

@@ -23,7 +23,6 @@
#include "Query.h"
#include "utils/DpiScaler.h"
#include "DownloadManager.h"
#include "DllMacro.h"
class QLabel;
@@ -31,7 +30,6 @@ class CaptionLabel;
class PlayableCover;
class QueryLabel;
class QScrollArea;
class QPushButton;
class DLLEXPORT TrackDetailView : public QWidget, private TomahawkUtils::DpiScaler
{
@@ -41,29 +39,21 @@ public:
explicit TrackDetailView( QWidget* parent = 0 );
~TrackDetailView();
void setBuyButtonVisible( bool visible );
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
void setPlaylistInterface( const Tomahawk::playlistinterface_ptr& playlistInterface );
signals:
void downloadAll();
void downloadCancel();
protected:
protected slots:
private slots:
void onAlbumUpdated();
void onCoverUpdated();
void onSocialActionsLoaded();
void onResultsChanged();
void onBuyButtonClicked();
void onDownloadManagerStateChanged( DownloadManager::DownloadManagerState newState, DownloadManager::DownloadManagerState oldState );
private:
void setSocialActions();
@@ -75,8 +65,6 @@ private:
QLabel* m_lovedIcon;
QLabel* m_lovedLabel;
CaptionLabel* m_resultsBoxLabel;
QPushButton* m_buyButton;
bool m_buyButtonVisible;
QWidget* m_infoBox;
QWidget* m_resultsBox;

View File

@@ -23,9 +23,7 @@
#include "PlayableModel.h"
#include "PlayableProxyModel.h"
#include "PlayableItem.h"
#include "DownloadManager.h"
#include "DropJob.h"
#include "Result.h"
#include "Source.h"
#include "TomahawkSettings.h"
#include "audio/AudioEngine.h"
@@ -47,9 +45,6 @@
#include <QScrollBar>
#include <QDrag>
// HACK
#include <QTableView>
#define SCROLL_TIMEOUT 280
using namespace Tomahawk;
@@ -88,13 +83,6 @@ TrackView::TrackView( QWidget* parent )
setEditTriggers( NoEditTriggers );
setHeader( m_header );
// HACK: enable moving of first column: QTBUG-33974 / https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
QTableView unused;
unused.setVerticalHeader( header() );
header()->setParent( this );
unused.setVerticalHeader( new QHeaderView( Qt::Horizontal, &unused ) );
setSortingEnabled( true );
sortByColumn( -1 );
setContextMenuPolicy( Qt::CustomContextMenu );
@@ -689,6 +677,7 @@ TrackView::wheelEvent( QWheelEvent* event )
QTreeView::wheelEvent( event );
m_delegate->resetHoverIndex();
repaint();
}
@@ -775,44 +764,6 @@ TrackView::onCustomContextMenu( const QPoint& pos )
m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionMarkListened
| ContextMenu::ActionDelete );
if ( proxyModel()->style() != PlayableProxyModel::Collection )
{
bool allDownloaded = true;
bool noneDownloadable = true;
bool downloadable = false;
foreach ( const QModelIndex& index, selectedIndexes() )
{
if ( index.column() )
continue;
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) );
if( item->query()->results().isEmpty() )
continue;
downloadable = !item->query()->results().first()->downloadFormats().isEmpty();
if ( downloadable )
{
noneDownloadable = false;
}
if ( downloadable && DownloadManager::instance()->localFileForDownload( item->query()->results().first()->downloadFormats().first().url.toString() ).isEmpty() )
{
allDownloaded = false;
}
if ( !allDownloaded || !noneDownloadable )
{
break;
}
}
if ( !allDownloaded || !noneDownloadable )
{
m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionDownload );
}
}
QList<query_ptr> queries;
foreach ( const QModelIndex& index, selectedIndexes() )
{
@@ -844,10 +795,6 @@ TrackView::onMenuTriggered( int action )
deleteSelectedItems();
break;
case ContextMenu::ActionDownload:
downloadSelectedItems();
break;
default:
break;
}
@@ -923,30 +870,6 @@ TrackView::deleteSelectedItems()
}
void
TrackView::downloadSelectedItems()
{
foreach ( const QModelIndex& index, selectedIndexes() )
{
if ( index.column() )
continue;
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) );
if ( !item )
continue;
if ( item->query()->results().isEmpty() || item->query()->results().first()->downloadFormats().isEmpty() )
continue;
if ( !DownloadManager::instance()->localFileForDownload( item->query()->results().first()->downloadFormats().first().url.toString() ).isEmpty() )
continue;
DownloadManager::instance()->addJob( item->result()->toDownloadJob( item->result()->downloadFormats().first() ) );
}
}
void
TrackView::verifySize()
{

View File

@@ -89,7 +89,6 @@ public:
public slots:
virtual void onItemActivated( const QModelIndex& index );
virtual void downloadSelectedItems();
virtual void deleteSelectedItems();
void playItem();

View File

@@ -68,12 +68,11 @@ public:
enum UrlType
{
UrlTypeAny = 0x00,
UrlTypePlaylist = 0x01,
UrlTypeTrack = 0x02,
UrlTypeAlbum = 0x04,
UrlTypeArtist = 0x08,
UrlTypeXspf = 0x10
Any = 0x00,
Playlist = 0x01,
Track = 0x02,
Album = 0x04,
Artist = 0x08
};
Q_DECLARE_FLAGS( UrlTypes, UrlType )
Q_FLAGS( UrlTypes )

View File

@@ -30,8 +30,6 @@
#include <QWidget>
#include <QUiLoader>
#include <QBoxLayout>
#include <QPushButton>
#include <QDesktopServices>
Tomahawk::ExternalResolverGui::ExternalResolverGui(const QString& filePath)
: Tomahawk::ExternalResolver(filePath)
@@ -84,30 +82,6 @@ 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 )
{
@@ -120,8 +94,6 @@ 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,8 +50,7 @@ protected:
QByteArray fixDataImagePaths( const QByteArray& data, bool compressed, const QVariantMap& images );
private:
void addChildProperties( QObject* widget, QVariantMap& m );
void setupClickHandlerOnUrlButtons( QObject* widget );
void addChildProperties( QObject* parent, QVariantMap& m );
};
}; //ns

View File

@@ -67,14 +67,6 @@ JSAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr& obj
}
void
JSAccount::showDebugger()
{
tLog() << Q_FUNC_INFO << name() << "Show debugger";
m_engine->showWebInspector();
}
QString
JSAccount::serializeQVariantMap( const QVariantMap& map )
{
@@ -178,41 +170,6 @@ 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

@@ -69,20 +69,15 @@ public:
void setResolver( JSResolver* resolver );
void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ) override;
void showDebugger() override;
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 );
ScriptEngine* m_engine;
std::unique_ptr<ScriptEngine> m_engine;
// HACK: the order of initializen is flawed, tbr
JSResolver* m_resolver;
};

View File

@@ -44,13 +44,6 @@
#include "Track.h"
#include "ScriptInfoPlugin.h"
#include "JSAccount.h"
#include "ScriptJob.h"
// lookupUrl stuff
#include "playlist/PlaylistTemplate.h"
#include "playlist/XspfPlaylistTemplate.h"
#include "database/Database.h"
#include "database/DatabaseImpl.h"
#include <QDir>
#include <QFile>
@@ -216,52 +209,21 @@ JSResolver::init()
}
const QByteArray scriptContents = scriptFile.readAll();
// tomahawk.js
{
// add c++ part of tomahawk javascript library
d->scriptAccount->addToJavaScriptWindowObject( "Tomahawk", d->resolverHelper );
// load es6-promises shim
d->scriptAccount->loadScript( RESPATH "js/rsvp-latest.min.js" );
// Load CrytoJS core
d->scriptAccount->loadScript( RESPATH "js/cryptojs-core.js" );
// Load CryptoJS modules
QStringList jsfiles;
jsfiles << "*.js";
QDir cryptojs( RESPATH "js/cryptojs" );
foreach ( QString jsfile, cryptojs.entryList( jsfiles ) )
{
d->scriptAccount->loadScript( RESPATH "js/cryptojs/" + jsfile );
}
// Load tomahawk.js
d->scriptAccount->loadScript( RESPATH "js/tomahawk.js" );
}
// tomahawk-infosystem.js
{
// TODO: be smarter about this, only include this if the resolver supports infoplugins
// add deps
d->scriptAccount->loadScript( RESPATH "js/tomahawk-infosystem.js" );
}
// add resolver dependencies, if any
d->scriptAccount->loadScripts( d->requiredScriptPaths );
d->scriptAccount->addToJavaScriptWindowObject( "Tomahawk", d->resolverHelper );
// add resolver
d->scriptAccount->loadScript( filePath() );
// HACK: register resolver object
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" );
// init resolver
scriptObject()->syncInvoke( "init" );
d->scriptAccount->evaluateJavaScript(
"var resolverInstance = new (require('main').default);"
"Tomahawk.PluginManager.registerPlugin('resolver', resolverInstance);"
);
QVariantMap m = scriptObject()->syncInvoke( "settings" ).toMap();
// init resolver
resolverInit();
QVariantMap m = resolverSettings();
d->name = m.value( "name" ).toString();
d->weight = m.value( "weight", 0 ).toUInt();
d->timeout = m.value( "timeout", 25 ).toUInt() * 1000;
@@ -311,8 +273,6 @@ JSResolver::start()
Q_D( JSResolver );
d->stopped = false;
d->resolverHelper->start();
if ( d->ready )
Tomahawk::Pipeline::instance()->addResolver( this );
else
@@ -327,13 +287,20 @@ 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 ) )
{
QVariantMap arguments;
arguments["url"] = url;
arguments["type"] = (int) type;
return scriptObject()->syncInvoke( "canParseUrl", arguments ).toBool();
QString eval = QString( "canParseUrl( '%1', %2 )" )
.arg( JSAccount::escape( QString( url ) ) )
.arg( (int) type );
return callOnResolver( eval ).toBool();
}
else
{
@@ -346,8 +313,14 @@ JSResolver::canParseUrl( const QString& url, UrlType type )
void
JSResolver::lookupUrl( const QString& url )
{
Q_D( const JSResolver );
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "lookupUrl", Qt::QueuedConnection,
Q_ARG( QString, url ) );
return;
}
Q_D( const JSResolver );
if ( !d->capabilities.testFlag( UrlLookup ) )
{
@@ -355,176 +328,19 @@ JSResolver::lookupUrl( const QString& url )
return;
}
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();
}
QString eval = QString( "lookupUrl( '%1' )" )
.arg( JSAccount::escape( QString( url ) ) );
void
JSResolver::onLookupUrlRequestDone( const QVariantMap& result )
{
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.
m_pendingUrl = QString();
m_pendingAlbum = album_ptr();
UrlTypes type = (UrlTypes) result.value( "type" ).toInt();
if ( type == UrlTypeArtist )
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
QString name = result.value( "name" ).toString();
Q_ASSERT( !name.isEmpty() );
emit informationFound( url, Artist::get( name, true ).objectCast<QObject>() );
}
else if ( type == UrlTypeAlbum )
{
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 informationFound( url, album.objectCast<QObject>() );
}
}
else if ( type == 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 << name() << "Got empty track information for " << url;
emit informationFound( url, QSharedPointer<QObject>() );
}
else
{
emit informationFound( url, query.objectCast<QObject>() );
}
}
else if ( type == 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 << 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 == 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 << name() << "Got playlist for " << url;
pltemplate->load();
}
else
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "No usable information found for " << url;
emit informationFound( url, QSharedPointer<QObject>() );
}
}
query_ptr
JSResolver::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;
}
void
JSResolver::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 the resolver doesn't return anything, async api is used
return;
}
emit informationFound( m_pendingUrl, m_pendingAlbum.objectCast<QObject>() );
m_pendingAlbum = album_ptr();
m_pendingUrl = QString();
}
void
JSResolver::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate )
{
tLog() << Q_FUNC_INFO;
emit informationFound( url, pltemplate.objectCast<QObject>() );
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
tDebug() << errorMessage << m;
}
@@ -540,59 +356,38 @@ JSResolver::error() const
void
JSResolver::resolve( const Tomahawk::query_ptr& query )
{
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query, "resolver" );
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() )
if ( QThread::currentThread() != thread() )
{
Tomahawk::Pipeline::instance()->reportError( qid, this );
QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) );
return;
}
QString eval;
if ( !query->isFullTextQuery() )
{
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() ) );
}
else
{
if ( !data.value( "artists" ).isNull() )
{
QList< artist_ptr > artists = scriptAccount()->parseArtistVariantList( data.value( "artists" ).toList() );
Tomahawk::Pipeline::instance()->reportArtists( qid, artists );
}
if ( !data.value( "albums" ).isNull() )
{
QList< album_ptr > albums = scriptAccount()->parseAlbumVariantList( data.value( "albums" ).toList() );
Tomahawk::Pipeline::instance()->reportAlbums( qid, albums );
}
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 );
eval = QString( "search( '%1', '%2' )" )
.arg( JSAccount::escape( query->id() ) )
.arg( JSAccount::escape( query->fullTextQuery() ) );
}
sender()->deleteLater();
QVariantMap m = callOnResolver( eval ).toMap();
}
void
JSResolver::stop()
{
Q_D( JSResolver );
d->stopped = true;
d->resolverHelper->stop();
scriptAccount()->stop();
@@ -606,7 +401,7 @@ JSResolver::loadUi()
{
Q_D( JSResolver );
QVariantMap m = scriptObject()->syncInvoke( "getConfigUi" ).toMap();
QVariantMap m = callOnResolver( "getConfigUi()" ).toMap();
bool compressed = m.value( "compressed", "false" ).toBool();
qDebug() << "Resolver has a preferences widget! compressed?" << compressed;
@@ -658,7 +453,7 @@ JSResolver::saveConfig()
// qDebug() << Q_FUNC_INFO << saveData;
d->resolverHelper->setResolverConfig( saveData.toMap() );
scriptObject()->syncInvoke( "saveUserConfig" );
callOnResolver( "saveUserConfig()" );
}
@@ -689,25 +484,39 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa
}
QVariantMap
JSResolver::resolverSettings()
{
return callOnResolver( "settings" ).toMap();
}
QVariantMap
JSResolver::resolverUserConfig()
{
return scriptObject()->syncInvoke( "getUserConfig" ).toMap();
return callOnResolver( "getUserConfig()" ).toMap();
}
QString
JSResolver::instanceUUID()
QVariantMap
JSResolver::resolverInit()
{
return Tomahawk::Database::instance()->impl()->dbid();
return callOnResolver( "init()" ).toMap();
}
ScriptJob*
JSResolver::getStreamUrl( const result_ptr& result )
QVariant
JSResolver::callOnResolver( const QString& scriptSource )
{
QVariantMap arguments;
arguments["url"] = result->url();
Q_D( JSResolver );
return scriptObject()->invoke( "getStreamUrl", arguments );
QString propertyName = scriptSource.split('(').first();
return d->scriptAccount->evaluateJavaScriptWithResult( QString(
"if(resolverInstance['_adapter_%1']) {"
" resolverInstance._adapter_%2;"
"} else {"
" resolverInstance.%2;"
"}"
).arg( propertyName ).arg( scriptSource ) );
}

View File

@@ -75,8 +75,6 @@ public:
ScriptAccount* scriptAccount() const;
ScriptJob* getStreamUrl( const result_ptr& result ) override;
public slots:
void resolve( const Tomahawk::query_ptr& query ) override;
void stop() override;
@@ -91,10 +89,6 @@ signals:
protected:
QVariant callOnResolver( const QString& scriptSource );
private slots:
void onResolveRequestDone(const QVariantMap& data);
void onLookupUrlRequestDone(const QVariantMap& data);
private:
void init();
@@ -102,20 +96,12 @@ private:
void onCapabilitiesChanged( Capabilities capabilities );
// encapsulate javascript calls
QVariantMap resolverSettings();
QVariantMap resolverUserConfig();
QVariantMap resolverInit();
Q_DECLARE_PRIVATE( JSResolver )
QScopedPointer<JSResolverPrivate> d_ptr;
// TODO: move lookupUrl stuff to its own plugin type
QString instanceUUID();
static Tomahawk::query_ptr parseTrack( const QVariantMap& track );
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 );
void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate );
};
} // ns: Tomahawk

View File

@@ -21,6 +21,10 @@
#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"
@@ -47,8 +51,6 @@
#include <QMap>
#include <QWebFrame>
#include <QLocale>
#include <QNetworkReply>
#include <taglib/asffile.h>
#include <taglib/flacfile.h>
#include <taglib/id3v2framefactory.h>
@@ -74,25 +76,11 @@ JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* paren
: QObject( parent )
, m_resolver( parent )
, m_scriptPath( scriptPath )
, m_stopped( false )
, m_urlCallbackIsAsync( false )
{
}
void
JSResolverHelper::start()
{
m_stopped = false;
}
void
JSResolverHelper::stop()
{
m_stopped = true;
}
QByteArray
JSResolverHelper::readRaw( const QString& fileName )
{
@@ -152,6 +140,55 @@ 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
{
@@ -446,13 +483,123 @@ 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;
@@ -468,9 +615,6 @@ JSResolverHelper::nativeReportCapabilities( const QVariant& v )
void
JSResolverHelper::reportScriptJobResults( const QVariantMap& result )
{
if( m_stopped )
return;
m_resolver->d_func()->scriptAccount->reportScriptJobResult( result );
}
@@ -478,9 +622,6 @@ 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 );
}
@@ -488,10 +629,28 @@ JSResolverHelper::registerScriptPlugin( const QString& type, const QString& obje
void
JSResolverHelper::unregisterScriptPlugin( const QString& type, const QString& objectId )
{
if( m_stopped )
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() )
return;
m_resolver->d_func()->scriptAccount->unregisterScriptPlugin( type, objectId );
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>() );
}
@@ -509,6 +668,31 @@ 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 )
@@ -519,6 +703,61 @@ 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(resolverInstance['_adapter_%1']) {"
" resolverInstance._adapter_%1( {qid: '%2', url: '%3'} );"
"} else {"
" resolverInstance.%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( "resolverInstance.%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,
@@ -657,27 +896,12 @@ 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 QVariantMap& options )
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
const QVariantMap& headers,
const QVariantMap& options )
{
QString url = options[ "url" ].toString();
QVariantMap headers = options[ "headers" ].toMap();
QNetworkRequest req( url );
foreach ( const QString& key, headers.keys() )
{
@@ -733,16 +957,17 @@ 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 );
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 );
QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" )
.arg( QString::number( requestId ) )
.arg( json );
m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript );
}
@@ -918,3 +1143,43 @@ 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,9 +54,6 @@ public:
*/
void setResolverConfig( const QVariantMap& config );
void start();
void stop();
/**
* Get the instance unique account id for this resolver.
*
@@ -64,6 +61,9 @@ 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,9 +89,20 @@ public:
int sizehint,
const QVariantMap& options );
Q_INVOKABLE void invokeNativeScriptJob( int requestId,
const QString& methodName,
const QVariantMap& params );
/**
* 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 );
/**
* Lucene++ indices for JS resolvers
@@ -111,10 +122,18 @@ 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 );
@@ -123,6 +142,10 @@ 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 );
@@ -131,20 +154,27 @@ 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;
bool m_stopped;
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;
};
} // ns: Tomahawk

View File

@@ -17,31 +17,10 @@
*/
#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,6 +19,7 @@
#ifndef RESOLVER_H
#define RESOLVER_H
#include "../ResultProvider.h"
#include "Typedefs.h"
#include "DllMacro.h"
@@ -35,23 +36,21 @@
namespace Tomahawk
{
class ScriptJob;
class DLLEXPORT Resolver : public QObject
class DLLEXPORT Resolver : public QObject, public ResultProvider
{
Q_OBJECT
public:
Resolver( QObject* parent = nullptr );
virtual QString name() const = 0;
virtual QPixmap icon( const QSize& size ) const;
Resolver() {}
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

@@ -32,8 +32,6 @@
#include "ScriptInfoPlugin.h"
// TODO:
#include "../Artist.h"
#include "../Album.h"
#include "../Result.h"
#include "../Track.h"
#include <QTime>
@@ -203,10 +201,6 @@ ScriptAccount::unregisterScriptPlugin( const QString& type, const QString& objec
{
m_infoPluginFactory->unregisterPlugin( object );
}
else if( type == "linkParser" )
{
// TODO
}
else
{
tLog() << "This plugin type is not handled by Tomahawk or simply cannot be removed yet";
@@ -237,10 +231,6 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
ScriptLinkGeneratorPlugin* lgp = new ScriptLinkGeneratorPlugin( object );
Utils::LinkGenerator::instance()->addPlugin( lgp );
}
else if( type == "linkParser" )
{
tLog() << "Plugin registered linkParser, which is not implemented yet. UrlLookup won't work";
}
else if ( type == "infoPlugin" )
{
m_infoPluginFactory->registerPlugin( object, this );
@@ -257,12 +247,6 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
}
void
ScriptAccount::showDebugger()
{
}
void
ScriptAccount::onJobDeleted( const QString& jobId )
{
@@ -270,67 +254,15 @@ ScriptAccount::onJobDeleted( const QString& jobId )
}
QList< Tomahawk::artist_ptr >
ScriptAccount::parseArtistVariantList( const QVariantList& artistList )
{
QList< Tomahawk::artist_ptr > artists;
QString artist;
foreach( const QVariant& a, artistList )
{
artist = a.toString().trimmed();
if ( artist.isEmpty() )
continue;
artists << Tomahawk::Artist::get( artist );
}
return artists;
}
QList< Tomahawk::album_ptr >
ScriptAccount::parseAlbumVariantList( const QVariantList& albumList )
{
QList< Tomahawk::album_ptr > albums;
QString artistString;
QString albumString;
foreach( const QVariant& av, albumList )
{
QVariantMap m = av.toMap();
artistString = m.value( "artist" ).toString().trimmed();
albumString = m.value( "album" ).toString().trimmed();
if ( artistString.isEmpty() || albumString.isEmpty() )
continue;
albums << Tomahawk::Album::get( Tomahawk::Artist::get( artistString ), albumString );
}
return albums;
}
QList< Tomahawk::result_ptr >
ScriptAccount::parseResultVariantList( const QVariantList& reslist )
{
QList< Tomahawk::result_ptr > results;
foreach( const QVariant& rv, reslist )
{
QVariantMap m = rv.toMap();
const QString artistString = m.value("artist").toString().trimmed();
const QString trackString = m.value("track").toString().trimmed();
if ( artistString.isEmpty() || trackString.isEmpty() )
{
tLog() << Q_FUNC_INFO << "Could not parse Track" << m;
continue;
}
int duration = m.value( "duration", 0 ).toInt();
if ( duration <= 0 && m.contains( "durationString" ) )
{
@@ -338,8 +270,8 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
duration = time.secsTo( QTime( 0, 0 ) ) * -1;
}
Tomahawk::track_ptr track = Tomahawk::Track::get( artistString,
trackString,
Tomahawk::track_ptr track = Tomahawk::Track::get( m.value( "artist" ).toString(),
m.value( "track" ).toString(),
m.value( "album" ).toString(),
m.value( "albumArtist" ).toString(),
duration,
@@ -370,17 +302,11 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
// rp->track()->setAttributes( attr );
}
QString mimetype = m.value( "mimetype" ).toString();
if ( mimetype.isEmpty() )
rp->setMimetype( m.value( "mimetype" ).toString() );
if ( rp->mimetype().isEmpty() )
{
mimetype = TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() );
}
Q_ASSERT( !mimetype.isEmpty() );
if ( !mimetype.isEmpty() )
{
rp->setMimetype( mimetype );
rp->setMimetype( TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() ) );
Q_ASSERT( !rp->mimetype().isEmpty() );
}
rp->setFriendlySource( name() );
@@ -400,6 +326,21 @@ 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;
}
@@ -407,29 +348,8 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
}
ScriptJob*
ScriptAccount::resolve( const scriptobject_ptr& scriptObject, const query_ptr& query, const QString& resolveType )
QSharedPointer< ScriptCollection >
ScriptAccount::scriptCollection( const QString& id ) const
{
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;
return m_collectionFactory->scriptPlugins().value( id );
}

View File

@@ -65,24 +65,18 @@ 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 );
virtual void showDebugger();
// helpers
QList< Tomahawk::artist_ptr > parseArtistVariantList( const QVariantList& artistList );
QList< Tomahawk::album_ptr > parseAlbumVariantList( const QVariantList& albumList );
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query, const QString& resolveType );
QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const;
private slots:
void onJobDeleted( const QString& jobId );

View File

@@ -26,10 +26,7 @@
#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>
@@ -226,15 +223,6 @@ 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 )
@@ -338,58 +326,3 @@ 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,15 +91,8 @@ 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

@@ -28,8 +28,6 @@
#include "utils/Logger.h"
#include "../Result.h"
#include <QtConcurrentRun>
using namespace Tomahawk;
ScriptCommand_AllTracks::ScriptCommand_AllTracks( const Tomahawk::collection_ptr& collection,
@@ -119,22 +117,20 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
Q_ASSERT( !collection.isNull() );
QtConcurrent::run( [] ( ScriptCommand_AllTracks* t, ScriptJob* job, const QVariantMap& result, const QSharedPointer< ScriptCollection >& collection )
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "results"].toList() );
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, t )
{
QList< Tomahawk::result_ptr > results = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() );
result->setResolvedByCollection( m_collection );
queries.append( result->toQuery() );
}
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, results )
{
result->setResolvedByCollection( collection );
queries.append( result->toQuery() );
}
tDebug() << Q_FUNC_INFO << "about to push" << queries.count() << "tracks";
tDebug() << Q_FUNC_INFO << "about to push" << queries.count() << "tracks";
emit tracks( queries );
emit done();
emit t->tracks( queries );
emit t->done();
job->deleteLater();
}, this, job, result, collection );
job->deleteLater();
}

View File

@@ -21,7 +21,7 @@
#include "ScriptEngine.h"
#include "jobview/ScriptErrorStatusMessage.h"
#include "jobview/ErrorStatusMessage.h"
#include "jobview/JobStatusModel.h"
#include "jobview/JobStatusView.h"
#include "utils/Logger.h"
@@ -51,13 +51,13 @@ ScriptEngine::ScriptEngine( JSAccount* parent )
settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
settings()->setOfflineStorageDefaultQuota(100 * 1024 * 1024 /* 100 Mb */);
settings()->setOfflineWebApplicationCacheQuota(100 * 1024 * 1024 /* 100 Mb */);
settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
// HACK
QStringList cmdArgs = QCoreApplication::instance()->arguments();
int position = cmdArgs.indexOf( "--show-inspector" ) + 1;
if ( position > 0 && !cmdArgs.at( position ).isEmpty() && parent->name().contains( cmdArgs.at( position ), Qt::CaseInsensitive ) ) {
QMetaObject::invokeMethod( this, "showWebInspector", Qt::QueuedConnection );
settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
QMetaObject::invokeMethod( this, "initWebInspector", Qt::QueuedConnection );
}
// Tomahawk is not a user agent
@@ -74,13 +74,24 @@ ScriptEngine::ScriptEngine( JSAccount* parent )
}
void
ScriptEngine::initWebInspector()
{
m_webInspector.reset( new QWebInspector() );
m_webInspector->setPage( this );
m_webInspector->setMinimumWidth( 800 );
m_webInspector->setMinimumHeight( 600 );
m_webInspector->show();
}
void
ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID )
{
tLog() << "JAVASCRIPT:" << QString( "%1:%2" ).arg( m_scriptPath ).arg( lineNumber ) << message << sourceID;
#ifdef QT_DEBUG
QFileInfo scriptPath( m_scriptPath );
JobStatusView::instance()->model()->addJob( new ScriptErrorStatusMessage( tr( "%1:%2 %3" ).arg( scriptPath.fileName() ).arg( lineNumber ).arg( message ), m_parent ) );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Resolver Error: %1:%2 %3" ).arg( scriptPath.fileName() ).arg( lineNumber ).arg( message ) ) );
#endif
}
@@ -99,7 +110,7 @@ ScriptEngine::sslErrorHandler( QNetworkReply* qnr, const QList<QSslError>& errli
QMessageBox question( TomahawkUtils::tomahawkWindow() );
question.setWindowTitle( tr( "SSL Error" ) );
question.setText( tr( "You have asked %applicationName to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br>"
question.setText( tr( "You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br>"
"<b>%2</b><br><br>"
"Do you want to trust this connection?" )
.arg( qnr->url().host() )
@@ -137,21 +148,6 @@ ScriptEngine::setScriptPath( const QString& scriptPath )
}
void
ScriptEngine::showWebInspector()
{
if ( m_webInspector.isNull() )
{
m_webInspector.reset( new QWebInspector() );
m_webInspector->setPage( this );
m_webInspector->setMinimumWidth( 800 );
m_webInspector->setMinimumHeight( 600 );
}
m_webInspector->show();
}
bool
ScriptEngine::shouldInterruptJavaScript()
{

View File

@@ -49,13 +49,13 @@ public:
public slots:
bool shouldInterruptJavaScript();
void showWebInspector();
protected:
virtual void javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID );
private slots:
void sslErrorHandler( QNetworkReply* qnr, const QList<QSslError>& errlist );
void initWebInspector();
private:
JSAccount* m_parent;

View File

@@ -19,7 +19,6 @@
#include "SourceList.h"
#include "../ScriptAccount.h"
#include "../../Pipeline.h"
using namespace Tomahawk;
@@ -30,14 +29,12 @@ 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

@@ -25,14 +25,14 @@ using namespace Tomahawk;
void
ScriptInfoPluginFactory::addPlugin( const QSharedPointer< ScriptInfoPlugin >& infoPlugin ) const
{
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin.data() );
}
void
ScriptInfoPluginFactory::removePlugin( const QSharedPointer< ScriptInfoPlugin >& infoPlugin ) const
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin );
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin.data() );
}
@@ -42,10 +42,10 @@ ScriptInfoPluginFactory::createPlugin( const scriptobject_ptr& object, ScriptAcc
// create infoplugin instance
ScriptInfoPlugin* scriptInfoPlugin = new ScriptInfoPlugin( object, scriptAccount->name() );
QSharedPointer< ScriptInfoPlugin > infoPlugin( scriptInfoPlugin );
Tomahawk::InfoSystem::InfoPluginPtr infoPlugin( scriptInfoPlugin );
// move it to infosystem thread
infoPlugin->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
return infoPlugin;
return QSharedPointer< ScriptInfoPlugin >( scriptInfoPlugin );
}

Some files were not shown because too many files have changed in this diff Show More