Compare commits

...

31 Commits
0.3.1 ... 0.3.2

Author SHA1 Message Date
Jonas Kvinge
59622c52ad Release 0.3.2 2018-09-24 18:42:21 +02:00
Jonas Kvinge
a8e9aba58b Save password as QString 2018-09-24 18:40:05 +02:00
Jonas Kvinge
36563cd1e1 Fix compile 2018-09-22 23:32:11 +02:00
Jonas Kvinge
072e712f0d Add missing QUrl include 2018-09-22 23:30:19 +02:00
Jonas Kvinge
121a186160 Improve contextview and engine code 2018-09-22 23:13:56 +02:00
Jonas Kvinge
15a2ccc21e Change size of comboxes in device settings 2018-09-22 15:54:38 +02:00
Jonas Kvinge
dbd8ea69eb Turn off debug about in Tidal 2018-09-22 15:39:24 +02:00
Jonas Kvinge
8a57356f64 More Tidal fixes 2018-09-22 15:37:42 +02:00
Jonas Kvinge
c77cb002f3 Add support for both ALSA hw and plughw 2018-09-21 23:29:00 +02:00
Jonas Kvinge
aa83a2b40b Add option to change url stream scheme for Tidal 2018-09-21 18:53:27 +02:00
Jonas Kvinge
65b04cac6e Update changelog 2018-09-21 01:20:16 +02:00
Jonas Kvinge
5e577190a8 Fix some copyrights 2018-09-21 01:12:21 +02:00
Jonas Kvinge
0143617056 Fix bug setting wrong temp metadata and bug in pipeline 2018-09-21 00:34:02 +02:00
Jonas Kvinge
a77dde7d3b Fix some includes 2018-09-20 22:48:52 +02:00
Jonas Kvinge
7dcdb7c673 Might be a good idea to actually add the URL Handler :) 2018-09-20 22:37:38 +02:00
Jonas Kvinge
6de8eb56cd Remove dead code in Tidal 2018-09-20 22:29:35 +02:00
Jonas Kvinge
04e272d9bc Fix mistake 2018-09-20 22:22:48 +02:00
Jonas Kvinge
17fe201473 Use URL Handler for Tidal 2018-09-20 22:13:30 +02:00
Jonas Kvinge
25249be37f Change orange to red 2018-09-20 17:44:31 +02:00
Jonas Kvinge
97ec12b5b3 Don't compile devices on windows 2018-09-20 17:36:23 +02:00
Jonas Kvinge
246f82bfad Replace obsolete QStyleOptionViewItemV2-4 2018-09-18 22:17:28 +02:00
Jonas Kvinge
db5679bbe9 Fix lsb-release command 2018-09-18 19:01:59 +02:00
Jonas Kvinge
feb0e1c45b Fix Fedora RPM suffix 2018-09-18 18:43:04 +02:00
Jonas Kvinge
edba837295 Fix RPM Suffix 2018-09-18 17:28:49 +02:00
Jonas Kvinge
55882360ef Fix RPM suffix 2018-09-18 00:45:33 +02:00
Jonas Kvinge
6039370ad6 Fix exclude 2018-09-18 00:45:07 +02:00
Jonas Kvinge
2d238c08d3 Change output filename 2018-09-18 00:31:21 +02:00
Jonas Kvinge
9b337b6a34 Add missing nsi 2018-09-17 19:30:23 +02:00
Jonas Kvinge
370db791aa Add nsi files for x86_64-w64-mingw32 2018-09-17 01:02:48 +02:00
Jonas Kvinge
9e3c547580 Turn back on git revision 2018-09-16 23:13:30 +02:00
Jonas Kvinge
ab5d9b62b8 Fix search error not shown in Tidal search 2018-09-15 22:34:44 +02:00
70 changed files with 2231 additions and 611 deletions

View File

@@ -2,6 +2,18 @@ Strawberry Music Player
======================= =======================
ChangeLog ChangeLog
Version 0.3.2:
* Fixed search error not shown in Tidal search
* Added URL handler for Tidal, now retriving URL's when playing instead of when searching
* Fixed bug in pipeline not setting url
* Fixed bug setting wrong temporary metadata
* Removed device module from windows, since it's not implemented for windows
* Added support for both ALSA hw and plughw
* Added option to change url stream scheme for Tidal
* Added encoding of Tidal token in the source code
* Added encoding of Tidal password in the configuration
Version 0.3.1: Version 0.3.1:
* Added new lyrics provider with lyrics from AudD and API Seeds * Added new lyrics provider with lyrics from AudD and API Seeds

View File

@@ -1,28 +1,65 @@
find_program(LSB_RELEASE_EXEC lsb_release) find_program(LSB_RELEASE_EXEC lsb_release)
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (LSB_RELEASE_EXEC) if (LSB_RELEASE_EXEC)
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target") execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -is | tr '[:upper:]' '[:lower:]' | cut -d' ' -f1"
set(RPM_ARCH x86_64 CACHE STRING "Architecture of the rpm file") OUTPUT_VARIABLE DIST_NAME
if (${LSB_RELEASE_ID_SHORT} STREQUAL "openSUSE") OUTPUT_STRIP_TRAILING_WHITESPACE
set(RPM_DISTRO opensuse CACHE STRING "Suffix of the rpm file") )
add_custom_target(rpm execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -ds | tr '[:upper:]' '[:lower:]' | sed 's/\"//g' | cut -d' ' -f2"
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh OUTPUT_VARIABLE DIST_RELEASE
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/ OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND rpmbuild -bs ${CMAKE_SOURCE_DIR}/dist/opensuse/strawberry.spec )
COMMAND rpmbuild -bb ${CMAKE_SOURCE_DIR}/dist/opensuse/strawberry.spec execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -ds | tr '[:upper:]' '[:lower:]' | sed 's/\"//g' | sed 's/\\.//g' | cut -d' ' -f3"
) OUTPUT_VARIABLE DIST_VERSION
endif() OUTPUT_STRIP_TRAILING_WHITESPACE
if (${LSB_RELEASE_ID_SHORT} STREQUAL "Fedora") )
set(RPM_DISTRO fedora CACHE STRING "Suffix of the rpm file") if (DIST_NAME)
add_custom_target(rpm message(STATUS "Distro Name: ${DIST_NAME}")
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh if (DIST_RELEASE)
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/ message(STATUS "Distro Release: ${DIST_RELEASE}")
COMMAND rpmbuild -bs ${CMAKE_SOURCE_DIR}/dist/fedora/strawberry.spec endif()
COMMAND rpmbuild -bb ${CMAKE_SOURCE_DIR}/dist/fedora/strawberry.spec if (DIST_VERSION)
) message(STATUS "Distro Version: ${DIST_VERSION}")
endif()
set(RPM_ARCH x86_64 CACHE STRING "Architecture of the rpm file")
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
if (${DIST_NAME} STREQUAL "opensuse")
if (DIST_RELEASE)
if (${DIST_RELEASE} STREQUAL "leap")
if (DIST_VERSION)
set(RPM_DISTRO "lp${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
else()
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
endif()
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
else()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
add_custom_target(rpm
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
COMMAND rpmbuild -bs ${CMAKE_SOURCE_DIR}/dist/opensuse/strawberry.spec
COMMAND rpmbuild -bb ${CMAKE_SOURCE_DIR}/dist/opensuse/strawberry.spec
)
elseif (${DIST_NAME} STREQUAL "fedora")
if (DIST_VERSION)
set(RPM_DISTRO "${DIST_NAME}${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
add_custom_target(rpm
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
COMMAND rpmbuild -bs ${CMAKE_SOURCE_DIR}/dist/fedora/strawberry.spec
COMMAND rpmbuild -bb ${CMAKE_SOURCE_DIR}/dist/fedora/strawberry.spec
)
else()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
endif() endif()
endif() endif()

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 0) set(STRAWBERRY_VERSION_MAJOR 0)
set(STRAWBERRY_VERSION_MINOR 3) set(STRAWBERRY_VERSION_MINOR 3)
set(STRAWBERRY_VERSION_PATCH 1) set(STRAWBERRY_VERSION_PATCH 2)
#set(STRAWBERRY_VERSION_PRERELEASE rc1) #set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF) set(INCLUDE_GIT_REVISION OFF)

8
dist/CMakeLists.txt vendored
View File

@@ -2,12 +2,16 @@ execute_process(COMMAND env LC_ALL="en_US.utf8" date "+%a %b %d %Y" OUTPUT_VARIA
execute_process(COMMAND env LC_ALL=C date "+%a, %-d %b %Y %H:%M:%S %z" OUTPUT_VARIABLE DEB_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND env LC_ALL=C date "+%a, %-d %b %Y %H:%M:%S %z" OUTPUT_VARIABLE DEB_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/opensuse/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/opensuse/strawberry.spec @ONLY) if (RPM_DISTRO)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/opensuse/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/opensuse/strawberry.spec @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec @ONLY)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog.in ${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog.in ${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-64.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-64.nsi @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug.nsi @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug.nsi @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug-64.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug-64.nsi @ONLY)
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/) install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)

View File

@@ -16,12 +16,14 @@ echo "Creating $name-$version.tar.xz..."
rm -f "$name-$version.tar.xz" rm -f "$name-$version.tar.xz"
tar -cJf $name-$version.tar.xz \ tar -cJf $name-$version.tar.xz \
--transform "s,^$rootnoslash,$name-$version," $exclude_vcs \ --transform "s,^$rootnoslash,$name-$version," $exclude_vcs \
--exclude "*.tar" \ --exclude="*.tar" \
--exclude "*.tar.*" \ --exclude="*.tar.*" \
--exclude "*.bz" \ --exclude="*.bz" \
--exclude "*.bz2" \ --exclude="*.bz2" \
--exclude "*.xz" \ --exclude="*.xz" \
--exclude ".directory" \ --exclude=".directory" \
--exclude "$root/CMakeLists.txt.user" \ --exclude="*.spec" \
--exclude "$root/build" \ --exclude="*.nsi" \
--exclude="$root/CMakeLists.txt.user" \
--exclude="$root/build" \
"$root" "$root"

445
dist/windows/strawberry-64.nsi.in vendored Normal file
View File

@@ -0,0 +1,445 @@
!define PRODUCT_NAME "Strawberry"
!define PRODUCT_PUBLISHER "Strawberry"
!define PRODUCT_VERSION_MAJOR @STRAWBERRY_VERSION_MAJOR@
!define PRODUCT_VERSION_MINOR @STRAWBERRY_VERSION_MINOR@
!define PRODUCT_VERSION_PATCH @STRAWBERRY_VERSION_PATCH@
!define PRODUCT_DISPLAY_VERSION "@STRAWBERRY_VERSION_PACKAGE@"
!define PRODUCT_DISPLAY_VERSION_SHORT "@STRAWBERRY_VERSION_PACKAGE@"
!define PRODUCT_WEB_SITE "http://www.strawbs.org/"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player"
; Set Application Capabilities info
!define CAPABILITIES_NAME "Strawberry Music Player"
!define CAPABILITIES_LOCAL_NAME "Strawberry"
!define CAPABILITIES_PROGID "Strawberry Music Player"
!define CAPABILITIES_PATH "Software\Clients\Media\Strawberry"
!define CAPABILITIES_DESCRIPTION "Strawberry Music Player"
!define CAPABILITIES_ICON "$INSTDIR\strawberry.ico"
!define CAPABILITIES_REINSTALL "Command to reinstall"
!define CAPABILITIES_HIDE_ICONS "Command to hide icons"
!define CAPABILITIES_SHOW_ICONS "Command to show icons"
SetCompressor /SOLID lzma
!addplugindir nsisplugins
!include "MUI2.nsh"
!include "FileAssociation.nsh"
!include "Capabilities.nsh"
!define MUI_ICON "strawberry.ico"
!define MUI_COMPONENTSPAGE_SMALLDESC
;!define MUI_FINISHPAGE_RUN
;!define MUI_FINISHPAGE_RUN_TEXT "Run Strawberry"
;!define MUI_FINISHPAGE_RUN_FUNCTION "RunStrawberry"
; Installer pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English" ;first language is the default language
Name "${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}-Release-x64.exe"
InstallDir "${PRODUCT_INSTALL_DIR}"
; Get the path where Strawberry was installed previously and set it as default path
InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin
;RequestExecutionLevel user
; Check for previous installation, and call the uninstaller if any
Function CheckPreviousInstall
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
StrCmp $R0 "" done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
previous version or `Cancel` to cancel this upgrade." \
IDOK uninst
Abort
; Run the uninstaller
uninst:
ClearErrors
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
done:
FunctionEnd
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
Call CheckPreviousInstall
FunctionEnd
;Function RunStrawberry
;ShellExecAsUser::ShellExecAsUser "" "$INSTDIR/strawberry.exe" ""
;FunctionEnd
Section "Delete old files" oldfiles
SectionEnd
Section "Strawberry" Strawberry
SetOutPath "$INSTDIR"
File "strawberry.exe"
File "strawberry-tagreader.exe"
File "strawberry.ico"
File "libbz2.dll"
File "libcdio-16.dll"
File "libchromaprint.dll"
File "libcrypto-1_1-x64.dll"
File "libfaad-2.dll"
File "libffi-6.dll"
File "libFLAC-8.dll"
File "libfreetype-6.dll"
File "libgcc_s_seh-1.dll"
File "libgio-2.0-0.dll"
File "libglib-2.0-0.dll"
File "libgmodule-2.0-0.dll"
File "libgobject-2.0-0.dll"
File "libgstapp-1.0-0.dll"
File "libgstaudio-1.0-0.dll"
File "libgstbase-1.0-0.dll"
File "libgstfft-1.0-0.dll"
File "libgstpbutils-1.0-0.dll"
File "libgstreamer-1.0-0.dll"
File "libgstriff-1.0-0.dll"
File "libgstrtp-1.0-0.dll"
File "libgstrtsp-1.0-0.dll"
File "libgstsdp-1.0-0.dll"
File "libgsttag-1.0-0.dll"
File "libgstvideo-1.0-0.dll"
File "libgstnet-1.0-0.dll"
File "libharfbuzz-0.dll"
File "libiconv-2.dll"
File "libintl-8.dll"
File "libjpeg-9.dll"
File "liblastfm5.dll"
File "libmp3lame-0.dll"
File "libogg-0.dll"
File "libopus-0.dll"
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-15.dll"
File "libspeex-1.dll"
File "libsqlite3-0.dll"
File "libssl-1_1-x64.dll"
File "libstdc++-6.dll"
File "libtag.dll"
File "libvorbis-0.dll"
File "libvorbisenc-2.dll"
File "libwavpack-1.dll"
File "libwinpthread-1.dll"
File "Qt5Concurrent.dll"
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Network.dll"
File "Qt5Sql.dll"
File "Qt5Widgets.dll"
File "Qt5Xml.dll"
File "Qt5WinExtras.dll"
File "zlib1.dll"
;File "libmpcdec-5.dll"
;File "libtheora-0.dll"
File "libfftw3-3.dll"
File "libxml2-2.dll"
File "libsoup-2.4-1.dll"
File "liblzma-5.dll"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
Var /GLOBAL AppExe
StrCpy $AppExe "$INSTDIR\strawberry.exe"
StrCpy $AppIcon "$INSTDIR\strawberry.ico"
${RegisterCapabilities}
${RegisterMediaType} ".mp3" $AppExe $AppIcon "MP3 Audio File"
${RegisterMediaType} ".flac" $AppExe $AppIcon "FLAC Audio File"
${RegisterMediaType} ".ogg" $AppExe $AppIcon "OGG Audio File"
${RegisterMediaType} ".spx" $AppExe $AppIcon "OGG Speex Audio File"
${RegisterMediaType} ".m4a" $AppExe $AppIcon "MP4 Audio File"
${RegisterMediaType} ".aac" $AppExe $AppIcon "AAC Audio File"
${RegisterMediaType} ".wma" $AppExe $AppIcon "WMA Audio File"
${RegisterMediaType} ".wav" $AppExe $AppIcon "WAV Audio File"
${RegisterMediaType} ".pls" $AppExe $AppIcon "PLS Audio File"
${RegisterMediaType} ".m3u" $AppExe $AppIcon "M3U Audio File"
${RegisterMediaType} ".xspf" $AppExe $AppIcon "XSPF Audio File"
${RegisterMediaType} ".asx" $AppExe $AppIcon "Windows Media Audio/Video playlist"
${RegisterMimeType} "audio/mp3" "mp3" "{cd3afa76-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/mp4" "m4a" "{cd3afa7c-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/x-ms-wma" "wma" "{cd3afa84-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/wav" "wav" "{cd3afa7b-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/mpegurl" "m3u" "{cd3afa78-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "application/x-wmplayer" "asx" "{cd3afa96-b84f-48f0-9393-7edc34128127}"
SectionEnd
Section "Qt Platforms" platforms
SetOutPath "$INSTDIR\platforms"
File "/oname=qwindows.dll" "platforms\qwindows.dll"
SectionEnd
Section "Qt SQL Drivers" sqldrivers
SetOutPath "$INSTDIR\sqldrivers"
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
SectionEnd
Section "Qt image format plugins" imageformats
SetOutPath "$INSTDIR\imageformats"
File "/oname=qgif.dll" "imageformats\qgif.dll"
File "/oname=qico.dll" "imageformats\qico.dll"
File "/oname=qjpeg.dll" "imageformats\qjpeg.dll"
SectionEnd
Section "Gstreamer plugins" gstreamer-plugins
SetOutPath "$INSTDIR\gstreamer-plugins"
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
File "/oname=libgstapp.dll" "gstreamer-plugins\libgstapp.dll"
File "/oname=libgstasf.dll" "gstreamer-plugins\libgstasf.dll"
File "/oname=libgstaiff.dll" "gstreamer-plugins\libgstaiff.dll"
File "/oname=libgstaudioconvert.dll" "gstreamer-plugins\libgstaudioconvert.dll"
File "/oname=libgstaudiofx.dll" "gstreamer-plugins\libgstaudiofx.dll"
File "/oname=libgstaudioparsers.dll" "gstreamer-plugins\libgstaudioparsers.dll"
File "/oname=libgstaudioresample.dll" "gstreamer-plugins\libgstaudioresample.dll"
File "/oname=libgstaudiotestsrc.dll" "gstreamer-plugins\libgstaudiotestsrc.dll"
File "/oname=libgstautodetect.dll" "gstreamer-plugins\libgstautodetect.dll"
File "/oname=libgstcoreelements.dll" "gstreamer-plugins\libgstcoreelements.dll"
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
File "/oname=libgstequalizer.dll" "gstreamer-plugins\libgstequalizer.dll"
File "/oname=libgstfaad.dll" "gstreamer-plugins\libgstfaad.dll"
File "/oname=libgstflac.dll" "gstreamer-plugins\libgstflac.dll"
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
File "/oname=libgstogg.dll" "gstreamer-plugins\libgstogg.dll"
File "/oname=libgstopusparse.dll" "gstreamer-plugins\libgstopusparse.dll"
File "/oname=libgstplayback.dll" "gstreamer-plugins\libgstplayback.dll"
File "/oname=libgstreplaygain.dll" "gstreamer-plugins\libgstreplaygain.dll"
File "/oname=libgstspectrum.dll" "gstreamer-plugins\libgstspectrum.dll"
File "/oname=libgstspeex.dll" "gstreamer-plugins\libgstspeex.dll"
File "/oname=libgsttaglib.dll" "gstreamer-plugins\libgsttaglib.dll"
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
File "/oname=libgstcdio.dll" "gstreamer-plugins\libgstcdio.dll"
File "/oname=libgsttcp.dll" "gstreamer-plugins\libgsttcp.dll"
File "/oname=libgstudp.dll" "gstreamer-plugins\libgstudp.dll"
File "/oname=libgstsoup.dll" "gstreamer-plugins\libgstsoup.dll"
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
SectionEnd
Section "Start menu items" startmenu
; Create Start Menu folders and shortcuts.
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\strawberry.exe"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstaller"
; Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\strawberry.ico"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd
Section "Uninstall"
; Kill strawberry.exe if it's running
; This calling convention is retarded...
;StrCpy $0 "strawberry.exe"
;KillProc::FindProcesses
;StrCmp $1 "-1" wooops
;StrCmp $0 "0" completed
;DetailPrint "Killing running strawberry.exe..."
;StrCpy $0 "strawberry.exe"
;KillProc::KillProcesses
;StrCmp $1 "-1" wooops
;Sleep 2000
;Goto completed
;wooops:
;DetailPrint "-> Error: Something went wrong while killing running strawberry.exe"
;Abort
;completed:
; Delete all the files
Delete "$INSTDIR\strawberry.ico"
Delete "$INSTDIR\strawberry.exe"
Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\libbz2.dll"
Delete "$INSTDIR\libcdio-16.dll"
Delete "$INSTDIR\libchromaprint.dll"
Delete "$INSTDIR\libcrypto-1_1.dll"
Delete "$INSTDIR\libfaad-2.dll"
Delete "$INSTDIR\libffi-6.dll"
Delete "$INSTDIR\libFLAC-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgmodule-2.0-0.dll"
Delete "$INSTDIR\libgobject-2.0-0.dll"
Delete "$INSTDIR\libgstapp-1.0-0.dll"
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbase-1.0-0.dll"
Delete "$INSTDIR\libgstfft-1.0-0.dll"
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
Delete "$INSTDIR\libgstriff-1.0-0.dll"
Delete "$INSTDIR\libgstrtp-1.0-0.dll"
Delete "$INSTDIR\libgstrtsp-1.0-0.dll"
Delete "$INSTDIR\libgstsdp-1.0-0.dll"
Delete "$INSTDIR\libgsttag-1.0-0.dll"
Delete "$INSTDIR\libgstvideo-1.0-0.dll"
Delete "$INSTDIR\libgstnet-1.0-0.dll"
Delete "$INSTDIR\libharfbuzz-0.dll"
Delete "$INSTDIR\libiconv-2.dll"
Delete "$INSTDIR\libintl-8.dll"
Delete "$INSTDIR\libjpeg-9.dll"
Delete "$INSTDIR\liblastfm5.dll"
Delete "$INSTDIR\libmp3lame-0.dll"
Delete "$INSTDIR\libogg-0.dll"
Delete "$INSTDIR\libopus-0.dll"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-15.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
Delete "$INSTDIR\libssl-1_1.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libtag.dll"
Delete "$INSTDIR\libvorbis-0.dll"
Delete "$INSTDIR\libvorbisenc-2.dll"
Delete "$INSTDIR\libwavpack-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\Qt5Concurrent.dll"
Delete "$INSTDIR\Qt5Core.dll"
Delete "$INSTDIR\Qt5Gui.dll"
Delete "$INSTDIR\Qt5Network.dll"
Delete "$INSTDIR\Qt5Sql.dll"
Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\Qt5Xml.dll"
Delete "$INSTDIR\Qt5WinExtras.dll"
Delete "$INSTDIR\zlib1.dll"
;Delete "$INSTDIR\libmpcdec-5.dll"
;Delete "$INSTDIR\libtheora-0.dll"
Delete "$INSTDIR\libfftw3-3.dll"
Delete "$INSTDIR\libxml2-2.dll"
Delete "$INSTDIR\libsoup-2.4-1.dll"
Delete "$INSTDIR\liblzma-5.dll"
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\imageformats\qgif.dll"
Delete "$INSTDIR\imageformats\qico.dll"
Delete "$INSTDIR\imageformats\qjpeg.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstapp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstasf.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaiff.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstflac.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstgio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsticydemux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstid3demux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstopusparse.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstplayback.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstreplaygain.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstspectrum.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstspeex.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttaglib.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttypefindfunctions.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcdio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttcp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstudp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstsoup.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.
RMDir "$INSTDIR\platforms"
RMDir "$INSTDIR\sqldrivers"
RMDir "$INSTDIR\imageformats"
RMDir "$INSTDIR\gstreamer-plugins"
RMDir "$INSTDIR\xine-plugins"
RMDir "$INSTDIR"
; Remove the Shortcuts
SetShellVarContext all
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
; Remove the entry from 'installed programs list'
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
; Unregister from Default Programs
${UnRegisterCapabilities}
SectionEnd

505
dist/windows/strawberry-debug-64.nsi.in vendored Normal file
View File

