Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50e3eeaafd | ||
|
|
843f528ebc | ||
|
|
faa0076988 | ||
|
|
59622c52ad | ||
|
|
a8e9aba58b | ||
|
|
36563cd1e1 | ||
|
|
072e712f0d | ||
|
|
121a186160 | ||
|
|
15a2ccc21e | ||
|
|
dbd8ea69eb | ||
|
|
8a57356f64 | ||
|
|
c77cb002f3 | ||
|
|
aa83a2b40b | ||
|
|
65b04cac6e | ||
|
|
5e577190a8 | ||
|
|
0143617056 | ||
|
|
a77dde7d3b | ||
|
|
7dcdb7c673 | ||
|
|
6de8eb56cd | ||
|
|
04e272d9bc | ||
|
|
17fe201473 | ||
|
|
25249be37f | ||
|
|
97ec12b5b3 | ||
|
|
246f82bfad | ||
|
|
db5679bbe9 | ||
|
|
feb0e1c45b | ||
|
|
edba837295 | ||
|
|
55882360ef | ||
|
|
6039370ad6 | ||
|
|
2d238c08d3 | ||
|
|
9b337b6a34 | ||
|
|
370db791aa | ||
|
|
9e3c547580 | ||
|
|
ab5d9b62b8 |
16
Changelog
16
Changelog
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
8
dist/CMakeLists.txt
vendored
@@ -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/)
|
||||
|
||||
18
dist/scripts/maketarball.sh.in
vendored
18
dist/scripts/maketarball.sh.in
vendored
@@ -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
445
dist/windows/strawberry-64.nsi.in
vendored
Normal 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
505
dist/windows/strawberry-debug-64.nsi.in
vendored
Normal 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
|
||||
2
dist/windows/strawberry.nsi.in
vendored
2
dist/windows/strawberry.nsi.in
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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*);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
|
||||
#include "internet/internetplaylistitem.h"
|
||||
|
||||
PlaylistItem::~PlaylistItem() {
|
||||
}
|
||||
PlaylistItem::~PlaylistItem() {}
|
||||
|
||||
PlaylistItem *PlaylistItem::NewFromSource(const Song::Source &source) {
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ void PlaylistView::drawTree(QPainter *painter, const QRegion ®ion) 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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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>&Minimum buffer fill</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -307,7 +307,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Strawberry Orange</string>
|
||||
<string>Strawberry Red</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
63
src/tidal/tidalurlhandler.cpp
Normal file
63
src/tidal/tidalurlhandler.cpp
Normal 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;
|
||||
}
|
||||
56
src/tidal/tidalurlhandler.h
Normal file
56
src/tidal/tidalurlhandler.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user