Compare commits

..

34 Commits
0.3.1 ... 0.3.3

Author SHA1 Message Date
Jonas Kvinge
50e3eeaafd Release 0.3.3 2018-09-24 19:13:45 +02:00
Jonas Kvinge
843f528ebc Revert change to Tidal login using clientUniqueKey 2018-09-24 19:10:49 +02:00
Jonas Kvinge
faa0076988 Turn back git revision 2018-09-24 19:10:15 +02:00
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 2236 additions and 611 deletions

View File

@@ -2,6 +2,22 @@ Strawberry Music Player
=======================
ChangeLog
Version 0.3.3:
* Fixed Tidal login
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:
* Added new lyrics provider with lyrics from AudD and API Seeds

View File

@@ -1,28 +1,65 @@
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)
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
set(RPM_ARCH x86_64 CACHE STRING "Architecture of the rpm file")
if (${LSB_RELEASE_ID_SHORT} STREQUAL "openSUSE")
set(RPM_DISTRO opensuse CACHE STRING "Suffix of the rpm file")
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
)
endif()
if (${LSB_RELEASE_ID_SHORT} STREQUAL "Fedora")
set(RPM_DISTRO fedora CACHE STRING "Suffix of the rpm file")
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
)
execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -is | tr '[:upper:]' '[:lower:]' | cut -d' ' -f1"
OUTPUT_VARIABLE DIST_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -ds | tr '[:upper:]' '[:lower:]' | sed 's/\"//g' | cut -d' ' -f2"
OUTPUT_VARIABLE DIST_RELEASE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
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
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (DIST_NAME)
message(STATUS "Distro Name: ${DIST_NAME}")
if (DIST_RELEASE)
message(STATUS "Distro Release: ${DIST_RELEASE}")
endif()
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()

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 0)
set(STRAWBERRY_VERSION_MINOR 3)
set(STRAWBERRY_VERSION_PATCH 1)
set(STRAWBERRY_VERSION_PATCH 3)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
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)
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)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec @ONLY)
if (RPM_DISTRO)
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}/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-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-64.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-debug-64.nsi @ONLY)
if (UNIX AND NOT APPLE)
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"
tar -cJf $name-$version.tar.xz \
--transform "s,^$rootnoslash,$name-$version," $exclude_vcs \
--exclude "*.tar" \
--exclude "*.tar.*" \
--exclude "*.bz" \
--exclude "*.bz2" \
--exclude "*.xz" \
--exclude ".directory" \
--exclude "$root/CMakeLists.txt.user" \
--exclude "$root/build" \
--exclude="*.tar" \
--exclude="*.tar.*" \
--exclude="*.bz" \
--exclude="*.bz2" \
--exclude="*.xz" \
--exclude=".directory" \
--exclude="*.spec" \
--exclude="*.nsi" \
--exclude="$root/CMakeLists.txt.user" \
--exclude="$root/build" \
"$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
Name "${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}.exe"
OutFile "${PRODUCT_NAME}Setup-${PRODUCT_DISPLAY_VERSION}-Release.exe"
InstallDir "${PRODUCT_INSTALL_DIR}"
; 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/musicbrainzcoverprovider.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/settingspage.cpp
settings/behavioursettingspage.cpp
@@ -247,16 +253,6 @@ set(SOURCES
globalshortcuts/gnomeglobalshortcutbackend.cpp
globalshortcuts/qxtglobalshortcutbackend.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/internetservice.cpp
@@ -268,13 +264,7 @@ set(SOURCES
tidal/tidalsearchmodel.cpp
tidal/tidalsearchsortmodel.cpp
tidal/tidalsearchitemdelegate.cpp
lyrics/lyricsproviders.cpp
lyrics/lyricsprovider.cpp
lyrics/lyricsfetcher.cpp
lyrics/lyricsfetchersearch.cpp
lyrics/auddlyricsprovider.cpp
lyrics/apiseedslyricsprovider.cpp
tidal/tidalurlhandler.cpp
)
@@ -369,7 +359,13 @@ set(HEADERS
covermanager/coverfromurldialog.h
covermanager/musicbrainzcoverprovider.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/settingspage.h
@@ -421,16 +417,6 @@ set(HEADERS
globalshortcuts/globalshortcuts.h
globalshortcuts/gnomeglobalshortcutbackend.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/internetservice.h
@@ -441,13 +427,7 @@ set(HEADERS
tidal/tidalsearch.h
tidal/tidalsearchview.h
tidal/tidalsearchmodel.h
lyrics/lyricsproviders.h
lyrics/lyricsprovider.h
lyrics/lyricsfetcher.h
lyrics/lyricsfetchersearch.h
lyrics/auddlyricsprovider.h
lyrics/apiseedslyricsprovider.h
tidal/tidalurlhandler.h
)
@@ -499,9 +479,6 @@ set(UI
widgets/osdpretty.ui
widgets/fileview.ui
widgets/loginstatewidget.ui
device/deviceproperties.ui
device/deviceviewcontainer.ui
globalshortcuts/globalshortcutgrabber.ui
@@ -679,6 +656,32 @@ optional_source(HAVE_DBUS
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)
optional_source(HAVE_DEVICEKIT
SOURCES device/devicekitlister.cpp

View File

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

View File

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

View File

@@ -66,9 +66,12 @@
#include "core/utilities.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
#include "collection/collectionmodel.h"
#include "collection/collectionitem.h"
#include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h"
#ifdef HAVE_GSTREAMER
#include "dialogs/organisedialog.h"
@@ -263,9 +266,6 @@ void ContextAlbumsView::SaveFocus() {
}
void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) {
// return;
QModelIndex current = model()->parent(child);
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
@@ -359,7 +359,6 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
if (!context_menu_) {
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()));
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
context_menu_->addSeparator();
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()));
#endif
#endif
context_menu_->addSeparator();
@@ -381,7 +382,7 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
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);
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool)));
#endif
@@ -418,13 +419,17 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
#ifdef HAVE_GSTREAMER
organise_->setVisible(regular_elements_only);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only);
#endif
#endif
// only when all selected items are editable
#ifdef HAVE_GSTREAMER
organise_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
#endif
context_menu_->popup(e->globalPos());
@@ -509,7 +514,7 @@ void ContextAlbumsView::EditTracks() {
#ifdef HAVE_GSTREAMER
void ContextAlbumsView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
@@ -517,7 +522,7 @@ void ContextAlbumsView::CopyToDevice() {
organise_dialog_->SetCopy(true);
organise_dialog_->SetSongs(GetSelectedSongs());
organise_dialog_->show();
#endif
}
#endif

View File

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

View File

@@ -82,7 +82,8 @@ ContextView::ContextView(QWidget *parent) :
timeline_fade_(new QTimeLine(1000, this)),
image_strawberry_(":/pictures/strawberry.png"),
active_(false),
downloading_covers_(false)
downloading_covers_(false),
lyrics_id_(-1)
{
ui_->setupUi(this);
@@ -173,10 +174,10 @@ void ContextView::Playing() {}
void ContextView::Stopped() {
active_ = false;
song_playing_ = song_empty_;
song_ = song_empty_;
song_playing_ = Song();
song_ = Song();
downloading_covers_ = false;
prev_artist_ = QString();
song_prev_ = Song();
lyrics_ = QString();
SetImage(image_strawberry_);
@@ -190,21 +191,40 @@ void ContextView::UpdateNoSong() {
void ContextView::SongChanged(const Song &song) {
image_previous_ = image_original_;
prev_artist_ = song_playing_.artist();
lyrics_ = song.lyrics();
song_playing_ = song;
song_ = song;
UpdateSong();
update();
if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
if (song_playing_.is_valid() && song.id() == song_playing_.id() && song.url() == song_playing_.url()) {
UpdateSong(song);
}
else {
song_prev_ = song_playing_;
lyrics_ = song.lyrics();
lyrics_id_ = -1;
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));
}
void ContextView::SetLabelDisabled(QLabel *label) {
label->setEnabled(false);
label->setVisible(false);
label->setMaximumSize(0, 0);
}
void ContextView::NoSong() {
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;
@@ -251,85 +271,58 @@ void ContextView::UpdateSong() {
"font: 11pt;"
"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()) {
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_->filetype->setText(song_.TextForFiletype());
ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec()));
if (song_.samplerate() <= 0) {
ui_->label_samplerate->setEnabled(false);
ui_->label_samplerate->setVisible(false);
ui_->label_samplerate->setMaximumSize(0, 0);
ui_->samplerate->setEnabled(false);
ui_->samplerate->setVisible(false);
ui_->samplerate->setMaximumSize(0, 0);
SetLabelEnabled(ui_->label_filetype);
SetLabelEnabled(ui_->filetype);
SetLabelEnabled(ui_->label_length);
SetLabelEnabled(ui_->length);
ui_->filetype->setText(song.TextForFiletype());
ui_->length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
if (song.samplerate() <= 0) {
SetLabelDisabled(ui_->label_samplerate);
SetLabelDisabled(ui_->samplerate);
ui_->samplerate->clear();
}
else {
ui_->label_samplerate->setEnabled(true);
ui_->label_samplerate->setVisible(true);
ui_->label_samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
ui_->samplerate->setEnabled(true);
ui_->samplerate->setVisible(true);
ui_->samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->samplerate, song_.samplerate(), "Hz");
SetLabelEnabled(ui_->label_samplerate);
SetLabelEnabled(ui_->samplerate);
SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
}
if (song_.bitdepth() <= 0) {
ui_->label_bitdepth->setEnabled(false);
ui_->label_bitdepth->setVisible(false);
ui_->label_bitdepth->setMaximumSize(0, 0);
ui_->bitdepth->setEnabled(false);
ui_->bitdepth->setVisible(false);
ui_->bitdepth->setMaximumSize(0, 0);
if (song.bitdepth() <= 0) {
SetLabelDisabled(ui_->label_bitdepth);
SetLabelDisabled(ui_->bitdepth);
ui_->bitdepth->clear();
}
else {
ui_->label_bitdepth->setEnabled(true);
ui_->label_bitdepth->setVisible(true);
ui_->label_bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
ui_->bitdepth->setEnabled(true);
ui_->bitdepth->setVisible(true);
ui_->bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
SetLabelEnabled(ui_->label_bitdepth);
SetLabelEnabled(ui_->bitdepth);
SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
}
if (song_.bitrate() <= 0) {
ui_->label_bitrate->setEnabled(false);
ui_->label_bitrate->setVisible(false);
ui_->label_bitrate->setMaximumSize(0, 0);
ui_->bitrate->setEnabled(false);
ui_->bitrate->setVisible(false);
ui_->bitrate->setMaximumSize(0, 0);
if (song.bitrate() <= 0) {
SetLabelDisabled(ui_->label_bitrate);
SetLabelDisabled(ui_->bitrate);
ui_->bitrate->clear();
}
else {
ui_->label_bitrate->setEnabled(true);
ui_->label_bitrate->setVisible(true);
ui_->label_bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
ui_->bitrate->setEnabled(true);
ui_->bitrate->setVisible(true);
ui_->bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
SetLabelEnabled(ui_->label_bitrate);
SetLabelEnabled(ui_->bitrate);
SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
}
ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
}
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_->length->clear();
ui_->samplerate->clear();
ui_->bitdepth->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);
}
@@ -403,18 +396,18 @@ void ContextView::UpdateSong() {
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;
CollectionBackend::AlbumList albumlist;
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) {
ui_->label_play_albums->setVisible(true);
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;");
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->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) {
if (id != lyrics_id_) return;
lyrics_ = lyrics;
lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) {
ui_->label_play_lyrics->setText(lyrics);
}
@@ -551,7 +597,9 @@ void ContextView::ScaleCover() {
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 (image == image_original_) return;
active_ = true;
downloading_covers_ = false;
@@ -572,6 +620,7 @@ void ContextView::SetImage(const QImage &image) {
DrawImage(&p);
p.end();
image_previous_ = image_original_;
image_original_ = image;
ScaleCover();
@@ -623,7 +672,7 @@ void ContextView::ActionShowData() {
s.beginGroup(kSettingsGroup);
s.setValue("show_data", action_show_data_->isChecked());
s.endGroup();
UpdateSong();
SetSong(song_);
}
void ContextView::ActionShowOutput() {
@@ -631,7 +680,7 @@ void ContextView::ActionShowOutput() {
s.beginGroup(kSettingsGroup);
s.setValue("show_output", action_show_output_->isChecked());
s.endGroup();
UpdateSong();
SetSong(song_);
}
void ContextView::ActionShowAlbums() {
@@ -639,8 +688,8 @@ void ContextView::ActionShowAlbums() {
s.beginGroup(kSettingsGroup);
s.setValue("show_albums", action_show_albums_->isChecked());
s.endGroup();
prev_artist_ = QString();
UpdateSong();
song_prev_ = Song();
SetSong(song_);
}
void ContextView::ActionShowLyrics() {
@@ -648,8 +697,11 @@ void ContextView::ActionShowLyrics() {
s.beginGroup(kSettingsGroup);
s.setValue("show_lyrics", action_show_lyrics_->isChecked());
s.endGroup();
UpdateSong();
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
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::SearchCoverAutomatically() {

View File

@@ -94,21 +94,23 @@ class ContextView : public QWidget {
AlbumCoverLoaderOptions cover_loader_options_;
Song song_;
Song song_playing_;
Song song_empty_;
Song song_prev_;
QImage image_original_;
QImage image_previous_;
QPixmap pixmap_current_;
QPixmap pixmap_previous_;
qreal pixmap_previous_opacity_;
std::unique_ptr<QMovie> spinner_animation_;
QString prev_artist_;
qint64 lyrics_id_;
QString lyrics_;
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 UpdateSong();
void SetSong(const Song &song);
void UpdateSong(const Song &song);
void SetImage(const QImage &image);
void DrawImage(QPainter *p);
void ScaleCover();

View File

@@ -38,7 +38,9 @@
#include "appearance.h"
#include "engine/enginedevice.h"
#include "device/devicemanager.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
#endif
#include "collection/collection.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h"
@@ -48,7 +50,6 @@
#ifdef HAVE_LIBLASTFM
#include "covermanager/lastfmcoverprovider.h"
#endif
//#include "covermanager/amazoncoverprovider.h"
#include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h"
@@ -81,7 +82,9 @@ class ApplicationImpl {
task_manager_([=]() { return new TaskManager(app); }),
player_([=]() { return new Player(app, app); }),
enginedevice_([=]() { return new EngineDevice(app); }),
#ifndef Q_OS_WIN
device_manager_([=]() { return new DeviceManager(app, app); }),
#endif
collection_([=]() { return new SCollection(app, app); }),
playlist_backend_([=]() {
PlaylistBackend *backend = new PlaylistBackend(app, app);
@@ -95,7 +98,6 @@ class ApplicationImpl {
#ifdef HAVE_LIBLASTFM
cover_providers->AddProvider(new LastFmCoverProvider(app));
#endif
//cover_providers->AddProvider(new AmazonCoverProvider(app));
cover_providers->AddProvider(new DiscogsCoverProvider(app));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app));
return cover_providers;
@@ -122,7 +124,9 @@ class ApplicationImpl {
Lazy<TaskManager> task_manager_;
Lazy<Player> player_;
Lazy<EngineDevice> enginedevice_;
#ifndef Q_OS_WIN
Lazy<DeviceManager> device_manager_;
#endif
Lazy<SCollection> collection_;
Lazy<PlaylistBackend> playlist_backend_;
Lazy<PlaylistManager> playlist_manager_;
@@ -148,7 +152,9 @@ Application::~Application() {
// 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.
#ifndef Q_OS_WIN
p_->device_manager_.reset();
#endif
for (QThread *thread : threads_) {
thread->quit();
@@ -198,9 +204,11 @@ CurrentArtLoader *Application::current_art_loader() const {
Database *Application::database() const { return p_->database_.get(); }
#ifndef Q_OS_WIN
DeviceManager *Application::device_manager() const {
return p_->device_manager_.get();
}
#endif
SCollection *Application::collection() const { return p_->collection_.get(); }

View File

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

View File

@@ -1,8 +1,8 @@
/*
* Strawberry Music Player
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -121,10 +121,12 @@
#include "covermanager/albumcoverchoicecontroller.h"
#include "covermanager/albumcoverloader.h"
#include "covermanager/currentartloader.h"
#include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h"
#include "device/deviceview.h"
#include "device/deviceviewcontainer.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
# include "device/deviceview.h"
# include "device/deviceviewcontainer.h"
#endif
#include "transcoder/transcodedialog.h"
#include "settings/behavioursettingspage.h"
#include "settings/playbacksettingspage.h"
@@ -167,8 +169,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
context_view_(new ContextView(this)),
collection_view_(new CollectionViewContainer(this)),
file_view_(new FileView(this)),
#ifndef Q_OS_WIN
device_view_container_(new DeviceViewContainer(this)),
device_view_(device_view_container_->view()),
#endif
playlist_list_(new PlaylistListContainer(this)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
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(file_view_, IconLoader::Load("document-open"), tr("Files"));
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"));
#endif
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal", "Tidal"));
//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()->SetApplication(app_);
#ifndef Q_OS_WIN
device_view_->SetApplication(app_);
#endif
playlist_list_->SetApplication(app_);
#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(MoveToCollection(QList<QUrl>)), SLOT(MoveFilesToCollection(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>)));
#endif
#endif
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(ResumeCollectionWatchers()), app_->collection(), SLOT(ResumeWatcher()));
#ifndef Q_OS_WIN
// Devices connections
connect(device_view_, SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
// Collection filter widget
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_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()));
#ifndef Q_OS_WIN
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(PlaylistCopyToDevice()));
#endif
#endif
//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()));
@@ -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*)));
#ifdef HAVE_GSTREAMER
#if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
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)));
#endif
@@ -817,8 +831,8 @@ void MainWindow::MediaStopped() {
tray_icon_->SetProgress(0);
tray_icon_->SetStopped();
song_playing_ = song_empty_;
song_ = song_empty_;
song_playing_ = Song();
song_ = Song();
image_original_ = QImage();
}
@@ -1324,7 +1338,9 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
playlist_copy_to_collection_->setVisible(false);
playlist_move_to_collection_->setVisible(false);
//playlist_organise_->setVisible(false);
#ifndef Q_OS_WIN
playlist_copy_to_device_->setVisible(false);
#endif
#endif
playlist_open_in_browser_->setVisible(false);
@@ -1393,7 +1409,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
}
#endif
#ifdef HAVE_GSTREAMER
#if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
playlist_copy_to_device_->setVisible(editable);
#endif
@@ -1830,6 +1846,7 @@ void MainWindow::MoveFilesToCollection(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_->SetCopy(true);
if (organise_dialog_->SetUrls(urls))
@@ -1837,6 +1854,7 @@ void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
#endif
}
#endif
@@ -1968,15 +1986,14 @@ void MainWindow::PlaylistSkip() {
}
#ifdef HAVE_GSTREAMER
#if defined(HAVE_GSTREAMER)
void MainWindow::PlaylistCopyToDevice() {
#if !defined(Q_OS_WIN)
QModelIndexList proxy_indexes = ui_->playlist->view()->selectionModel()->selectedRows();
SongList songs;
for (const QModelIndex &proxy_index : proxy_indexes) {
QModelIndex index = app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index);
songs << app_->playlist_manager()->current()->item_at(index.row())->Metadata();
}
@@ -1985,9 +2002,9 @@ void MainWindow::PlaylistCopyToDevice() {
if (organise_dialog_->SetSongs(songs))
organise_dialog_->show();
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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -63,8 +64,10 @@ class ContextView;
class CollectionViewContainer;
class AlbumCoverChoiceController;
class CommandlineOptions;
#ifndef Q_OS_WIN
class DeviceView;
class DeviceViewContainer;
#endif
class EditTagDialog;
class Equalizer;
class ErrorDialog;
@@ -134,7 +137,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void resizeEvent(QResizeEvent *event);
void closeEvent(QCloseEvent *event);
#ifdef Q_OS_WIN32
#ifdef Q_OS_WIN
bool winEvent(MSG *message, long *result);
#endif
@@ -183,7 +186,9 @@ signals:
#ifdef HAVE_GSTREAMER
void PlaylistCopyToCollection();
void PlaylistMoveToCollection();
#ifndef Q_OS_WIN_
void PlaylistCopyToDevice();
#endif
void PlaylistOrganiseSelected(bool copy);
#endif
//void PlaylistDelete();
@@ -202,7 +207,9 @@ signals:
#ifdef HAVE_GSTREAMER
void CopyFilesToCollection(const QList<QUrl>& urls);
void MoveFilesToCollection(const QList<QUrl>& urls);
//#ifndef Q_OS_WIN
void CopyFilesToDevice(const QList<QUrl>& urls);
//#endif
#endif
void EditFileTags(const QList<QUrl>& urls);
@@ -308,8 +315,10 @@ signals:
ContextView *context_view_;
CollectionViewContainer *collection_view_;
FileView *file_view_;
#ifndef Q_OS_WIN
DeviceViewContainer *device_view_container_;
DeviceView *device_view_;
#endif
PlaylistListContainer *playlist_list_;
Lazy<SettingsDialog> settings_dialog_;
@@ -347,7 +356,9 @@ signals:
#ifdef HAVE_GSTREAMER
QAction *playlist_copy_to_collection_;
QAction *playlist_move_to_collection_;
#ifndef Q_OS_WIN
QAction *playlist_copy_to_device_;
#endif
//QAction *playlist_delete_;
#endif
QAction *playlist_open_in_browser_;
@@ -378,7 +389,6 @@ signals:
Song song_;
Song song_playing_;
Song song_empty_;
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_;
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 (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) {
Song song = item->Metadata();
if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) {
song.set_length_nanosec(result.length_nanosec_);
update = true;
}
if (update) {
item->SetTemporaryMetadata(song);
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;
loading_async_ = QUrl();
@@ -245,6 +260,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
loading_async_ = result.original_url_;
break;
}
}
void Player::Next() { NextInternal(Engine::Manual); }
@@ -492,8 +508,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
}
else {
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) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
if (!item) return;
if (bundle.url != item->Metadata().url()) return;
Engine::SimpleMetaBundle bundle_copy = bundle;
// 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) {
bundle_copy.artist = bundle_copy.title.left(space_dash_pos).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.title = bundle_copy.title.mid(dash_pos + 1).trimmed();
}
@@ -661,7 +679,7 @@ void Player::TrackAboutToEnd() {
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) {
const QString scheme = handler->scheme();
if (url_handlers_.contains(scheme)) {
@@ -698,7 +716,7 @@ void Player::RegisterUrlHandler(UrlHandler *handler) {
}
void Player::UnregisterUrlHandler(UrlHandler *handler) {
const QString scheme = url_handlers_.key(handler);
if (scheme.isEmpty()) {
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.comment.isEmpty()) d->comment_ = bundle.comment;
if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre;
if (!bundle.bitrate.isEmpty()) d->bitrate_ = bundle.bitrate.toInt();
if (!bundle.samplerate.isEmpty()) d->samplerate_ = bundle.samplerate.toInt();
if (!bundle.bitdepth.isEmpty()) d->samplerate_ = bundle.bitdepth.toInt();
if (!bundle.length.isEmpty()) set_length_nanosec(bundle.length.toLongLong());
if (!bundle.year.isEmpty()) d->year_ = bundle.year.toInt();
if (!bundle.tracknr.isEmpty()) d->track_ = bundle.tracknr.toInt();
if (bundle.bitrate > 0) d->bitrate_ = bundle.bitrate;
if (bundle.samplerate > 0) d->samplerate_ = bundle.samplerate;
if (bundle.bitdepth > 0) d->samplerate_ = bundle.bitdepth;
if (bundle.length > 0) set_length_nanosec(bundle.length);
if (bundle.year > 0) d->year_ = bundle.year;
if (bundle.tracknr > 0) d->track_ = bundle.tracknr;
}

View File

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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -23,12 +24,10 @@
#include <QtGlobal>
#include <QObject>
#include <QUrl>
#include <QIcon>
#include "song.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) {}
QIcon UrlHandler::icon() const { return QIcon(); }

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -27,7 +28,8 @@
#include <QObject>
#include <QString>
#include <QUrl>
#include <QIcon>
#include "song.h"
class UrlHandler : public QObject {
Q_OBJECT
@@ -37,7 +39,6 @@ class UrlHandler : public QObject {
// The URL scheme that this handler handles.
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.
struct LoadResult {
@@ -53,7 +54,7 @@ class UrlHandler : public QObject {
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().
// Might be something unplayable like lastfm://...
@@ -64,6 +65,9 @@ class UrlHandler : public QObject {
// The actual url to something that gstreamer can play.
QUrl media_url_;
// The type of the stream
Song::FileType filetype_;
// Track length, if we are able to get it only now
qint64 length_nanosec_;
};
@@ -78,8 +82,9 @@ class UrlHandler : public QObject {
virtual void TrackAboutToEnd() {};
virtual void TrackSkipped() {};
signals:
signals:
void AsyncLoadComplete(const UrlHandler::LoadResult &result);
};
#endif // URLHANDLER_H

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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) {
if (url.scheme() != "file") return false;
@@ -723,18 +709,13 @@ bool UrlOnSameDriveAsStrawberry(const QUrl &url) {
}
QUrl GetRelativePathToStrawberryBin(const QUrl &url) {
QDir appPath(QCoreApplication::applicationDirPath());
return QUrl::fromLocalFile(appPath.relativeFilePath(url.toLocalFile()));
}
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;
}
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
ScopedWCharArray::ScopedWCharArray(const QString &str)

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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.
bool IsLaptop();
QString SystemLanguageName();
QString GetRandomStringWithChars(const int len);
QString GetRandomStringWithCharsAndNumbers(const int len);
QString GetRandomString(const int len, const QString &UseCharacters);
}
class ScopedWCharArray {

View File

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

View File

@@ -1,8 +1,8 @@
/*
* Strawberry Music Player
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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.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.card = card;
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);
}

View File

@@ -60,11 +60,12 @@ 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);
url_ = url;
media_url_ = media_url;
original_url_ = original_url;
beginning_nanosec_ = beginning_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 Play(0);
}
void Engine::Base::SetVolume(uint value) {

View File

@@ -68,8 +68,8 @@ public:
virtual bool Init() = 0;
virtual State state() const = 0;
virtual void StartPreloading(const QUrl&, bool, qint64, qint64) {}
virtual bool Load(const QUrl &url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
virtual void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool, qint64, qint64) {}
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 void Stop(bool stop_after = false) = 0;
virtual void Pause() = 0;
@@ -93,10 +93,11 @@ public:
virtual bool ValidOutput(const QString &output) = 0;
virtual QString DefaultOutput() = 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).
// 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);
static uint MakeVolumeLogarithmic(uint volume);
@@ -163,7 +164,8 @@ protected:
uint volume_;
quint64 beginning_nanosec_;
qint64 end_nanosec_;
QUrl url_;
QUrl media_url_;
QUrl original_url_;
Scope scope_;
bool buffering_;
bool equalizer_enabled_;
@@ -202,17 +204,19 @@ private:
};
struct SimpleMetaBundle {
QUrl url;
QString title;
QString artist;
QString album;
QString comment;
QString genre;
QString bitrate;
QString samplerate;
QString bitdepth;
QString length;
QString year;
QString tracknr;
qlonglong length;
int year;
int tracknr;
int samplerate;
int bitdepth;
qlonglong bitrate;
QString lyrics;
};
} // namespace

View File

@@ -25,39 +25,35 @@
#include "enginetype.h"
namespace Engine {
Engine::EngineType EngineTypeFromName(QString enginename) {
QString lower = enginename.toLower();
if (lower == "xine") return Engine::Xine;
else if (lower == "gstreamer") return Engine::GStreamer;
else if (lower == "phonon") return Engine::Phonon;
else if (lower == "vlc") return Engine::VLC;
else return Engine::None;
if (lower == "xine") return Engine::Xine;
else if (lower == "gstreamer") return Engine::GStreamer;
else if (lower == "phonon") return Engine::Phonon;
else if (lower == "vlc") return Engine::VLC;
else return Engine::None;
}
QString EngineName(Engine::EngineType enginetype) {
switch (enginetype) {
case Engine::Xine: return QObject::tr("xine");
case Engine::GStreamer: return QObject::tr("gstreamer");
case Engine::Phonon: return QObject::tr("phonon");
case Engine::VLC: return QObject::tr("vlc");
case Engine::Xine: return QString("xine");
case Engine::GStreamer: return QString("gstreamer");
case Engine::Phonon: return QString("phonon");
case Engine::VLC: return QString("vlc");
case Engine::None:
default: return QObject::tr("None");
default: return QString("None");
}
}
QString EngineDescription(Engine::EngineType enginetype) {
switch (enginetype) {
case Engine::Xine: return QObject::tr("Xine");
case Engine::GStreamer: return QObject::tr("GStreamer");
case Engine::Phonon: return QObject::tr("Phonon");
case Engine::VLC: return QObject::tr("VLC");
case Engine::Xine: return QString("Xine");
case Engine::GStreamer: return QString("GStreamer");
case Engine::Phonon: return QString("Phonon");
case Engine::VLC: return QString("VLC");
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 {
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()) {
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();
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)
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();
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));
if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_)
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.
return true;
}
@@ -188,7 +188,7 @@ bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
//SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
//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 (crossfade) StartFadeout();
@@ -229,7 +229,8 @@ void GstEngine::Stop(bool stop_after) {
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;
// 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);
}
bool GstEngine::ALSADeviceSupport(const QString &output) {
return (output == kALSASink);
}
void GstEngine::ReloadSettings() {
Engine::Base::ReloadSettings();
@@ -571,7 +576,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
BufferingFinished();
emit StateChanged(Engine::Error);
// unable to play media stream with this url
emit InvalidSongRequested(url_);
emit InvalidSongRequested(media_url_);
emit Error(message);
@@ -645,9 +650,9 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
if (ret == GST_STATE_CHANGE_FAILURE) {
// Failure, but we got a redirection URL - try loading that instead
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;
current_pipeline_ = CreatePipeline(redirect_url, end_nanosec_);
current_pipeline_ = CreatePipeline(redirect_url, current_pipeline_->original_url(), end_nanosec_);
Play(offset_nanosec);
return;
}
@@ -661,14 +666,14 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
StartTimers();
// initial offset
// Initial offset
if (offset_nanosec != 0 || beginning_nanosec_ != 0) {
Seek(offset_nanosec);
}
emit StateChanged(Engine::Playing);
// we've successfully started playing a media stream with this url
emit ValidSongRequested(url_);
// We've successfully started playing a media stream with this 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(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;
}
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();
if (!ret->InitFromUrl(url, end_nanosec)) ret.reset();
if (!ret->InitFromUrl(gst_url, original_url, end_nanosec)) ret.reset();
return ret;
}

View File

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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -33,6 +34,7 @@
#include <QList>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QTimeLine>
#include <QMetaObject>
#include <QtDebug>
@@ -351,7 +353,6 @@ bool GstEnginePipeline::InitAudioBin() {
// Let the audio output of the tee autonegotiate the bit depth and format.
GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw");
gst_element_link_filtered(convert, audiosink_, 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;
pipeline_ = engine_->CreateElement("playbin");
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_), "pad-added", &NewPadCallback, this);
@@ -500,9 +503,11 @@ void GstEnginePipeline::StreamStartMessageReceived() {
if (next_uri_set_) {
next_uri_set_ = false;
url_ = next_url_;
media_url_ = next_media_url_;
original_url_ = next_original_url_;
end_offset_nanosec_ = next_end_offset_nanosec_;
next_url_ = QByteArray();
next_media_url_ = QByteArray();
next_original_url_ = QUrl();
next_beginning_offset_nanosec_ = 0;
next_end_offset_nanosec_ = 0;
@@ -579,10 +584,18 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
gst_message_parse_tag(msg, &taglist);
Engine::SimpleMetaBundle bundle;
bundle.title = ParseTag(taglist, GST_TAG_TITLE);
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST);
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT);
bundle.album = ParseTag(taglist, GST_TAG_ALBUM);
bundle.url = original_url_;
bundle.title = ParseStrTag(taglist, GST_TAG_TITLE);
bundle.artist = ParseStrTag(taglist, GST_TAG_ARTIST);
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);
@@ -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;
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) {
if (msg->src != GST_OBJECT(pipeline_)) {
@@ -630,7 +654,7 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
if (next_uri_set_ && new_state == GST_STATE_READY) {
// Revert uri and go back to PLAY state again
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);
}
}
@@ -758,10 +782,11 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i
quint64 end_time = start_time + duration;
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.
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_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.
// 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;
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();
}
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_end_offset_nanosec_ = end_nanosec;

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@@ -41,6 +42,7 @@
#include <QList>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QTimerEvent>
class GstEngine;
@@ -70,7 +72,7 @@ class GstEnginePipeline : public QObject {
void set_mono_playback(bool enabled);
// 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);
// 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);
// 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);
bool has_next_valid_url() const { return !next_url_.isNull() && !next_url_.isEmpty(); }
void SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec);
bool has_next_valid_url() const { return !next_media_url_.isNull() && !next_media_url_.isEmpty(); }
void SetSourceDevice(QString device) { source_device_ = device; }
// 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_; }
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
qint64 position() const;
@@ -149,7 +152,8 @@ signals:
void StreamStatusMessageReceived(GstMessage*);
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 InitAudioBin();
@@ -212,8 +216,10 @@ signals:
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.
QByteArray url_;
QByteArray next_url_;
QByteArray media_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.
qint64 end_offset_nanosec_;

View File

@@ -64,8 +64,8 @@ bool PhononEngine::CanDecode(const QUrl &url) {
return true;
}
bool PhononEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
media_object_->setCurrentSource(Phonon::MediaSource(url));
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(media_url));
return true;
}
@@ -189,3 +189,7 @@ bool PhononEngine::ValidOutput(const QString &output) {
bool PhononEngine::CustomDeviceSupport(const QString &output) {
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 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);
void Stop(bool stop_after = false);
void Pause();
@@ -66,6 +66,7 @@ class PhononEngine : public Engine::Base {
QString DefaultOutput() { return ""; }
bool ValidOutput(const QString &output);
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
protected:
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;
// 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);
@@ -238,6 +238,10 @@ bool VLCEngine::CustomDeviceSupport(const QString &output) {
return (output == "auto" ? false : true);
}
bool VLCEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
uint VLCEngine::position() const {
if (!Initialised()) return (0);

View File

@@ -48,7 +48,7 @@ class VLCEngine : public Engine::Base {
bool Init();
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);
void Stop(bool stop_after = false);
void Pause();
@@ -64,6 +64,7 @@ class VLCEngine : public Engine::Base {
bool ValidOutput(const QString &output);
QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
private:
libvlc_instance_t *instance_;

View File

@@ -180,20 +180,20 @@ Engine::State XineEngine::state() const {
return Engine::Empty;
case XINE_STATUS_STOP:
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;
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_);
//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) {
#ifndef XINE_SAFE_MODE
@@ -345,6 +345,10 @@ bool XineEngine::CustomDeviceSupport(const QString &output) {
return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio");
}
bool XineEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
void XineEngine::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
if (url_.scheme().toLower() == "file") return 0;
if (media_url_.scheme().toLower() == "file") return 0;
else {
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.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.year = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
b.tracknr = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
if (b.tracknr.isEmpty())
b.tracknr = QFileInfo(url.path()).fileName();
b.year = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
b.tracknr = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
//if (b.tracknr <= 0) b.tracknr = QFileInfo(url.path()).fileName();
}
else {
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") {
result = true;
b.url = url;
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
int bitsPerSample = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS);
int nbrChannels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000;
int bitrate = (samplerate * bitdepth * channels) / 1000;
b.bitrate = QString::number(bitrate);
b.samplerate = QString::number(samplerate);
b.samplerate = samplerate;
b.bitdepth = bitdepth;
b.bitrate = bitrate;
int pos, time, length = 0;
xine_get_pos_length(tmpstream, &pos, &time, &length);
b.length = QString::number(length / 1000);
b.length = length / 1000;
}
xine_close(tmpstream);
}
@@ -746,7 +750,7 @@ bool XineEngine::event(QEvent *e) {
return true;
case XineEvent::InfoMessage:
emit InfoMessage((*message).arg(url_.toString()));
emit InfoMessage((*message).arg(media_url_.toString()));
delete message;
return true;
@@ -767,7 +771,7 @@ bool XineEngine::event(QEvent *e) {
case XineEvent::Redirecting:
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);
delete message;
return true;
@@ -783,15 +787,18 @@ bool XineEngine::event(QEvent *e) {
Engine::SimpleMetaBundle XineEngine::fetchMetaData() const {
Engine::SimpleMetaBundle bundle;
bundle.url = original_url_;
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.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.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.samplerate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE));
bundle.year = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
bundle.tracknr = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER));
bundle.length = 0;
bundle.year = atoi(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
bundle.tracknr = atoi(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;
@@ -895,7 +902,7 @@ void XineEngine::DetermineAndShowErrorMessage() {
// 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!
body = "There is no available decoder.";
QString const ext = QFileInfo(url_.path()).completeSuffix();
QString const ext = QFileInfo(media_url_.path()).completeSuffix();
// TODO:
// if (ext == "mp3" && EngineController::installDistroCodec("xine-engine"))
// return;

View File

@@ -70,7 +70,7 @@ class XineEngine : public Engine::Base {
bool Init();
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);
void Stop(bool stop_after = false);
void Pause();
@@ -87,6 +87,7 @@ class XineEngine : public Engine::Base {
bool ValidOutput(const QString &output);
QString DefaultOutput() { return "auto"; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
void ReloadSettings();
@@ -115,7 +116,8 @@ class XineEngine : public Engine::Base {
float preamp_;
std::unique_ptr<PruneScopeThread> prune_;
QUrl url_;
QUrl media_url_;
QUrl original_url_;
static int last_error_;
static time_t last_error_time_;

View File

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

View File

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

View File

@@ -42,10 +42,11 @@ class InternetService : public QObject {
Q_OBJECT
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() {}
Song::Source source() const { return source_; }
QString name() const { return name_; }
QString url_scheme() const { return url_scheme_; }
InternetModel *model() const { return model_; }
virtual bool has_initial_load_settings() const { return false; }
virtual void InitialLoadSettings() {}
@@ -61,6 +62,7 @@ class InternetService : public QObject {
InternetModel *model_;
Song::Source source_;
QString name_;
QString url_scheme_;
};
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) {
switch (column) {
case Column_Title: return tr("Title");
case Column_Artist: return tr("Artist");
@@ -1456,14 +1456,18 @@ void Playlist::StopAfter(int row) {
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()->Url() != url) return;
// 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);
@@ -1762,7 +1766,7 @@ void Playlist::ItemChanged(PlaylistItemPtr item) {
}
void Playlist::InformOfCurrentSongChange() {
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

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;
@@ -227,7 +227,7 @@ QStyleOptionViewItemV4 PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem
if (view_->header()->logicalIndexAt(top_left) != index.column())
return option;
QStyleOptionViewItemV4 ret(option);
QStyleOptionViewItem ret(option);
if (index.data(Playlist::Role_IsCurrent).toBool()) {
// 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;
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;

View File

@@ -40,8 +40,7 @@
#include "internet/internetplaylistitem.h"
PlaylistItem::~PlaylistItem() {
}
PlaylistItem::~PlaylistItem() {}
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 {
QStyleOptionViewItemV4 opt(option);
QStyleOptionViewItem opt(option);
bool is_current = index.data(Playlist::Role_IsCurrent).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_row_ = index.row();

View File

@@ -189,7 +189,7 @@ class PlaylistView : public QTreeView {
private:
void ReloadBarPixmaps();
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) {
background_image_type_ = bg;

View File

@@ -31,6 +31,7 @@
#include <QSlider>
#include <QSpinBox>
#include <QLabel>
#include <QListView>
#include "backendsettingspage.h"
@@ -112,6 +113,19 @@ void BackendSettingsPage::Load() {
ui_->stickslider_replaygainpreamp->setValue(s_.value("rgpreamp", 0.0).toDouble() * 10 + 150);
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 (engine()->state() == Engine::Empty) {
@@ -139,6 +153,8 @@ void BackendSettingsPage::ConnectSignals() {
connect(ui_->lineedit_device, SIGNAL(textChanged(const QString &)), SLOT(DeviceStringChanged()));
connect(ui_->slider_bufferminfill, SIGNAL(valueChanged(int)), SLOT(BufferMinFillChanged(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);
}
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);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
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()) {
ui_->lineedit_device->setText(device.toString());
if (!found) {
bool have_custom(false);
int index_custom = 0;
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
if (ui_->combobox_device->itemText(i) == "Custom") {
have_custom = true;
index_custom = i;
if (ui_->combobox_device->currentText() != "Custom") ui_->combobox_device->setCurrentIndex(i);
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>();
output_name = output.name;
}
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>();
s_.setValue("engine", EngineName(enginetype));
@@ -330,6 +364,10 @@ void BackendSettingsPage::Save() {
s_.setValue("rgpreamp", float(ui_->stickslider_replaygainpreamp->value()) / 10 - 15);
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,
// 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);
@@ -347,7 +385,7 @@ void BackendSettingsPage::Cancel() {
void BackendSettingsPage::EngineChanged(int index) {
if (!configloaded_ || !EngineInitialised()) return;
QVariant v = ui_->combobox_engine->itemData(index);
Engine::EngineType enginetype = v.value<Engine::EngineType>();
@@ -386,12 +424,7 @@ void BackendSettingsPage::DeviceSelectionChanged(int index) {
if (engine()->CustomDeviceSupport(output.name)) {
ui_->lineedit_device->setEnabled(true);
if (ui_->combobox_device->currentText() == "Custom") {
ui_->combobox_device->setItemData(index, QVariant(ui_->lineedit_device->text()));
}
else {
if (device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
}
if (ui_->combobox_device->currentText() != "Custom" && device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
}
else {
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>();
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) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (ui_->combobox_device->itemText(i) == "Custom") continue;
if (device.toString() == ui_->lineedit_device->text()) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
@@ -430,7 +475,6 @@ void BackendSettingsPage::DeviceStringChanged() {
}
}
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);
}
}
@@ -502,3 +546,73 @@ void BackendSettingsPage::XineWarning() {
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 RgPreampChanged(int value);
void BufferMinFillChanged(int value);
void radiobutton_alsa_hw_clicked(bool checked);
void radiobutton_alsa_plughw_clicked(bool checked);
private:
enum alsa_plugin {
alsa_hw = 1,
alsa_plughw = 2
};
Ui_BackendSettingsPage *ui_;
void ConnectSignals();
@@ -78,6 +86,7 @@ private:
void ShowWarning(QString text);
void ResetWarning();
void XineWarning();
void SwitchALSADevices(alsa_plugin alsaplugin);
QSettings s_;
bool configloaded_;

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>596</width>
<height>638</height>
<height>720</height>
</rect>
</property>
<property name="windowTitle">
@@ -19,72 +19,119 @@
<property name="title">
<string>Audio output</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_engine">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Engine</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_engine">
<item>
<widget class="QLabel" name="label_engine">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Engine</string>
</property>
</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 row="0" column="1">
<widget class="QComboBox" name="combobox_engine"/>
<item>
<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 row="1" column="0">
<widget class="QLabel" name="label_output">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
</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>
<layout class="QHBoxLayout" name="horizontalLayout_device" stretch="0,0,0,0">
<item>
<widget class="QLabel" name="label_device">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_device">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>240</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
@@ -92,32 +139,98 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</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">
<string/>
</property>
</widget>
</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>
</item>
</layout>
@@ -155,7 +268,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_bufferminfill">
<property name="text">
<string>Minimum buffer fill</string>
<string>&amp;Minimum buffer fill</string>
</property>
</widget>
</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"));
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
QMenu *menu = new QMenu(this);
@@ -186,7 +186,7 @@ void NotificationsSettingsPage::Load() {
QRgb color = pretty_popup_->background_color();
if (color == OSDPretty::kPresetBlue)
ui_->notifications_bg_preset->setCurrentIndex(0);
else if (color == OSDPretty::kPresetOrange)
else if (color == OSDPretty::kPresetRed)
ui_->notifications_bg_preset->setCurrentIndex(1);
else
ui_->notifications_bg_preset->setCurrentIndex(2);
@@ -246,7 +246,7 @@ void NotificationsSettingsPage::PrettyColorPresetChanged(int index) {
break;
case 1:
pretty_popup_->set_background_color(OSDPretty::kPresetOrange);
pretty_popup_->set_background_color(OSDPretty::kPresetRed);
break;
case 2:

View File

@@ -307,7 +307,7 @@
</item>
<item>
<property name="text">
<string>Strawberry Orange</string>
<string>Strawberry Red</string>
</property>
</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 <QScrollArea>
#include <QTreeWidgetItem>
#include <QComboBox>
#include <QSettings>
#include "core/logging.h"
#include "widgets/osd.h"
@@ -108,6 +110,8 @@ public:
void set_output_changed(bool output_changed) { output_changed_ = output_changed; }
void ComboBoxLoadFromSettings(QSettings &s, QComboBox *combobox, QString setting, QString default_value);
signals:
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_->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(LoginSuccess()), SLOT(LoginSuccess()));
@@ -56,6 +56,9 @@ TidalSettingsPage::TidalSettingsPage(SettingsDialog *parent)
ui_->combobox_quality->addItem("High", "HIGH");
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("320x320", "320x320");
ui_->combobox_coversize->addItem("640x640", "640x640");
@@ -71,21 +74,17 @@ void TidalSettingsPage::Load() {
QSettings s;
s.beginGroup(kSettingsGroup);
ui_->username->setText(s.value("username").toString());
ui_->password->setText(s.value("password").toString());
QString quality = s.value("quality", "HIGH").toString();
ui_->combobox_quality->setCurrentIndex(ui_->combobox_quality->findData(quality));
QByteArray password = s.value("password").toByteArray();
if (password.isEmpty()) ui_->password->setText("");
else ui_->password->setText(QString::fromUtf8(QByteArray::fromBase64(password)));
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_quality, "quality", "HIGH");
ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 40).toInt());
ui_->spinbox_songssearchlimit->setValue(s.value("songssearchlimit", 10).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 100).toInt());
ui_->spinbox_songssearchlimit->setValue(s.value("songssearchlimit", 100).toInt());
ui_->checkbox_fetchalbums->setChecked(s.value("fetchalbums", false).toBool());
QString coversize = s.value("coversize", "320x320").toString();
ui_->combobox_coversize->setCurrentIndex(ui_->combobox_coversize->findData(coversize));
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_coversize, "coversize", "320x320");
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_streamurl, "streamurl", "http");
s.endGroup();
if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
@@ -97,13 +96,14 @@ void TidalSettingsPage::Save() {
QSettings s;
s.beginGroup(kSettingsGroup);
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("searchdelay", ui_->spinbox_searchdelay->value());
s.setValue("albumssearchlimit", ui_->spinbox_albumssearchlimit->value());
s.setValue("songssearchlimit", ui_->spinbox_songssearchlimit->value());
s.setValue("fetchalbums", ui_->checkbox_fetchalbums->isChecked());
s.setValue("coversize", ui_->combobox_coversize->itemData(ui_->combobox_coversize->currentIndex()));
s.setValue("streamurl", ui_->combobox_streamurl->itemData(ui_->combobox_streamurl->currentIndex()));
s.endGroup();
service_->ReloadSettings();

View File

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

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>715</width>
<height>483</height>
<height>547</height>
</rect>
</property>
<property name="windowTitle">
@@ -281,6 +281,39 @@
</item>
</layout>
</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>
</widget>
</item>

View File

@@ -39,6 +39,7 @@
#include <QSettings>
#include "core/application.h"
#include "core/player.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/mergedproxymodel.h"
@@ -51,20 +52,22 @@
#include "internet/internetmodel.h"
#include "tidalservice.h"
#include "tidalsearch.h"
#include "tidalurlhandler.h"
#include "settings/tidalsettingspage.h"
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::kAuthUrl = "https://listen.tidal.com/v1/login/username";
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;
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)),
url_handler_(new TidalUrlHandler(app, this)),
timer_searchdelay_(new QTimer(this)),
searchdelay_(1500),
albumssearchlimit_(1),
@@ -73,14 +76,20 @@ TidalService::TidalService(Application *app, InternetModel *parent)
user_id_(0),
pending_search_id_(0),
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);
connect(timer_searchdelay_, SIGNAL(timeout()), SLOT(StartSearch()));
connect(this, SIGNAL(Login(int)), SLOT(SendLogin(int)));
connect(this, SIGNAL(Login(QString, QString, int)), SLOT(SendLogin(QString, QString, int)));
connect(this, SIGNAL(Login()), SLOT(SendLogin()));
connect(this, SIGNAL(Login(QString, QString)), SLOT(SendLogin(QString, QString)));
app->player()->RegisterUrlHandler(url_handler_);
ReloadSettings();
LoadSessionID();
@@ -98,13 +107,16 @@ void TidalService::ReloadSettings() {
QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup);
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();
searchdelay_ = s.value("searchdelay", 1500).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 40).toInt();
songssearchlimit_ = s.value("songssearchlimit", 10).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 100).toInt();
songssearchlimit_ = s.value("songssearchlimit", 100).toInt();
fetchalbums_ = s.value("fetchalbums", false).toBool();
coversize_ = s.value("coversize", "320x320").toString();
streamurl_ = s.value("streamurl", "http").toString();
s.endGroup();
}
@@ -117,17 +129,18 @@ void TidalService::LoadSessionID() {
session_id_ = s.value("session_id").toString();
user_id_ = s.value("user_id").toInt();
country_code_ = s.value("country_code").toString();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12).toLower();
s.endGroup();
}
void TidalService::SendLogin(const int search_id) {
SendLogin(username_, password_, search_id);
void TidalService::SendLogin() {
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_attempts_++;
@@ -138,7 +151,11 @@ void TidalService::SendLogin(const QString &username, const QString &password, c
typedef QPair<QByteArray, QByteArray> EncodedArg;
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("clientVersion", "2.2.1--7");
//<< Arg("clientUniqueKey", clientuniquekey_);
QStringList query_items;
QUrlQuery url_query;
@@ -152,12 +169,13 @@ void TidalService::SendLogin(const QString &username, const QString &password, c
QNetworkRequest req(url);
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());
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();
@@ -167,7 +185,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
if (reply->error() < 200) {
// This is a network error, there is nothing more to do.
QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
if (search_id != 0) Error(failure_reason);
Error(failure_reason);
emit LoginFailure(failure_reason);
return;
}
@@ -189,7 +207,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
else {
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);
return;
}
@@ -201,21 +219,21 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
if (error.error != QJsonParseError::NoError) {
QString failure_reason("Authentication reply from server missing Json data.");
if (search_id != 0) Error(failure_reason);
Error(failure_reason);
emit LoginFailure(failure_reason);
return;
}
if (json_doc.isNull() || json_doc.isEmpty()) {
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);
return;
}
if (!json_doc.isObject()) {
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);
return;
}
@@ -223,14 +241,14 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
QJsonObject json_obj = json_doc.object();
if (json_obj.isEmpty()) {
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);
return;
}
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");
if (search_id != 0) Error(failure_reason);
QString failure_reason("Authentication reply from server is missing userId, sessionId or countryCode");
Error(failure_reason);
emit LoginFailure(failure_reason);
return;
}
@@ -238,6 +256,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) {
country_code_ = json_obj["countryCode"].toString();
session_id_ = json_obj["sessionId"].toString();
user_id_ = json_obj["userId"].toInt();
clientuniquekey_ = Utilities::GetRandomStringWithChars(12).toLower();
QSettings s;
s.beginGroup(TidalSettingsPage::kSettingsGroup);
@@ -248,10 +267,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_;
if (search_id != 0) {
qLog(Debug) << "Tidal: Resuming search";
login_attempts_ = 0;
if (search_id_ != 0) {
qLog(Debug) << "Tidal: Resuming search" << search_id_;
SendSearch();
}
if (!stream_request_url_.isEmpty()) {
qLog(Debug) << "Tidal: Resuming get stream url" << stream_request_url_;
emit GetStreamURL(stream_request_url_);
}
emit LoginSuccess();
@@ -295,6 +320,8 @@ QNetworkReply *TidalService::CreateRequest(const QString &ressource_name, const
QUrl url(kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query);
QNetworkRequest req(url);
req.setRawHeader("Origin", "http://listen.tidal.com");
req.setRawHeader("X-Tidal-SessionId", session_id_.toUtf8());
QNetworkReply *reply = network_->get(req);
//qLog(Debug) << "Tidal: Sending request" << url;
@@ -303,7 +330,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;
@@ -334,14 +361,15 @@ QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, bool sendlogin) {
else {
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) {
// Session is probably expired, attempt to login once
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:" << QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
qLog(Error) << "Tidal:" << "Attempting to login.";
emit Login(search_id_);
emit Login();
}
else {
Error(failure_reason);
@@ -439,7 +467,7 @@ void TidalService::StartSearch() {
search_text_ = pending_search_text_;
if (authenticated()) SendSearch();
else emit Login(username_, password_, search_id_);
else emit Login(username_, password_);
}
@@ -451,9 +479,7 @@ void TidalService::ClearSearch() {
search_text_.clear();
search_error_.clear();
albums_requested_ = 0;
songs_requested_ = 0;
albums_received_ = 0;
songs_received_ = 0;
requests_album_.clear();
requests_song_.clear();
login_attempts_ = 0;
@@ -525,9 +551,7 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
// This was a tracks search
if (!fetchalbums_) {
Song song = ParseSong(0, value);
requests_song_.insert(song.id(), song);
songs_requested_++;
GetStreamURL(0, song.id());
songs_ << song;
continue;
}
QJsonValue json_value_album = json_obj["album"];
@@ -573,12 +597,13 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
QString artist = json_artist["name"].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));
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;
}
albums.insert(0, artist_album);
@@ -594,11 +619,6 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
emit ProgressSetMaximum(albums_requested_);
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();
@@ -607,11 +627,7 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
void TidalService::GetAlbum(const int album_id) {
QList<Param> parameters;
parameters << Param("token", session_id_)
<< Param("soundQuality", quality_);
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);
}
@@ -649,15 +665,7 @@ void TidalService::GetAlbumFinished(QNetworkReply *reply, int search_id, int alb
QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc()));
song.set_album(album_full);
}
requests_song_.insert(song.id(), 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_);
songs_ << song;
}
CheckFinish();
@@ -702,10 +710,9 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
QJsonArray json_artists = json_obj["artists"].toArray();
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 url = json_obj["url"].toString();
QString urlstr = json_obj["url"].toString();
int track = json_obj["trackNumber"].toInt();
int disc = json_obj["volumeNumber"].toInt();
bool allow_streaming = json_obj["allowStreaming"].toBool();
@@ -760,9 +767,6 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
song.set_title(title);
song.set_track(track);
song.set_disc(disc);
song.set_bitrate(0);
song.set_samplerate(0);
song.set_bitdepth(0);
QVariant q_duration = json_duration.toVariant();
if (q_duration.isValid()) {
@@ -773,81 +777,81 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
cover = cover.replace("-", "/");
QUrl cover_url (QString("%1/images/%2/%3.jpg").arg(kResourcesUrl).arg(cover).arg(coversize_));
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);
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;
parameters << Param("token", session_id_)
<< Param("soundQuality", quality_);
parameters << Param("soundQuality", quality_);
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();
if (requests_song_.contains(song_id)) requests_song_.remove(song_id);
if (original_url != stream_request_url_) return;
if (search_id != search_id_) return;
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);
QJsonObject json_obj = ExtractJsonObj(reply, true);
if (json_obj.isEmpty()) {
requests_song_.remove(song_id);
CheckFinish();
if (!stream_request_url_.isEmpty() && !login_sent_) {
emit StreamURLFinished(QUrl(), Song::FileType_Stream);
stream_request_url_ = QUrl();
}
return;
}
if (!json_obj.contains("url") || !json_obj.contains("codec")) {
qLog(Error) << "Tidal: Invalid Json reply, stream missing url or codec.";
qLog(Debug) << json_obj;
requests_song_.remove(song_id);
CheckFinish();
emit StreamURLFinished(QUrl(), Song::FileType_Stream);
stream_request_url_ = QUrl();
return;
}
song.set_url(QUrl(json_obj["url"].toString()));
stream_request_url_ = QUrl();
QString codec = json_obj["codec"].toString().toLower();
song.set_filetype(Song::FiletypeByExtension(codec));
if (song.filetype() == Song::FileType_Unknown) {
QUrl new_url(json_obj["url"].toString());
QString codec(json_obj["codec"].toString().toLower());
Song::FileType filetype(Song::FiletypeByExtension(codec));
if (filetype == Song::FileType_Unknown) {
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();
songs_ << song;
requests_song_.remove(song_id);
CheckFinish();
emit StreamURLFinished(new_url, filetype);
}
void TidalService::CheckFinish() {
if (!login_sent_ && albums_requested_ <= albums_received_ && songs_requested_ <= songs_received_) {
if (songs_.isEmpty()) emit SearchError(search_id_, search_error_);
if (search_id_ == 0) return;
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_);
ClearSearch();
}
@@ -857,6 +861,15 @@ void TidalService::CheckFinish() {
void TidalService::Error(QString error, QString debug) {
qLog(Error) << "Tidal:" << error;
if (!debug.isEmpty()) qLog(Debug) << debug;
search_error_ = error;
CheckFinish();
if (search_id_ != 0) {
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 <QHash>
#include <QString>
#include <QUrl>
#include <QNetworkReply>
#include <QTimer>
#include <QDateTime>
@@ -39,6 +40,7 @@
#include "settings/tidalsettingspage.h"
class NetworkAccessManager;
class TidalUrlHandler;
class TidalService : public InternetService {
Q_OBJECT
@@ -48,7 +50,7 @@ class TidalService : public InternetService {
~TidalService();
static const Song::Source kSource;
static const char *kServiceName;
static const int kLoginAttempts;
void ReloadSettings();
@@ -59,9 +61,11 @@ class TidalService : public InternetService {
const bool login_sent() { return login_sent_; }
const bool authenticated() { return (!session_id_.isEmpty() && !country_code_.isEmpty()); }
void GetStreamURL(const QUrl &url);
signals:
void Login(const int search_id = 0);
void Login(const QString &username, const QString &password, const int search_id = 0);
void Login();
void Login(const QString &username, const QString &password);
void LoginSuccess();
void LoginFailure(QString failure_reason);
void SearchResults(int id, SongList songs);
@@ -69,18 +73,20 @@ class TidalService : public InternetService {
void UpdateStatus(QString text);
void ProgressSetMaximum(int max);
void UpdateProgress(int max);
void GetStreamURLFinished(QNetworkReply *reply, const QUrl url);
void StreamURLFinished(const QUrl url, const Song::FileType);
public slots:
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:
void SendLogin(const int search_id = 0);
void HandleAuthReply(QNetworkReply *reply, int search_id);
void SendLogin();
void HandleAuthReply(QNetworkReply *reply);
void StartSearch();
void SearchFinished(QNetworkReply *reply, int search_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:
void ClearSearch();
@@ -91,16 +97,16 @@ class TidalService : public InternetService {
void SendSearch();
void GetAlbum(const int album_id);
Song ParseSong(const int album_id_requested, const QJsonValue &value);
void GetStreamURL(const int album_id, const int song_id);
void CheckFinish();
void Error(QString error, QString debug = "");
void Error(QString error, QString debug = QString());
static const char *kApiUrl;
static const char *kAuthUrl;
static const char *kResourcesUrl;
static const char *kApiToken;
static const char *kApiTokenB64;
NetworkAccessManager *network_;
TidalUrlHandler *url_handler_;
QTimer *timer_searchdelay_;
QString username_;
@@ -111,9 +117,11 @@ class TidalService : public InternetService {
int songssearchlimit_;
bool fetchalbums_;
QString coversize_;
QString streamurl_;
QString session_id_;
quint64 user_id_;
QString country_code_;
QString clientuniquekey_;
int pending_search_id_;
int next_pending_search_id_;
@@ -123,15 +131,14 @@ class TidalService : public InternetService {
int search_id_;
QString search_text_;
QHash<int, int> requests_album_;
QHash<int, Song> requests_song_;
QHash<int, QUrl> requests_song_;
int albums_requested_;
int albums_received_;
int songs_requested_;
int songs_received_;
SongList songs_;
QString search_error_;
bool login_sent_;
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) {
// 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())
option.features = QStyleOptionViewItemV2::WrapText;
option.features = QStyleOptionViewItem::WrapText;
option.locale = locale();
option.locale.setNumberOptions(QLocale::OmitGroupSeparator);
option.widget = this;

View File

@@ -72,7 +72,7 @@ const int OSDPretty::kMaxIconSize = 100;
const int OSDPretty::kSnapProximity = 20;
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)

View File

@@ -67,7 +67,7 @@ class OSDPretty : public QWidget {
static const int kSnapProximity;
static const QRgb kPresetBlue;
static const QRgb kPresetOrange;
static const QRgb kPresetRed;
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) {
if (!playing_) return;
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) 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;
active_ = true;
downloading_covers_ = false;