@@ -0,0 +1,505 @@
!define PRODUCT_NAME "Strawberry"
!define PRODUCT_PUBLISHER "Strawberry"
!define PRODUCT_VERSION_MAJOR @STRAWBERRY_VERSION_MAJOR@
!define PRODUCT_VERSION_MINOR @STRAWBERRY_VERSION_MINOR@
!define PRODUCT_VERSION_PATCH @STRAWBERRY_VERSION_PATCH@
!define PRODUCT_DISPLAY_VERSION "@STRAWBERRY_VERSION_PACKAGE@"
!define PRODUCT_DISPLAY_VERSION_SHORT "@STRAWBERRY_VERSION_PACKAGE@"
!define PRODUCT_WEB_SITE "http://www.strawbs.org/"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player"
; Set Application Capabilities info
!define CAPABILITIES_NAME "Strawberry Music Player"
!define CAPABILITIES_LOCAL_NAME "Strawberry"
!define CAPABILITIES_PROGID "Strawberry Music Player"
!define CAPABILITIES_PATH "Software\Clients\Media\Strawberry"
!define CAPABILITIES_DESCRIPTION "Strawberry Music Player"
!define CAPABILITIES_ICON "$INSTDIR\strawberry.ico"
!define CAPABILITIES_REINSTALL "Command to reinstall"
!define CAPABILITIES_HIDE_ICONS "Command to hide icons"
!define CAPABILITIES_SHOW_ICONS "Command to show icons"
SetCompressor /SOLID lzma
!addplugindir nsisplugins
!include "MUI2.nsh"
!include "FileAssociation.nsh"
!include "Capabilities.nsh"
!define MUI_ICON "strawberry.ico"
!define MUI_COMPONENTSPAGE_SMALLDESC
;!define MUI_FINISHPAGE_RUN
;!define MUI_FINISHPAGE_RUN_TEXT "Run Strawberry"
;!define MUI_FINISHPAGE_RUN_FUNCTION "RunStrawberry"
; Installer pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English" ;first language is the default language
Name "${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
InstallDir "${PRODUCT_INSTALL_DIR}"
; Get the path where Strawberry was installed previously and set it as default path
InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin
;RequestExecutionLevel user
; Check for previous installation, and call the uninstaller if any
Function CheckPreviousInstall
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
StrCmp $R0 "" done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
previous version or `Cancel` to cancel this upgrade." \
IDOK uninst
Abort
; Run the uninstaller
uninst:
ClearErrors
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
done:
FunctionEnd
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
Call CheckPreviousInstall
FunctionEnd
;Function RunStrawberry
;ShellExecAsUser::ShellExecAsUser "" "$INSTDIR/strawberry.exe" ""
;FunctionEnd
Section "Delete old files" oldfiles
SectionEnd
Section "Strawberry" Strawberry
SetOutPath "$INSTDIR"
File "strawberry.exe"
File "strawberry-tagreader.exe"
File "strawberry.ico"
File "libbz2.dll"
File "libcdio-16.dll"
File "libchromaprint.dll"
File "libcrypto-1_1-x64.dll"
File "libfaad-2.dll"
File "libffi-6.dll"
File "libFLAC-8.dll"
File "libfreetype-6.dll"
File "libgcc_s_seh-1.dll"
File "libgio-2.0-0.dll"
File "libglib-2.0-0.dll"
File "libgmodule-2.0-0.dll"
File "libgobject-2.0-0.dll"
File "libgstapp-1.0-0.dll"
File "libgstaudio-1.0-0.dll"
File "libgstbase-1.0-0.dll"
File "libgstfft-1.0-0.dll"
File "libgstpbutils-1.0-0.dll"
File "libgstreamer-1.0-0.dll"
File "libgstriff-1.0-0.dll"
File "libgstrtp-1.0-0.dll"
File "libgstrtsp-1.0-0.dll"
File "libgstsdp-1.0-0.dll"
File "libgsttag-1.0-0.dll"
File "libgstvideo-1.0-0.dll"
File "libgstnet-1.0-0.dll"
File "libharfbuzz-0.dll"
File "libiconv-2.dll"
File "libintl-8.dll"
File "libjpeg-9.dll"
File "liblastfm5.dll"
File "libmp3lame-0.dll"
File "libogg-0.dll"
File "libopus-0.dll"
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-15.dll"
File "libspeex-1.dll"
File "libsqlite3-0.dll"
File "libssl-1_1-x64.dll"
File "libstdc++-6.dll"
File "libtag.dll"
File "libvorbis-0.dll"
File "libvorbisenc-2.dll"
File "libwavpack-1.dll"
File "libwinpthread-1.dll"
File "Qt5Concurrent.dll"
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Network.dll"
File "Qt5Sql.dll"
File "Qt5Widgets.dll"
File "Qt5Xml.dll"
File "Qt5WinExtras.dll"
File "zlib1.dll"
File "libxine-2.dll"
File "libmpcdec-5.dll"
File "libtheora-0.dll"
File "libfftw3-3.dll"
File "libxml2-2.dll"
File "libsoup-2.4-1.dll"
File "liblzma-5.dll"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
Var /GLOBAL AppExe
StrCpy $AppExe "$INSTDIR\strawberry.exe"
StrCpy $AppIcon "$INSTDIR\strawberry.ico"
${RegisterCapabilities}
${RegisterMediaType} ".mp3" $AppExe $AppIcon "MP3 Audio File"
${RegisterMediaType} ".flac" $AppExe $AppIcon "FLAC Audio File"
${RegisterMediaType} ".ogg" $AppExe $AppIcon "OGG Audio File"
${RegisterMediaType} ".spx" $AppExe $AppIcon "OGG Speex Audio File"
${RegisterMediaType} ".m4a" $AppExe $AppIcon "MP4 Audio File"
${RegisterMediaType} ".aac" $AppExe $AppIcon "AAC Audio File"
${RegisterMediaType} ".wma" $AppExe $AppIcon "WMA Audio File"
${RegisterMediaType} ".wav" $AppExe $AppIcon "WAV Audio File"
${RegisterMediaType} ".pls" $AppExe $AppIcon "PLS Audio File"
${RegisterMediaType} ".m3u" $AppExe $AppIcon "M3U Audio File"
${RegisterMediaType} ".xspf" $AppExe $AppIcon "XSPF Audio File"
${RegisterMediaType} ".asx" $AppExe $AppIcon "Windows Media Audio/Video playlist"
${RegisterMimeType} "audio/mp3" "mp3" "{cd3afa76-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/mp4" "m4a" "{cd3afa7c-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/x-ms-wma" "wma" "{cd3afa84-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/wav" "wav" "{cd3afa7b-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "audio/mpegurl" "m3u" "{cd3afa78-b84f-48f0-9393-7edc34128127}"
${RegisterMimeType} "application/x-wmplayer" "asx" "{cd3afa96-b84f-48f0-9393-7edc34128127}"
SectionEnd
Section "Qt Platforms" platforms
SetOutPath "$INSTDIR\platforms"
File "/oname=qwindows.dll" "platforms\qwindows.dll"
SectionEnd
Section "Qt SQL Drivers" sqldrivers
SetOutPath "$INSTDIR\sqldrivers"
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
SectionEnd
Section "Qt image format plugins" imageformats
SetOutPath "$INSTDIR\imageformats"
File "/oname=qgif.dll" "imageformats\qgif.dll"
File "/oname=qico.dll" "imageformats\qico.dll"
File "/oname=qjpeg.dll" "imageformats\qjpeg.dll"
SectionEnd
Section "Gstreamer plugins" gstreamer-plugins
SetOutPath "$INSTDIR\gstreamer-plugins"
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
File "/oname=libgstapp.dll" "gstreamer-plugins\libgstapp.dll"
File "/oname=libgstasf.dll" "gstreamer-plugins\libgstasf.dll"
File "/oname=libgstaiff.dll" "gstreamer-plugins\libgstaiff.dll"
File "/oname=libgstaudioconvert.dll" "gstreamer-plugins\libgstaudioconvert.dll"
File "/oname=libgstaudiofx.dll" "gstreamer-plugins\libgstaudiofx.dll"
File "/oname=libgstaudioparsers.dll" "gstreamer-plugins\libgstaudioparsers.dll"
File "/oname=libgstaudioresample.dll" "gstreamer-plugins\libgstaudioresample.dll"
File "/oname=libgstaudiotestsrc.dll" "gstreamer-plugins\libgstaudiotestsrc.dll"
File "/oname=libgstautodetect.dll" "gstreamer-plugins\libgstautodetect.dll"
File "/oname=libgstcoreelements.dll" "gstreamer-plugins\libgstcoreelements.dll"
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
File "/oname=libgstequalizer.dll" "gstreamer-plugins\libgstequalizer.dll"
File "/oname=libgstfaad.dll" "gstreamer-plugins\libgstfaad.dll"
File "/oname=libgstflac.dll" "gstreamer-plugins\libgstflac.dll"
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
File "/oname=libgstogg.dll" "gstreamer-plugins\libgstogg.dll"
File "/oname=libgstopusparse.dll" "gstreamer-plugins\libgstopusparse.dll"
File "/oname=libgstplayback.dll" "gstreamer-plugins\libgstplayback.dll"
File "/oname=libgstreplaygain.dll" "gstreamer-plugins\libgstreplaygain.dll"
File "/oname=libgstspectrum.dll" "gstreamer-plugins\libgstspectrum.dll"
File "/oname=libgstspeex.dll" "gstreamer-plugins\libgstspeex.dll"
File "/oname=libgsttaglib.dll" "gstreamer-plugins\libgsttaglib.dll"
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
File "/oname=libgstcdio.dll" "gstreamer-plugins\libgstcdio.dll"
File "/oname=libgsttcp.dll" "gstreamer-plugins\libgsttcp.dll"
File "/oname=libgstudp.dll" "gstreamer-plugins\libgstudp.dll"
File "/oname=libgstsoup.dll" "gstreamer-plugins\libgstsoup.dll"
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
SectionEnd
Section "Xine plugins" xine-plugins
SetOutPath "$INSTDIR\xine-plugins"
File "/oname=xineplug_ao_out_directx2.dll" "xine-plugins\xineplug_ao_out_directx2.dll"
File "/oname=xineplug_ao_out_directx.dll" "xine-plugins\xineplug_ao_out_directx.dll"
File "/oname=xineplug_decode_dts.dll" "xine-plugins\xineplug_decode_dts.dll"
File "/oname=xineplug_decode_dvaudio.dll" "xine-plugins\xineplug_decode_dvaudio.dll"
File "/oname=xineplug_decode_faad.dll" "xine-plugins\xineplug_decode_faad.dll"
File "/oname=xineplug_decode_gsm610.dll" "xine-plugins\xineplug_decode_gsm610.dll"
File "/oname=xineplug_decode_lpcm.dll" "xine-plugins\xineplug_decode_lpcm.dll"
File "/oname=xineplug_decode_mad.dll" "xine-plugins\xineplug_decode_mad.dll"
File "/oname=xineplug_decode_mpc.dll" "xine-plugins\xineplug_decode_mpc.dll"
File "/oname=xineplug_decode_mpeg2.dll" "xine-plugins\xineplug_decode_mpeg2.dll"
File "/oname=xineplug_dmx_asf.dll" "xine-plugins\xineplug_dmx_asf.dll"
File "/oname=xineplug_dmx_audio.dll" "xine-plugins\xineplug_dmx_audio.dll"
File "/oname=xineplug_dmx_playlist.dll" "xine-plugins\xineplug_dmx_playlist.dll"
File "/oname=xineplug_dmx_slave.dll" "xine-plugins\xineplug_dmx_slave.dll"
File "/oname=xineplug_flac.dll" "xine-plugins\xineplug_flac.dll"
File "/oname=xineplug_wavpack.dll" "xine-plugins\xineplug_wavpack.dll"
File "/oname=xineplug_xiph.dll" "xine-plugins\xineplug_xiph.dll"
File "/oname=xineplug_inp_cdda.dll" "xine-plugins\xineplug_inp_cdda.dll"
File "/oname=xineplug_post_audio_filters.dll" "xine-plugins\xineplug_post_audio_filters.dll"
File "/oname=xineplug_post_goom.dll" "xine-plugins\xineplug_post_goom.dll"
File "/oname=xineplug_post_mosaico.dll" "xine-plugins\xineplug_post_mosaico.dll"
File "/oname=xineplug_post_planar.dll" "xine-plugins\xineplug_post_planar.dll"
File "/oname=xineplug_post_switch.dll" "xine-plugins\xineplug_post_switch.dll"
File "/oname=xineplug_post_tvtime.dll" "xine-plugins\xineplug_post_tvtime.dll"
File "/oname=xineplug_post_visualizations.dll" "xine-plugins\xineplug_post_visualizations.dll"
SectionEnd
Section "Start menu items" startmenu
; Create Start Menu folders and shortcuts.
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\strawberry.exe"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstaller"
; Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\strawberry.ico"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd
Section "Uninstall"
; Kill strawberry.exe if it's running
; This calling convention is retarded...
;StrCpy $0 "strawberry.exe"
;KillProc::FindProcesses
;StrCmp $1 "-1" wooops
;StrCmp $0 "0" completed
;DetailPrint "Killing running strawberry.exe..."
;StrCpy $0 "strawberry.exe"
;KillProc::KillProcesses
;StrCmp $1 "-1" wooops
;Sleep 2000
;Goto completed
;wooops:
;DetailPrint "-> Error: Something went wrong while killing running strawberry.exe"
;Abort
;completed:
; Delete all the files
Delete "$INSTDIR\strawberry.ico"
Delete "$INSTDIR\strawberry.exe"
Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\libbz2.dll"
Delete "$INSTDIR\libcdio-16.dll"
Delete "$INSTDIR\libchromaprint.dll"
Delete "$INSTDIR\libcrypto-1_1.dll"
Delete "$INSTDIR\libfaad-2.dll"
Delete "$INSTDIR\libffi-6.dll"
Delete "$INSTDIR\libFLAC-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgmodule-2.0-0.dll"
Delete "$INSTDIR\libgobject-2.0-0.dll"
Delete "$INSTDIR\libgstapp-1.0-0.dll"
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbase-1.0-0.dll"
Delete "$INSTDIR\libgstfft-1.0-0.dll"
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
Delete "$INSTDIR\libgstriff-1.0-0.dll"
Delete "$INSTDIR\libgstrtp-1.0-0.dll"
Delete "$INSTDIR\libgstrtsp-1.0-0.dll"
Delete "$INSTDIR\libgstsdp-1.0-0.dll"
Delete "$INSTDIR\libgsttag-1.0-0.dll"
Delete "$INSTDIR\libgstvideo-1.0-0.dll"
Delete "$INSTDIR\libgstnet-1.0-0.dll"
Delete "$INSTDIR\libharfbuzz-0.dll"
Delete "$INSTDIR\libiconv-2.dll"
Delete "$INSTDIR\libintl-8.dll"
Delete "$INSTDIR\libjpeg-9.dll"
Delete "$INSTDIR\liblastfm5.dll"
Delete "$INSTDIR\libmp3lame-0.dll"
Delete "$INSTDIR\libogg-0.dll"
Delete "$INSTDIR\libopus-0.dll"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-15.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
Delete "$INSTDIR\libssl-1_1.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libtag.dll"
Delete "$INSTDIR\libvorbis-0.dll"
Delete "$INSTDIR\libvorbisenc-2.dll"
Delete "$INSTDIR\libwavpack-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\Qt5Concurrent.dll"
Delete "$INSTDIR\Qt5Core.dll"
Delete "$INSTDIR\Qt5Gui.dll"
Delete "$INSTDIR\Qt5Network.dll"
Delete "$INSTDIR\Qt5Sql.dll"
Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\Qt5Xml.dll"
Delete "$INSTDIR\Qt5WinExtras.dll"
Delete "$INSTDIR\zlib1.dll"
Delete "$INSTDIR\libxine-2.dll"
Delete "$INSTDIR\libmpcdec-5.dll"
Delete "$INSTDIR\libtheora-0.dll"
Delete "$INSTDIR\libfftw3-3.dll"
Delete "$INSTDIR\libxml2-2.dll"
Delete "$INSTDIR\libsoup-2.4-1.dll"
Delete "$INSTDIR\liblzma-5.dll"
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\imageformats\qgif.dll"
Delete "$INSTDIR\imageformats\qico.dll"
Delete "$INSTDIR\imageformats\qjpeg.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstapp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstasf.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaiff.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstflac.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstgio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsticydemux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstid3demux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstopusparse.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstplayback.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstreplaygain.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstspectrum.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstspeex.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttaglib.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttypefindfunctions.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcdio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttcp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstudp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstsoup.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
Delete "$INSTDIR\xine-plugins\xineplug_ao_out_directx2.dll"
Delete "$INSTDIR\xine-plugins\xineplug_ao_out_directx.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_dts.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_dvaudio.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_faad.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_gsm610.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_lpcm.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_mad.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_mpc.dll"
Delete "$INSTDIR\xine-plugins\xineplug_decode_mpeg2.dll"
Delete "$INSTDIR\xine-plugins\xineplug_dmx_asf.dll"
Delete "$INSTDIR\xine-plugins\xineplug_dmx_audio.dll"
Delete "$INSTDIR\xine-plugins\xineplug_dmx_playlist.dll"
Delete "$INSTDIR\xine-plugins\xineplug_dmx_slave.dll"
Delete "$INSTDIR\xine-plugins\xineplug_flac.dll"
Delete "$INSTDIR\xine-plugins\xineplug_wavpack.dll"
Delete "$INSTDIR\xine-plugins\xineplug_xiph.dll"
Delete "$INSTDIR\xine-plugins\xineplug_inp_cdda.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_audio_filters.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_goom.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_mosaico.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_planar.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_switch.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_tvtime.dll"
Delete "$INSTDIR\xine-plugins\xineplug_post_visualizations.dll"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.
RMDir "$INSTDIR\platforms"
RMDir "$INSTDIR\sqldrivers"
RMDir "$INSTDIR\imageformats"
RMDir "$INSTDIR\gstreamer-plugins"
RMDir "$INSTDIR\xine-plugins"
RMDir "$INSTDIR"
; Remove the Shortcuts
SetShellVarContext all
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
; Remove the entry from 'installed programs list'
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
; Unregister from Default Programs
${UnRegisterCapabilities}
SectionEnd

View File

@@ -48,7 +48,7 @@ SetCompressor /SOLID lzma
!insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "English" ;first language is the default language
Name "${PRODUCT_NAME}" Name "${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}.exe" OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}-Release.exe"
InstallDir "${PRODUCT_INSTALL_DIR}" InstallDir "${PRODUCT_INSTALL_DIR}"
; Get the path where Strawberry was installed previously and set it as default path ; Get the path where Strawberry was installed previously and set it as default path

View File

@@ -192,8 +192,14 @@ set(SOURCES
covermanager/coverfromurldialog.cpp covermanager/coverfromurldialog.cpp
covermanager/musicbrainzcoverprovider.cpp covermanager/musicbrainzcoverprovider.cpp
covermanager/discogscoverprovider.cpp covermanager/discogscoverprovider.cpp
#covermanager/amazoncoverprovider.cpp
lyrics/lyricsproviders.cpp
lyrics/lyricsprovider.cpp
lyrics/lyricsfetcher.cpp
lyrics/lyricsfetchersearch.cpp
lyrics/auddlyricsprovider.cpp
lyrics/apiseedslyricsprovider.cpp
settings/settingsdialog.cpp settings/settingsdialog.cpp
settings/settingspage.cpp settings/settingspage.cpp
settings/behavioursettingspage.cpp settings/behavioursettingspage.cpp
@@ -247,16 +253,6 @@ set(SOURCES
globalshortcuts/gnomeglobalshortcutbackend.cpp globalshortcuts/gnomeglobalshortcutbackend.cpp
globalshortcuts/qxtglobalshortcutbackend.cpp globalshortcuts/qxtglobalshortcutbackend.cpp
globalshortcuts/globalshortcutgrabber.cpp globalshortcuts/globalshortcutgrabber.cpp
device/connecteddevice.cpp
device/devicedatabasebackend.cpp
device/devicelister.cpp
device/devicemanager.cpp
device/deviceproperties.cpp
device/devicestatefiltermodel.cpp
device/deviceview.cpp
device/deviceviewcontainer.cpp
device/filesystemdevice.cpp
internet/internetmodel.cpp internet/internetmodel.cpp
internet/internetservice.cpp internet/internetservice.cpp
@@ -268,13 +264,7 @@ set(SOURCES
tidal/tidalsearchmodel.cpp tidal/tidalsearchmodel.cpp
tidal/tidalsearchsortmodel.cpp tidal/tidalsearchsortmodel.cpp
tidal/tidalsearchitemdelegate.cpp tidal/tidalsearchitemdelegate.cpp
tidal/tidalurlhandler.cpp
lyrics/lyricsproviders.cpp
lyrics/lyricsprovider.cpp
lyrics/lyricsfetcher.cpp
lyrics/lyricsfetchersearch.cpp
lyrics/auddlyricsprovider.cpp
lyrics/apiseedslyricsprovider.cpp
) )
@@ -369,7 +359,13 @@ set(HEADERS
covermanager/coverfromurldialog.h covermanager/coverfromurldialog.h
covermanager/musicbrainzcoverprovider.h covermanager/musicbrainzcoverprovider.h
covermanager/discogscoverprovider.h covermanager/discogscoverprovider.h
#covermanager/amazoncoverprovider.h
lyrics/lyricsproviders.h
lyrics/lyricsprovider.h
lyrics/lyricsfetcher.h
lyrics/lyricsfetchersearch.h
lyrics/auddlyricsprovider.h
lyrics/apiseedslyricsprovider.h
settings/settingsdialog.h settings/settingsdialog.h
settings/settingspage.h settings/settingspage.h
@@ -421,16 +417,6 @@ set(HEADERS
globalshortcuts/globalshortcuts.h globalshortcuts/globalshortcuts.h
globalshortcuts/gnomeglobalshortcutbackend.h globalshortcuts/gnomeglobalshortcutbackend.h
globalshortcuts/globalshortcutgrabber.h globalshortcuts/globalshortcutgrabber.h
device/connecteddevice.h
device/devicedatabasebackend.h
device/devicelister.h
device/devicemanager.h
device/deviceproperties.h
device/devicestatefiltermodel.h
device/deviceviewcontainer.h
device/deviceview.h
device/filesystemdevice.h
internet/internetmodel.h internet/internetmodel.h
internet/internetservice.h internet/internetservice.h
@@ -441,13 +427,7 @@ set(HEADERS
tidal/tidalsearch.h tidal/tidalsearch.h
tidal/tidalsearchview.h tidal/tidalsearchview.h
tidal/tidalsearchmodel.h tidal/tidalsearchmodel.h
tidal/tidalurlhandler.h
lyrics/lyricsproviders.h
lyrics/lyricsprovider.h
lyrics/lyricsfetcher.h
lyrics/lyricsfetchersearch.h
lyrics/auddlyricsprovider.h
lyrics/apiseedslyricsprovider.h
) )
@@ -499,9 +479,6 @@ set(UI
widgets/osdpretty.ui widgets/osdpretty.ui
widgets/fileview.ui widgets/fileview.ui
widgets/loginstatewidget.ui widgets/loginstatewidget.ui
device/deviceproperties.ui
device/deviceviewcontainer.ui
globalshortcuts/globalshortcutgrabber.ui globalshortcuts/globalshortcutgrabber.ui
@@ -679,6 +656,32 @@ optional_source(HAVE_DBUS
core/mpris2.h core/mpris2.h
) )
optional_source(UNIX
SOURCES
device/connecteddevice.cpp
device/devicedatabasebackend.cpp
device/devicelister.cpp
device/devicemanager.cpp
device/devicestatefiltermodel.cpp
device/filesystemdevice.cpp
device/deviceviewcontainer.cpp
device/deviceview.cpp
device/deviceproperties.cpp
HEADERS
device/connecteddevice.h
device/devicedatabasebackend.h
device/devicelister.h
device/devicemanager.h
device/devicestatefiltermodel.h
device/filesystemdevice.h
device/deviceviewcontainer.h
device/deviceview.h
device/deviceproperties.h
UI
device/deviceproperties.ui
device/deviceviewcontainer.ui
)
if(HAVE_DBUS) if(HAVE_DBUS)
optional_source(HAVE_DEVICEKIT optional_source(HAVE_DEVICEKIT
SOURCES device/devicekitlister.cpp SOURCES device/devicekitlister.cpp

View File

@@ -68,8 +68,10 @@
#include "collectionitem.h" #include "collectionitem.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "collectionview.h" #include "collectionview.h"
#include "device/devicemanager.h" #ifndef Q_OS_WIN
#include "device/devicestatefiltermodel.h" # include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h" #include "dialogs/edittagdialog.h"
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
#include "dialogs/organisedialog.h" #include "dialogs/organisedialog.h"
@@ -460,7 +462,9 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
context_menu_->addSeparator(); context_menu_->addSeparator();
organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise())); organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
#ifndef Q_OS_WIN
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice())); copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
#endif
//delete_ = context_menu_->addAction(IconLoader::Load("edit-delete"), tr("Delete from disk..."), this, SLOT(Delete())); //delete_ = context_menu_->addAction(IconLoader::Load("edit-delete"), tr("Delete from disk..."), this, SLOT(Delete()));
#endif #endif
@@ -477,7 +481,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_->addMenu(filter_->menu()); context_menu_->addMenu(filter_->menu());
#ifdef HAVE_GSTREAMER #if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0); copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool))); connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool)));
#endif #endif
@@ -518,7 +522,9 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
// only when no smart playlists selected // only when no smart playlists selected
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
organise_->setVisible(regular_elements_only); organise_->setVisible(regular_elements_only);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only); copy_to_device_->setVisible(regular_elements_only);
#endif
//delete_->setVisible(regular_elements_only); //delete_->setVisible(regular_elements_only);
#endif #endif
show_in_various_->setVisible(regular_elements_only); show_in_various_->setVisible(regular_elements_only);
@@ -527,7 +533,9 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
// only when all selected items are editable // only when all selected items are editable
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
organise_->setEnabled(regular_elements == regular_editable); organise_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable); copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
//delete_->setEnabled(regular_elements == regular_editable); //delete_->setEnabled(regular_elements == regular_editable);
#endif #endif
@@ -670,7 +678,7 @@ void CollectionView::EditTracks() {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
void CollectionView::CopyToDevice() { void CollectionView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organise_dialog_) if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager())); organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
@@ -678,7 +686,7 @@ void CollectionView::CopyToDevice() {
organise_dialog_->SetCopy(true); organise_dialog_->SetCopy(true);
organise_dialog_->SetSongs(GetSelectedSongs()); organise_dialog_->SetSongs(GetSelectedSongs());
organise_dialog_->show(); organise_dialog_->show();
#endif
} }
#endif #endif

View File

@@ -151,7 +151,9 @@ signals:
QAction *open_in_new_playlist_; QAction *open_in_new_playlist_;
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
QAction *organise_; QAction *organise_;
#ifndef Q_OS_WIN
QAction *copy_to_device_; QAction *copy_to_device_;
#endif
#endif #endif
QAction *delete_; QAction *delete_;
QAction *edit_track_; QAction *edit_track_;

View File

@@ -66,9 +66,12 @@
#include "core/utilities.h" #include "core/utilities.h"
#include "collection/collectionbackend.h" #include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h" #include "collection/collectiondirectorymodel.h"
#include "collection/collectionmodel.h"
#include "collection/collectionitem.h" #include "collection/collectionitem.h"
#include "device/devicemanager.h" #ifndef Q_OS_WIN
#include "device/devicestatefiltermodel.h" # include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h" #include "dialogs/edittagdialog.h"
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
#include "dialogs/organisedialog.h" #include "dialogs/organisedialog.h"
@@ -263,9 +266,6 @@ void ContextAlbumsView::SaveFocus() {
} }
void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) { void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) {
// return;
QModelIndex current = model()->parent(child); QModelIndex current = model()->parent(child);
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type); QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
@@ -359,7 +359,6 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
if (!context_menu_) { if (!context_menu_) {
context_menu_ = new QMenu(this); context_menu_ = new QMenu(this);
//context_menu_->setStyleSheet("background-color: #3DADE8;");
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist())); add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load())); load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
@@ -371,7 +370,9 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
context_menu_->addSeparator(); context_menu_->addSeparator();
organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise())); organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
#ifndef Q_OS_WIN
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice())); copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
#endif
#endif #endif
context_menu_->addSeparator(); context_menu_->addSeparator();
@@ -381,7 +382,7 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_->addSeparator(); context_menu_->addSeparator();
#ifdef HAVE_GSTREAMER #if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0); copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool))); connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool)));
#endif #endif
@@ -418,13 +419,17 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
organise_->setVisible(regular_elements_only); organise_->setVisible(regular_elements_only);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only); copy_to_device_->setVisible(regular_elements_only);
#endif
#endif #endif
// only when all selected items are editable // only when all selected items are editable
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
organise_->setEnabled(regular_elements == regular_editable); organise_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable); copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
#endif #endif
context_menu_->popup(e->globalPos()); context_menu_->popup(e->globalPos());
@@ -509,7 +514,7 @@ void ContextAlbumsView::EditTracks() {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
void ContextAlbumsView::CopyToDevice() { void ContextAlbumsView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organise_dialog_) if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager())); organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
@@ -517,7 +522,7 @@ void ContextAlbumsView::CopyToDevice() {
organise_dialog_->SetCopy(true); organise_dialog_->SetCopy(true);
organise_dialog_->SetSongs(GetSelectedSongs()); organise_dialog_->SetSongs(GetSelectedSongs());
organise_dialog_->show(); organise_dialog_->show();
#endif
} }
#endif #endif

View File

@@ -103,7 +103,9 @@ signals:
void OpenInNewPlaylist(); void OpenInNewPlaylist();
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
void Organise(); void Organise();
//#ifndef Q_OS_WIN
void CopyToDevice(); void CopyToDevice();
//#endif
#endif #endif
void EditTracks(); void EditTracks();
void ShowInBrowser(); void ShowInBrowser();
@@ -124,7 +126,9 @@ signals:
QAction *open_in_new_playlist_; QAction *open_in_new_playlist_;
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
QAction *organise_; QAction *organise_;
#ifndef Q_OS_WIN
QAction *copy_to_device_; QAction *copy_to_device_;
#endif
#endif #endif
QAction *delete_; QAction *delete_;
QAction *edit_track_; QAction *edit_track_;

View File

@@ -82,7 +82,8 @@ ContextView::ContextView(QWidget *parent) :
timeline_fade_(new QTimeLine(1000, this)), timeline_fade_(new QTimeLine(1000, this)),
image_strawberry_(":/pictures/strawberry.png"), image_strawberry_(":/pictures/strawberry.png"),
active_(false), active_(false),
downloading_covers_(false) downloading_covers_(false),
lyrics_id_(-1)
{ {
ui_->setupUi(this); ui_->setupUi(this);
@@ -173,10 +174,10 @@ void ContextView::Playing() {}
void ContextView::Stopped() { void ContextView::Stopped() {
active_ = false; active_ = false;
song_playing_ = song_empty_; song_playing_ = Song();
song_ = song_empty_; song_ = Song();
downloading_covers_ = false; downloading_covers_ = false;
prev_artist_ = QString(); song_prev_ = Song();
lyrics_ = QString(); lyrics_ = QString();
SetImage(image_strawberry_); SetImage(image_strawberry_);
@@ -190,21 +191,40 @@ void ContextView::UpdateNoSong() {
void ContextView::SongChanged(const Song &song) { void ContextView::SongChanged(const Song &song) {
image_previous_ = image_original_; if (song_playing_.is_valid() && song.id() == song_playing_.id() && song.url() == song_playing_.url()) {
prev_artist_ = song_playing_.artist(); UpdateSong(song);
lyrics_ = song.lyrics(); }
song_playing_ = song; else {
song_ = song; song_prev_ = song_playing_;
UpdateSong(); lyrics_ = song.lyrics();
update(); lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title()); song_playing_ = song;
song_ = song;
SetSong(song);
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
lyrics_fetcher_->Clear();
lyrics_id_ = lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
}
}
} }
void ContextView::SetText(QLabel *label, int value, const QString &suffix, const QString &def) { void ContextView::SetLabelEnabled(QLabel *label) {
label->setEnabled(true);
label->setVisible(true);
label->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
void ContextView::SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def) {
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix)); label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
} }
void ContextView::SetLabelDisabled(QLabel *label) {
label->setEnabled(false);
label->setVisible(false);
label->setMaximumSize(0, 0);
}
void ContextView::NoSong() { void ContextView::NoSong() {
ui_->label_stop_top->setStyleSheet( ui_->label_stop_top->setStyleSheet(
@@ -232,7 +252,7 @@ void ContextView::NoSong() {
} }
void ContextView::UpdateSong() { void ContextView::SetSong(const Song &song) {
QList <QLabel *> labels_play_data; QList <QLabel *> labels_play_data;
@@ -251,85 +271,58 @@ void ContextView::UpdateSong() {
"font: 11pt;" "font: 11pt;"
"font-weight: regular;" "font-weight: regular;"
); );
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song_.PrettyTitle().toHtmlEscaped(), song_.artist().toHtmlEscaped(), song_.album().toHtmlEscaped())); ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
if (action_show_data_->isChecked()) { if (action_show_data_->isChecked()) {
for (QLabel *l : labels_play_data) {
l->setEnabled(true);
l->setVisible(true);
l->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
ui_->layout_play_data->setEnabled(true); ui_->layout_play_data->setEnabled(true);
ui_->filetype->setText(song_.TextForFiletype()); SetLabelEnabled(ui_->label_filetype);
ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec())); SetLabelEnabled(ui_->filetype);
if (song_.samplerate() <= 0) { SetLabelEnabled(ui_->label_length);
ui_->label_samplerate->setEnabled(false); SetLabelEnabled(ui_->length);
ui_->label_samplerate->setVisible(false); ui_->filetype->setText(song.TextForFiletype());
ui_->label_samplerate->setMaximumSize(0, 0); ui_->length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
ui_->samplerate->setEnabled(false); if (song.samplerate() <= 0) {
ui_->samplerate->setVisible(false); SetLabelDisabled(ui_->label_samplerate);
ui_->samplerate->setMaximumSize(0, 0); SetLabelDisabled(ui_->samplerate);
ui_->samplerate->clear(); ui_->samplerate->clear();
} }
else { else {
ui_->label_samplerate->setEnabled(true); SetLabelEnabled(ui_->label_samplerate);
ui_->label_samplerate->setVisible(true); SetLabelEnabled(ui_->samplerate);
ui_->label_samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
ui_->samplerate->setEnabled(true);
ui_->samplerate->setVisible(true);
ui_->samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->samplerate, song_.samplerate(), "Hz");
} }
if (song_.bitdepth() <= 0) { if (song.bitdepth() <= 0) {
ui_->label_bitdepth->setEnabled(false); SetLabelDisabled(ui_->label_bitdepth);
ui_->label_bitdepth->setVisible(false); SetLabelDisabled(ui_->bitdepth);
ui_->label_bitdepth->setMaximumSize(0, 0);
ui_->bitdepth->setEnabled(false);
ui_->bitdepth->setVisible(false);
ui_->bitdepth->setMaximumSize(0, 0);
ui_->bitdepth->clear(); ui_->bitdepth->clear();
} }
else { else {
ui_->label_bitdepth->setEnabled(true); SetLabelEnabled(ui_->label_bitdepth);
ui_->label_bitdepth->setVisible(true); SetLabelEnabled(ui_->bitdepth);
ui_->label_bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
ui_->bitdepth->setEnabled(true);
ui_->bitdepth->setVisible(true);
ui_->bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
} }
if (song_.bitrate() <= 0) { if (song.bitrate() <= 0) {
ui_->label_bitrate->setEnabled(false); SetLabelDisabled(ui_->label_bitrate);
ui_->label_bitrate->setVisible(false); SetLabelDisabled(ui_->bitrate);
ui_->label_bitrate->setMaximumSize(0, 0);
ui_->bitrate->setEnabled(false);
ui_->bitrate->setVisible(false);
ui_->bitrate->setMaximumSize(0, 0);
ui_->bitrate->clear(); ui_->bitrate->clear();
} }
else { else {
ui_->label_bitrate->setEnabled(true); SetLabelEnabled(ui_->label_bitrate);
ui_->label_bitrate->setVisible(true); SetLabelEnabled(ui_->bitrate);
ui_->label_bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
ui_->bitrate->setEnabled(true);
ui_->bitrate->setVisible(true);
ui_->bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
} }
ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed); ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
} }
else { else {
for (QLabel *l : labels_play_data) {
l->setEnabled(false);
l->setVisible(false);
l->setMaximumSize(0, 0);
}
ui_->layout_play_data->setEnabled(false);
ui_->filetype->clear(); ui_->filetype->clear();
ui_->length->clear(); ui_->length->clear();
ui_->samplerate->clear(); ui_->samplerate->clear();
ui_->bitdepth->clear(); ui_->bitdepth->clear();
ui_->bitrate->clear(); ui_->bitrate->clear();
for (QLabel *l : labels_play_data) {
SetLabelDisabled(l);
}
ui_->layout_play_data->setEnabled(false);
ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed); ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed);
} }
@@ -403,18 +396,18 @@ void ContextView::UpdateSong() {
ui_->device->setMaximumSize(0, 0); ui_->device->setMaximumSize(0, 0);
} }
if (action_show_albums_->isChecked() && prev_artist_ != song_.artist()) { if (action_show_albums_->isChecked() && song_prev_.artist() != song.artist()) {
const QueryOptions opt; const QueryOptions opt;
CollectionBackend::AlbumList albumlist; CollectionBackend::AlbumList albumlist;
ui_->widget_play_albums->albums_model()->Reset(); ui_->widget_play_albums->albums_model()->Reset();
albumlist = app_->collection_backend()->GetAlbumsByArtist(song_.artist(), opt); albumlist = app_->collection_backend()->GetAlbumsByArtist(song.artist(), opt);
if (albumlist.count() > 1) { if (albumlist.count() > 1) {
ui_->label_play_albums->setVisible(true); ui_->label_play_albums->setVisible(true);
ui_->label_play_albums->setMinimumSize(0, 20); ui_->label_play_albums->setMinimumSize(0, 20);
ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song_.artist().toHtmlEscaped())); ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;"); ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
for (CollectionBackend::Album album : albumlist) { for (CollectionBackend::Album album : albumlist) {
SongList songs = app_->collection_backend()->GetSongs(song_.artist(), album.album_name, opt); SongList songs = app_->collection_backend()->GetSongs(song.artist(), album.album_name, opt);
ui_->widget_play_albums->albums_model()->AddSongs(songs); ui_->widget_play_albums->albums_model()->AddSongs(songs);
} }
ui_->widget_play_albums->setEnabled(true); ui_->widget_play_albums->setEnabled(true);
@@ -454,9 +447,62 @@ void ContextView::UpdateSong() {
} }
void ContextView::UpdateSong(const Song &song) {
if (song.artist() != song_playing_.artist() || song.album() != song_playing_.album() || song.title() != song_playing_.title()) {
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
}
if (action_show_data_->isChecked()) {
if (song.filetype() != song_playing_.filetype()) ui_->filetype->setText(song.TextForFiletype());
if (song.length_nanosec() != song_playing_.length_nanosec()) ui_->label_length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
if (song.samplerate() != song_playing_.samplerate()) {
if (song.samplerate() <= 0) {
SetLabelDisabled(ui_->label_samplerate);
SetLabelDisabled(ui_->samplerate);
ui_->samplerate->clear();
}
else {
SetLabelEnabled(ui_->label_samplerate);
SetLabelEnabled(ui_->samplerate);
SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
}
}
if (song.bitdepth() != song_playing_.bitdepth()) {
if (song.bitdepth() <= 0) {
SetLabelDisabled(ui_->label_bitdepth);
SetLabelDisabled(ui_->bitdepth);
ui_->bitdepth->clear();
}
else {
SetLabelEnabled(ui_->label_bitdepth);
SetLabelEnabled(ui_->bitdepth);
SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
}
}
if (song.bitrate() != song_playing_.bitrate()) {
if (song.bitrate() <= 0) {
SetLabelDisabled(ui_->label_bitrate);
SetLabelDisabled(ui_->bitrate);
ui_->bitrate->clear();
}
else {
SetLabelEnabled(ui_->label_bitrate);
SetLabelEnabled(ui_->bitrate);
SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
}
}
}
song_playing_ = song;
song_ = song;
}
void ContextView::UpdateLyrics(quint64 id, const QString lyrics) { void ContextView::UpdateLyrics(quint64 id, const QString lyrics) {
if (id != lyrics_id_) return;
lyrics_ = lyrics; lyrics_ = lyrics;
lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) { if (action_show_lyrics_->isChecked()) {
ui_->label_play_lyrics->setText(lyrics); ui_->label_play_lyrics->setText(lyrics);
} }
@@ -551,7 +597,9 @@ void ContextView::ScaleCover() {
void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) { void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) {
if (song.id() != song_playing_.id() || song.url() != song_playing_.url()) return;
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return; if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
if (image == image_original_) return;
active_ = true; active_ = true;
downloading_covers_ = false; downloading_covers_ = false;
@@ -572,6 +620,7 @@ void ContextView::SetImage(const QImage &image) {
DrawImage(&p); DrawImage(&p);
p.end(); p.end();
image_previous_ = image_original_;
image_original_ = image; image_original_ = image;
ScaleCover(); ScaleCover();
@@ -623,7 +672,7 @@ void ContextView::ActionShowData() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_data", action_show_data_->isChecked()); s.setValue("show_data", action_show_data_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowOutput() { void ContextView::ActionShowOutput() {
@@ -631,7 +680,7 @@ void ContextView::ActionShowOutput() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_output", action_show_output_->isChecked()); s.setValue("show_output", action_show_output_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowAlbums() { void ContextView::ActionShowAlbums() {
@@ -639,8 +688,8 @@ void ContextView::ActionShowAlbums() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_albums", action_show_albums_->isChecked()); s.setValue("show_albums", action_show_albums_->isChecked());
s.endGroup(); s.endGroup();
prev_artist_ = QString(); song_prev_ = Song();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowLyrics() { void ContextView::ActionShowLyrics() {
@@ -648,8 +697,11 @@ void ContextView::ActionShowLyrics() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_lyrics", action_show_lyrics_->isChecked()); s.setValue("show_lyrics", action_show_lyrics_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title()); if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
lyrics_fetcher_->Clear();
lyrics_id_ = lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
}
} }
void ContextView::SearchCoverAutomatically() { void ContextView::SearchCoverAutomatically() {

View File

@@ -94,21 +94,23 @@ class ContextView : public QWidget {
AlbumCoverLoaderOptions cover_loader_options_; AlbumCoverLoaderOptions cover_loader_options_;
Song song_; Song song_;
Song song_playing_; Song song_playing_;
Song song_empty_; Song song_prev_;
QImage image_original_; QImage image_original_;
QImage image_previous_; QImage image_previous_;
QPixmap pixmap_current_; QPixmap pixmap_current_;
QPixmap pixmap_previous_; QPixmap pixmap_previous_;
qreal pixmap_previous_opacity_; qreal pixmap_previous_opacity_;
std::unique_ptr<QMovie> spinner_animation_; std::unique_ptr<QMovie> spinner_animation_;
qint64 lyrics_id_;
QString prev_artist_;
QString lyrics_; QString lyrics_;
void AddActions(); void AddActions();
void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString()); void SetLabelEnabled(QLabel *label);
void SetLabelDisabled(QLabel *label);
void SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
void NoSong(); void NoSong();
void UpdateSong(); void SetSong(const Song &song);
void UpdateSong(const Song &song);
void SetImage(const QImage &image); void SetImage(const QImage &image);
void DrawImage(QPainter *p); void DrawImage(QPainter *p);
void ScaleCover(); void ScaleCover();

View File

@@ -38,7 +38,9 @@
#include "appearance.h" #include "appearance.h"
#include "engine/enginedevice.h" #include "engine/enginedevice.h"
#include "device/devicemanager.h" #ifndef Q_OS_WIN
# include "device/devicemanager.h"
#endif
#include "collection/collection.h" #include "collection/collection.h"
#include "playlist/playlistbackend.h" #include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h" #include "playlist/playlistmanager.h"
@@ -48,7 +50,6 @@
#ifdef HAVE_LIBLASTFM #ifdef HAVE_LIBLASTFM
#include "covermanager/lastfmcoverprovider.h" #include "covermanager/lastfmcoverprovider.h"
#endif #endif
//#include "covermanager/amazoncoverprovider.h"
#include "covermanager/discogscoverprovider.h" #include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h" #include "covermanager/musicbrainzcoverprovider.h"
@@ -81,7 +82,9 @@ class ApplicationImpl {
task_manager_([=]() { return new TaskManager(app); }), task_manager_([=]() { return new TaskManager(app); }),
player_([=]() { return new Player(app, app); }), player_([=]() { return new Player(app, app); }),
enginedevice_([=]() { return new EngineDevice(app); }), enginedevice_([=]() { return new EngineDevice(app); }),
#ifndef Q_OS_WIN
device_manager_([=]() { return new DeviceManager(app, app); }), device_manager_([=]() { return new DeviceManager(app, app); }),
#endif
collection_([=]() { return new SCollection(app, app); }), collection_([=]() { return new SCollection(app, app); }),
playlist_backend_([=]() { playlist_backend_([=]() {
PlaylistBackend *backend = new PlaylistBackend(app, app); PlaylistBackend *backend = new PlaylistBackend(app, app);
@@ -95,7 +98,6 @@ class ApplicationImpl {
#ifdef HAVE_LIBLASTFM #ifdef HAVE_LIBLASTFM
cover_providers->AddProvider(new LastFmCoverProvider(app)); cover_providers->AddProvider(new LastFmCoverProvider(app));
#endif #endif
//cover_providers->AddProvider(new AmazonCoverProvider(app));
cover_providers->AddProvider(new DiscogsCoverProvider(app)); cover_providers->AddProvider(new DiscogsCoverProvider(app));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app)); cover_providers->AddProvider(new MusicbrainzCoverProvider(app));
return cover_providers; return cover_providers;
@@ -122,7 +124,9 @@ class ApplicationImpl {
Lazy<TaskManager> task_manager_; Lazy<TaskManager> task_manager_;
Lazy<Player> player_; Lazy<Player> player_;
Lazy<EngineDevice> enginedevice_; Lazy<EngineDevice> enginedevice_;
#ifndef Q_OS_WIN
Lazy<DeviceManager> device_manager_; Lazy<DeviceManager> device_manager_;
#endif
Lazy<SCollection> collection_; Lazy<SCollection> collection_;
Lazy<PlaylistBackend> playlist_backend_; Lazy<PlaylistBackend> playlist_backend_;
Lazy<PlaylistManager> playlist_manager_; Lazy<PlaylistManager> playlist_manager_;
@@ -148,7 +152,9 @@ Application::~Application() {
// It's important that the device manager is deleted before the database. // It's important that the device manager is deleted before the database.
// Deleting the database deletes all objects that have been created in its thread, including some device collection backends. // Deleting the database deletes all objects that have been created in its thread, including some device collection backends.
#ifndef Q_OS_WIN
p_->device_manager_.reset(); p_->device_manager_.reset();
#endif
for (QThread *thread : threads_) { for (QThread *thread : threads_) {
thread->quit(); thread->quit();
@@ -198,9 +204,11 @@ CurrentArtLoader *Application::current_art_loader() const {
Database *Application::database() const { return p_->database_.get(); } Database *Application::database() const { return p_->database_.get(); }
#ifndef Q_OS_WIN
DeviceManager *Application::device_manager() const { DeviceManager *Application::device_manager() const {
return p_->device_manager_.get(); return p_->device_manager_.get();
} }
#endif
SCollection *Application::collection() const { return p_->collection_.get(); } SCollection *Application::collection() const { return p_->collection_.get(); }

View File

@@ -45,7 +45,9 @@ class CollectionBackend;
class CollectionModel; class CollectionModel;
class PlaylistBackend; class PlaylistBackend;
class PlaylistManager; class PlaylistManager;
#ifndef Q_OS_WIN
class DeviceManager; class DeviceManager;
#endif
class CoverProviders; class CoverProviders;
class AlbumCoverLoader; class AlbumCoverLoader;
class CurrentArtLoader; class CurrentArtLoader;
@@ -68,7 +70,9 @@ class Application : public QObject {
TaskManager *task_manager() const; TaskManager *task_manager() const;
Player *player() const; Player *player() const;
EngineDevice *enginedevice() const; EngineDevice *enginedevice() const;
#ifndef Q_OS_WIN
DeviceManager *device_manager() const; DeviceManager *device_manager() const;
#endif
SCollection *collection() const; SCollection *collection() const;

View File

@@ -1,8 +1,8 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -121,10 +121,12 @@
#include "covermanager/albumcoverchoicecontroller.h" #include "covermanager/albumcoverchoicecontroller.h"
#include "covermanager/albumcoverloader.h" #include "covermanager/albumcoverloader.h"
#include "covermanager/currentartloader.h" #include "covermanager/currentartloader.h"
#include "device/devicemanager.h" #ifndef Q_OS_WIN
#include "device/devicestatefiltermodel.h" # include "device/devicemanager.h"
#include "device/deviceview.h" # include "device/devicestatefiltermodel.h"
#include "device/deviceviewcontainer.h" # include "device/deviceview.h"
# include "device/deviceviewcontainer.h"
#endif
#include "transcoder/transcodedialog.h" #include "transcoder/transcodedialog.h"
#include "settings/behavioursettingspage.h" #include "settings/behavioursettingspage.h"
#include "settings/playbacksettingspage.h" #include "settings/playbacksettingspage.h"
@@ -167,8 +169,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
context_view_(new ContextView(this)), context_view_(new ContextView(this)),
collection_view_(new CollectionViewContainer(this)), collection_view_(new CollectionViewContainer(this)),
file_view_(new FileView(this)), file_view_(new FileView(this)),
#ifndef Q_OS_WIN
device_view_container_(new DeviceViewContainer(this)), device_view_container_(new DeviceViewContainer(this)),
device_view_(device_view_container_->view()), device_view_(device_view_container_->view()),
#endif
playlist_list_(new PlaylistListContainer(this)), playlist_list_(new PlaylistListContainer(this)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)), settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
cover_manager_([=]() { cover_manager_([=]() {
@@ -246,7 +250,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->tabs->addTab(collection_view_, IconLoader::Load("vinyl"), tr("Collection")); ui_->tabs->addTab(collection_view_, IconLoader::Load("vinyl"), tr("Collection"));
ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files")); ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files"));
ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), tr("Playlists")); ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), tr("Playlists"));
#ifndef Q_OS_WIN
ui_->tabs->addTab(device_view_, IconLoader::Load("device"), tr("Devices")); ui_->tabs->addTab(device_view_, IconLoader::Load("device"), tr("Devices"));
#endif
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal", "Tidal")); ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal", "Tidal"));
//ui_->tabs->AddSpacer(); //ui_->tabs->AddSpacer();
@@ -284,7 +290,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
collection_view_->view()->setModel(collection_sort_model_); collection_view_->view()->setModel(collection_sort_model_);
collection_view_->view()->SetApplication(app_); collection_view_->view()->SetApplication(app_);
#ifndef Q_OS_WIN
device_view_->SetApplication(app_); device_view_->SetApplication(app_);
#endif
playlist_list_->SetApplication(app_); playlist_list_->SetApplication(app_);
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
@@ -344,7 +352,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(file_view_, SIGNAL(CopyToCollection(QList<QUrl>)), SLOT(CopyFilesToCollection(QList<QUrl>))); connect(file_view_, SIGNAL(CopyToCollection(QList<QUrl>)), SLOT(CopyFilesToCollection(QList<QUrl>)));
connect(file_view_, SIGNAL(MoveToCollection(QList<QUrl>)), SLOT(MoveFilesToCollection(QList<QUrl>))); connect(file_view_, SIGNAL(MoveToCollection(QList<QUrl>)), SLOT(MoveFilesToCollection(QList<QUrl>)));
connect(file_view_, SIGNAL(EditTags(QList<QUrl>)), SLOT(EditFileTags(QList<QUrl>))); connect(file_view_, SIGNAL(EditTags(QList<QUrl>)), SLOT(EditFileTags(QList<QUrl>)));
#ifndef Q_OS_WIN
connect(file_view_, SIGNAL(CopyToDevice(QList<QUrl>)), SLOT(CopyFilesToDevice(QList<QUrl>))); connect(file_view_, SIGNAL(CopyToDevice(QList<QUrl>)), SLOT(CopyFilesToDevice(QList<QUrl>)));
#endif
#endif #endif
file_view_->SetTaskManager(app_->task_manager()); file_view_->SetTaskManager(app_->task_manager());
@@ -469,8 +479,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(app_->task_manager(), SIGNAL(PauseCollectionWatchers()), app_->collection(), SLOT(PauseWatcher())); connect(app_->task_manager(), SIGNAL(PauseCollectionWatchers()), app_->collection(), SLOT(PauseWatcher()));
connect(app_->task_manager(), SIGNAL(ResumeCollectionWatchers()), app_->collection(), SLOT(ResumeWatcher())); connect(app_->task_manager(), SIGNAL(ResumeCollectionWatchers()), app_->collection(), SLOT(ResumeWatcher()));
#ifndef Q_OS_WIN
// Devices connections // Devices connections
connect(device_view_, SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*))); connect(device_view_, SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
// Collection filter widget // Collection filter widget
QActionGroup *collection_view_group = new QActionGroup(this); QActionGroup *collection_view_group = new QActionGroup(this);
@@ -529,7 +541,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Copy to collection..."), this, SLOT(PlaylistCopyToCollection())); playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Copy to collection..."), this, SLOT(PlaylistCopyToCollection()));
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load("go-jump"), tr("Move to collection..."), this, SLOT(PlaylistMoveToCollection())); playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load("go-jump"), tr("Move to collection..."), this, SLOT(PlaylistMoveToCollection()));
//playlist_organise_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(PlaylistMoveToCollection())); //playlist_organise_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(PlaylistMoveToCollection()));
#ifndef Q_OS_WIN
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(PlaylistCopyToDevice())); playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(PlaylistCopyToDevice()));
#endif
#endif #endif
//playlist_delete_ = playlist_menu_->addAction(IconLoader::Load("edit-delete"), tr("Delete from disk..."), this, SLOT(PlaylistDelete())); //playlist_delete_ = playlist_menu_->addAction(IconLoader::Load("edit-delete"), tr("Delete from disk..."), this, SLOT(PlaylistDelete()));
playlist_open_in_browser_ = playlist_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, SLOT(PlaylistOpenInBrowser())); playlist_open_in_browser_ = playlist_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, SLOT(PlaylistOpenInBrowser()));
@@ -551,7 +565,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->playlist, SIGNAL(UndoRedoActionsChanged(QAction*, QAction*)), SLOT(PlaylistUndoRedoChanged(QAction*, QAction*))); connect(ui_->playlist, SIGNAL(UndoRedoActionsChanged(QAction*, QAction*)), SLOT(PlaylistUndoRedoChanged(QAction*, QAction*)));
#ifdef HAVE_GSTREAMER #if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0); playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), playlist_copy_to_device_, SLOT(setDisabled(bool))); connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), playlist_copy_to_device_, SLOT(setDisabled(bool)));
#endif #endif
@@ -817,8 +831,8 @@ void MainWindow::MediaStopped() {
tray_icon_->SetProgress(0); tray_icon_->SetProgress(0);
tray_icon_->SetStopped(); tray_icon_->SetStopped();
song_playing_ = song_empty_; song_playing_ = Song();
song_ = song_empty_; song_ = Song();
image_original_ = QImage(); image_original_ = QImage();
} }
@@ -1324,7 +1338,9 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
playlist_copy_to_collection_->setVisible(false); playlist_copy_to_collection_->setVisible(false);
playlist_move_to_collection_->setVisible(false); playlist_move_to_collection_->setVisible(false);
//playlist_organise_->setVisible(false); //playlist_organise_->setVisible(false);
#ifndef Q_OS_WIN
playlist_copy_to_device_->setVisible(false); playlist_copy_to_device_->setVisible(false);
#endif
#endif #endif
playlist_open_in_browser_->setVisible(false); playlist_open_in_browser_->setVisible(false);
@@ -1393,7 +1409,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
} }
#endif #endif
#ifdef HAVE_GSTREAMER #if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
playlist_copy_to_device_->setVisible(editable); playlist_copy_to_device_->setVisible(editable);
#endif #endif
@@ -1830,6 +1846,7 @@ void MainWindow::MoveFilesToCollection(const QList<QUrl> &urls) {
} }
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) { void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
#ifndef Q_OS_WIN
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true); organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true); organise_dialog_->SetCopy(true);
if (organise_dialog_->SetUrls(urls)) if (organise_dialog_->SetUrls(urls))
@@ -1837,6 +1854,7 @@ void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
else { else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device")); QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
} }
#endif
} }
#endif #endif
@@ -1968,15 +1986,14 @@ void MainWindow::PlaylistSkip() {
} }
#ifdef HAVE_GSTREAMER #if defined(HAVE_GSTREAMER)
void MainWindow::PlaylistCopyToDevice() { void MainWindow::PlaylistCopyToDevice() {
#if !defined(Q_OS_WIN)
QModelIndexList proxy_indexes = ui_->playlist->view()->selectionModel()->selectedRows(); QModelIndexList proxy_indexes = ui_->playlist->view()->selectionModel()->selectedRows();
SongList songs; SongList songs;
for (const QModelIndex &proxy_index : proxy_indexes) { for (const QModelIndex &proxy_index : proxy_indexes) {
QModelIndex index = app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index); QModelIndex index = app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index);
songs << app_->playlist_manager()->current()->item_at(index.row())->Metadata(); songs << app_->playlist_manager()->current()->item_at(index.row())->Metadata();
} }
@@ -1985,9 +2002,9 @@ void MainWindow::PlaylistCopyToDevice() {
if (organise_dialog_->SetSongs(songs)) if (organise_dialog_->SetSongs(songs))
organise_dialog_->show(); organise_dialog_->show();
else { else {
QMessageBox::warning(this, tr("Error"), QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
tr("None of the selected songs were suitable for copying to a device"));
} }
#endif
} }
#endif #endif

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -63,8 +64,10 @@ class ContextView;
class CollectionViewContainer; class CollectionViewContainer;
class AlbumCoverChoiceController; class AlbumCoverChoiceController;
class CommandlineOptions; class CommandlineOptions;
#ifndef Q_OS_WIN
class DeviceView; class DeviceView;
class DeviceViewContainer; class DeviceViewContainer;
#endif
class EditTagDialog; class EditTagDialog;
class Equalizer; class Equalizer;
class ErrorDialog; class ErrorDialog;
@@ -134,7 +137,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void resizeEvent(QResizeEvent *event); void resizeEvent(QResizeEvent *event);
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN
bool winEvent(MSG *message, long *result); bool winEvent(MSG *message, long *result);
#endif #endif
@@ -183,7 +186,9 @@ signals:
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
void PlaylistCopyToCollection(); void PlaylistCopyToCollection();
void PlaylistMoveToCollection(); void PlaylistMoveToCollection();
#ifndef Q_OS_WIN_
void PlaylistCopyToDevice(); void PlaylistCopyToDevice();
#endif
void PlaylistOrganiseSelected(bool copy); void PlaylistOrganiseSelected(bool copy);
#endif #endif
//void PlaylistDelete(); //void PlaylistDelete();
@@ -202,7 +207,9 @@ signals:
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
void CopyFilesToCollection(const QList<QUrl>& urls); void CopyFilesToCollection(const QList<QUrl>& urls);
void MoveFilesToCollection(const QList<QUrl>& urls); void MoveFilesToCollection(const QList<QUrl>& urls);
//#ifndef Q_OS_WIN
void CopyFilesToDevice(const QList<QUrl>& urls); void CopyFilesToDevice(const QList<QUrl>& urls);
//#endif
#endif #endif
void EditFileTags(const QList<QUrl>& urls); void EditFileTags(const QList<QUrl>& urls);
@@ -308,8 +315,10 @@ signals:
ContextView *context_view_; ContextView *context_view_;
CollectionViewContainer *collection_view_; CollectionViewContainer *collection_view_;
FileView *file_view_; FileView *file_view_;
#ifndef Q_OS_WIN
DeviceViewContainer *device_view_container_; DeviceViewContainer *device_view_container_;
DeviceView *device_view_; DeviceView *device_view_;
#endif
PlaylistListContainer *playlist_list_; PlaylistListContainer *playlist_list_;
Lazy<SettingsDialog> settings_dialog_; Lazy<SettingsDialog> settings_dialog_;
@@ -347,7 +356,9 @@ signals:
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
QAction *playlist_copy_to_collection_; QAction *playlist_copy_to_collection_;
QAction *playlist_move_to_collection_; QAction *playlist_move_to_collection_;
#ifndef Q_OS_WIN
QAction *playlist_copy_to_device_; QAction *playlist_copy_to_device_;
#endif
//QAction *playlist_delete_; //QAction *playlist_delete_;
#endif #endif
QAction *playlist_open_in_browser_; QAction *playlist_open_in_browser_;
@@ -378,7 +389,6 @@ signals:
Song song_; Song song_;
Song song_playing_; Song song_playing_;
Song song_empty_;
QImage image_original_; QImage image_original_;
}; };

View File

@@ -224,14 +224,29 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
qLog(Debug) << "URL handler for" << result.original_url_ << "returned" << result.media_url_; qLog(Debug) << "URL handler for" << result.original_url_ << "returned" << result.media_url_;
Song song = item->Metadata();
bool update(false);
// If there was no filetype in the song's metadata, use the one provided by URL handler, if there is one
if (
(item->Metadata().filetype() == Song::FileType_Unknown && result.filetype_ != Song::FileType_Unknown)
||
(item->Metadata().filetype() == Song::FileType_Stream && result.filetype_ != Song::FileType_Stream)
)
{
song.set_filetype(result.filetype_);
update = true;
}
// If there was no length info in song's metadata, use the one provided by URL handler, if there is one // If there was no length info in song's metadata, use the one provided by URL handler, if there is one
if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) { if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) {
Song song = item->Metadata();
song.set_length_nanosec(result.length_nanosec_); song.set_length_nanosec(result.length_nanosec_);
update = true;
}
if (update) {
item->SetTemporaryMetadata(song); item->SetTemporaryMetadata(song);
app_->playlist_manager()->active()->InformOfCurrentSongChange(); app_->playlist_manager()->active()->InformOfCurrentSongChange();
} }
engine_->Play(result.media_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec()); engine_->Play(result.media_url_, result.original_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec());
current_item_ = item; current_item_ = item;
loading_async_ = QUrl(); loading_async_ = QUrl();
@@ -245,6 +260,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
loading_async_ = result.original_url_; loading_async_ = result.original_url_;
break; break;
} }
} }
void Player::Next() { NextInternal(Engine::Manual); } void Player::Next() { NextInternal(Engine::Manual); }
@@ -492,8 +508,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
} }
else { else {
loading_async_ = QUrl(); loading_async_ = QUrl();
engine_->Play(current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec()); engine_->Play(current_item_->Url(), current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec());
} }
} }
@@ -531,10 +546,12 @@ void Player::SeekBackward() {
} }
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle &bundle) { void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle &bundle) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item(); PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
if (!item) return; if (!item) return;
if (bundle.url != item->Metadata().url()) return;
Engine::SimpleMetaBundle bundle_copy = bundle; Engine::SimpleMetaBundle bundle_copy = bundle;
// Maybe the metadata is from icycast and has "Artist - Title" shoved together in the title field. // Maybe the metadata is from icycast and has "Artist - Title" shoved together in the title field.
@@ -545,7 +562,8 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle &bundle) {
if (space_dash_pos != -1) { if (space_dash_pos != -1) {
bundle_copy.artist = bundle_copy.title.left(space_dash_pos).trimmed(); bundle_copy.artist = bundle_copy.title.left(space_dash_pos).trimmed();
bundle_copy.title = bundle_copy.title.mid(space_dash_pos + 3).trimmed(); bundle_copy.title = bundle_copy.title.mid(space_dash_pos + 3).trimmed();
} else { }
else {
bundle_copy.artist = bundle_copy.title.left(dash_pos).trimmed(); bundle_copy.artist = bundle_copy.title.left(dash_pos).trimmed();
bundle_copy.title = bundle_copy.title.mid(dash_pos + 1).trimmed(); bundle_copy.title = bundle_copy.title.mid(dash_pos + 1).trimmed();
} }
@@ -661,7 +679,7 @@ void Player::TrackAboutToEnd() {
break; break;
} }
} }
engine_->StartPreloading(url, next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec()); engine_->StartPreloading(url, next_item->Url(), next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec());
} }
@@ -682,7 +700,7 @@ void Player::InvalidSongRequested(const QUrl &url) {
} }
void Player::RegisterUrlHandler(UrlHandler *handler) { void Player::RegisterUrlHandler(UrlHandler *handler) {
const QString scheme = handler->scheme(); const QString scheme = handler->scheme();
if (url_handlers_.contains(scheme)) { if (url_handlers_.contains(scheme)) {
@@ -698,7 +716,7 @@ void Player::RegisterUrlHandler(UrlHandler *handler) {
} }
void Player::UnregisterUrlHandler(UrlHandler *handler) { void Player::UnregisterUrlHandler(UrlHandler *handler) {
const QString scheme = url_handlers_.key(handler); const QString scheme = url_handlers_.key(handler);
if (scheme.isEmpty()) { if (scheme.isEmpty()) {
qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme() << "that wasn't registered"; qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme() << "that wasn't registered";

View File

@@ -996,12 +996,12 @@ void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
if (!bundle.album.isEmpty()) d->album_ = bundle.album; if (!bundle.album.isEmpty()) d->album_ = bundle.album;
if (!bundle.comment.isEmpty()) d->comment_ = bundle.comment; if (!bundle.comment.isEmpty()) d->comment_ = bundle.comment;
if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre; if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre;
if (!bundle.bitrate.isEmpty()) d->bitrate_ = bundle.bitrate.toInt(); if (bundle.bitrate > 0) d->bitrate_ = bundle.bitrate;
if (!bundle.samplerate.isEmpty()) d->samplerate_ = bundle.samplerate.toInt(); if (bundle.samplerate > 0) d->samplerate_ = bundle.samplerate;
if (!bundle.bitdepth.isEmpty()) d->samplerate_ = bundle.bitdepth.toInt(); if (bundle.bitdepth > 0) d->samplerate_ = bundle.bitdepth;
if (!bundle.length.isEmpty()) set_length_nanosec(bundle.length.toLongLong()); if (bundle.length > 0) set_length_nanosec(bundle.length);
if (!bundle.year.isEmpty()) d->year_ = bundle.year.toInt(); if (bundle.year > 0) d->year_ = bundle.year;
if (!bundle.tracknr.isEmpty()) d->track_ = bundle.tracknr.toInt(); if (bundle.tracknr > 0) d->track_ = bundle.tracknr;
} }

View File

@@ -308,7 +308,6 @@ class Song {
void set_image(const QImage &i); void set_image(const QImage &i);
// Comparison functions // Comparison functions
bool IsMetadataEqual(const Song &other) const; bool IsMetadataEqual(const Song &other) const;
bool IsOnSameAlbum(const Song &other) const; bool IsOnSameAlbum(const Song &other) const;

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -23,12 +24,10 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QUrl> #include <QUrl>
#include <QIcon>
#include "song.h"
#include "urlhandler.h" #include "urlhandler.h"
UrlHandler::LoadResult::LoadResult(const QUrl &original_url, Type type, const QUrl &media_url, qint64 length_nanosec) : original_url_(original_url), type_(type), media_url_(media_url), length_nanosec_(length_nanosec) {} UrlHandler::LoadResult::LoadResult(const QUrl &original_url, Type type, const QUrl &media_url, const Song::FileType &filetype, qint64 length_nanosec) : original_url_(original_url), type_(type), media_url_(media_url), filetype_(filetype), length_nanosec_(length_nanosec) {}
UrlHandler::UrlHandler(QObject *parent) : QObject(parent) {} UrlHandler::UrlHandler(QObject *parent) : QObject(parent) {}
QIcon UrlHandler::icon() const { return QIcon(); }

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,7 +28,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QIcon>
#include "song.h"
class UrlHandler : public QObject { class UrlHandler : public QObject {
Q_OBJECT Q_OBJECT
@@ -37,7 +39,6 @@ class UrlHandler : public QObject {
// The URL scheme that this handler handles. // The URL scheme that this handler handles.
virtual QString scheme() const = 0; virtual QString scheme() const = 0;
virtual QIcon icon() const;
// Returned by StartLoading() and LoadNext(), indicates what the player should do when it wants to load a URL. // Returned by StartLoading() and LoadNext(), indicates what the player should do when it wants to load a URL.
struct LoadResult { struct LoadResult {
@@ -53,7 +54,7 @@ class UrlHandler : public QObject {
TrackAvailable, TrackAvailable,
}; };
LoadResult(const QUrl &original_url = QUrl(), Type type = NoMoreTracks, const QUrl &media_url = QUrl(), qint64 length_nanosec_ = -1); LoadResult(const QUrl &original_url = QUrl(), Type type = NoMoreTracks, const QUrl &media_url = QUrl(), const Song::FileType &filetype = Song::FileType_Stream, qint64 length_nanosec_ = -1);
// The url that the playlist item has in Url(). // The url that the playlist item has in Url().
// Might be something unplayable like lastfm://... // Might be something unplayable like lastfm://...
@@ -64,6 +65,9 @@ class UrlHandler : public QObject {
// The actual url to something that gstreamer can play. // The actual url to something that gstreamer can play.
QUrl media_url_; QUrl media_url_;
// The type of the stream
Song::FileType filetype_;
// Track length, if we are able to get it only now // Track length, if we are able to get it only now
qint64 length_nanosec_; qint64 length_nanosec_;
}; };
@@ -78,8 +82,9 @@ class UrlHandler : public QObject {
virtual void TrackAboutToEnd() {}; virtual void TrackAboutToEnd() {};
virtual void TrackSkipped() {}; virtual void TrackSkipped() {};
signals: signals:
void AsyncLoadComplete(const UrlHandler::LoadResult &result); void AsyncLoadComplete(const UrlHandler::LoadResult &result);
}; };
#endif // URLHANDLER_H #endif // URLHANDLER_H

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -690,21 +691,6 @@ bool IsLaptop() {
} }
QString SystemLanguageName() {
#if QT_VERSION >= 0x040800
QString system_language = QLocale::system().uiLanguages().empty() ? QLocale::system().name() : QLocale::system().uiLanguages().first();
// uiLanguages returns strings with "-" as separators for language/region;
// however QTranslator needs "_" separators
system_language.replace("-", "_");
#else
QString system_language = QLocale::system().name();
#endif
return system_language;
}
bool UrlOnSameDriveAsStrawberry(const QUrl &url) { bool UrlOnSameDriveAsStrawberry(const QUrl &url) {
if (url.scheme() != "file") return false; if (url.scheme() != "file") return false;
@@ -723,18 +709,13 @@ bool UrlOnSameDriveAsStrawberry(const QUrl &url) {
} }
QUrl GetRelativePathToStrawberryBin(const QUrl &url) { QUrl GetRelativePathToStrawberryBin(const QUrl &url) {
QDir appPath(QCoreApplication::applicationDirPath()); QDir appPath(QCoreApplication::applicationDirPath());
return QUrl::fromLocalFile(appPath.relativeFilePath(url.toLocalFile())); return QUrl::fromLocalFile(appPath.relativeFilePath(url.toLocalFile()));
} }
QString PathWithoutFilenameExtension(const QString &filename) { QString PathWithoutFilenameExtension(const QString &filename) {
if (filename.section('/', -1, -1).contains('.')) return filename.section('.', 0, -2);
if (filename.section('/', -1, -1).contains('.'))
return filename.section('.', 0, -2);
return filename; return filename;
} }
QString FiddleFileExtension(const QString &filename, const QString &new_extension) { QString FiddleFileExtension(const QString &filename, const QString &new_extension) {
@@ -786,6 +767,28 @@ void CheckPortable() {
} }
QString GetRandomStringWithChars(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
return GetRandomString(len, UseCharacters);
}
QString GetRandomStringWithCharsAndNumbers(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
return GetRandomString(len, UseCharacters);
}
QString GetRandomString(const int len, const QString &UseCharacters) {
QString randstr;
for(int i=0 ; i < len ; ++i) {
int index = qrand() % UseCharacters.length();
QChar nextchar = UseCharacters.at(index);
randstr.append(nextchar);
}
return randstr;
}
} // namespace Utilities } // namespace Utilities
ScopedWCharArray::ScopedWCharArray(const QString &str) ScopedWCharArray::ScopedWCharArray(const QString &str)

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -149,7 +150,10 @@ int GetThreadId();
// Returns true if this machine has a battery. // Returns true if this machine has a battery.
bool IsLaptop(); bool IsLaptop();
QString SystemLanguageName(); QString GetRandomStringWithChars(const int len);
QString GetRandomStringWithCharsAndNumbers(const int len);
QString GetRandomString(const int len, const QString &UseCharacters);
} }
class ScopedWCharArray { class ScopedWCharArray {

View File

@@ -81,7 +81,7 @@ void DeviceItemDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, con
} }
// Draw the background // Draw the background
const QStyleOptionViewItemV3 *vopt = qstyleoption_cast<const QStyleOptionViewItemV3*>(&opt); const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem*>(&opt);
const QWidget *widget = vopt->widget; const QWidget *widget = vopt->widget;
QStyle *style = widget->style() ? widget->style() : QApplication::style(); QStyle *style = widget->style() ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, p, widget); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, p, widget);

View File

@@ -1,8 +1,8 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -99,10 +99,13 @@ QList<DeviceFinder::Device> AlsaDeviceFinder::ListDevices() {
Device device; Device device;
device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo)).arg(snd_pcm_info_get_name(pcminfo)); device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo)).arg(snd_pcm_info_get_name(pcminfo));
device.value = QString("hw:%1,%2").arg(card).arg(dev);
device.iconname = GuessIconName(device.description); device.iconname = GuessIconName(device.description);
device.card = card; device.card = card;
device.device = dev; device.device = dev;
device.value = QString("hw:%1,%2").arg(card).arg(dev);
ret.append(device);
device.value = QString("plughw:%1,%2").arg(card).arg(dev);
ret.append(device); ret.append(device);
} }

View File

@@ -60,11 +60,12 @@ Engine::Base::Base()
Engine::Base::~Base() {} Engine::Base::~Base() {}
bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool Engine::Base::Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
Q_UNUSED(force_stop_at_end); Q_UNUSED(force_stop_at_end);
url_ = url; media_url_ = media_url;
original_url_ = original_url;
beginning_nanosec_ = beginning_nanosec; beginning_nanosec_ = beginning_nanosec;
end_nanosec_ = end_nanosec; end_nanosec_ = end_nanosec;
@@ -73,12 +74,13 @@ bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_en
} }
bool Engine::Base::Play(const QUrl &url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool Engine::Base::Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Load(url, flags, force_stop_at_end, beginning_nanosec, end_nanosec)) if (!Load(media_url, original_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec))
return false; return false;
return Play(0); return Play(0);
} }
void Engine::Base::SetVolume(uint value) { void Engine::Base::SetVolume(uint value) {

View File

@@ -68,8 +68,8 @@ public:
virtual bool Init() = 0; virtual bool Init() = 0;
virtual State state() const = 0; virtual State state() const = 0;
virtual void StartPreloading(const QUrl&, bool, qint64, qint64) {} virtual void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool, qint64, qint64) {}
virtual bool Load(const QUrl &url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); virtual bool Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
virtual bool Play(quint64 offset_nanosec) = 0; virtual bool Play(quint64 offset_nanosec) = 0;
virtual void Stop(bool stop_after = false) = 0; virtual void Stop(bool stop_after = false) = 0;
virtual void Pause() = 0; virtual void Pause() = 0;
@@ -93,10 +93,11 @@ public:
virtual bool ValidOutput(const QString &output) = 0; virtual bool ValidOutput(const QString &output) = 0;
virtual QString DefaultOutput() = 0; virtual QString DefaultOutput() = 0;
virtual bool CustomDeviceSupport(const QString &output) = 0; virtual bool CustomDeviceSupport(const QString &output) = 0;
virtual bool ALSADeviceSupport(const QString &output) = 0;
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length). // Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown. // Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
bool Play(const QUrl &u, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
void SetVolume(uint value); void SetVolume(uint value);
static uint MakeVolumeLogarithmic(uint volume); static uint MakeVolumeLogarithmic(uint volume);
@@ -163,7 +164,8 @@ protected:
uint volume_; uint volume_;
quint64 beginning_nanosec_; quint64 beginning_nanosec_;
qint64 end_nanosec_; qint64 end_nanosec_;
QUrl url_; QUrl media_url_;
QUrl original_url_;
Scope scope_; Scope scope_;
bool buffering_; bool buffering_;
bool equalizer_enabled_; bool equalizer_enabled_;
@@ -202,17 +204,19 @@ private:
}; };
struct SimpleMetaBundle { struct SimpleMetaBundle {
QUrl url;
QString title; QString title;
QString artist; QString artist;
QString album; QString album;
QString comment; QString comment;
QString genre; QString genre;
QString bitrate; qlonglong length;
QString samplerate; int year;
QString bitdepth; int tracknr;
QString length; int samplerate;
QString year; int bitdepth;
QString tracknr; qlonglong bitrate;
QString lyrics;
}; };
} // namespace } // namespace

View File

@@ -25,39 +25,35 @@
#include "enginetype.h" #include "enginetype.h"
namespace Engine { namespace Engine {
Engine::EngineType EngineTypeFromName(QString enginename) { Engine::EngineType EngineTypeFromName(QString enginename) {
QString lower = enginename.toLower(); QString lower = enginename.toLower();
if (lower == "xine") return Engine::Xine;
if (lower == "xine") return Engine::Xine; else if (lower == "gstreamer") return Engine::GStreamer;
else if (lower == "gstreamer") return Engine::GStreamer; else if (lower == "phonon") return Engine::Phonon;
else if (lower == "phonon") return Engine::Phonon; else if (lower == "vlc") return Engine::VLC;
else if (lower == "vlc") return Engine::VLC; else return Engine::None;
else return Engine::None;
} }
QString EngineName(Engine::EngineType enginetype) { QString EngineName(Engine::EngineType enginetype) {
switch (enginetype) { switch (enginetype) {
case Engine::Xine: return QObject::tr("xine"); case Engine::Xine: return QString("xine");
case Engine::GStreamer: return QObject::tr("gstreamer"); case Engine::GStreamer: return QString("gstreamer");
case Engine::Phonon: return QObject::tr("phonon"); case Engine::Phonon: return QString("phonon");
case Engine::VLC: return QObject::tr("vlc"); case Engine::VLC: return QString("vlc");
case Engine::None: case Engine::None:
default: return QObject::tr("None"); default: return QString("None");
} }
} }
QString EngineDescription(Engine::EngineType enginetype) { QString EngineDescription(Engine::EngineType enginetype) {
switch (enginetype) { switch (enginetype) {
case Engine::Xine: return QObject::tr("Xine"); case Engine::Xine: return QString("Xine");
case Engine::GStreamer: return QObject::tr("GStreamer"); case Engine::GStreamer: return QString("GStreamer");
case Engine::Phonon: return QObject::tr("Phonon"); case Engine::Phonon: return QString("Phonon");
case Engine::VLC: return QObject::tr("VLC"); case Engine::VLC: return QString("VLC");
case Engine::None: case Engine::None:
default: return QObject::tr("None"); default: return QString("None");
} }
} }

View File

@@ -137,7 +137,7 @@ bool GstEngine::Init() {
Engine::State GstEngine::state() const { Engine::State GstEngine::state() const {
if (!current_pipeline_) return url_.isEmpty() ? Engine::Empty : Engine::Idle; if (!current_pipeline_) return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
switch (current_pipeline_->state()) { switch (current_pipeline_->state()) {
case GST_STATE_NULL: case GST_STATE_NULL:
@@ -154,32 +154,32 @@ Engine::State GstEngine::state() const {
} }
void GstEngine::StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) { void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) {
EnsureInitialised(); EnsureInitialised();
QByteArray gst_url = FixupUrl(url); QByteArray gst_url = FixupUrl(media_url);
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully) // No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
if (current_pipeline_) if (current_pipeline_)
current_pipeline_->SetNextUrl(gst_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0); current_pipeline_->SetNextUrl(gst_url, original_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
} }
bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool GstEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
EnsureInitialised(); EnsureInitialised();
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec); Engine::Base::Load(media_url, original_url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
QByteArray gst_url = FixupUrl(url); QByteArray gst_url = FixupUrl(media_url);
bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) || (autocrossfade_enabled_ && change & Engine::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & Engine::Intro)); bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) || (autocrossfade_enabled_ && change & Engine::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & Engine::Intro));
if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_) if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_)
crossfade = false; crossfade = false;
if (!crossfade && current_pipeline_ && current_pipeline_->url() == gst_url && change & Engine::Auto) { if (!crossfade && current_pipeline_ && current_pipeline_->media_url() == gst_url && change & Engine::Auto) {
// We're not crossfading, and the pipeline is already playing the URI we want, so just do nothing. // We're not crossfading, and the pipeline is already playing the URI we want, so just do nothing.
return true; return true;
} }
@@ -188,7 +188,7 @@ bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
//SetEqualizerParameters(equalizer_preamp_, equalizer_gains_); //SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
//SetStereoBalance(stereo_balance_); //SetStereoBalance(stereo_balance_);
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, force_stop_at_end ? end_nanosec : 0); shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, original_url, force_stop_at_end ? end_nanosec : 0);
if (!pipeline) return false; if (!pipeline) return false;
if (crossfade) StartFadeout(); if (crossfade) StartFadeout();
@@ -229,7 +229,8 @@ void GstEngine::Stop(bool stop_after) {
StopTimers(); StopTimers();
url_ = QUrl(); // To ensure we return Empty from state() media_url_ = QUrl(); // To ensure we return Empty from state()
original_url_ = QUrl();
beginning_nanosec_ = end_nanosec_ = 0; beginning_nanosec_ = end_nanosec_ = 0;
// Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback. // Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback.
@@ -402,6 +403,10 @@ bool GstEngine::CustomDeviceSupport(const QString &output) {
return (output == kALSASink || output == kOpenALSASink || output == kOSSSink || output == kOSS4Sink || output == kPulseSink || output == kA2DPSink || output == kAVDTPSink); return (output == kALSASink || output == kOpenALSASink || output == kOSSSink || output == kOSS4Sink || output == kPulseSink || output == kA2DPSink || output == kAVDTPSink);
} }
bool GstEngine::ALSADeviceSupport(const QString &output) {
return (output == kALSASink);
}
void GstEngine::ReloadSettings() { void GstEngine::ReloadSettings() {
Engine::Base::ReloadSettings(); Engine::Base::ReloadSettings();
@@ -571,7 +576,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
BufferingFinished(); BufferingFinished();
emit StateChanged(Engine::Error); emit StateChanged(Engine::Error);
// unable to play media stream with this url // unable to play media stream with this url
emit InvalidSongRequested(url_); emit InvalidSongRequested(media_url_);
emit Error(message); emit Error(message);
@@ -645,9 +650,9 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
if (ret == GST_STATE_CHANGE_FAILURE) { if (ret == GST_STATE_CHANGE_FAILURE) {
// Failure, but we got a redirection URL - try loading that instead // Failure, but we got a redirection URL - try loading that instead
QByteArray redirect_url = current_pipeline_->redirect_url(); QByteArray redirect_url = current_pipeline_->redirect_url();
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->url()) { if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->media_url()) {
qLog(Info) << "Redirecting to" << redirect_url; qLog(Info) << "Redirecting to" << redirect_url;
current_pipeline_ = CreatePipeline(redirect_url, end_nanosec_); current_pipeline_ = CreatePipeline(redirect_url, current_pipeline_->original_url(), end_nanosec_);
Play(offset_nanosec); Play(offset_nanosec);
return; return;
} }
@@ -661,14 +666,14 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
StartTimers(); StartTimers();
// initial offset // Initial offset
if (offset_nanosec != 0 || beginning_nanosec_ != 0) { if (offset_nanosec != 0 || beginning_nanosec_ != 0) {
Seek(offset_nanosec); Seek(offset_nanosec);
} }
emit StateChanged(Engine::Playing); emit StateChanged(Engine::Playing);
// we've successfully started playing a media stream with this url // We've successfully started playing a media stream with this url
emit ValidSongRequested(url_); emit ValidSongRequested(media_url_);
} }
@@ -819,25 +824,14 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
connect(ret.get(), SIGNAL(BufferingProgress(int)), SLOT(BufferingProgress(int))); connect(ret.get(), SIGNAL(BufferingProgress(int)), SLOT(BufferingProgress(int)));
connect(ret.get(), SIGNAL(BufferingFinished()), SLOT(BufferingFinished())); connect(ret.get(), SIGNAL(BufferingFinished()), SLOT(BufferingFinished()));
return ret;
}
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl &url, qint64 end_nanosec) {
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
if (!ret->InitFromUrl(url.toEncoded(), end_nanosec)) ret.reset();
return ret; return ret;
} }
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &url, qint64 end_nanosec) { shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec) {
shared_ptr<GstEnginePipeline> ret = CreatePipeline(); shared_ptr<GstEnginePipeline> ret = CreatePipeline();
if (!ret->InitFromUrl(gst_url, original_url, end_nanosec)) ret.reset();
if (!ret->InitFromUrl(url, end_nanosec)) ret.reset();
return ret; return ret;
} }

View File

@@ -68,8 +68,8 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
bool Init(); bool Init();
Engine::State state() const; Engine::State state() const;
void StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec); void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec);
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@@ -87,6 +87,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
bool ValidOutput(const QString &output); bool ValidOutput(const QString &output);
QString DefaultOutput() { return kAutoSink; } QString DefaultOutput() { return kAutoSink; }
bool CustomDeviceSupport(const QString &output); bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
void EnsureInitialised() { initialising_.waitForFinished(); } void EnsureInitialised() { initialising_.waitForFinished(); }
void InitialiseGStreamer(); void InitialiseGStreamer();
@@ -156,8 +157,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
void StopTimers(); void StopTimers();
std::shared_ptr<GstEnginePipeline> CreatePipeline(); std::shared_ptr<GstEnginePipeline> CreatePipeline();
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl &url, qint64 end_nanosec); std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec);
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &url, qint64 end_nanosec);
void UpdateScope(int chunk_length); void UpdateScope(int chunk_length);

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -33,6 +34,7 @@
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <QString> #include <QString>
#include <QUrl>
#include <QTimeLine> #include <QTimeLine>
#include <QMetaObject> #include <QMetaObject>
#include <QtDebug> #include <QtDebug>
@@ -351,7 +353,6 @@ bool GstEnginePipeline::InitAudioBin() {
// Let the audio output of the tee autonegotiate the bit depth and format. // Let the audio output of the tee autonegotiate the bit depth and format.
GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw"); GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw");
gst_element_link_filtered(convert, audiosink_, caps); gst_element_link_filtered(convert, audiosink_, caps);
gst_caps_unref(caps); gst_caps_unref(caps);
@@ -371,14 +372,16 @@ bool GstEnginePipeline::InitFromString(const QString &pipeline) {
} }
bool GstEnginePipeline::InitFromUrl(const QByteArray &url, qint64 end_nanosec) { bool GstEnginePipeline::InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec) {
media_url_ = media_url;
original_url_ = original_url;
end_offset_nanosec_ = end_nanosec; end_offset_nanosec_ = end_nanosec;
pipeline_ = engine_->CreateElement("playbin"); pipeline_ = engine_->CreateElement("playbin");
if (pipeline_ == nullptr) return false; if (pipeline_ == nullptr) return false;
g_object_set(G_OBJECT(pipeline_), "uri", url.constData(), nullptr); g_object_set(G_OBJECT(pipeline_), "uri", media_url.constData(), nullptr);
CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this); CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this);
CHECKED_GCONNECT(G_OBJECT(pipeline_), "pad-added", &NewPadCallback, this); CHECKED_GCONNECT(G_OBJECT(pipeline_), "pad-added", &NewPadCallback, this);
@@ -500,9 +503,11 @@ void GstEnginePipeline::StreamStartMessageReceived() {
if (next_uri_set_) { if (next_uri_set_) {
next_uri_set_ = false; next_uri_set_ = false;
url_ = next_url_; media_url_ = next_media_url_;
original_url_ = next_original_url_;
end_offset_nanosec_ = next_end_offset_nanosec_; end_offset_nanosec_ = next_end_offset_nanosec_;
next_url_ = QByteArray(); next_media_url_ = QByteArray();
next_original_url_ = QUrl();
next_beginning_offset_nanosec_ = 0; next_beginning_offset_nanosec_ = 0;
next_end_offset_nanosec_ = 0; next_end_offset_nanosec_ = 0;
@@ -579,10 +584,18 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
gst_message_parse_tag(msg, &taglist); gst_message_parse_tag(msg, &taglist);
Engine::SimpleMetaBundle bundle; Engine::SimpleMetaBundle bundle;
bundle.title = ParseTag(taglist, GST_TAG_TITLE); bundle.url = original_url_;
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST); bundle.title = ParseStrTag(taglist, GST_TAG_TITLE);
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT); bundle.artist = ParseStrTag(taglist, GST_TAG_ARTIST);
bundle.album = ParseTag(taglist, GST_TAG_ALBUM); bundle.comment = ParseStrTag(taglist, GST_TAG_COMMENT);
bundle.album = ParseStrTag(taglist, GST_TAG_ALBUM);
bundle.length = 0;
bundle.year = 0;
bundle.tracknr = 0;
bundle.samplerate = 0;
bundle.bitdepth = 0;
bundle.bitrate = ParseUIntTag(taglist, GST_TAG_BITRATE) / 1000;
bundle.lyrics = ParseStrTag(taglist, GST_TAG_LYRICS);
gst_tag_list_free(taglist); gst_tag_list_free(taglist);
@@ -593,7 +606,7 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
} }
QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const { QString GstEnginePipeline::ParseStrTag(GstTagList *list, const char *tag) const {
gchar *data = nullptr; gchar *data = nullptr;
bool success = gst_tag_list_get_string(list, tag, &data); bool success = gst_tag_list_get_string(list, tag, &data);
@@ -607,6 +620,17 @@ QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
} }
guint GstEnginePipeline::ParseUIntTag(GstTagList *list, const char *tag) const {
guint data;
bool success = gst_tag_list_get_uint(list, tag, &data);
guint ret = 0;
if (success && data) ret = data;
return ret;
}
void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) { void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
if (msg->src != GST_OBJECT(pipeline_)) { if (msg->src != GST_OBJECT(pipeline_)) {
@@ -630,7 +654,7 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
if (next_uri_set_ && new_state == GST_STATE_READY) { if (next_uri_set_ && new_state == GST_STATE_READY) {
// Revert uri and go back to PLAY state again // Revert uri and go back to PLAY state again
next_uri_set_ = false; next_uri_set_ = false;
g_object_set(G_OBJECT(pipeline_), "uri", url_.constData(), nullptr); g_object_set(G_OBJECT(pipeline_), "uri", media_url_.constData(), nullptr);
SetState(GST_STATE_PLAYING); SetState(GST_STATE_PLAYING);
} }
} }
@@ -758,10 +782,11 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i
quint64 end_time = start_time + duration; quint64 end_time = start_time + duration;
if (end_time > instance->end_offset_nanosec_) { if (end_time > instance->end_offset_nanosec_) {
if (instance->has_next_valid_url() && instance->next_url_ == instance->url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) { if (instance->has_next_valid_url() && instance->next_media_url_ == instance->media_url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) {
// The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on. // The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on.
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_; instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
instance->next_url_ = QByteArray(); instance->next_media_url_ = QByteArray();
instance->next_original_url_ = QUrl();
instance->next_beginning_offset_nanosec_ = 0; instance->next_beginning_offset_nanosec_ = 0;
instance->next_end_offset_nanosec_ = 0; instance->next_end_offset_nanosec_ = 0;
@@ -814,7 +839,7 @@ void GstEnginePipeline::AboutToFinishCallback(GstPlayBin *bin, gpointer self) {
// Set the next uri. When the current song ends it will be played automatically and a STREAM_START message is send to the bus. // Set the next uri. When the current song ends it will be played automatically and a STREAM_START message is send to the bus.
// When the next uri is not playable an error message is send when the pipeline goes to PLAY (or PAUSE) state or immediately if it is currently in PLAY state. // When the next uri is not playable an error message is send when the pipeline goes to PLAY (or PAUSE) state or immediately if it is currently in PLAY state.
instance->next_uri_set_ = true; instance->next_uri_set_ = true;
g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_url_.constData(), nullptr); g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_media_url_.constData(), nullptr);
} }
} }
@@ -1057,9 +1082,10 @@ void GstEnginePipeline::RemoveAllBufferConsumers() {
buffer_consumers_.clear(); buffer_consumers_.clear();
} }
void GstEnginePipeline::SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec) { void GstEnginePipeline::SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec) {
next_url_ = url; next_media_url_ = media_url;
next_original_url_ = original_url;
next_beginning_offset_nanosec_ = beginning_nanosec; next_beginning_offset_nanosec_ = beginning_nanosec;
next_end_offset_nanosec_ = end_nanosec; next_end_offset_nanosec_ = end_nanosec;

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -41,6 +42,7 @@
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <QString> #include <QString>
#include <QUrl>
#include <QTimerEvent> #include <QTimerEvent>
class GstEngine; class GstEngine;
@@ -70,7 +72,7 @@ class GstEnginePipeline : public QObject {
void set_mono_playback(bool enabled); void set_mono_playback(bool enabled);
// Creates the pipeline, returns false on error // Creates the pipeline, returns false on error
bool InitFromUrl(const QByteArray &url, qint64 end_nanosec); bool InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec);
bool InitFromString(const QString &pipeline); bool InitFromString(const QString &pipeline);
// GstBufferConsumers get fed audio data. Thread-safe. // GstBufferConsumers get fed audio data. Thread-safe.
@@ -88,13 +90,14 @@ class GstEnginePipeline : public QObject {
void StartFader(qint64 duration_nanosec, QTimeLine::Direction direction = QTimeLine::Forward, QTimeLine::CurveShape shape = QTimeLine::LinearCurve, bool use_fudge_timer = true); void StartFader(qint64 duration_nanosec, QTimeLine::Direction direction = QTimeLine::Forward, QTimeLine::CurveShape shape = QTimeLine::LinearCurve, bool use_fudge_timer = true);
// If this is set then it will be loaded automatically when playback finishes for gapless playback // If this is set then it will be loaded automatically when playback finishes for gapless playback
void SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec); void SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec);
bool has_next_valid_url() const { return !next_url_.isNull() && !next_url_.isEmpty(); } bool has_next_valid_url() const { return !next_media_url_.isNull() && !next_media_url_.isEmpty(); }
void SetSourceDevice(QString device) { source_device_ = device; } void SetSourceDevice(QString device) { source_device_ = device; }
// Get information about the music playback // Get information about the music playback
QByteArray url() const { return url_; } QByteArray media_url() const { return media_url_; }
QUrl original_url() const { return original_url_; }
bool is_valid() const { return valid_; } bool is_valid() const { return valid_; }
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware. // Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
qint64 position() const; qint64 position() const;
@@ -149,7 +152,8 @@ signals:
void StreamStatusMessageReceived(GstMessage*); void StreamStatusMessageReceived(GstMessage*);
void StreamStartMessageReceived(); void StreamStartMessageReceived();
QString ParseTag(GstTagList *list, const char *tag) const; QString ParseStrTag(GstTagList *list, const char *tag) const;
guint ParseUIntTag(GstTagList *list, const char *tag) const;
bool InitDecodeBin(GstElement* new_bin); bool InitDecodeBin(GstElement* new_bin);
bool InitAudioBin(); bool InitAudioBin();
@@ -212,8 +216,10 @@ signals:
bool segment_start_received_; bool segment_start_received_;
// The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing. // The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing.
QByteArray url_; QByteArray media_url_;
QByteArray next_url_; QUrl original_url_;
QByteArray next_media_url_;
QUrl next_original_url_;
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position. // If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
qint64 end_offset_nanosec_; qint64 end_offset_nanosec_;

View File

@@ -64,8 +64,8 @@ bool PhononEngine::CanDecode(const QUrl &url) {
return true; return true;
} }
bool PhononEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool PhononEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
media_object_->setCurrentSource(Phonon::MediaSource(url)); media_object_->setCurrentSource(Phonon::MediaSource(media_url));
return true; return true;
} }
@@ -189,3 +189,7 @@ bool PhononEngine::ValidOutput(const QString &output) {
bool PhononEngine::CustomDeviceSupport(const QString &output) { bool PhononEngine::CustomDeviceSupport(const QString &output) {
return false; return false;
} }
bool PhononEngine::ALSADeviceSupport(const QString &output) {
return false;
}

View File

@@ -48,7 +48,7 @@ class PhononEngine : public Engine::Base {
bool CanDecode(const QUrl &url); bool CanDecode(const QUrl &url);
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@@ -66,6 +66,7 @@ class PhononEngine : public Engine::Base {
QString DefaultOutput() { return ""; } QString DefaultOutput() { return ""; }
bool ValidOutput(const QString &output); bool ValidOutput(const QString &output);
bool CustomDeviceSupport(const QString &output); bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
protected: protected:
void SetVolumeSW( uint percent ); void SetVolumeSW( uint percent );

View File

@@ -100,12 +100,12 @@ bool VLCEngine::Initialised() const {
} }
bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool VLCEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Initialised()) return false; if (!Initialised()) return false;
// Create the media object // Create the media object
VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, url.toEncoded().constData())); VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, media_url.toEncoded().constData()));
libvlc_media_player_set_media(player_, media); libvlc_media_player_set_media(player_, media);
@@ -238,6 +238,10 @@ bool VLCEngine::CustomDeviceSupport(const QString &output) {
return (output == "auto" ? false : true); return (output == "auto" ? false : true);
} }
bool VLCEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
uint VLCEngine::position() const { uint VLCEngine::position() const {
if (!Initialised()) return (0); if (!Initialised()) return (0);

View File

@@ -48,7 +48,7 @@ class VLCEngine : public Engine::Base {
bool Init(); bool Init();
Engine::State state() const { return state_; } Engine::State state() const { return state_; }
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@@ -64,6 +64,7 @@ class VLCEngine : public Engine::Base {
bool ValidOutput(const QString &output); bool ValidOutput(const QString &output);
QString DefaultOutput() { return ""; } QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &output); bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
private: private:
libvlc_instance_t *instance_; libvlc_instance_t *instance_;

View File

@@ -180,20 +180,20 @@ Engine::State XineEngine::state() const {
return Engine::Empty; return Engine::Empty;
case XINE_STATUS_STOP: case XINE_STATUS_STOP:
default: default:
return url_.isEmpty() ? Engine::Empty : Engine::Idle; return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
} }
} }
bool XineEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool XineEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!EnsureStream()) return false; if (!EnsureStream()) return false;
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec); Engine::Base::Load(media_url, original_url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
xine_close(stream_); xine_close(stream_);
//int result = xine_open(stream_, url.path().toUtf8()); //int result = xine_open(stream_, url.path().toUtf8());
int result = xine_open(stream_, url.toString().toUtf8()); int result = xine_open(stream_, media_url.toString().toUtf8());
if (result) { if (result) {
#ifndef XINE_SAFE_MODE #ifndef XINE_SAFE_MODE
@@ -345,6 +345,10 @@ bool XineEngine::CustomDeviceSupport(const QString &output) {
return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio"); return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio");
} }
bool XineEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
void XineEngine::ReloadSettings() { void XineEngine::ReloadSettings() {
Engine::Base::ReloadSettings(); Engine::Base::ReloadSettings();
@@ -371,7 +375,7 @@ uint XineEngine::length() const {
// Xine often delivers nonsense values for VBR files and such, so we only use the length for remote files // Xine often delivers nonsense values for VBR files and such, so we only use the length for remote files
if (url_.scheme().toLower() == "file") return 0; if (media_url_.scheme().toLower() == "file") return 0;
else { else {
int pos = 0, time = 0, length = 0; int pos = 0, time = 0, length = 0;
@@ -473,10 +477,9 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
b.artist = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST)); b.artist = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST));
b.album = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM)); b.album = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM));
b.genre = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE)); b.genre = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE));
b.year = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR)); b.year = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
b.tracknr = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER)); b.tracknr = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
if (b.tracknr.isEmpty()) //if (b.tracknr <= 0) b.tracknr = QFileInfo(url.path()).fileName();
b.tracknr = QFileInfo(url.path()).fileName();
} }
else { else {
b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName()); b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName());
@@ -486,18 +489,19 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
if (audioCodec == "CDDA" || audioCodec == "WAV") { if (audioCodec == "CDDA" || audioCodec == "WAV") {
result = true; result = true;
b.url = url;
int samplerate = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE); int samplerate = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
int bitdepth = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS);
int channels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
// Xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV so we calculate the bitrate by our own // Xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV so we calculate the bitrate by our own
int bitsPerSample = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS); int bitrate = (samplerate * bitdepth * channels) / 1000;
int nbrChannels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000;
b.bitrate = QString::number(bitrate); b.samplerate = samplerate;
b.samplerate = QString::number(samplerate); b.bitdepth = bitdepth;
b.bitrate = bitrate;
int pos, time, length = 0; int pos, time, length = 0;
xine_get_pos_length(tmpstream, &pos, &time, &length); xine_get_pos_length(tmpstream, &pos, &time, &length);
b.length = QString::number(length / 1000); b.length = length / 1000;
} }
xine_close(tmpstream); xine_close(tmpstream);
} }
@@ -746,7 +750,7 @@ bool XineEngine::event(QEvent *e) {
return true; return true;
case XineEvent::InfoMessage: case XineEvent::InfoMessage:
emit InfoMessage((*message).arg(url_.toString())); emit InfoMessage((*message).arg(media_url_.toString()));
delete message; delete message;
return true; return true;
@@ -767,7 +771,7 @@ bool XineEngine::event(QEvent *e) {
case XineEvent::Redirecting: case XineEvent::Redirecting:
emit StatusText(QString("Redirecting to: ").arg(*message)); emit StatusText(QString("Redirecting to: ").arg(*message));
Load(QUrl(*message), Engine::Auto, false, 0, 0); Load(QUrl(*message), original_url_, Engine::Auto, false, 0, 0);
Play(0); Play(0);
delete message; delete message;
return true; return true;
@@ -783,15 +787,18 @@ bool XineEngine::event(QEvent *e) {
Engine::SimpleMetaBundle XineEngine::fetchMetaData() const { Engine::SimpleMetaBundle XineEngine::fetchMetaData() const {
Engine::SimpleMetaBundle bundle; Engine::SimpleMetaBundle bundle;
bundle.url = original_url_;
bundle.title = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TITLE)); bundle.title = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TITLE));
bundle.artist = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ARTIST)); bundle.artist = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ARTIST));
bundle.album = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ALBUM)); bundle.album = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ALBUM));
bundle.comment = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_COMMENT)); bundle.comment = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_COMMENT));
bundle.genre = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_GENRE)); bundle.genre = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_GENRE));
bundle.bitrate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000); bundle.length = 0;
bundle.samplerate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE)); bundle.year = atoi(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
bundle.year = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_YEAR)); bundle.tracknr = atoi(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER));
bundle.tracknr = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER)); bundle.samplerate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
bundle.bitdepth = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITS);
bundle.bitrate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000;
return bundle; return bundle;
@@ -895,7 +902,7 @@ void XineEngine::DetermineAndShowErrorMessage() {
// xine can read the plugin but it didn't find any codec // xine can read the plugin but it didn't find any codec
// THUS xine=daft for telling us it could handle the format in canDecode! // THUS xine=daft for telling us it could handle the format in canDecode!
body = "There is no available decoder."; body = "There is no available decoder.";
QString const ext = QFileInfo(url_.path()).completeSuffix(); QString const ext = QFileInfo(media_url_.path()).completeSuffix();
// TODO: // TODO:
// if (ext == "mp3" && EngineController::installDistroCodec("xine-engine")) // if (ext == "mp3" && EngineController::installDistroCodec("xine-engine"))
// return; // return;

View File

@@ -70,7 +70,7 @@ class XineEngine : public Engine::Base {
bool Init(); bool Init();
Engine::State state() const; Engine::State state() const;
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@@ -87,6 +87,7 @@ class XineEngine : public Engine::Base {
bool ValidOutput(const QString &output); bool ValidOutput(const QString &output);
QString DefaultOutput() { return "auto"; } QString DefaultOutput() { return "auto"; }
bool CustomDeviceSupport(const QString &output); bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
void ReloadSettings(); void ReloadSettings();
@@ -115,7 +116,8 @@ class XineEngine : public Engine::Base {
float preamp_; float preamp_;
std::unique_ptr<PruneScopeThread> prune_; std::unique_ptr<PruneScopeThread> prune_;
QUrl url_; QUrl media_url_;
QUrl original_url_;
static int last_error_; static int last_error_;
static time_t last_error_time_; static time_t last_error_time_;

View File

@@ -110,7 +110,6 @@ class InternetModel : public QStandardItemModel {
template <typename T> template <typename T>
static T *Service() { static T *Service() {
//return static_cast<T*>(ServiceByName(T::kServiceName));
return static_cast<T*>(ServiceBySource(T::kSource)); return static_cast<T*>(ServiceBySource(T::kSource));
} }

View File

@@ -27,6 +27,6 @@
#include "internetmodel.h" #include "internetmodel.h"
#include "internetservice.h" #include "internetservice.h"
InternetService::InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent) InternetService::InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, InternetModel *model, QObject *parent)
: QObject(parent), app_(app), model_(model), source_(source), name_(name) { : QObject(parent), app_(app), model_(model), source_(source), name_(name), url_scheme_(url_scheme) {
} }

View File

@@ -42,10 +42,11 @@ class InternetService : public QObject {
Q_OBJECT Q_OBJECT
public: public:
InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent = nullptr); InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, InternetModel *model, QObject *parent = nullptr);
virtual ~InternetService() {} virtual ~InternetService() {}
Song::Source source() const { return source_; } Song::Source source() const { return source_; }
QString name() const { return name_; } QString name() const { return name_; }
QString url_scheme() const { return url_scheme_; }
InternetModel *model() const { return model_; } InternetModel *model() const { return model_; }
virtual bool has_initial_load_settings() const { return false; } virtual bool has_initial_load_settings() const { return false; }
virtual void InitialLoadSettings() {} virtual void InitialLoadSettings() {}
@@ -61,6 +62,7 @@ class InternetService : public QObject {
InternetModel *model_; InternetModel *model_;
Song::Source source_; Song::Source source_;
QString name_; QString name_;
QString url_scheme_;
}; };
Q_DECLARE_METATYPE(InternetService*); Q_DECLARE_METATYPE(InternetService*);

View File

@@ -1129,7 +1129,7 @@ bool Playlist::ComparePathDepths(Qt::SortOrder order, shared_ptr<PlaylistItem> _
} }
QString Playlist::column_name(Column column) { QString Playlist::column_name(Column column) {
switch (column) { switch (column) {
case Column_Title: return tr("Title"); case Column_Title: return tr("Title");
case Column_Artist: return tr("Artist"); case Column_Artist: return tr("Artist");
@@ -1456,14 +1456,18 @@ void Playlist::StopAfter(int row) {
void Playlist::SetStreamMetadata(const QUrl &url, const Song &song) { void Playlist::SetStreamMetadata(const QUrl &url, const Song &song) {
//qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() << song.title();
if (!current_item()) return; if (!current_item()) return;
if (current_item()->Url() != url) return; if (current_item()->Url() != url) return;
// Don't update the metadata if it's only a minor change from before // Don't update the metadata if it's only a minor change from before
if (current_item()->Metadata().artist() == song.artist() && current_item()->Metadata().title() == song.title()) return; if (current_item()->Metadata().artist() == song.artist() && current_item()->Metadata().title() == song.title() && current_item()->Metadata().album() == song.album()) return;
// TODO: Update context & playlist if changed, but don't show popup.
//(song.bitrate() <= 0 || current_item()->Metadata().bitrate() == song.bitrate())
//(song.samplerate() <= 0 || current_item()->Metadata().samplerate() == song.samplerate())
//(song.bitdepth() <= 0 || current_item()->Metadata().bitdepth() == song.bitdepth())
qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() << song.title();
current_item()->SetTemporaryMetadata(song); current_item()->SetTemporaryMetadata(song);
@@ -1762,7 +1766,7 @@ void Playlist::ItemChanged(PlaylistItemPtr item) {
} }
void Playlist::InformOfCurrentSongChange() { void Playlist::InformOfCurrentSongChange() {
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount - 1)); emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount - 1));
// if the song is invalid, we won't play it - there's no point in informing anybody about the change // if the song is invalid, we won't play it - there's no point in informing anybody about the change

View File

@@ -218,7 +218,7 @@ void PlaylistDelegateBase::paint(QPainter *painter, const QStyleOptionViewItem &
} }
QStyleOptionViewItemV4 PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem &option, const QModelIndex &index) const {
if (!view_) return option; if (!view_) return option;
@@ -227,7 +227,7 @@ QStyleOptionViewItemV4 PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem
if (view_->header()->logicalIndexAt(top_left) != index.column()) if (view_->header()->logicalIndexAt(top_left) != index.column())
return option; return option;
QStyleOptionViewItemV4 ret(option); QStyleOptionViewItem ret(option);
if (index.data(Playlist::Role_IsCurrent).toBool()) { if (index.data(Playlist::Role_IsCurrent).toBool()) {
// Move the text in a bit on the first column for the song that's currently playing // Move the text in a bit on the first column for the song that's currently playing

View File

@@ -84,7 +84,7 @@ class PlaylistDelegateBase : public QueuedItemDelegate {
QString displayText(const QVariant &value, const QLocale &locale) const; QString displayText(const QVariant &value, const QLocale &locale) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QStyleOptionViewItemV4 Adjusted(const QStyleOptionViewItem &option, const QModelIndex &index) const; QStyleOptionViewItem Adjusted(const QStyleOptionViewItem &option, const QModelIndex &index) const;
static const int kMinHeight; static const int kMinHeight;

View File

@@ -40,8 +40,7 @@
#include "internet/internetplaylistitem.h" #include "internet/internetplaylistitem.h"
PlaylistItem::~PlaylistItem() { PlaylistItem::~PlaylistItem() {}
}
PlaylistItem *PlaylistItem::NewFromSource(const Song::Source &source) { PlaylistItem *PlaylistItem::NewFromSource(const Song::Source &source) {

View File

@@ -382,7 +382,7 @@ void PlaylistView::drawTree(QPainter *painter, const QRegion &region) const {
void PlaylistView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { void PlaylistView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QStyleOptionViewItemV4 opt(option); QStyleOptionViewItem opt(option);
bool is_current = index.data(Playlist::Role_IsCurrent).toBool(); bool is_current = index.data(Playlist::Role_IsCurrent).toBool();
bool is_paused = index.data(Playlist::Role_IsPaused).toBool(); bool is_paused = index.data(Playlist::Role_IsPaused).toBool();
@@ -452,7 +452,7 @@ void PlaylistView::drawRow(QPainter *painter, const QStyleOptionViewItem &option
} }
void PlaylistView::UpdateCachedCurrentRowPixmap(QStyleOptionViewItemV4 option, const QModelIndex &index) { void PlaylistView::UpdateCachedCurrentRowPixmap(QStyleOptionViewItem option, const QModelIndex &index) {
cached_current_row_rect_ = option.rect; cached_current_row_rect_ = option.rect;
cached_current_row_row_ = index.row(); cached_current_row_row_ = index.row();

View File

@@ -189,7 +189,7 @@ class PlaylistView : public QTreeView {
private: private:
void ReloadBarPixmaps(); void ReloadBarPixmaps();
QList<QPixmap> LoadBarPixmap(const QString &filename); QList<QPixmap> LoadBarPixmap(const QString &filename);
void UpdateCachedCurrentRowPixmap(QStyleOptionViewItemV4 option, const QModelIndex &index); void UpdateCachedCurrentRowPixmap(QStyleOptionViewItem option, const QModelIndex &index);
void set_background_image_type(BackgroundImageType bg) { void set_background_image_type(BackgroundImageType bg) {
background_image_type_ = bg; background_image_type_ = bg;

View File

@@ -31,6 +31,7 @@
#include <QSlider> #include <QSlider>
#include <QSpinBox> #include <QSpinBox>
#include <QLabel> #include <QLabel>
#include <QListView>
#include "backendsettingspage.h" #include "backendsettingspage.h"
@@ -112,6 +113,19 @@ void BackendSettingsPage::Load() {
ui_->stickslider_replaygainpreamp->setValue(s_.value("rgpreamp", 0.0).toDouble() * 10 + 150); ui_->stickslider_replaygainpreamp->setValue(s_.value("rgpreamp", 0.0).toDouble() * 10 + 150);
ui_->checkbox_replaygaincompression->setChecked(s_.value("rgcompression", true).toBool()); ui_->checkbox_replaygaincompression->setChecked(s_.value("rgcompression", true).toBool());
int alsaplug_int = alsa_plugin(s_.value("alsaplugin", 0).toInt());
if (alsa_plugin(alsaplug_int)) {
alsa_plugin alsaplugin = alsa_plugin(alsaplug_int);
switch (alsaplugin) {
case alsa_plugin::alsa_hw:
ui_->radiobutton_alsa_hw->setChecked(true);
break;
case alsa_plugin::alsa_plughw:
ui_->radiobutton_alsa_plughw->setChecked(true);
break;
}
}
if (!EngineInitialised()) return; if (!EngineInitialised()) return;
if (engine()->state() == Engine::Empty) { if (engine()->state() == Engine::Empty) {
@@ -139,6 +153,8 @@ void BackendSettingsPage::ConnectSignals() {
connect(ui_->lineedit_device, SIGNAL(textChanged(const QString &)), SLOT(DeviceStringChanged())); connect(ui_->lineedit_device, SIGNAL(textChanged(const QString &)), SLOT(DeviceStringChanged()));
connect(ui_->slider_bufferminfill, SIGNAL(valueChanged(int)), SLOT(BufferMinFillChanged(int))); connect(ui_->slider_bufferminfill, SIGNAL(valueChanged(int)), SLOT(BufferMinFillChanged(int)));
connect(ui_->stickslider_replaygainpreamp, SIGNAL(valueChanged(int)), SLOT(RgPreampChanged(int))); connect(ui_->stickslider_replaygainpreamp, SIGNAL(valueChanged(int)), SLOT(RgPreampChanged(int)));
connect(ui_->radiobutton_alsa_hw, SIGNAL(clicked(bool)), SLOT(radiobutton_alsa_hw_clicked(bool)));
connect(ui_->radiobutton_alsa_plughw, SIGNAL(clicked(bool)), SLOT(radiobutton_alsa_plughw_clicked(bool)));
} }
@@ -271,6 +287,27 @@ void BackendSettingsPage::Load_Device(QString output, QVariant device) {
ui_->lineedit_device->setEnabled(false); ui_->lineedit_device->setEnabled(false);
} }
if (engine()->ALSADeviceSupport(output)) {
ui_->radiobutton_alsa_hw->setEnabled(true);
ui_->radiobutton_alsa_plughw->setEnabled(true);
if (device.toString().contains(QRegExp("^plughw:.*"))) {
ui_->radiobutton_alsa_hw->setChecked(false);
ui_->radiobutton_alsa_plughw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_plughw);
}
else {
ui_->radiobutton_alsa_plughw->setChecked(false);
ui_->radiobutton_alsa_hw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_hw);
}
}
else {
ui_->radiobutton_alsa_hw->setEnabled(false);
ui_->radiobutton_alsa_hw->setChecked(false);
ui_->radiobutton_alsa_plughw->setEnabled(false);
ui_->radiobutton_alsa_plughw->setChecked(false);
}
bool found(false); bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) { for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant d = ui_->combobox_device->itemData(i).value<QVariant>(); QVariant d = ui_->combobox_device->itemData(i).value<QVariant>();
@@ -285,17 +322,12 @@ void BackendSettingsPage::Load_Device(QString output, QVariant device) {
if (engine()->CustomDeviceSupport(output) && device.type() == QVariant::String && !device.toString().isEmpty()) { if (engine()->CustomDeviceSupport(output) && device.type() == QVariant::String && !device.toString().isEmpty()) {
ui_->lineedit_device->setText(device.toString()); ui_->lineedit_device->setText(device.toString());
if (!found) { if (!found) {
bool have_custom(false);
int index_custom = 0;
for (int i = 0; i < ui_->combobox_device->count(); ++i) { for (int i = 0; i < ui_->combobox_device->count(); ++i) {
if (ui_->combobox_device->itemText(i) == "Custom") { if (ui_->combobox_device->itemText(i) == "Custom") {
have_custom = true;
index_custom = i;
if (ui_->combobox_device->currentText() != "Custom") ui_->combobox_device->setCurrentIndex(i); if (ui_->combobox_device->currentText() != "Custom") ui_->combobox_device->setCurrentIndex(i);
break; break;
} }
} }
if (have_custom) ui_->combobox_device->setItemData(index_custom, QVariant(device.toString()));
} }
} }
@@ -315,7 +347,9 @@ void BackendSettingsPage::Save() {
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>(); EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
output_name = output.name; output_name = output.name;
} }
if (ui_->combobox_device->currentText().isEmpty()) device_value = QVariant(); if (ui_->combobox_device->currentText().isEmpty()) device_value = QVariant();
else if (ui_->combobox_device->currentText() == "Custom") device_value = ui_->lineedit_device->text();
else device_value = ui_->combobox_device->itemData(ui_->combobox_device->currentIndex()).value<QVariant>(); else device_value = ui_->combobox_device->itemData(ui_->combobox_device->currentIndex()).value<QVariant>();
s_.setValue("engine", EngineName(enginetype)); s_.setValue("engine", EngineName(enginetype));
@@ -330,6 +364,10 @@ void BackendSettingsPage::Save() {
s_.setValue("rgpreamp", float(ui_->stickslider_replaygainpreamp->value()) / 10 - 15); s_.setValue("rgpreamp", float(ui_->stickslider_replaygainpreamp->value()) / 10 - 15);
s_.setValue("rgcompression", ui_->checkbox_replaygaincompression->isChecked()); s_.setValue("rgcompression", ui_->checkbox_replaygaincompression->isChecked());
if (ui_->radiobutton_alsa_hw->isChecked()) s_.setValue("alsaplugin", static_cast<int>(alsa_plugin::alsa_hw));
else if (ui_->radiobutton_alsa_plughw->isChecked()) s_.setValue("alsaplugin", static_cast<int>(alsa_plugin::alsa_plughw));
else s_.remove("alsaplugin");
// If engine has not been changed, but output or device has been changed, // If engine has not been changed, but output or device has been changed,
// then set_output_changed(true) to reinitialize engine when dialog closes. // then set_output_changed(true) to reinitialize engine when dialog closes.
if (enginetype == enginetype_current_ && (output_name != output_current_ || device_value != device_current_)) dialog()->set_output_changed(true); if (enginetype == enginetype_current_ && (output_name != output_current_ || device_value != device_current_)) dialog()->set_output_changed(true);
@@ -347,7 +385,7 @@ void BackendSettingsPage::Cancel() {
void BackendSettingsPage::EngineChanged(int index) { void BackendSettingsPage::EngineChanged(int index) {
if (!configloaded_ || !EngineInitialised()) return; if (!configloaded_ || !EngineInitialised()) return;
QVariant v = ui_->combobox_engine->itemData(index); QVariant v = ui_->combobox_engine->itemData(index);
Engine::EngineType enginetype = v.value<Engine::EngineType>(); Engine::EngineType enginetype = v.value<Engine::EngineType>();
@@ -386,12 +424,7 @@ void BackendSettingsPage::DeviceSelectionChanged(int index) {
if (engine()->CustomDeviceSupport(output.name)) { if (engine()->CustomDeviceSupport(output.name)) {
ui_->lineedit_device->setEnabled(true); ui_->lineedit_device->setEnabled(true);
if (ui_->combobox_device->currentText() == "Custom") { if (ui_->combobox_device->currentText() != "Custom" && device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
ui_->combobox_device->setItemData(index, QVariant(ui_->lineedit_device->text()));
}
else {
if (device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
}
} }
else { else {
ui_->lineedit_device->setEnabled(false); ui_->lineedit_device->setEnabled(false);
@@ -409,10 +442,22 @@ void BackendSettingsPage::DeviceStringChanged() {
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>(); EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
bool found(false); bool found(false);
if (engine()->ALSADeviceSupport(output.name)) {
if (ui_->lineedit_device->text().contains(QRegExp("^hw:.*")) && !ui_->radiobutton_alsa_hw->isChecked()) {
ui_->radiobutton_alsa_hw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_hw);
}
else if (ui_->lineedit_device->text().contains(QRegExp("^plughw:.*")) && !ui_->radiobutton_alsa_plughw->isChecked()) {
ui_->radiobutton_alsa_plughw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_plughw);
}
}
for (int i = 0; i < ui_->combobox_device->count(); ++i) { for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>(); QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue; if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue; if (device.toString().isEmpty()) continue;
if (ui_->combobox_device->itemText(i) == "Custom") continue;
if (device.toString() == ui_->lineedit_device->text()) { if (device.toString() == ui_->lineedit_device->text()) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i); if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true; found = true;
@@ -430,7 +475,6 @@ void BackendSettingsPage::DeviceStringChanged() {
} }
} }
if (ui_->combobox_device->currentText() == "Custom") { if (ui_->combobox_device->currentText() == "Custom") {
ui_->combobox_device->setItemData(ui_->combobox_device->currentIndex(), QVariant(ui_->lineedit_device->text()));
if ((ui_->lineedit_device->text().isEmpty()) && (ui_->combobox_device->count() > 0) && (ui_->combobox_device->currentIndex() != 0)) ui_->combobox_device->setCurrentIndex(0); if ((ui_->lineedit_device->text().isEmpty()) && (ui_->combobox_device->count() > 0) && (ui_->combobox_device->currentIndex() != 0)) ui_->combobox_device->setCurrentIndex(0);
} }
} }
@@ -502,3 +546,73 @@ void BackendSettingsPage::XineWarning() {
xinewarning_ = true; xinewarning_ = true;
} }
void BackendSettingsPage::SwitchALSADevices(alsa_plugin alsaplugin) {
// All ALSA devices are listed twice, one for "hw" and one for "plughw"
// Only show one of them by making the other ones invisible based on the alsa plugin radiobuttons
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QListView *view = qobject_cast<QListView *>(ui_->combobox_device->view());
if (!view) continue;
if (alsaplugin == alsa_plugin::alsa_hw && ui_->combobox_device->itemData(i).toString().contains(QRegExp("^plughw:.*"))) {
view->setRowHidden(i, true);
}
else if (alsaplugin == alsa_plugin::alsa_plughw && ui_->combobox_device->itemData(i).toString().contains(QRegExp("^hw:.*"))) {
view->setRowHidden(i, true);
}
else {
view->setRowHidden(i, false);
}
}
}
void BackendSettingsPage::radiobutton_alsa_hw_clicked(bool checked) {
if (!configloaded_ || !EngineInitialised()) return;
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
if (!engine()->ALSADeviceSupport(output.name)) return;
if (ui_->lineedit_device->text().contains(QRegExp("^plughw:.*"))) {
SwitchALSADevices(alsa_plugin::alsa_hw);
QString device_new = ui_->lineedit_device->text().replace(QRegExp("^plughw:"), "hw:");
bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (device.toString() == device_new) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
}
}
if (!found) ui_->lineedit_device->setText(device_new);
}
}
void BackendSettingsPage::radiobutton_alsa_plughw_clicked(bool checked) {
if (!configloaded_ || !EngineInitialised()) return;
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
if (!engine()->ALSADeviceSupport(output.name)) return;
if (ui_->lineedit_device->text().contains(QRegExp("^hw:.*"))) {
SwitchALSADevices(alsa_plugin::alsa_plughw);
QString device_new = ui_->lineedit_device->text().replace(QRegExp("^hw:"), "plughw:");
bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (device.toString() == device_new) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
}
}
if (!found) ui_->lineedit_device->setText(device_new);
}
}

View File

@@ -63,8 +63,16 @@ public:
void DeviceStringChanged(); void DeviceStringChanged();
void RgPreampChanged(int value); void RgPreampChanged(int value);
void BufferMinFillChanged(int value); void BufferMinFillChanged(int value);
void radiobutton_alsa_hw_clicked(bool checked);
void radiobutton_alsa_plughw_clicked(bool checked);
private: private:
enum alsa_plugin {
alsa_hw = 1,
alsa_plughw = 2
};
Ui_BackendSettingsPage *ui_; Ui_BackendSettingsPage *ui_;
void ConnectSignals(); void ConnectSignals();
@@ -78,6 +86,7 @@ private:
void ShowWarning(QString text); void ShowWarning(QString text);
void ResetWarning(); void ResetWarning();
void XineWarning(); void XineWarning();
void SwitchALSADevices(alsa_plugin alsaplugin);
QSettings s_; QSettings s_;
bool configloaded_; bool configloaded_;

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>596</width> <width>596</width>
<height>638</height> <height>720</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -19,72 +19,119 @@
<property name="title"> <property name="title">
<string>Audio output</string> <string>Audio output</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_3"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="fieldGrowthPolicy"> <item>
<enum>QFormLayout::AllNonFixedFieldsGrow</enum> <layout class="QHBoxLayout" name="horizontalLayout_engine">
</property> <item>
<item row="0" column="0"> <widget class="QLabel" name="label_engine">
<widget class="QLabel" name="label_engine"> <property name="minimumSize">
<property name="minimumSize"> <size>
<size> <width>90</width>
<width>60</width> <height>0</height>
<height>0</height> </size>
</size> </property>
</property> <property name="text">
<property name="text"> <string>Engine</string>
<string>Engine</string> </property>
</property> </widget>
</widget> </item>
<item>
<widget class="QComboBox" name="combobox_engine">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="spacer_engine">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QComboBox" name="combobox_engine"/> <layout class="QHBoxLayout" name="horizontalLayout_output">
<item>
<widget class="QLabel" name="label_output">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_output">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="spacer_output">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item row="1" column="0"> <item>
<widget class="QLabel" name="label_output"> <layout class="QHBoxLayout" name="horizontalLayout_device" stretch="0,0,0,0">
<property name="enabled"> <item>
<bool>true</bool> <widget class="QLabel" name="label_device">
</property> <property name="minimumSize">
<property name="minimumSize"> <size>
<size> <width>90</width>
<width>60</width> <height>0</height>
<height>0</height> </size>
</size> </property>
</property> <property name="text">
<property name="text"> <string>Device</string>
<string>Output</string> </property>
</property> </widget>
</widget> </item>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="combobox_output">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_device">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_device" stretch="0,0">
<item> <item>
<widget class="QComboBox" name="combobox_device"> <widget class="QComboBox" name="combobox_device">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="minimumSize">
<size>
<width>240</width>
<height>0</height>
</size>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -92,32 +139,98 @@
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="minimumSize">
<size>
<width>90</width>
<height>30</height>
</size>
</property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>160</width> <width>160</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string extracomment="Leave blank for the default."/>
</property>
<property name="hint" stdset="0"> <property name="hint" stdset="0">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="spacer_device">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_alsaplugin">
<item>
<widget class="QLabel" name="label_alsaplugin">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>ALSA plugin</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radiobutton_alsa_hw">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>hw</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radiobutton_alsa_plughw">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>plughw</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer_alsaplugin">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
@@ -155,7 +268,7 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_bufferminfill"> <widget class="QLabel" name="label_bufferminfill">
<property name="text"> <property name="text">
<string>Minimum buffer fill</string> <string>&amp;Minimum buffer fill</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@@ -65,7 +65,7 @@ NotificationsSettingsPage::NotificationsSettingsPage(SettingsDialog* dialog)
pretty_popup_->SetMessage(tr("OSD Preview"), tr("Drag to reposition"), QImage(":/pictures/nocover.png")); pretty_popup_->SetMessage(tr("OSD Preview"), tr("Drag to reposition"), QImage(":/pictures/nocover.png"));
ui_->notifications_bg_preset->setItemData(0, QColor(OSDPretty::kPresetBlue), Qt::DecorationRole); ui_->notifications_bg_preset->setItemData(0, QColor(OSDPretty::kPresetBlue), Qt::DecorationRole);
ui_->notifications_bg_preset->setItemData(1, QColor(OSDPretty::kPresetOrange), Qt::DecorationRole); ui_->notifications_bg_preset->setItemData(1, QColor(OSDPretty::kPresetRed), Qt::DecorationRole);
// Create and populate the helper menus // Create and populate the helper menus
QMenu *menu = new QMenu(this); QMenu *menu = new QMenu(this);
@@ -186,7 +186,7 @@ void NotificationsSettingsPage::Load() {
QRgb color = pretty_popup_->background_color(); QRgb color = pretty_popup_->background_color();
if (color == OSDPretty::kPresetBlue) if (color == OSDPretty::kPresetBlue)
ui_->notifications_bg_preset->setCurrentIndex(0); ui_->notifications_bg_preset->setCurrentIndex(0);
else if (color == OSDPretty::kPresetOrange) else if (color == OSDPretty::kPresetRed)
ui_->notifications_bg_preset->setCurrentIndex(1); ui_->notifications_bg_preset->setCurrentIndex(1);
else else
ui_->notifications_bg_preset->setCurrentIndex(2); ui_->notifications_bg_preset->setCurrentIndex(2);
@@ -246,7 +246,7 @@ void NotificationsSettingsPage::PrettyColorPresetChanged(int index) {
break; break;
case 1: case 1:
pretty_popup_->set_background_color(OSDPretty::kPresetOrange); pretty_popup_->set_background_color(OSDPretty::kPresetRed);
break; break;
case 2: case 2:

View File

@@ -307,7 +307,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Strawberry Orange</string> <string>Strawberry Red</string>
</property> </property>
</item> </item>
<item> <item>

View File

@@ -282,3 +282,10 @@ void SettingsDialog::CurrentItemChanged(QTreeWidgetItem *item) {
} }
} }
void SettingsDialog::ComboBoxLoadFromSettings(QSettings &s, QComboBox *combobox, QString setting, QString default_value) {
QString value = s.value(setting, default_value).toString();
int i = combobox->findData(value);
if (i == -1) i = combobox->findData(default_value);
combobox->setCurrentIndex(i);
}

View File

@@ -36,6 +36,8 @@
#include <QAbstractButton> #include <QAbstractButton>
#include <QScrollArea> #include <QScrollArea>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QComboBox>
#include <QSettings>
#include "core/logging.h" #include "core/logging.h"
#include "widgets/osd.h" #include "widgets/osd.h"
@@ -108,6 +110,8 @@ public:
void set_output_changed(bool output_changed) { output_changed_ = output_changed; } void set_output_changed(bool output_changed) { output_changed_ = output_changed; }
void ComboBoxLoadFromSettings(QSettings &s, QComboBox *combobox, QString setting, QString default_value);
signals: signals:
void NotificationPreview(OSD::Behaviour, QString, QString); void NotificationPreview(OSD::Behaviour, QString, QString);

View File

@@ -45,7 +45,7 @@ TidalSettingsPage::TidalSettingsPage(SettingsDialog *parent)
connect(ui_->button_login, SIGNAL(clicked()), SLOT(LoginClicked())); connect(ui_->button_login, SIGNAL(clicked()), SLOT(LoginClicked()));
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked())); connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked()));
connect(this, SIGNAL(Login(QString, QString, int)), service_, SLOT(SendLogin(QString, QString, int))); connect(this, SIGNAL(Login(QString, QString)), service_, SLOT(SendLogin(QString, QString)));
connect(service_, SIGNAL(LoginFailure(QString)), SLOT(LoginFailure(QString))); connect(service_, SIGNAL(LoginFailure(QString)), SLOT(LoginFailure(QString)));
connect(service_, SIGNAL(LoginSuccess()), SLOT(LoginSuccess())); connect(service_, SIGNAL(LoginSuccess()), SLOT(LoginSuccess()));
@@ -56,6 +56,9 @@ TidalSettingsPage::TidalSettingsPage(SettingsDialog *parent)
ui_->combobox_quality->addItem("High", "HIGH"); ui_->combobox_quality->addItem("High", "HIGH");
ui_->combobox_quality->addItem("Lossless", "LOSSLESS"); ui_->combobox_quality->addItem("Lossless", "LOSSLESS");
ui_->combobox_streamurl->addItem("HTTP", "http");
ui_->combobox_streamurl->addItem("HTTPS", "https");
ui_->combobox_coversize->addItem("160x160", "160x160"); ui_->combobox_coversize->addItem("160x160", "160x160");
ui_->combobox_coversize->addItem("320x320", "320x320"); ui_->combobox_coversize->addItem("320x320", "320x320");
ui_->combobox_coversize->addItem("640x640", "640x640"); ui_->combobox_coversize->addItem("640x640", "640x640");
@@ -71,21 +74,17 @@ void TidalSettingsPage::Load() {
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
ui_->username->setText(s.value("username").toString()); ui_->username->setText(s.value("username").toString());
ui_->password->setText(s.value("password").toString()); QByteArray password = s.value("password").toByteArray();
if (password.isEmpty()) ui_->password->setText("");
QString quality = s.value("quality", "HIGH").toString(); else ui_->password->setText(QString::fromUtf8(QByteArray::fromBase64(password)));
ui_->combobox_quality->setCurrentIndex(ui_->combobox_quality->findData(quality)); dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_quality, "quality", "HIGH");
ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt()); ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 40).toInt()); ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 100).toInt());
ui_->spinbox_songssearchlimit->setValue(s.value("songssearchlimit", 10).toInt()); ui_->spinbox_songssearchlimit->setValue(s.value("songssearchlimit", 100).toInt());
ui_->checkbox_fetchalbums->setChecked(s.value("fetchalbums", false).toBool()); ui_->checkbox_fetchalbums->setChecked(s.value("fetchalbums", false).toBool());
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_coversize, "coversize", "320x320");
QString coversize = s.value("coversize", "320x320").toString(); dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_streamurl, "streamurl", "http");
ui_->combobox_coversize->setCurrentIndex(ui_->combobox_coversize->findData(coversize));
s.endGroup(); s.endGroup();
if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn); if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
@@ -97,13 +96,14 @@ void TidalSettingsPage::Save() {
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("username", ui_->username->text()); s.setValue("username", ui_->username->text());
s.setValue("password", ui_->password->text()); s.setValue("password", QString::fromUtf8(ui_->password->text().toUtf8().toBase64()));
s.setValue("quality", ui_->combobox_quality->itemData(ui_->combobox_quality->currentIndex())); s.setValue("quality", ui_->combobox_quality->itemData(ui_->combobox_quality->currentIndex()));
s.setValue("searchdelay", ui_->spinbox_searchdelay->value()); s.setValue("searchdelay", ui_->spinbox_searchdelay->value());
s.setValue("albumssearchlimit", ui_->spinbox_albumssearchlimit->value()); s.setValue("albumssearchlimit", ui_->spinbox_albumssearchlimit->value());
s.setValue("songssearchlimit", ui_->spinbox_songssearchlimit->value()); s.setValue("songssearchlimit", ui_->spinbox_songssearchlimit->value());
s.setValue("fetchalbums", ui_->checkbox_fetchalbums->isChecked()); s.setValue("fetchalbums", ui_->checkbox_fetchalbums->isChecked());
s.setValue("coversize", ui_->combobox_coversize->itemData(ui_->combobox_coversize->currentIndex())); s.setValue("coversize", ui_->combobox_coversize->itemData(ui_->combobox_coversize->currentIndex()));
s.setValue("streamurl", ui_->combobox_streamurl->itemData(ui_->combobox_streamurl->currentIndex()));
s.endGroup(); s.endGroup();
service_->ReloadSettings(); service_->ReloadSettings();

View File

@@ -49,7 +49,7 @@ class TidalSettingsPage : public SettingsPage {
bool eventFilter(QObject *object, QEvent *event); bool eventFilter(QObject *object, QEvent *event);
signals: signals:
void Login(const QString &username, const QString &password, const int search_id = 0); void Login(const QString &username, const QString &password);
private slots: private slots:
void LoginClicked(); void LoginClicked();

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>715</width> <width>715</width>
<height>483</height> <height>547</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -281,6 +281,39 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_streamurl">
<item>
<widget class="QLabel" name="label_streamurl">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Stream URL scheme</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_streamurl"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@@ -39,6 +39,7 @@
#include <QSettings> #include <QSettings>
#include "core/application.h" #include "core/application.h"
#include "core/player.h"
#include "core/closure.h" #include "core/closure.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/mergedproxymodel.h" #include "core/mergedproxymodel.h"
@@ -51,20 +52,22 @@
#include "internet/internetmodel.h" #include "internet/internetmodel.h"
#include "tidalservice.h" #include "tidalservice.h"
#include "tidalsearch.h" #include "tidalsearch.h"
#include "tidalurlhandler.h"
#include "settings/tidalsettingspage.h" #include "settings/tidalsettingspage.h"
const Song::Source TidalService::kSource = Song::Source_Tidal; const Song::Source TidalService::kSource = Song::Source_Tidal;
const char *TidalService::kServiceName = "Tidal";
const char *TidalService::kApiUrl = "https://listen.tidal.com/v1"; const char *TidalService::kApiUrl = "https://listen.tidal.com/v1";
const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username"; const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username";
const char *TidalService::kResourcesUrl = "http://resources.tidal.com"; const char *TidalService::kResourcesUrl = "http://resources.tidal.com";
const char *TidalService::kApiToken = "P5Xbeo5LFvESeDy6"; const char *TidalService::kApiTokenB64 = "UDVYYmVvNUxGdkVTZUR5Ng==";
const int TidalService::kLoginAttempts = 2;
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
TidalService::TidalService(Application *app, InternetModel *parent) TidalService::TidalService(Application *app, InternetModel *parent)
: InternetService(kSource, kServiceName, app, parent, parent), : InternetService(Song::Source_Tidal, "Tidal", "tidal", app, parent, parent),
network_(new NetworkAccessManager(this)), network_(new NetworkAccessManager(this)),
url_handler_(new TidalUrlHandler(app, this)),
timer_searchdelay_(new QTimer(this)), timer_searchdelay_(new QTimer(this)),
searchdelay_(1500), searchdelay_(1500),
albumssearchlimit_(1), albumssearchlimit_(1),
@@ -73,14 +76,20 @@ TidalService::TidalService(Application *app, InternetModel *parent)
user_id_(0), user_id_(0),
pending_search_id_(0), pending_search_id_(0),
next_pending_search_id_(1), next_pending_search_id_(1),
login_sent_(false) search_id_(0),
albums_requested_(0),
albums_received_(0),
login_sent_(false),
login_attempts_(0)
{ {
timer_searchdelay_->setSingleShot(true); timer_searchdelay_->setSingleShot(true);
connect(timer_searchdelay_, SIGNAL(timeout()), SLOT(StartSearch())); connect(timer_searchdelay_, SIGNAL(timeout()), SLOT(StartSearch()));
connect(this, SIGNAL(Login(int)), SLOT(SendLogin(int))); connect(this, SIGNAL(Login()), SLOT(SendLogin()));
connect(this, SIGNAL(Login(QString, QString, int)), SLOT(SendLogin(QString, QString, int))); connect(this, SIGNAL(Login(QString, QString)), SLOT(SendLogin(QString, QString)));
app->player()->RegisterUrlHandler(url_handler_);
ReloadSettings(); ReloadSettings();
LoadSessionID(); LoadSessionID();
@@ -98,13 +107,16 @@ void TidalService::ReloadSettings() {
QSettings s; QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup); s.beginGroup(TidalSettingsPage::kSettingsGroup);
username_ = s.value("username").toString(); username_ = s.value("username").toString();
password_ = s.value("password").toString(); QByteArray password = s.value("password").toByteArray();
if (password.isEmpty()) password_.clear();
else password_ = QString::fromUtf8(QByteArray::fromBase64(password));
quality_ = s.value("quality").toString(); quality_ = s.value("quality").toString();
searchdelay_ = s.value("searchdelay", 1500).toInt(); searchdelay_ = s.value("searchdelay", 1500).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 40).toInt(); albumssearchlimit_ = s.value("albumssearchlimit", 100).toInt();
songssearchlimit_ = s.value("songssearchlimit", 10).toInt(); songssearchlimit_ = s.value("songssearchlimit", 100).toInt();
fetchalbums_ = s.value("fetchalbums", false).toBool(); fetchalbums_ = s.value("fetchalbums", false).toBool();
coversize_ = s.value("coversize", "320x320").toString(); coversize_ = s.value("coversize", "320x320").toString();
streamurl_ = s.value("streamurl", "http").toString();
s.endGroup(); s.endGroup();
} }
@@ -117,17 +129,18 @@ void TidalService::LoadSessionID() {
session_id_ = s.value("session_id").toString(); session_id_ = s.value("session_id").toString();
user_id_ = s.value("user_id").toInt(); user_id_ = s.value("user_id").toInt();
country_code_ = s.value("country_code").toString(); country_code_ = s.value("country_code").toString();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12).toLower();
s.endGroup(); s.endGroup();
} }
void TidalService::SendLogin(const int search_id) { void TidalService::SendLogin() {
SendLogin(username_, password_, search_id); SendLogin(username_, password_);
} }
void TidalService::SendLogin(const QString &username, const QString &password, const int search_id) { void TidalService::SendLogin(const QString &username, const QString &password) {
if (search_id != 0) emit UpdateStatus("Authenticating..."); if (search_id_ != 0) emit UpdateStatus("Authenticating...");
login_sent_ = true; login_sent_ = true;
login_attempts_++; login_attempts_++;
@@ -138,7 +151,10 @@ void TidalService::SendLogin(const QString &username, const QString &password, c
typedef QPair<QByteArray, QByteArray> EncodedArg; typedef QPair<QByteArray, QByteArray> EncodedArg;
typedef QList<EncodedArg> EncodedArgList; typedef QList<EncodedArg> EncodedArgList;
ArgList args = ArgList() <<Arg("token", kApiToken) << Arg("username", username) << Arg("password", password) << Arg("clientVersion", "2.2.1--7"); ArgList args = ArgList() << Arg("token", QByteArray::fromBase64(kApiTokenB64))
<< Arg("username", username)
<< Arg("password", password)
<< Arg("clientUniqueKey", clientuniquekey_);
QStringList query_items; QStringList query_items;
QUrlQuery url_query; QUrlQuery url_query;
@@ -152,12 +168,13 @@ void TidalService::SendLogin(const QString &username, const QString &password, c
QNetworkRequest req(url); QNetworkRequest req(url);
req.setRawHeader("Origin", "http://listen.tidal.com"); req.setRawHeader("Origin", "http://listen.tidal.com");
req.setRawHeader("X-Tidal-Token", QByteArray::fromBase64(kApiTokenB64));
QNetworkReply *reply = network_->post(req, url_query.toString(QUrl::FullyEncoded).toUtf8()); QNetworkReply *reply = network_->post(req, url_query.toString(QUrl::FullyEncoded).toUtf8());
NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*, int)), reply, search_id); NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*)), reply);
} }
void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { void TidalService::HandleAuthReply(QNetworkReply *reply) {
reply->deleteLater(); reply->deleteLater();
@@ -167,7 +184,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
if (reply->error() < 200) { if (reply->error() < 200) {
// This is a network error, there is nothing more to do. // This is a network error, there is nothing more to do.
QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
@@ -189,7 +206,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
else { else {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
} }
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
@@ -201,21 +218,21 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
QString failure_reason("Authentication reply from server missing Json data."); QString failure_reason("Authentication reply from server missing Json data.");
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
if (json_doc.isNull() || json_doc.isEmpty()) { if (json_doc.isNull() || json_doc.isEmpty()) {
QString failure_reason("Authentication reply from server has empty Json document."); QString failure_reason("Authentication reply from server has empty Json document.");
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
if (!json_doc.isObject()) { if (!json_doc.isObject()) {
QString failure_reason("Authentication reply from server has Json document that is not an object."); QString failure_reason("Authentication reply from server has Json document that is not an object.");
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
@@ -223,14 +240,14 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
QJsonObject json_obj = json_doc.object(); QJsonObject json_obj = json_doc.object();
if (json_obj.isEmpty()) { if (json_obj.isEmpty()) {
QString failure_reason("Authentication reply from server has empty Json object."); QString failure_reason("Authentication reply from server has empty Json object.");
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
if ( !json_obj.contains("userId") || !json_obj.contains("sessionId") || !json_obj.contains("countryCode") ) { if ( !json_obj.contains("userId") || !json_obj.contains("sessionId") || !json_obj.contains("countryCode") ) {
QString failure_reason = tr("Authentication reply from server is missing userId, sessionId or countryCode"); QString failure_reason("Authentication reply from server is missing userId, sessionId or countryCode");
if (search_id != 0) Error(failure_reason); Error(failure_reason);
emit LoginFailure(failure_reason); emit LoginFailure(failure_reason);
return; return;
} }
@@ -238,6 +255,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
country_code_ = json_obj["countryCode"].toString(); country_code_ = json_obj["countryCode"].toString();
session_id_ = json_obj["sessionId"].toString(); session_id_ = json_obj["sessionId"].toString();
user_id_ = json_obj["userId"].toInt(); user_id_ = json_obj["userId"].toInt();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12).toLower();
QSettings s; QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup); s.beginGroup(TidalSettingsPage::kSettingsGroup);
@@ -248,10 +266,16 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
qLog(Debug) << "Tidal: Login successful" << "user id" << user_id_ << "session id" << session_id_ << "country code" << country_code_; qLog(Debug) << "Tidal: Login successful" << "user id" << user_id_ << "session id" << session_id_ << "country code" << country_code_;
if (search_id != 0) { login_attempts_ = 0;
qLog(Debug) << "Tidal: Resuming search";
if (search_id_ != 0) {
qLog(Debug) << "Tidal: Resuming search" << search_id_;
SendSearch(); SendSearch();
} }
if (!stream_request_url_.isEmpty()) {
qLog(Debug) << "Tidal: Resuming get stream url" << stream_request_url_;
emit GetStreamURL(stream_request_url_);
}
emit LoginSuccess(); emit LoginSuccess();
@@ -295,6 +319,8 @@ QNetworkReply *TidalService::CreateRequest(const QString &ressource_name, const
QUrl url(kApiUrl + QString("/") + ressource_name); QUrl url(kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);
req.setRawHeader("Origin", "http://listen.tidal.com");
req.setRawHeader("X-Tidal-SessionId", session_id_.toUtf8());
QNetworkReply *reply = network_->get(req); QNetworkReply *reply = network_->get(req);
//qLog(Debug) << "Tidal: Sending request" << url; //qLog(Debug) << "Tidal: Sending request" << url;
@@ -303,7 +329,7 @@ QNetworkReply *TidalService::CreateRequest(const QString &ressource_name, const
} }
QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, bool sendlogin) { QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, const bool sendlogin) {
QByteArray data; QByteArray data;
@@ -334,14 +360,15 @@ QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, bool sendlogin) {
else { else {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
} }
qLog(Debug) << reply->error();
if (reply->error() == QNetworkReply::ContentAccessDenied || reply->error() == QNetworkReply::ContentOperationNotPermittedError || reply->error() == QNetworkReply::AuthenticationRequiredError) { if (reply->error() == QNetworkReply::ContentAccessDenied || reply->error() == QNetworkReply::ContentOperationNotPermittedError || reply->error() == QNetworkReply::AuthenticationRequiredError) {
// Session is probably expired, attempt to login once // Session is probably expired, attempt to login once
Logout(); Logout();
if (sendlogin && login_attempts_ < 1 && !username_.isEmpty() && !password_.isEmpty()) { if (sendlogin && login_attempts_ < kLoginAttempts && !username_.isEmpty() && !password_.isEmpty()) {
qLog(Error) << "Tidal:" << failure_reason; qLog(Error) << "Tidal:" << failure_reason;
qLog(Error) << "Tidal:" << QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); qLog(Error) << "Tidal:" << QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
qLog(Error) << "Tidal:" << "Attempting to login."; qLog(Error) << "Tidal:" << "Attempting to login.";
emit Login(search_id_); emit Login();
} }
else { else {
Error(failure_reason); Error(failure_reason);
@@ -439,7 +466,7 @@ void TidalService::StartSearch() {
search_text_ = pending_search_text_; search_text_ = pending_search_text_;
if (authenticated()) SendSearch(); if (authenticated()) SendSearch();
else emit Login(username_, password_, search_id_); else emit Login(username_, password_);
} }
@@ -451,9 +478,7 @@ void TidalService::ClearSearch() {
search_text_.clear(); search_text_.clear();
search_error_.clear(); search_error_.clear();
albums_requested_ = 0; albums_requested_ = 0;
songs_requested_ = 0;
albums_received_ = 0; albums_received_ = 0;
songs_received_ = 0;
requests_album_.clear(); requests_album_.clear();
requests_song_.clear(); requests_song_.clear();
login_attempts_ = 0; login_attempts_ = 0;
@@ -525,9 +550,7 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
// This was a tracks search // This was a tracks search
if (!fetchalbums_) { if (!fetchalbums_) {
Song song = ParseSong(0, value); Song song = ParseSong(0, value);
requests_song_.insert(song.id(), song); songs_ << song;
songs_requested_++;
GetStreamURL(0, song.id());
continue; continue;
} }
QJsonValue json_value_album = json_obj["album"]; QJsonValue json_value_album = json_obj["album"];
@@ -573,12 +596,13 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
QString artist = json_artist["name"].toString(); QString artist = json_artist["name"].toString();
QString quality = json_obj["audioQuality"].toString(); QString quality = json_obj["audioQuality"].toString();
QString copyright = json_obj["copyright"].toString();
//qLog(Debug) << "Tidal:" << artist << album << quality; //qLog(Debug) << "Tidal:" << artist << album << quality << copyright;
QString artist_album(QString("%1-%2").arg(artist).arg(album)); QString artist_album(QString("%1-%2").arg(artist).arg(album));
if (albums.contains(artist_album)) { if (albums.contains(artist_album)) {
qLog(Debug) << "Tidal: Skipping duplicate album" << artist << album << quality; qLog(Debug) << "Tidal: Skipping duplicate album" << artist << album << quality << copyright;
continue; continue;
} }
albums.insert(0, artist_album); albums.insert(0, artist_album);
@@ -594,11 +618,6 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
emit ProgressSetMaximum(albums_requested_); emit ProgressSetMaximum(albums_requested_);
emit UpdateProgress(0); emit UpdateProgress(0);
} }
else if (songs_requested_ > 0) {
emit UpdateStatus(QString("Retriving %1 song%2...").arg(songs_requested_).arg(songs_requested_ == 1 ? "" : "s"));
emit ProgressSetMaximum(songs_requested_);
emit UpdateProgress(songs_received_);
}
CheckFinish(); CheckFinish();
@@ -607,11 +626,7 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
void TidalService::GetAlbum(const int album_id) { void TidalService::GetAlbum(const int album_id) {
QList<Param> parameters; QList<Param> parameters;
parameters << Param("token", session_id_)
<< Param("soundQuality", quality_);
QNetworkReply *reply = CreateRequest(QString("albums/%1/tracks").arg(album_id), parameters); QNetworkReply *reply = CreateRequest(QString("albums/%1/tracks").arg(album_id), parameters);
NewClosure(reply, SIGNAL(finished()), this, SLOT(GetAlbumFinished(QNetworkReply*, int, int)), reply, search_id_, album_id); NewClosure(reply, SIGNAL(finished()), this, SLOT(GetAlbumFinished(QNetworkReply*, int, int)), reply, search_id_, album_id);
} }
@@ -649,15 +664,7 @@ void TidalService::GetAlbumFinished(QNetworkReply *reply, int search_id, int alb
QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc())); QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc()));
song.set_album(album_full); song.set_album(album_full);
} }
requests_song_.insert(song.id(), song); songs_ << song;
songs_requested_++;
GetStreamURL(album_id, song.id());
}
if (albums_requested_ <= albums_received_) {
emit UpdateStatus(QString("Retriving %1 song%2...").arg(songs_requested_).arg(songs_requested_ == 1 ? "" : "s"));
emit ProgressSetMaximum(songs_requested_);
emit UpdateProgress(songs_received_);
} }
CheckFinish(); CheckFinish();
@@ -702,10 +709,9 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
QJsonArray json_artists = json_obj["artists"].toArray(); QJsonArray json_artists = json_obj["artists"].toArray();
int song_id = json_obj["id"].toInt(); int song_id = json_obj["id"].toInt();
if (requests_song_.contains(song_id)) return requests_song_.value(song_id);
QString title = json_obj["title"].toString(); QString title = json_obj["title"].toString();
QString url = json_obj["url"].toString(); QString urlstr = json_obj["url"].toString();
int track = json_obj["trackNumber"].toInt(); int track = json_obj["trackNumber"].toInt();
int disc = json_obj["volumeNumber"].toInt(); int disc = json_obj["volumeNumber"].toInt();
bool allow_streaming = json_obj["allowStreaming"].toBool(); bool allow_streaming = json_obj["allowStreaming"].toBool();
@@ -760,9 +766,6 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
song.set_title(title); song.set_title(title);
song.set_track(track); song.set_track(track);
song.set_disc(disc); song.set_disc(disc);
song.set_bitrate(0);
song.set_samplerate(0);
song.set_bitdepth(0);
QVariant q_duration = json_duration.toVariant(); QVariant q_duration = json_duration.toVariant();
if (q_duration.isValid()) { if (q_duration.isValid()) {
@@ -773,81 +776,81 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
cover = cover.replace("-", "/"); cover = cover.replace("-", "/");
QUrl cover_url (QString("%1/images/%2/%3.jpg").arg(kResourcesUrl).arg(cover).arg(coversize_)); QUrl cover_url (QString("%1/images/%2/%3.jpg").arg(kResourcesUrl).arg(cover).arg(coversize_));
song.set_art_automatic(cover_url.toEncoded()); song.set_art_automatic(cover_url.toEncoded());
QUrl url;
url.setScheme(url_handler_->scheme());
url.setPath(QString::number(song_id));
song.set_url(url);
song.set_valid(true); song.set_valid(true);
return song; return song;
} }
void TidalService::GetStreamURL(const int album_id, const int song_id) { void TidalService::GetStreamURL(const QUrl &url) {
stream_request_url_ = url;
int song_id = url.path().toInt();
requests_song_.insert(song_id, url);
QList<Param> parameters; QList<Param> parameters;
parameters << Param("token", session_id_) parameters << Param("soundQuality", quality_);
<< Param("soundQuality", quality_);
QNetworkReply *reply = CreateRequest(QString("tracks/%1/streamUrl").arg(song_id), parameters); QNetworkReply *reply = CreateRequest(QString("tracks/%1/streamUrl").arg(song_id), parameters);
NewClosure(reply, SIGNAL(finished()), this, SLOT(GetStreamURLFinished(QNetworkReply*, int, int)), reply, search_id_, song_id); NewClosure(reply, SIGNAL(finished()), this, SLOT(GetStreamURLFinished(QNetworkReply*, int, QUrl)), reply, song_id, url);
} }
void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int search_id, const int song_id) { void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int song_id, const QUrl original_url) {
reply->deleteLater(); reply->deleteLater();
if (requests_song_.contains(song_id)) requests_song_.remove(song_id);
if (original_url != stream_request_url_) return;
if (search_id != search_id_) return; QJsonObject json_obj = ExtractJsonObj(reply, true);
if (!requests_song_.contains(song_id)) {
CheckFinish();
return;
}
Song song = requests_song_.value(song_id);
songs_received_++;
if (albums_requested_ <= albums_received_) {
emit UpdateProgress(songs_received_);
}
QJsonObject json_obj = ExtractJsonObj(reply);
if (json_obj.isEmpty()) { if (json_obj.isEmpty()) {
requests_song_.remove(song_id); if (!stream_request_url_.isEmpty() && !login_sent_) {
CheckFinish(); emit StreamURLFinished(QUrl(), Song::FileType_Stream);
stream_request_url_ = QUrl();
}
return; return;
} }
if (!json_obj.contains("url") || !json_obj.contains("codec")) { if (!json_obj.contains("url") || !json_obj.contains("codec")) {
qLog(Error) << "Tidal: Invalid Json reply, stream missing url or codec."; qLog(Error) << "Tidal: Invalid Json reply, stream missing url or codec.";
qLog(Debug) << json_obj; qLog(Debug) << json_obj;
requests_song_.remove(song_id); emit StreamURLFinished(QUrl(), Song::FileType_Stream);
CheckFinish(); stream_request_url_ = QUrl();
return; return;
} }
song.set_url(QUrl(json_obj["url"].toString())); stream_request_url_ = QUrl();
QString codec = json_obj["codec"].toString().toLower(); QUrl new_url(json_obj["url"].toString());
song.set_filetype(Song::FiletypeByExtension(codec)); QString codec(json_obj["codec"].toString().toLower());
if (song.filetype() == Song::FileType_Unknown) { Song::FileType filetype(Song::FiletypeByExtension(codec));
if (filetype == Song::FileType_Unknown) {
qLog(Debug) << "Tidal: Unknown codec" << codec; qLog(Debug) << "Tidal: Unknown codec" << codec;
song.set_filetype(Song::FileType_Stream); filetype = Song::FileType_Stream;
} }
song.set_valid(true); if (new_url.scheme() != streamurl_) new_url.setScheme(streamurl_);
//qLog(Debug) << song.artist() << song.album() << song.title() << song.url() << song.filetype(); emit StreamURLFinished(new_url, filetype);
songs_ << song;
requests_song_.remove(song_id);
CheckFinish();
} }
void TidalService::CheckFinish() { void TidalService::CheckFinish() {
if (!login_sent_ && albums_requested_ <= albums_received_ && songs_requested_ <= songs_received_) { if (search_id_ == 0) return;
if (songs_.isEmpty()) emit SearchError(search_id_, search_error_);
if (!login_sent_ && albums_requested_ <= albums_received_) {
if (songs_.isEmpty()) {
if (search_error_.isEmpty()) emit SearchError(search_id_, "Unknown error");
else emit SearchError(search_id_, search_error_);
}
else emit SearchResults(search_id_, songs_); else emit SearchResults(search_id_, songs_);
ClearSearch(); ClearSearch();
} }
@@ -857,6 +860,15 @@ void TidalService::CheckFinish() {
void TidalService::Error(QString error, QString debug) { void TidalService::Error(QString error, QString debug) {
qLog(Error) << "Tidal:" << error; qLog(Error) << "Tidal:" << error;
if (!debug.isEmpty()) qLog(Debug) << debug; if (!debug.isEmpty()) qLog(Debug) << debug;
search_error_ = error; if (search_id_ != 0) {
CheckFinish(); if (!error.isEmpty()) {
search_error_ += error;
search_error_ += "<br />";
}
CheckFinish();
}
if (!stream_request_url_.isEmpty() && !login_sent_) {
emit StreamURLFinished(QUrl(), Song::FileType_Stream);
stream_request_url_ = QUrl();
}
} }

View File

@@ -26,6 +26,7 @@
#include <QObject> #include <QObject>
#include <QHash> #include <QHash>
#include <QString> #include <QString>
#include <QUrl>
#include <QNetworkReply> #include <QNetworkReply>
#include <QTimer> #include <QTimer>
#include <QDateTime> #include <QDateTime>
@@ -39,6 +40,7 @@
#include "settings/tidalsettingspage.h" #include "settings/tidalsettingspage.h"
class NetworkAccessManager; class NetworkAccessManager;
class TidalUrlHandler;
class TidalService : public InternetService { class TidalService : public InternetService {
Q_OBJECT Q_OBJECT
@@ -48,7 +50,7 @@ class TidalService : public InternetService {
~TidalService(); ~TidalService();
static const Song::Source kSource; static const Song::Source kSource;
static const char *kServiceName; static const int kLoginAttempts;
void ReloadSettings(); void ReloadSettings();
@@ -59,9 +61,11 @@ class TidalService : public InternetService {
const bool login_sent() { return login_sent_; } const bool login_sent() { return login_sent_; }
const bool authenticated() { return (!session_id_.isEmpty() && !country_code_.isEmpty()); } const bool authenticated() { return (!session_id_.isEmpty() && !country_code_.isEmpty()); }
void GetStreamURL(const QUrl &url);
signals: signals:
void Login(const int search_id = 0); void Login();
void Login(const QString &username, const QString &password, const int search_id = 0); void Login(const QString &username, const QString &password);
void LoginSuccess(); void LoginSuccess();
void LoginFailure(QString failure_reason); void LoginFailure(QString failure_reason);
void SearchResults(int id, SongList songs); void SearchResults(int id, SongList songs);
@@ -69,18 +73,20 @@ class TidalService : public InternetService {
void UpdateStatus(QString text); void UpdateStatus(QString text);
void ProgressSetMaximum(int max); void ProgressSetMaximum(int max);
void UpdateProgress(int max); void UpdateProgress(int max);
void GetStreamURLFinished(QNetworkReply *reply, const QUrl url);
void StreamURLFinished(const QUrl url, const Song::FileType);
public slots: public slots:
void ShowConfig(); void ShowConfig();
void SendLogin(const QString &username, const QString &password, const int search_id = 0); void SendLogin(const QString &username, const QString &password);
private slots: private slots:
void SendLogin(const int search_id = 0); void SendLogin();
void HandleAuthReply(QNetworkReply *reply, int search_id); void HandleAuthReply(QNetworkReply *reply);
void StartSearch(); void StartSearch();
void SearchFinished(QNetworkReply *reply, int search_id); void SearchFinished(QNetworkReply *reply, int search_id);
void GetAlbumFinished(QNetworkReply *reply, int search_id, int album_id); void GetAlbumFinished(QNetworkReply *reply, int search_id, int album_id);
void GetStreamURLFinished(QNetworkReply *reply, const int search_id, const int song_id); void GetStreamURLFinished(QNetworkReply *reply, const int song_id, const QUrl original_url);
private: private:
void ClearSearch(); void ClearSearch();
@@ -91,16 +97,16 @@ class TidalService : public InternetService {
void SendSearch(); void SendSearch();
void GetAlbum(const int album_id); void GetAlbum(const int album_id);
Song ParseSong(const int album_id_requested, const QJsonValue &value); Song ParseSong(const int album_id_requested, const QJsonValue &value);
void GetStreamURL(const int album_id, const int song_id);
void CheckFinish(); void CheckFinish();
void Error(QString error, QString debug = ""); void Error(QString error, QString debug = QString());
static const char *kApiUrl; static const char *kApiUrl;
static const char *kAuthUrl; static const char *kAuthUrl;
static const char *kResourcesUrl; static const char *kResourcesUrl;
static const char *kApiToken; static const char *kApiTokenB64;
NetworkAccessManager *network_; NetworkAccessManager *network_;
TidalUrlHandler *url_handler_;
QTimer *timer_searchdelay_; QTimer *timer_searchdelay_;
QString username_; QString username_;
@@ -111,9 +117,11 @@ class TidalService : public InternetService {
int songssearchlimit_; int songssearchlimit_;
bool fetchalbums_; bool fetchalbums_;
QString coversize_; QString coversize_;
QString streamurl_;
QString session_id_; QString session_id_;
quint64 user_id_; quint64 user_id_;
QString country_code_; QString country_code_;
QString clientuniquekey_;
int pending_search_id_; int pending_search_id_;
int next_pending_search_id_; int next_pending_search_id_;
@@ -123,15 +131,14 @@ class TidalService : public InternetService {
int search_id_; int search_id_;
QString search_text_; QString search_text_;
QHash<int, int> requests_album_; QHash<int, int> requests_album_;
QHash<int, Song> requests_song_; QHash<int, QUrl> requests_song_;
int albums_requested_; int albums_requested_;
int albums_received_; int albums_received_;
int songs_requested_;
int songs_received_;
SongList songs_; SongList songs_;
QString search_error_; QString search_error_;
bool login_sent_; bool login_sent_;
int login_attempts_; int login_attempts_;
QUrl stream_request_url_;
}; };

View File

@@ -0,0 +1,63 @@
/*
* Strawberry Music Player
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <QObject>
#include <QString>
#include <QUrl>
#include "core/application.h"
#include "core/taskmanager.h"
#include "core/iconloader.h"
#include "core/logging.h"
#include "core/song.h"
#include "tidal/tidalservice.h"
#include "tidalurlhandler.h"
TidalUrlHandler::TidalUrlHandler(
Application *app, TidalService *service)
: UrlHandler(service), app_(app), service_(service), task_id_(-1) {
connect(service, SIGNAL(StreamURLFinished(QUrl, Song::FileType)), this, SLOT(GetStreamURLFinished(QUrl, Song::FileType)));
}
UrlHandler::LoadResult TidalUrlHandler::StartLoading(const QUrl &url) {
LoadResult ret(url);
if (task_id_ != -1) return ret;
last_original_url_ = url;
task_id_ = app_->task_manager()->StartTask(QString("Loading %1 stream...").arg(url.scheme()));
service_->GetStreamURL(url);
ret.type_ = LoadResult::WillLoadAsynchronously;
return ret;
}
void TidalUrlHandler::GetStreamURLFinished(QUrl url, Song::FileType filetype) {
if (task_id_ == -1) return;
CancelTask();
emit AsyncLoadComplete(LoadResult(last_original_url_, LoadResult::TrackAvailable, url, filetype));
}
void TidalUrlHandler::CancelTask() {
app_->task_manager()->SetTaskFinished(task_id_);
task_id_ = -1;
}

View File

@@ -0,0 +1,56 @@
/*
* Strawberry Music Player
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TIDALURLHANDLER_H
#define TIDALURLHANDLER_H
#include <QObject>
#include <QString>
#include <QUrl>
#include "core/urlhandler.h"
#include "core/song.h"
#include "tidal/tidalservice.h"
class Application;
class TidalService;
class TidalUrlHandler : public UrlHandler {
Q_OBJECT
public:
TidalUrlHandler(Application *app, TidalService *service);
QString scheme() const { return service_->url_scheme(); }
LoadResult StartLoading(const QUrl &url);
void CancelTask();
private slots:
void GetStreamURLFinished(QUrl url, Song::FileType filetype);
private:
Application *app_;
TidalService *service_;
int task_id_;
QUrl last_original_url_;
};
#endif

View File

@@ -233,9 +233,9 @@ QModelIndex GroupedIconView::indexAt(const QPoint &p) const {
void GroupedIconView::paintEvent(QPaintEvent *e) { void GroupedIconView::paintEvent(QPaintEvent *e) {
// This code was adapted from QListView::paintEvent(), changed to use the visualRect() of items, and to draw headers. // This code was adapted from QListView::paintEvent(), changed to use the visualRect() of items, and to draw headers.
QStyleOptionViewItemV4 option(viewOptions()); QStyleOptionViewItem option(viewOptions());
if (isWrapping()) if (isWrapping())
option.features = QStyleOptionViewItemV2::WrapText; option.features = QStyleOptionViewItem::WrapText;
option.locale = locale(); option.locale = locale();
option.locale.setNumberOptions(QLocale::OmitGroupSeparator); option.locale.setNumberOptions(QLocale::OmitGroupSeparator);
option.widget = this; option.widget = this;

View File

@@ -72,7 +72,7 @@ const int OSDPretty::kMaxIconSize = 100;
const int OSDPretty::kSnapProximity = 20; const int OSDPretty::kSnapProximity = 20;
const QRgb OSDPretty::kPresetBlue = qRgb(102, 150, 227); const QRgb OSDPretty::kPresetBlue = qRgb(102, 150, 227);
const QRgb OSDPretty::kPresetOrange = qRgb(254, 156, 67); const QRgb OSDPretty::kPresetRed = qRgb(202, 22, 16);
OSDPretty::OSDPretty(Mode mode, QWidget *parent) OSDPretty::OSDPretty(Mode mode, QWidget *parent)

View File

@@ -67,7 +67,7 @@ class OSDPretty : public QWidget {
static const int kSnapProximity; static const int kSnapProximity;
static const QRgb kPresetBlue; static const QRgb kPresetBlue;
static const QRgb kPresetOrange; static const QRgb kPresetRed;
static bool IsTransparencyAvailable(); static bool IsTransparencyAvailable();

View File

@@ -273,9 +273,8 @@ void PlayingWidget::SongChanged(const Song &song) {
void PlayingWidget::AlbumArtLoaded(const Song &song, const QString &, const QImage &image) { void PlayingWidget::AlbumArtLoaded(const Song &song, const QString &, const QImage &image) {
if (!playing_) return; if (!playing_ || song.id() != song_playing_.id() || song.url() != song_playing_.url() || song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
if (timeline_fade_->state() == QTimeLine::Running && image == image_original_) return;
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
active_ = true; active_ = true;
downloading_covers_ = false; downloading_covers_ = false;