Compare commits

...

116 Commits
0.9.1 ... 0.9.3

Author SHA1 Message Date
Jonas Kvinge
655c872ac9 Release 0.9.3 2021-04-18 18:44:43 +02:00
Strawbs Bot
cb0f2b48fd Update translations 2021-04-18 01:03:10 +02:00
Strawbs Bot
20a84bdefc Update translations 2021-04-17 01:02:28 +02:00
Jonas Kvinge
83ad7d4935 Set exit before quit is called 2021-04-16 18:34:38 +02:00
Jonas Kvinge
ae2ca175d3 Fix remembering hidden state
Prevent SetHiddenInTray() or Exit() from being called again after
qApp->quit() in MainWindow::closeEvent()
2021-04-16 18:27:24 +02:00
Jonas Kvinge
c855108b94 Update Changelog 2021-04-15 21:06:19 +02:00
Strawbs Bot
5e80b694c2 Update translations 2021-04-15 01:02:00 +02:00
Jonas Kvinge
99640aa2ad Add Windows Qt 6 build to CI 2021-04-14 21:11:01 +02:00
Jonas Kvinge
4c0f7c3dd4 Add commandline option to resize window 2021-04-14 19:50:38 +02:00
Jonas Kvinge
2a8490ef31 Use modern user interface and locked list also for uninstalling 2021-04-14 18:07:23 +02:00
Jonas Kvinge
43dc694963 Include gst-discoverer-1.0.exe in windows setup 2021-04-14 16:56:30 +02:00
Strawbs Bot
e03ee00554 Update translations 2021-04-14 01:02:25 +02:00
Jonas Kvinge
01a07ec0e8 Enable stream discoverer on all systems
Fixes #689
2021-04-14 00:16:37 +02:00
Jonas Kvinge
74b7738a9d Create GLib main event loop on non-glib systems 2021-04-14 00:16:07 +02:00
Jonas Kvinge
10aaaebc38 Enable stream discoverer on all Unix except macOS 2021-04-13 22:11:44 +02:00
Jonas Kvinge
a8d072150d Remove avresample-4.dll from nsi 2021-04-13 19:11:48 +02:00
Jonas Kvinge
7877bb7956 Add setting for OSD Pretty fading 2021-04-13 17:27:10 +02:00
Strawbs Bot
bd31e3df89 Update translations 2021-04-13 01:01:55 +02:00
Jonas Kvinge
531e97b499 Only use QPlatformNativeInterface on X11 2021-04-12 19:40:34 +02:00
Jonas Kvinge
43876e967a Enable Qt6WinExtras.dll in nsi 2021-04-12 15:41:28 +02:00
Jonas Kvinge
efaf917939 Add pragma unused 2021-04-11 19:16:07 +02:00
Jonas Kvinge
7d3ceb7d8c Fix Tidal authentication on macOS 2021-04-11 18:33:35 +02:00
Jonas Kvinge
201ac47943 Add new screenshot 2021-04-11 09:11:24 +02:00
Jonas Kvinge
5a70285c10 Update Changelog 2021-04-11 03:24:21 +02:00
Jonas Kvinge
e13c27d32c Allow editing of playlist metadata for streams 2021-04-11 02:01:53 +02:00
Strawbs Bot
9c9b673eeb Update translations 2021-04-11 01:13:36 +02:00
Jonas Kvinge
7323fe0000 Fix queued item painting 2021-04-11 01:03:02 +02:00
Jonas Kvinge
460d045cbe Fix rescan when collection directory is removed and added 2021-04-11 01:01:57 +02:00
Jonas Kvinge
4983c1cfc9 FIx tidal search field not showing on macOS 2021-04-10 09:56:59 +02:00
Jonas Kvinge
c7bc9d471a Allow keep running option on macOS too 2021-04-10 08:22:08 +02:00
Jonas Kvinge
a36b91ffe8 Cast to quint32 2021-04-10 08:21:58 +02:00
Jonas Kvinge
88da0db0f7 Update copyrights 2021-04-10 07:32:38 +02:00
Jonas Kvinge
816c2b0ca8 Use qint32 for unique worker number 2021-04-10 07:20:36 +02:00
Jonas Kvinge
d6ff68339b Use qint64 for unique worker number 2021-04-10 07:02:08 +02:00
Jonas Kvinge
fdf483c98b Update README.md 2021-04-10 06:36:18 +02:00
Jonas Kvinge
eb020c1cb8 Register tidal URL scheme on macOS 2021-04-10 06:04:31 +02:00
Jonas Kvinge
6fa331ad06 Make macdeployqt work with Qt 5 too 2021-04-10 03:28:38 +02:00
Jonas Kvinge
5a58ac2845 Make CollectionQuery subclass QSqlQuery, don't copy QSqlQuery 2021-04-10 03:21:05 +02:00
Strawbs Bot
0c4edc4d17 Update translations 2021-04-09 01:02:09 +02:00
Jonas Kvinge
b5cea4c27e Make enabling FTS3 non-fatal 2021-04-08 22:33:43 +02:00
Jonas Kvinge
b7de5d6df3 Only enable FTS3 when schema needs upgrading 2021-04-08 22:13:21 +02:00
Strawbs Bot
d7310ba307 Update translations 2021-04-07 01:03:08 +02:00
Jonas Kvinge
cabb94b856 Move taglib include dir to system include dirs 2021-04-05 22:25:52 +02:00
Jonas Kvinge
82b649f67d Move taglib include dir to system include dirs 2021-04-05 22:22:25 +02:00
Strawbs Bot
8753782a0c Update translations 2021-04-04 01:02:57 +02:00
Strawbs Bot
566a139edc Update translations 2021-04-02 01:03:04 +02:00
Jonas Kvinge
39dda0f16b Check that rating position is to the right or left of the rectangle.
Adapted from Clementine.

Author: Jim Broadus
2021-04-02 00:47:53 +02:00
Jonas Kvinge
bddb371e31 Fix open in file browser when inode/directory mimetype is set to thunar
Fixes #677
2021-04-01 18:31:19 +02:00
Jonas Kvinge
128223a28a Add setting for configuring the color for the currently playing song
Fixes #676
2021-04-01 02:18:15 +02:00
Jonas Kvinge
45b9d0553f Update FUNDING.yml 2021-04-01 00:27:54 +02:00
Jonas Kvinge
a87f0e0475 Also strip variables with uppercase letters in OpenInFileManager() 2021-03-31 23:14:16 +02:00
Jonas Kvinge
beacea0482 Improve macdeployqt patch 2021-03-31 01:00:51 +02:00
Jonas Kvinge
85b59e79d3 Update ccpp.yml 2021-03-30 20:41:13 +02:00
Jonas Kvinge
9a947c5544 Use LockedList nsis plugin 2021-03-30 20:35:48 +02:00
Strawbs Bot
e4cebf4cbe Update translations 2021-03-30 01:03:27 +02:00
Jonas Kvinge
f63e05b7d4 Use QString::SkipEmptyParts for older Qt versions 2021-03-29 22:45:01 +02:00
Jonas Kvinge
84b7fa02bb Strip all variables from command in OpenInFileManager
Fixes #674
2021-03-29 22:37:55 +02:00
Strawbs Bot
36e597a045 Update translations 2021-03-29 01:28:10 +02:00
Strawbs Bot
f760b3f271 Update translations 2021-03-27 01:12:06 +01:00
Jonas Kvinge
d4c8fa6a24 Use const QJsonValueRef 2021-03-26 23:55:55 +01:00
Jonas Kvinge
c6604734c9 Remove using std::placeholders 2021-03-26 23:33:56 +01:00
Jonas Kvinge
91ab8e22b7 Use QJsonValueRef 2021-03-26 22:10:43 +01:00
Jonas Kvinge
14fb647647 Fix uninitialized variables 2021-03-26 21:30:13 +01:00
Jonas Kvinge
8a39a43ad5 Make sure QCache::insert is successful
Otherwise object is deleted by QCache::insert
2021-03-26 21:07:47 +01:00
Jonas Kvinge
a3d23a6f57 Turn back git revision 2021-03-26 20:35:18 +01:00
Jonas Kvinge
ae5da12afb Release 0.9.2 2021-03-25 23:24:16 +01:00
Jonas Kvinge
a3042b8f79 Update Changelog 2021-03-25 23:23:35 +01:00
Jonas Kvinge
29cbfe7c1a Change last played to qint64 2021-03-25 23:23:13 +01:00
Strawbs Bot
1ddc3292cc Update translations 2021-03-25 01:03:59 +01:00
Jonas Kvinge
658e1d122e Remove set current to all artists when opening cover manager
Caused sluggish opening
2021-03-24 22:30:58 +01:00
Jonas Kvinge
d936a080db Check for empty image 2021-03-24 22:30:37 +01:00
Jonas Kvinge
2a92af1e09 Change static_cast 2021-03-24 22:29:56 +01:00
Jonas Kvinge
9cde537066 Fix moodbar 2021-03-23 01:44:00 +01:00
Strawbs Bot
ffc5446914 Update translations 2021-03-22 01:23:51 +01:00
Jonas Kvinge
5f70b32795 Remove unused using std::shared_ptr 2021-03-21 20:23:42 +01:00
Jonas Kvinge
6b6117653a Allow audio formats unsupported by taglib to be added
Fixes #669
2021-03-21 19:19:35 +01:00
Jonas Kvinge
59bffed47f Use static_cast 2021-03-21 18:53:02 +01:00
Jonas Kvinge
f91a679cdf Add override 2021-03-21 18:40:33 +01:00
Jonas Kvinge
66b8d27d66 Change to QList 2021-03-21 06:26:48 +01:00
Jonas Kvinge
1219f504ea Update CI 2021-03-21 06:09:24 +01:00
Jonas Kvinge
ee5a191e39 Change to qint64 2021-03-21 06:07:11 +01:00
Jonas Kvinge
f577aa8d4f Update Changelog 2021-03-21 04:55:59 +01:00
Jonas Kvinge
2436c87372 Use variable 2021-03-21 04:55:00 +01:00
Jonas Kvinge
e90b5e63e5 Move variable 2021-03-21 04:49:22 +01:00
Jonas Kvinge
78588d8cdf Fix various clazy warnings 2021-03-21 04:47:11 +01:00
Jonas Kvinge
20c1c1d4be Remove unused variables 2021-03-21 04:43:55 +01:00
Jonas Kvinge
329dfb21b9 These don't need to be slots 2021-03-21 04:38:47 +01:00
Jonas Kvinge
02c30211a7 Call AbstractMessageHandler::AbortAll 2021-03-21 04:37:30 +01:00
Jonas Kvinge
645da2713d Remove emit from slots 2021-03-21 04:36:54 +01:00
Jonas Kvinge
20c5a79efa Fix playlist tabbar close and save 2021-03-21 04:28:22 +01:00
Jonas Kvinge
b224aeb0d8 Use 4arg connects for networkreplies 2021-03-21 00:37:17 +01:00
Jonas Kvinge
73eebd6162 Set album cover filename by pattern as default when saving cover to album directory 2021-03-20 23:13:54 +01:00
Strawbs Bot
99a1851f4d Update translations 2021-03-20 23:02:12 +01:00
Jonas Kvinge
d9d89d0927 Update Changelog 2021-03-20 21:21:36 +01:00
Jonas Kvinge
54f2aa5f77 Update copyrights 2021-03-20 21:14:47 +01:00
Jonas Kvinge
d022e9dd00 Close the file 2021-03-20 19:23:33 +01:00
Jonas Kvinge
17cf8ec1c6 Move declaration 2021-03-20 19:11:05 +01:00
Jonas Kvinge
c4a6d81cda Fix copying album covers to iPod
Cover file was deleted too soon.
File needs to be accessible until itdb_write is called.

Fixes #663
2021-03-20 19:00:42 +01:00
Jonas Kvinge
66ed485803 Check for existence of directory 2021-03-20 18:58:38 +01:00
Jonas Kvinge
6de585d1c8 Make sure process files isn't again called after finishing 2021-03-20 15:22:21 +01:00
Jonas Kvinge
9498638988 Make macdeployqt use environment variables for gstreamer 2021-03-20 14:19:49 +01:00
Jonas Kvinge
87dad82210 Update README.md 2021-03-19 00:35:13 +01:00
Jonas Kvinge
e37aec16ac Enable tumbleweed 2021-03-19 00:25:33 +01:00
Jonas Kvinge
6fb48af598 Fix macOS deployment 2021-03-18 23:08:50 +01:00
Jonas Kvinge
8193be36e5 Use a modified version of macdeployqt 2021-03-17 21:12:12 +01:00
Jonas Kvinge
a7df2bc4fb Use original file when loading cover from file in album cover manager 2021-03-16 17:18:11 +01:00
Jonas Kvinge
5eda028af3 Minor code improvements to album cover loader 2021-03-16 17:17:22 +01:00
Jonas Kvinge
f77475dbfe Specifically set get image 2021-03-16 17:16:40 +01:00
Jonas Kvinge
96c1a35c8e Use macdeploy.py for Qt plugins 2021-03-16 17:13:33 +01:00
Jonas Kvinge
0ef26be03f Remove DEBUG define 2021-03-16 17:12:44 +01:00
Jonas Kvinge
f5bb15f72e Fix QSearchField on macOS 2021-03-15 22:38:06 +01:00
Jonas Kvinge
d84aebd8f4 Format code 2021-03-15 22:37:49 +01:00
Jonas Kvinge
7d8d9f4c4d Format code 2021-03-14 23:20:16 +01:00
Jonas Kvinge
61b201810d Fix marking songs available 2021-03-14 23:08:05 +01:00
Jonas Kvinge
c75db8dc60 Turn back git revision 2021-03-14 02:32:42 +01:00
Jonas Kvinge
e0bf4091dd Update Changelog 2021-03-13 16:17:06 +01:00
551 changed files with 13455 additions and 10459 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,3 @@
github: jonaski
patreon: jonaskvinge
custom: https://paypal.me/jonaskvinge

View File

@@ -72,83 +72,6 @@ jobs:
run: ../dist/scripts/maketarball.sh
build_opensuse_lp151:
name: Build openSUSE Leap 15.1
runs-on: ubuntu-latest
container:
image: opensuse/leap:15.1
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
taglib-devel
libQt5Core-devel
libQt5Gui-devel
libQt5Widgets-devel
libQt5Concurrent-devel
libQt5Network-devel
libQt5Sql-devel
libQt5DBus-devel
libQt5Test-devel
libqt5-qtx11extras-devel
libqt5-qtbase-common-devel
libQt5Sql5-sqlite
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_lp152_qt5:
name: Build openSUSE Leap 15.2 Qt 5
runs-on: ubuntu-latest
@@ -225,6 +148,7 @@ jobs:
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_lp152_qt6:
name: Build openSUSE Leap 15.2 Qt 6
runs-on: ubuntu-latest
@@ -302,165 +226,317 @@ jobs:
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
#build_opensuse_tumbleweed_qt5:
#name: Build openSUSE Tumbleweed Qt 5
#runs-on: ubuntu-latest
#container:
#image: opensuse/tumbleweed
#steps:
#- uses: actions/checkout@v1.2.0
#- name: Lock packages
#run: zypper --non-interactive --gpg-auto-import-keys addlock openssh-server
#- name: Update packages
#run: zypper --non-interactive --gpg-auto-import-keys ref
#- name: Upgrade packages
#run: zypper --non-interactive --gpg-auto-import-keys dup
#- name: Install openSUSE dependencies
#run: >
#zypper --non-interactive --gpg-auto-import-keys install
#lsb-release
#rpm-build
#git
#tar
#make
#cmake
#gcc
#gcc-c++
#gettext-tools
#glibc-devel
#libboost_headers-devel
#boost-devel
#glib2-devel
#glib2-tools
#dbus-1-devel
#alsa-devel
#libnotify-devel
#libgnutls-devel
#protobuf-devel
#sqlite3-devel
#libpulse-devel
#gstreamer-devel
#gstreamer-plugins-base-devel
#vlc-devel
#taglib-devel
#libQt5Core-devel
#libQt5Gui-devel
#libQt5Widgets-devel
#libQt5Concurrent-devel
#libQt5Network-devel
#libQt5Sql-devel
#libQt5DBus-devel
#libQt5Test-devel
#libqt5-qtx11extras-devel
#libqt5-qtbase-common-devel
#libQt5Sql5-sqlite
#libqt5-linguist-devel
#libcdio-devel
#libgpod-devel
#libmtp-devel
#libchromaprint-devel
#desktop-file-utils
#update-desktop-files
#appstream-glib
#hicolor-icon-theme
#- name: Create Build Environment
#shell: bash
#run: cmake -E make_directory build
#- name: Configure CMake
#shell: bash
#working-directory: build
#run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT5=ON
#- name: Create source tarball
#working-directory: build
#run: ../dist/scripts/maketarball.sh
#- name: Create RPM build sources directories
#run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
#- name: Copy source tarball
#working-directory: build
#run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
#- name: Build RPM
#working-directory: build
#run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_lp153_qt5:
name: Build openSUSE Leap 15.3 Qt 5
runs-on: ubuntu-latest
container:
image: opensuse/leap:15.3
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
taglib-devel
libQt5Core-devel
libQt5Gui-devel
libQt5Widgets-devel
libQt5Concurrent-devel
libQt5Network-devel
libQt5Sql-devel
libQt5DBus-devel
libQt5Test-devel
libqt5-qtx11extras-devel
libqt5-qtbase-common-devel
libQt5Sql5-sqlite
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT5=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
#build_opensuse_tumbleweed_qt6:
#name: Build openSUSE Tumbleweed Qt 6
#runs-on: ubuntu-latest
#container:
#image: opensuse/tumbleweed
#steps:
#- uses: actions/checkout@v1.2.0
#- name: Lock packages
#run: zypper --non-interactive --gpg-auto-import-keys addlock openssh-server
#- name: Update packages
#run: zypper --non-interactive --gpg-auto-import-keys ref
#- name: Upgrade packages
#run: zypper --non-interactive --gpg-auto-import-keys dup
#- name: Install openSUSE dependencies
#run: >
#zypper --non-interactive --gpg-auto-import-keys install
#lsb-release
#rpm-build
#git
#tar
#make
#cmake
#gcc
#gcc-c++
#gettext-tools
#glibc-devel
#libboost_headers-devel
#boost-devel
#glib2-devel
#glib2-tools
#dbus-1-devel
#alsa-devel
#libnotify-devel
#libgnutls-devel
#protobuf-devel
#sqlite3-devel
#libpulse-devel
#gstreamer-devel
#gstreamer-plugins-base-devel
#vlc-devel
#taglib-devel
#qt6-core-devel
#qt6-gui-devel
#qt6-widgets-devel
#qt6-concurrent-devel
#qt6-network-devel
#qt6-sql-devel
#qt6-dbus-devel
#qt6-test-devel
#qt6-base-common-devel
#qt6-sql-sqlite
#qt6-linguist-devel
#libcdio-devel
#libgpod-devel
#libmtp-devel
#libchromaprint-devel
#desktop-file-utils
#update-desktop-files
#appstream-glib
#hicolor-icon-theme
#- name: Create Build Environment
#shell: bash
#run: cmake -E make_directory build
#- name: Configure CMake
#shell: bash
#working-directory: build
#run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT6=ON
#- name: Create source tarball
#working-directory: build
#run: ../dist/scripts/maketarball.sh
#- name: Create RPM build sources directories
#run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
#- name: Copy source tarball
#working-directory: build
#run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
#- name: Build RPM
#working-directory: build
#run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_lp153_qt6:
name: Build openSUSE Leap 15.3 Qt 6
runs-on: ubuntu-latest
container:
image: opensuse/leap:15.3
steps:
- uses: actions/checkout@v1.2.0
- name: Add Qt 6 repo
run: zypper -n ar -c -f -n 'repo-qt6' https://download.opensuse.org/repositories/home:/jonaski:/qt6/openSUSE_Leap_15.3/ repo-qt6
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
taglib-devel
qt6-core-devel
qt6-gui-devel
qt6-widgets-devel
qt6-concurrent-devel
qt6-network-devel
qt6-sql-devel
qt6-dbus-devel
qt6-test-devel
qt6-base-common-devel
qt6-sql-sqlite
qt6-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT6=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_tumbleweed_qt5:
name: Build openSUSE Tumbleweed Qt 5
runs-on: ubuntu-latest
container:
image: opensuse/tumbleweed
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Upgrade packages
run: zypper --non-interactive --gpg-auto-import-keys dup
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
taglib-devel
libQt5Core-devel
libQt5Gui-devel
libQt5Widgets-devel
libQt5Concurrent-devel
libQt5Network-devel
libQt5Sql-devel
libQt5DBus-devel
libQt5Test-devel
libqt5-qtx11extras-devel
libqt5-qtbase-common-devel
libQt5Sql5-sqlite
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT5=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_tumbleweed_qt6:
name: Build openSUSE Tumbleweed Qt 6
runs-on: ubuntu-latest
container:
image: opensuse/tumbleweed
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Upgrade packages
run: zypper --non-interactive --gpg-auto-import-keys dup
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
taglib-devel
qt6-core-devel
qt6-gui-devel
qt6-widgets-devel
qt6-concurrent-devel
qt6-network-devel
qt6-sql-devel
qt6-dbus-devel
qt6-test-devel
qt6-base-common-devel
qt6-sql-sqlite
qt6-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON -DBUILD_WITH_QT6=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_fedora_32:
@@ -618,6 +694,83 @@ jobs:
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_fedora_34:
name: Build Fedora 34
runs-on: ubuntu-latest
container:
image: fedora:34
env:
RPM_BUILD_NCPUS: "2"
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: yum update --assumeyes
- name: Upgrade packages
run: yum upgrade --assumeyes
- name: Install Fedora dependencies
run: >
dnf install --assumeyes
@development-tools
redhat-lsb-core
git
glibc
gcc-c++
rpmdevtools
make
cmake
pkgconfig
glib
man
tar
gettext
openssh
boost-devel
dbus-devel
protobuf-devel
protobuf-compiler
sqlite-devel
alsa-lib-devel
pulseaudio-libs-devel
libnotify-devel
gnutls-devel
qt5-qtbase-devel
qt5-qtx11extras-devel
qt5-qttools-devel
gstreamer1-devel
gstreamer1-plugins-base-devel
taglib-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
fftw-devel
desktop-file-utils
libappstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WERROR=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
working-directory: build
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_centos_8:
name: Build CentOS 8
runs-on: ubuntu-latest
@@ -1055,8 +1208,7 @@ jobs:
- name: Link Sparkle
shell: bash
run: |
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
- name: Create Build Environment
shell: bash
@@ -1068,7 +1220,8 @@ jobs:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
Qt6_DIR: /usr/local/opt/qt6/lib/cmake
Qt5LinguistTools_DIR: /usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
GST_SCANNER_PATH: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
GIO_EXTRA_MODULES: /usr/local/lib/gio/modules
GST_PLUGIN_SCANNER: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
GST_PLUGIN_PATH: /usr/local/lib/gstreamer-1.0
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON -DBUILD_WERROR=ON -DUSE_BUNDLE=ON -DCMAKE_PREFIX_PATH=/usr/local/opt/qt6/lib/cmake
@@ -1082,9 +1235,6 @@ jobs:
working-directory: build
shell: bash
run: make install
- name: Hack to make macdeployqt find plugins
shell: bash
run: sudo ln -s /usr/local/Cellar/qt/$(ls /usr/local/Cellar/qt/ | tail -n1)/share/qt/plugins /usr/local/plugins
- name: Create DMG
working-directory: build
shell: bash
@@ -1131,8 +1281,7 @@ jobs:
- name: Link Sparkle
shell: bash
run: |
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
- name: Create Build Environment
shell: bash
@@ -1144,7 +1293,8 @@ jobs:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
Qt5_DIR: /usr/local/opt/qt6/lib/cmake
Qt5LinguistTools_DIR: /usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
GST_SCANNER_PATH: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
GIO_EXTRA_MODULES: /usr/local/lib/gio/modules
GST_PLUGIN_SCANNER: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
GST_PLUGIN_PATH: /usr/local/lib/gstreamer-1.0
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON -DBUILD_WERROR=ON -DUSE_BUNDLE=ON -DCMAKE_PREFIX_PATH=/usr/local/opt/qt6/lib/cmake
@@ -1158,9 +1308,6 @@ jobs:
working-directory: build
shell: bash
run: make install
- name: Hack to make macdeployqt find plugins
shell: bash
run: sudo ln -s /usr/local/Cellar/qt/$(ls /usr/local/Cellar/qt/ | tail -n1)/share/qt/plugins /usr/local/plugins
- name: Create DMG
working-directory: build
shell: bash
@@ -1171,8 +1318,8 @@ jobs:
path: build/strawberry-*-bigsur-*.dmg
build-windows:
name: Build Windows
build-windows-qt5:
name: Build Windows Qt 5
runs-on: ubuntu-latest
container:
image: jonaski/strawberry-mxe2
@@ -1196,6 +1343,7 @@ jobs:
cmake ..
-DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-x86_64-w64-mingw32-shared.cmake
-DCMAKE_BUILD_TYPE=Release
-DBUILD_WITH_QT5=ON
-DBUILD_WERROR=ON
-DARCH=x86_64
-DENABLE_WIN32_CONSOLE=OFF
@@ -1286,7 +1434,7 @@ jobs:
- name: Copy extra binaries
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,gst-launch-1.0.exe} .
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,gst-launch-1.0.exe,gst-discoverer-1.0.exe} .
- name: Copy dependencies
working-directory: build
@@ -1310,6 +1458,159 @@ jobs:
working-directory: build
run: cp ${GITHUB_WORKSPACE}/dist/windows/*.nsi ${GITHUB_WORKSPACE}/dist/windows/*.nsh ${GITHUB_WORKSPACE}/dist/windows/*.ico .
- name: Copy COPYING license file
working-directory: build
run: cp ${GITHUB_WORKSPACE}/COPYING .
- name: Build Windows installer
working-directory: build
run: makensis strawberry.nsi
build-windows-qt6:
name: Build Windows Qt 6
runs-on: ubuntu-latest
container:
image: jonaski/strawberry-mxe2
steps:
- uses: actions/checkout@v1.2.0
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Link MXE directory
shell: bash
run: ln -s /usr/src/strawberry-mxe ~/mxe-shared
- name: Run CMake
shell: bash
env:
PKG_CONFIG_PATH: /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/lib/pkgconfig
working-directory: build
run: >
cmake ..
-DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-x86_64-w64-mingw32-shared.cmake
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_PREFIX_PATH=/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/qt6
-DBUILD_WITH_QT6=ON
-DBUILD_WERROR=ON
-DARCH=x86_64
-DENABLE_WIN32_CONSOLE=OFF
-DENABLE_DBUS=OFF
-DENABLE_LIBGPOD=OFF
-DENABLE_LIBMTP=OFF
-DProtobuf_PROTOC_EXECUTABLE=/usr/src/strawberry-mxe/usr/x86_64-pc-linux-gnu/bin/protoc
- name: Run Make
working-directory: build
run: make -j2
- name: Create directories
working-directory: build
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins nsisplugins
- name: Copy GIO modules
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/lib/gio/modules/libgiognutls.dll ${GITHUB_WORKSPACE}/build/gio-modules/
- name: Copy Qt platforms plugins
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/qt5/plugins/platforms/qwindows.dll ${GITHUB_WORKSPACE}/build/platforms/
- name: Copy Qt styles plugins
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/qt5/plugins/styles/qwindowsvistastyle.dll ${GITHUB_WORKSPACE}/build/styles/
- name: Copy Qt SQL drivers plugins
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/qt5/plugins/sqldrivers/qsqlite.dll ${GITHUB_WORKSPACE}/build/sqldrivers/
- name: Copy Qt imageformats plugins
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/qt5/plugins/imageformats/*.dll ${GITHUB_WORKSPACE}/build/imageformats/
- name: Copy gstreamer plugins
working-directory: build
run: >
cp
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstapp.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstcoreelements.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudioconvert.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudiofx.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudiomixer.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudioparsers.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudiorate.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudioresample.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaudiotestsrc.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstautodetect.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstplayback.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstvolume.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstspectrum.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstequalizer.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstreplaygain.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgsttypefindfunctions.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstgio.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstdirectsound.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstwasapi.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstpbtypes.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstapetag.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgsticydemux.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstid3demux.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgsttaglib.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgsttcp.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstudp.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstsoup.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstcdio.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstrtp.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstrtsp.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstflac.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstwavparse.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstwavpack.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstogg.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstvorbis.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstopus.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstopusparse.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstspeex.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstlame.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstaiff.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstfaac.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstfaad.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstisomp4.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstasf.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstasfmux.dll
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstlibav.dll
${GITHUB_WORKSPACE}/build/gstreamer-plugins/
- name: Copy extra binaries
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{libssl-1_1-x64.dll,libcrypto-1_1-x64.dll,sqlite3.exe,killproc.exe,gst-launch-1.0.exe,gst-discoverer-1.0.exe} .
- name: Copy dependencies
working-directory: build
run: >
/usr/src/strawberry-mxe/tools/copydlldeps.sh
-c
-d .
-F .
-F ./platforms
-F ./styles
-F ./sqldrivers
-F ./imageformats
-F ./gstreamer-plugins
-R /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared
- name: Strip binaries
working-directory: build
run: find . -type f \( -iname \*.dll -o -iname \*.exe \) -exec /usr/src/strawberry-mxe/usr/bin/x86_64-w64-mingw32.shared-strip {} \;
- name: Copy nsis files
working-directory: build
run: cp ${GITHUB_WORKSPACE}/dist/windows/*.nsi ${GITHUB_WORKSPACE}/dist/windows/*.nsh ${GITHUB_WORKSPACE}/dist/windows/*.ico .
- name: Copy COPYING license file
working-directory: build
run: cp ${GITHUB_WORKSPACE}/COPYING .
- name: Build Windows installer
working-directory: build
run: makensis strawberry.nsi

View File

@@ -19,8 +19,7 @@ before_install:
- brew install libcdio libmtp
- brew install create-dmg
- brew install --cask sparkle
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
- export Qt6_DIR=/usr/local/opt/qt6/lib/cmake
- export Qt6LinguistTools_DIR=/usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
- ls /usr/local/lib/gstreamer-1.0
@@ -31,7 +30,6 @@ before_script:
script:
- make -j8
- make install
- sudo ln -s /usr/local/Cellar/qt/$(ls /usr/local/Cellar/qt/ | tail -n1)/share/qt/plugins /usr/local/plugins
- make dmg2
after_success:
- ls -lh strawberry*.dmg

10
3rdparty/README.md vendored
View File

@@ -14,6 +14,12 @@ URL: https://github.com/itay-grudev/SingleApplication
SPMediaKeyTap
-------------
Used on macOS to exclusively enable strawberry to grab global media shortcuts.
Can safely be deleted on other platforms.
This is used for macOS only to enable strawberry to grab global shortcuts and can safely be deleted on other
platforms.
macdeployqt
-----------
A modified version of Qt's official macdeployqt utility that fixes some issues,
this version also deploys gstreamer plugins.
Can safely be deleted on other platforms.

7
3rdparty/macdeployqt/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,7 @@
add_executable(macdeployqt main.cpp shared.cpp)
target_link_libraries(macdeployqt PRIVATE
"-framework AppKit"
${QtCore_LIBRARIES}
)
#execute_process(COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/macdeployqt ${CMAKE_BINARY_DIR})

298
3rdparty/macdeployqt/main.cpp vendored Normal file
View File

@@ -0,0 +1,298 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#undef QT_NO_DEBUG_OUTPUT
#undef QT_NO_WARNING_OUTPUT
#undef QT_NO_INFO_OUTPUT
#include <QCoreApplication>
#include <QDir>
#include <QLibraryInfo>
#include "shared.h"
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString appBundlePath;
if (argc > 1)
appBundlePath = QString::fromLocal8Bit(argv[1]);
if (argc < 2 || appBundlePath.startsWith("-")) {
qDebug() << "Usage: macdeployqt app-bundle [options]";
qDebug() << "";
qDebug() << "Options:";
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
qDebug() << " -no-plugins : Skip plugin deployment";
qDebug() << " -dmg : Create a .dmg disk image";
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
qDebug() << " -always-overwrite : Copy files even if the target file exists";
qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
qDebug() << " -libpath=<path> : Add the given path to the library search path";
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
qDebug() << " -plugins-dir=<path> : Set plugins directory";
qDebug() << "";
qDebug() << "macdeployqt takes an application bundle as input and makes it";
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
qDebug() << "the application uses.";
qDebug() << "";
qDebug() << "Plugins related to a framework are copied in with the";
qDebug() << "framework. The accessibility, image formats, and text codec";
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
qDebug() << "";
qDebug() << "Qt plugins may use private API and will cause the app to be";
qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
qDebug() << "";
qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
qDebug() << "documentation for more information about deployment on OS X.";
return 1;
}
appBundlePath = QDir::cleanPath(appBundlePath);
if (QDir().exists(appBundlePath) == false) {
qDebug() << "Error: Could not find app bundle" << appBundlePath;
return 1;
}
bool plugins = true;
bool dmg = false;
QByteArray filesystem("HFS+");
bool useDebugLibs = false;
extern bool runStripEnabled;
extern bool alwaysOwerwriteEnabled;
extern QStringList librarySearchPath;
QStringList additionalExecutables;
bool qmldirArgumentUsed = false;
QStringList qmlDirs;
QStringList qmlImportPaths;
extern bool runCodesign;
extern QString codesignIdentiy;
extern bool hardenedRuntime;
extern bool appstoreCompliant;
extern bool deployFramework;
extern bool secureTimestamp;
QString plugin_dir;
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
if (argument == QByteArray("-no-plugins")) {
LogDebug() << "Argument found:" << argument;
plugins = false;
} else if (argument == QByteArray("-dmg")) {
LogDebug() << "Argument found:" << argument;
dmg = true;
} else if (argument == QByteArray("-no-strip")) {
LogDebug() << "Argument found:" << argument;
runStripEnabled = false;
} else if (argument == QByteArray("-use-debug-libs")) {
LogDebug() << "Argument found:" << argument;
useDebugLibs = true;
runStripEnabled = false;
} else if (argument.startsWith(QByteArray("-verbose"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
bool ok = false;
int number = argument.mid(index+1).toInt(&ok);
if (!ok)
LogError() << "Could not parse verbose level";
else
logLevel = number;
} else if (argument.startsWith(QByteArray("-executable"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing executable path";
else
additionalExecutables << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-qmldir"))) {
LogDebug() << "Argument found:" << argument;
qmldirArgumentUsed = true;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing qml directory path";
else
qmlDirs << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-qmlimport"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing qml import path";
else
qmlImportPaths << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-libpath"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing library search path";
else
librarySearchPath << argument.mid(index+1);
} else if (argument == QByteArray("-always-overwrite")) {
LogDebug() << "Argument found:" << argument;
alwaysOwerwriteEnabled = true;
} else if (argument.startsWith(QByteArray("-codesign"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
if (index < 0 || index >= argument.size()) {
LogError() << "Missing code signing identity";
} else {
runCodesign = true;
codesignIdentiy = argument.mid(index+1);
}
} else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
if (index < 0 || index >= argument.size()) {
LogError() << "Missing code signing identity";
} else {
runCodesign = true;
hardenedRuntime = true;
secureTimestamp = true;
codesignIdentiy = argument.mid(index+1);
}
} else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
LogDebug() << "Argument found:" << argument;
hardenedRuntime = true;
} else if (argument.startsWith(QByteArray("-timestamp"))) {
LogDebug() << "Argument found:" << argument;
secureTimestamp = true;
} else if (argument == QByteArray("-appstore-compliant")) {
LogDebug() << "Argument found:" << argument;
appstoreCompliant = true;
// Undocumented option, may not work as intented
} else if (argument == QByteArray("-deploy-framework")) {
LogDebug() << "Argument found:" << argument;
deployFramework = true;
} else if (argument.startsWith(QByteArray("-fs"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing filesystem type";
else
filesystem = argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-plugins-dir"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing filesystem type";
else
plugin_dir = argument.mid(index+1);
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
return 1;
}
}
DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
if (deploymentInfo.isDebug)
useDebugLibs = true;
if (deployFramework && deploymentInfo.isFramework)
fixupFramework(appBundlePath);
// Convenience: Look for .qml files in the current directoty if no -qmldir specified.
if (qmlDirs.isEmpty()) {
QDir dir;
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
qmlDirs += QStringLiteral(".");
}
}
if (!qmlDirs.isEmpty()) {
bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
if (!ok && qmldirArgumentUsed)
return 1; // exit if the user explicitly asked for qml import deployment
// Update deploymentInfo.deployedFrameworks - the QML imports
// may have brought in extra frameworks as dependencies.
deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
deploymentInfo.deployedFrameworks =
QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
deploymentInfo.deployedFrameworks.end()).values();
}
if (plugins) {
if (plugin_dir.isEmpty()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
#else
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
#endif
}
else {
deploymentInfo.pluginPath = plugin_dir;
}
if (deploymentInfo.pluginPath.isEmpty()) {
LogError() << "Missing Qt plugins path\n";
return 1;
}
if (!QDir(deploymentInfo.pluginPath).exists()) {
LogError() << "Plugins path does not exist\n" << deploymentInfo.pluginPath;
return 1;
}
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
if (!deploymentInfo.pluginPath.isEmpty()) {
LogNormal();
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
createQtConf(appBundlePath);
}
}
if (runStripEnabled)
stripAppBinary(appBundlePath);
if (!FinalCheck(appBundlePath)) {
return 1;
}
if (runCodesign)
codesign(codesignIdentiy, appBundlePath);
if (dmg) {
LogNormal();
createDiskImage(appBundlePath, filesystem);
}
return 0;
}

1832
3rdparty/macdeployqt/shared.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

141
3rdparty/macdeployqt/shared.h vendored Normal file
View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MAC_DEPLOMYMENT_SHARED_H
#define MAC_DEPLOMYMENT_SHARED_H
#include <QString>
#include <QStringList>
#include <QDebug>
#include <QSet>
#include <QVersionNumber>
extern int logLevel;
#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
extern bool runStripEnabled;
class FrameworkInfo
{
public:
bool isDylib;
QString frameworkDirectory;
QString frameworkName;
QString frameworkPath;
QString binaryDirectory;
QString binaryName;
QString binaryPath;
QString rpathUsed;
QString version;
QString installName;
QString deployedInstallName;
QString sourceFilePath;
QString frameworkDestinationDirectory;
QString binaryDestinationDirectory;
bool isDebugLibrary() const
{
return binaryName.contains(QLatin1String("_debug"));
}
};
class DylibInfo
{
public:
QString binaryPath;
QVersionNumber currentVersion;
QVersionNumber compatibilityVersion;
};
class OtoolInfo
{
public:
QString installName;
QString binaryPath;
QVersionNumber currentVersion;
QVersionNumber compatibilityVersion;
QList<DylibInfo> dependencies;
};
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
QDebug operator<<(QDebug debug, const FrameworkInfo &info);
class ApplicationBundleInfo
{
public:
QString path;
QString binaryPath;
QStringList libraryPaths;
};
class DeploymentInfo
{
public:
QString qtPath;
QString pluginPath;
QStringList deployedFrameworks;
QSet<QString> rpathsUsed;
bool useLoaderPath;
bool isFramework;
bool isDebug;
bool containsModule(const QString &module, const QString &libInFix) const;
};
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
OtoolInfo findDependencyInfo(const QString &binaryPath);
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
QString findAppBinary(const QString &appBundlePath);
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
QString copyFramework(const FrameworkInfo &framework, const QString path);
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
void createQtConf(const QString &appBundlePath);
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
void changeIdentification(const QString &id, const QString &binaryPath);
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
void runStrip(const QString &binaryPath);
void stripAppBinary(const QString &bundlePath);
QString findAppBinary(const QString &appBundlePath);
QStringList findAppFrameworkNames(const QString &appBundlePath);
QStringList findAppFrameworkPaths(const QString &appBundlePath);
void codesignFile(const QString &identity, const QString &filePath);
QSet<QString> codesignBundle(const QString &identity,
const QString &appBundlePath,
QList<QString> additionalBinariesContainingRpaths);
void codesign(const QString &identity, const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
void fixupFramework(const QString &appBundlePath);
bool FinalCheck(const QString &appBundlePath);
#endif

View File

@@ -220,7 +220,7 @@ void SingleApplicationPrivate::startSecondary() {
}
bool SingleApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
bool SingleApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
QElapsedTimer time;
time.start();
@@ -241,7 +241,7 @@ bool SingleApplicationPrivate::connectToPrimary(int timeout, ConnectionType conn
socket_->connectToServer(blockServerName_);
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(timeout - time.elapsed());
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
}
// If connected break out of the loop
@@ -277,7 +277,7 @@ bool SingleApplicationPrivate::connectToPrimary(int timeout, ConnectionType conn
socket_->write(header);
socket_->write(initMsg);
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
bool result = socket_->waitForBytesWritten(static_cast<int>(timeout - time.elapsed()));
socket_->flush();
return result;
@@ -326,17 +326,17 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, this, [nextConnSocket, this]() {
connectionMap_.remove(nextConnSocket);
nextConnSocket->deleteLater();
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageHeader:
@@ -346,7 +346,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
readInitMessageBody(nextConnSocket);
break;
case StageConnected:
Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId);
slotDataAvailable(nextConnSocket, info.instanceId);
break;
default:
break;
@@ -438,7 +438,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
}
if (sock->bytesAvailable() > 0) {
Q_EMIT this->slotDataAvailable(sock, instanceId);
slotDataAvailable(sock, instanceId);
}
}
@@ -453,7 +453,7 @@ void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
if (closedSocket->bytesAvailable() > 0) {
Q_EMIT slotDataAvailable(closedSocket, instanceId);
slotDataAvailable(closedSocket, instanceId);
}
}

View File

@@ -85,7 +85,7 @@ class SingleApplicationPrivate : public QObject {
void initializeMemoryBlock();
void startPrimary();
void startSecondary();
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum();
qint64 primaryPid();
QString primaryUser();

View File

@@ -220,7 +220,7 @@ void SingleCoreApplicationPrivate::startSecondary() {
}
bool SingleCoreApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
QElapsedTimer time;
time.start();
@@ -241,7 +241,7 @@ bool SingleCoreApplicationPrivate::connectToPrimary(int timeout, ConnectionType
socket_->connectToServer(blockServerName_);
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(timeout - time.elapsed());
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
}
// If connected break out of the loop
@@ -277,7 +277,7 @@ bool SingleCoreApplicationPrivate::connectToPrimary(int timeout, ConnectionType
socket_->write(header);
socket_->write(initMsg);
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
bool result = socket_->waitForBytesWritten(timeout - static_cast<int>(time.elapsed()));
socket_->flush();
return result;
@@ -326,17 +326,17 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, this, [nextConnSocket, this]() {
connectionMap_.remove(nextConnSocket);
nextConnSocket->deleteLater();
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageHeader:
@@ -346,7 +346,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
readInitMessageBody(nextConnSocket);
break;
case StageConnected:
Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId);
slotDataAvailable(nextConnSocket, info.instanceId);
break;
default:
break;
@@ -438,7 +438,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
}
if (sock->bytesAvailable() > 0) {
Q_EMIT this->slotDataAvailable(sock, instanceId);
slotDataAvailable(sock, instanceId);
}
}
@@ -453,7 +453,7 @@ void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, c
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
if (closedSocket->bytesAvailable() > 0) {
Q_EMIT slotDataAvailable(closedSocket, instanceId);
slotDataAvailable(closedSocket, instanceId);
}
}

View File

@@ -85,7 +85,7 @@ class SingleCoreApplicationPrivate : public QObject {
void initializeMemoryBlock();
void startPrimary();
void startSecondary();
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum();
qint64 primaryPid();
QString primaryUser();

View File

@@ -69,11 +69,6 @@ add_compile_options(${COMPILE_OPTIONS})
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
add_definitions(-DNDEBUG)
add_definitions(-DQT_NO_DEBUG_OUTPUT)
#add_definitions(-DQT_NO_WARNING_OUTPUT)
endif()
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(DEBUG ON)
endif()
if(APPLE)
@@ -227,16 +222,16 @@ if(X11_FOUND)
else()
message(WARNING, "Missing X11/XF86keysym.h")
endif()
endif(X11_FOUND)
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
if(QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
if(QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
endif(X11_FOUND)
# TAGLIB
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
@@ -256,7 +251,8 @@ set(SINGLEAPPLICATION_LIBRARIES singleapplication)
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
if(APPLE)
find_library(SPARKLE Sparkle)
find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle")
add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)

View File

@@ -2,6 +2,40 @@ Strawberry Music Player
=======================
ChangeLog
0.9.3:
Bugfixes:
* Fix "Show in file browser" to work with thunar.
* Check that the clicked rating position is to the right or left of the rectangle.
* Fix rescan when collection directory is removed and readded.
* Create GLib main event loop on non-glib systems to fix stream discoverer.
* (macOS) Fix intermittent abort on startup.
* (macOS) Fix Tidal and Qobuz search field not showing.
* (macOS) Add tidal URL scheme to Info.plist.
* (macOS) Fix Tidal OAuth authentication.
Enhancements:
* Allow editing playlist metadata for radio streams.
* Make CollectionQuery subclass QSqlQuery, avoid copying QSqlQuery.
* Only enable FTS3 when schema needs upgrading, since FTS5 is used for search.
* Add setting for configuring the color for the currently playing song.
* Add setting to turn on OSD Pretty fading.
* Add commandline option to resize window.
* (Windows) Show dialog with programs that needs to close in nsis installer.
* (macOS) Make macdeployqt work with Qt 5 too.
* (macOS) Show keep running option in behaviour settings.
0.9.2:
Bugfixes:
* Fix marking songs available.
* Fix crash when transcoding music, or copying music to devices with transcoding.
* Fix copying album covers to iPod.
* Fix playlist tabbar close and save right click actions.
* Fix slow opening of cover manager.
* (macOS) Fix crash when opening cover manager.
* (macOS) Fix broken Qt plugins resulting in album covers not showing.
0.9.1:
Bugfixes:
@@ -9,7 +43,7 @@ ChangeLog
* Fix overwriting existing newer last played when importing last played from last.fm.
* Fix memory leak on song change when moodbar is disabled.
* Fix playlist filter search for text with spaces with Qt 6.
* Fix 'Except between tracks on the same album' backend fade option always greyed out.
* Fix 'Except between tracks on the same album' backend fade option always grayed out.
* Fix read and save vorbis comment grouping tag.
* Fix QAtomicInteger compile error on armv.
* Fix compile error with protobuf 3.15.0 and newer.
@@ -31,6 +65,7 @@ ChangeLog
* Add right click actions to clear set cover, and option delete covers.
* Show artist and album underneath the albums in the cover manager when all Artists is selected.
* Disable unavailable right click cover actions.
* Remove 3rdparty TagLib now that TagLib 1.12 is available.
* (macOS) Update and improve build deployment/bundling for Qt 6.
New features:

View File

@@ -5,7 +5,7 @@
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
![Browse](https://www.strawberrymusicplayer.org/pictures/screenshot-002-large.png)
![Browse](https://raw.githubusercontent.com/strawberrymusicplayer/strawberry/master/data/screenshot/screenshot.png)
Resources:
@@ -15,7 +15,8 @@ Resources:
* Buildbot: https://buildbot.strawberrymusicplayer.org/
* Latest builds: https://builds.strawberrymusicplayer.org/
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
* PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
* Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
* Translations: https://translate.zanata.org/iteration/view/strawberry/master
### :bangbang: Opening an issue:

View File

@@ -1,9 +1,11 @@
find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
if(MACDEPLOYQT_EXECUTABLE)
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
else()
message(WARNING "Missing macdeployqt executable.")
endif()
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
#if(MACDEPLOYQT_EXECUTABLE)
# message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
#else()
# message(WARNING "Missing macdeployqt executable.")
#endif()
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
if(CREATEDMG_EXECUTABLE)
@@ -19,14 +21,16 @@ endif()
if(MACDEPLOYQT_EXECUTABLE AND CREATEDMG_EXECUTABLE AND MACOS_VERSION_PACKAGE)
add_custom_target(dmg
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(dmg2
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CREATEDMG_EXECUTABLE} --skip-jenkins --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 KiB

View File

@@ -38,6 +38,19 @@
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
<key>SUPublicEDKey</key>
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>org.strawberrymusicplayer.strawberry</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tidal</string>
</array>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>

View File

@@ -1,514 +0,0 @@
#!/usr/bin/python
# Strawberry Music Player
# This file was part of Clementine.
#
# 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/>.
from distutils import spawn
import logging
import os
import re
import subprocess
import sys
import traceback
LOGGER = logging.getLogger('macdeploy')
LIBRARY_SEARCH_PATH = ['/usr/local/lib', '/usr/local/opt/icu4c/lib']
FRAMEWORK_SEARCH_PATH = [
'/Library/Frameworks',
os.path.join(os.environ['HOME'], 'Library/Frameworks'),
'/Library/Frameworks/Sparkle.framework/Versions'
]
QT_PLUGINS = [
'platforms/libqcocoa.dylib',
'platforminputcontexts/libqtvirtualkeyboardplugin.dylib',
'styles/libqmacstyle.dylib',
'sqldrivers/libqsqlite.dylib',
'bearer/libqgenericbearer.dylib',
'iconengines/libqsvgicon.dylib',
'imageformats/libqgif.dylib',
'imageformats/libqicns.dylib',
'imageformats/libqico.dylib',
'imageformats/libqjpeg.dylib',
'imageformats/libqsvg.dylib',
'imageformats/libqtiff.dylib',
'printsupport/libcocoaprintersupport.dylib',
'virtualkeyboard/libqtvirtualkeyboard_hangul.dylib',
'virtualkeyboard/libqtvirtualkeyboard_openwnn.dylib',
'virtualkeyboard/libqtvirtualkeyboard_pinyin.dylib',
'virtualkeyboard/libqtvirtualkeyboard_tcime.dylib',
'virtualkeyboard/libqtvirtualkeyboard_thai.dylib',
]
QT_PLUGINS_SEARCH_PATH = [
'/usr/local/opt/qt/plugins',
]
GSTREAMER_SEARCH_PATH = [
'/usr/local/lib/gstreamer-1.0',
'/usr/local/Cellar/gstreamer',
]
GSTREAMER_PLUGINS = [
'libgstapetag.dylib',
'libgstapp.dylib',
'libgstaudioconvert.dylib',
'libgstaudiofx.dylib',
'libgstaudiomixer.dylib',
'libgstaudioparsers.dylib',
'libgstaudiorate.dylib',
'libgstaudioresample.dylib',
'libgstaudiotestsrc.dylib',
'libgstaudiovisualizers.dylib',
'libgstauparse.dylib',
'libgstautoconvert.dylib',
'libgstautodetect.dylib',
'libgstcoreelements.dylib',
'libgstequalizer.dylib',
'libgstgio.dylib',
'libgsticydemux.dylib',
'libgstid3demux.dylib',
'libgstlevel.dylib',
'libgstosxaudio.dylib',
'libgstplayback.dylib',
'libgstrawparse.dylib',
'libgstreplaygain.dylib',
'libgstsoup.dylib',
'libgstspectrum.dylib',
'libgsttypefindfunctions.dylib',
'libgstvolume.dylib',
'libgstxingmux.dylib',
'libgsttcp.dylib',
'libgstudp.dylib',
'libgstpbtypes.dylib',
'libgstrtp.dylib',
'libgstrtsp.dylib',
'libgstflac.dylib',
'libgstwavparse.dylib',
'libgstfaac.dylib',
'libgstfaad.dylib',
'libgstogg.dylib',
'libgstopus.dylib',
'libgstopusparse.dylib',
'libgstasf.dylib',
'libgstspeex.dylib',
'libgsttaglib.dylib',
'libgstvorbis.dylib',
'libgstisomp4.dylib',
'libgstlibav.dylib',
'libgstaiff.dylib',
'libgstlame.dylib',
'libgstmusepack.dylib',
]
GIO_MODULES_SEARCH_PATH = ['/usr/local/lib/gio/modules',]
INSTALL_NAME_TOOL_APPLE = 'install_name_tool'
INSTALL_NAME_TOOL_CROSS = 'x86_64-apple-darwin-%s' % INSTALL_NAME_TOOL_APPLE
INSTALL_NAME_TOOL = INSTALL_NAME_TOOL_CROSS if spawn.find_executable(INSTALL_NAME_TOOL_CROSS) else INSTALL_NAME_TOOL_APPLE
OTOOL_APPLE = 'otool'
OTOOL_CROSS = 'x86_64-apple-darwin-%s' % OTOOL_APPLE
OTOOL = OTOOL_CROSS if spawn.find_executable(OTOOL_CROSS) else OTOOL_APPLE
class Error(Exception):
pass
class CouldNotFindFrameworkError(Error):
pass
class CouldNotFindGioModuleError(Error):
pass
class CouldNotFindQtPluginError(Error):
pass
class CouldNotParseFrameworkNameError(Error):
pass
class InstallNameToolError(Error):
pass
class CouldNotFindGstreamerPluginError(Error):
pass
if len(sys.argv) < 2:
print('Usage: %s <bundle.app>' % sys.argv[0])
bundle_dir = sys.argv[1]
bundle_name = os.path.basename(bundle_dir).split('.')[0]
commands = []
frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks')
commands.append(['mkdir', '-p', frameworks_dir])
resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources')
commands.append(['mkdir', '-p', resources_dir])
plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
binary = os.path.join(bundle_dir, 'Contents', 'MacOS', bundle_name)
tagreader_binary = os.path.join(plugins_dir, bundle_name + "-tagreader")
fixed_libraries = set()
fixed_frameworks = set()
def GetBrokenLibraries(binary):
#print("Checking libs for binary: %s" % binary)
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
broken_libs = {'frameworks': [], 'libs': []}
for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
#print("Checking line: %s" % line)
if not line: # skip empty lines
continue
if os.path.basename(binary) == os.path.basename(line):
#print("mnope %s-%s" % (os.path.basename(binary), os.path.basename(line)))
continue
if re.match(r'^\s*/System/', line):
#print("system framework: %s" % line)
continue # System framework
elif re.match(r'^\s*/usr/lib/', line):
#print("unix style system lib: %s" % line)
continue # unix style system library
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@rpath', line) or re.match(r'^\s*@loader_path', line):
# Potentially already fixed library
if line.count('/') == 1:
relative_path = os.path.join(*line.split('/')[1:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['libs'].append(relative_path)
elif line.count('/') == 2:
relative_path = os.path.join(*line.split('/')[2:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['libs'].append(relative_path)
elif line.count('/') >= 3:
relative_path = os.path.join(*line.split('/')[3:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['frameworks'].append(relative_path)
else:
print("GetBrokenLibraries Error: %s" % line)
elif re.search(r'\w+\.framework', line):
#print("framework: %s" % line)
broken_libs['frameworks'].append(line)
else:
broken_libs['libs'].append(line)
return broken_libs
def FindFramework(path):
for search_path in FRAMEWORK_SEARCH_PATH:
abs_path = os.path.join(search_path, path)
if os.path.exists(abs_path):
LOGGER.debug("Found framework '%s' in '%s'", path, search_path)
return abs_path
raise CouldNotFindFrameworkError(path)
def FindLibrary(path):
if os.path.exists(path):
return path
for search_path in LIBRARY_SEARCH_PATH:
abs_path = os.path.join(search_path, path)
if os.path.exists(abs_path):
LOGGER.debug("Found library '%s' in '%s'", path, search_path)
return abs_path
else: # try harder---look for lib name in library folders
newpath = os.path.join(search_path,os.path.basename(path))
if os.path.exists(newpath):
return newpath
raise CouldNotFindFrameworkError(path)
def FixAllLibraries(broken_libs):
for framework in broken_libs['frameworks']:
FixFramework(framework)
for lib in broken_libs['libs']:
FixLibrary(lib)
def FixFramework(path):
if path in fixed_frameworks:
return
else:
fixed_frameworks.add(path)
abs_path = FindFramework(path)
broken_libs = GetBrokenLibraries(abs_path)
FixAllLibraries(broken_libs)
new_path = CopyFramework(abs_path)
id = os.sep.join(new_path.split(os.sep)[3:])
FixFrameworkId(new_path, id)
for framework in broken_libs['frameworks']:
FixFrameworkInstallPath(framework, new_path)
for library in broken_libs['libs']:
FixLibraryInstallPath(library, new_path)
def FixLibrary(path):
if path in fixed_libraries:
return
# Always bundle libraries provided by homebrew (/usr/local).
if not re.match(r'^\s*/usr/local', path) and FindSystemLibrary(os.path.basename(path)) is not None:
return
fixed_libraries.add(path)
abs_path = FindLibrary(path)
if abs_path == "":
print("Could not resolve %s, not fixing!" % path)
return
broken_libs = GetBrokenLibraries(abs_path)
FixAllLibraries(broken_libs)
new_path = CopyLibrary(abs_path)
FixLibraryId(new_path)
for framework in broken_libs['frameworks']:
FixFrameworkInstallPath(framework, new_path)
for library in broken_libs['libs']:
FixLibraryInstallPath(library, new_path)
def FixPlugin(abs_path, subdir):
broken_libs = GetBrokenLibraries(abs_path)
FixAllLibraries(broken_libs)
new_path = CopyPlugin(abs_path, subdir)
for framework in broken_libs['frameworks']:
FixFrameworkInstallPath(framework, new_path)
for library in broken_libs['libs']:
FixLibraryInstallPath(library, new_path)
def FixBinary(path):
broken_libs = GetBrokenLibraries(path)
FixAllLibraries(broken_libs)
for framework in broken_libs['frameworks']:
FixFrameworkInstallPath(framework, path)
for library in broken_libs['libs']:
FixLibraryInstallPath(library, path)
def CopyLibrary(path):
new_path = os.path.join(frameworks_dir, os.path.basename(path))
#args = ['cp', path, new_path]
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
commands.append(args)
commands.append(['chmod', '+w', new_path])
LOGGER.info("Copying library '%s'", path)
return new_path
def CopyPlugin(path, subdir):
new_path = os.path.join(plugins_dir, subdir, os.path.basename(path))
args = ['mkdir', '-p', os.path.dirname(new_path)]
commands.append(args)
#args = ['cp', path, new_path]
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
commands.append(args)
commands.append(['chmod', '+w', new_path])
LOGGER.info("Copying plugin '%s'", path)
return new_path
def CopyFramework(path):
parts = path.split(os.sep)
for i, part in enumerate(parts):
if re.match(r'\w+\.framework', part):
full_path = os.path.join(frameworks_dir, *parts[i:-1])
framework_name = part.split(".framework")[0]
break
def CopyFramework(src_binary):
while os.path.islink(src_binary):
src_binary = os.path.realpath(src_binary)
m = re.match(r'(.*/([^/]+)\.framework)/Versions/([^/]+)/.*', src_binary)
if not m:
raise CouldNotParseFrameworkNameError(src_binary)
src_base = m.group(1)
name = m.group(2)
version = m.group(3)
LOGGER.info('Copying framework %s version %s', name, version)
dest_base = os.path.join(frameworks_dir, '%s.framework' % name)
dest_dir = os.path.join(dest_base, 'Versions', version)
dest_binary = os.path.join(dest_dir, name)
commands.append(['mkdir', '-p', dest_dir])
commands.append(['cp', src_binary, dest_binary])
commands.append(['chmod', '+w', dest_binary])
# Copy special files from various places:
# QtCore has Resources/qt_menu.nib (copy to app's Resources)
# Sparkle has Resources/*
# Qt* have Resources/Info.plist
resources_src = os.path.join(src_base, 'Resources')
menu_nib = os.path.join(resources_src, 'qt_menu.nib')
if os.path.exists(menu_nib):
LOGGER.info("Copying qt_menu.nib '%s'", menu_nib)
commands.append(['cp', '-r', menu_nib, resources_dir])
elif os.path.exists(resources_src):
LOGGER.info("Copying resources dir '%s'", resources_src)
commands.append(['cp', '-r', resources_src, dest_dir])
info_plist = os.path.join(src_base, 'Contents', 'Info.plist')
if os.path.exists(info_plist):
LOGGER.info("Copying special file '%s'", info_plist)
resources_dest = os.path.join(dest_dir, 'Resources')
commands.append(['mkdir', resources_dest])
commands.append(['cp', '-r', info_plist, resources_dest])
# Create symlinks in the Framework to make it look like
# https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
commands.append([
'ln', '-sf', 'Versions/Current/%s' % name, os.path.join(dest_base, name)
])
commands.append([
'ln', '-sf', 'Versions/Current/Resources',
os.path.join(dest_base, 'Resources')
])
commands.append(
['ln', '-sf', version, os.path.join(dest_base, 'Versions/Current')])
return dest_binary
def FixId(path, library_name):
id = '@executable_path/../Frameworks/%s' % library_name
args = [INSTALL_NAME_TOOL, '-id', id, path]
commands.append(args)
def FixLibraryId(path):
library_name = os.path.basename(path)
FixId(path, library_name)
def FixFrameworkId(path, id):
FixId(path, id)
def FixInstallPath(library_path, library, new_path):
args = [INSTALL_NAME_TOOL, '-change', library_path, new_path, library]
commands.append(args)
def FindSystemLibrary(library_name):
for path in ['/lib', '/usr/lib']:
full_path = os.path.join(path, library_name)
if os.path.exists(full_path):
return full_path
return None
def FixLibraryInstallPath(library_path, library):
system_library = FindSystemLibrary(os.path.basename(library_path))
if system_library is None or re.match(r'^\s*/usr/local', library_path):
new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path)
FixInstallPath(library_path, library, new_path)
else:
FixInstallPath(library_path, library, system_library)
def FixFrameworkInstallPath(library_path, library):
parts = library_path.split(os.sep)
full_path = ""
for i, part in enumerate(parts):
if re.match(r'\w+\.framework', part):
full_path = os.path.join(*parts[i:])
break
if full_path:
new_path = '@executable_path/../Frameworks/%s' % full_path
FixInstallPath(library_path, library, new_path)
def FindQtPlugin(name):
for path in QT_PLUGINS_SEARCH_PATH:
if os.path.exists(path):
if os.path.exists(os.path.join(path, name)):
return os.path.join(path, name)
raise CouldNotFindQtPluginError(name)
def FindGstreamerPlugin(name):
for path in GSTREAMER_SEARCH_PATH:
if os.path.exists(path):
for dir, dirs, files in os.walk(path):
if name in files:
return os.path.join(dir, name)
raise CouldNotFindGstreamerPluginError(name)
def FindGioModule(name):
for path in GIO_MODULES_SEARCH_PATH:
if os.path.exists(path):
for dir, dirs, files in os.walk(path):
if name in files:
return os.path.join(dir, name)
raise CouldNotFindGioModuleError(name)
def main():
logging.basicConfig(filename='macdeploy.log', level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s')
FixBinary(binary)
FixBinary(tagreader_binary)
# macdeployqt needs to handle strawberry-tagreader for Qt deployment, so we can't use FixPlugin() here.
#try:
# FixPlugin('strawberry-tagreader', '.')
#except:
# print('Failed to find blob: %s' % traceback.format_exc())
for plugin in GSTREAMER_PLUGINS:
FixPlugin(FindGstreamerPlugin(plugin), 'gstreamer')
FixPlugin(FindGstreamerPlugin('gst-plugin-scanner'), '.')
FixPlugin(FindGioModule('libgiognutls.so'), 'gio-modules')
#FixPlugin(FindGioModule('libgiognomeproxy.so'), 'gio-modules')
#for plugin in QT_PLUGINS:
#FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
if len(sys.argv) <= 2:
print('Would run %d commands:' % len(commands))
for command in commands:
print(' '.join(command))
#print('OK?')
#raw_input()
for command in commands:
p = subprocess.Popen(command)
os.waitpid(p.pid, 0)
if __name__ == "__main__":
main()

View File

@@ -66,9 +66,10 @@
!define CAPABILITIES_HIDE_ICONS "Command to hide icons"
!define CAPABILITIES_SHOW_ICONS "Command to show icons"
Unicode True
SetCompressor /SOLID lzma
!addplugindir nsisplugins
!include "MUI2.nsh"
!include "FileAssociation.nsh"
!include "Capabilities.nsh"
@@ -79,14 +80,21 @@ SetCompressor /SOLID lzma
!define MUI_COMPONENTSPAGE_SMALLDESC
ReserveFile "${NSISDIR}/Plugins/x86-unicode/LockedList.dll"
ReserveFile "${NSISDIR}/Plugins/LockedList64.dll"
!define LockedListPATH $InstallDir
; Installer pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE COPYING
Page Custom LockedListPageShow
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_CONFIRM
UninstPage custom un.LockedListPageShow
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
@@ -133,7 +141,16 @@ InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallStri
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin
;RequestExecutionLevel user
Function LockedListPageShow
LockedList::AddModule /NOUNLOAD \strawberry.exe
LockedList::Dialog /heading "Checking for running programs:" /noprograms "No programs need to close." /searching "Searching for running programs..."
FunctionEnd
Function un.LockedListPageShow
LockedList::AddModule /NOUNLOAD \strawberry.exe
LockedList::Dialog /heading "Checking for running programs:" /noprograms "No programs need to close." /searching "Searching for running programs..."
FunctionEnd
; Check for previous installation, and call the uninstaller if any
Function CheckPreviousInstall
@@ -169,13 +186,12 @@ SectionEnd
Section "Strawberry" Strawberry
SetOutPath "$INSTDIR"
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
File "strawberry.exe"
File "strawberry-tagreader.exe"
File "strawberry.ico"
File "sqlite3.exe"
File "gst-launch-1.0.exe"
File "gst-discoverer-1.0.exe"
!ifdef arch_x86
File "libgcc_s_sjlj-1.dll"
@@ -192,7 +208,6 @@ Section "Strawberry" Strawberry
File "avcodec-58.dll"
File "avfilter-7.dll"
File "avformat-58.dll"
File "avresample-4.dll"
File "avutil-56.dll"
File "libbrotlicommon.dll"
File "libbrotlidec.dll"
@@ -268,7 +283,7 @@ Section "Strawberry" Strawberry
File "Qt6Network.dll"
File "Qt6Sql.dll"
File "Qt6Widgets.dll"
;File "Qt6WinExtras.dll"
File "Qt6WinExtras.dll"
File "libqtsparkle-qt6.dll"
!else
File "Qt5Concurrent.dll"
@@ -290,8 +305,6 @@ Section "Strawberry" Strawberry
File "libtermcap.dll"
!endif
File "killproc.exe"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
Var /GLOBAL AppExe
@@ -436,8 +449,6 @@ SectionEnd
Section "Uninstall"
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
; Delete all the files
Delete "$INSTDIR\strawberry.ico"
@@ -445,6 +456,7 @@ Section "Uninstall"
Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\sqlite3.exe"
Delete "$INSTDIR\gst-launch-1.0.exe"
Delete "$INSTDIR\gst-discoverer-1.0.exe"
!ifdef arch_x86
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
@@ -461,7 +473,6 @@ Section "Uninstall"
Delete "$INSTDIR\avcodec-58.dll"
Delete "$INSTDIR\avfilter-7.dll"
Delete "$INSTDIR\avformat-58.dll"
Delete "$INSTDIR\avresample-4.dll"
Delete "$INSTDIR\avutil-56.dll"
Delete "$INSTDIR\libbrotlicommon.dll"
Delete "$INSTDIR\libbrotlidec.dll"
@@ -611,7 +622,6 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstrtp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstrtsp.dll"
Delete "$INSTDIR\killproc.exe"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.

View File

@@ -22,6 +22,7 @@
#include <QtGlobal>
#include <cstring>
#include <cmath>
#include <glib.h>
@@ -74,7 +75,7 @@ static void gst_fastspectrum_class_init (GstFastSpectrumClass * klass) {
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass);
GstCaps *caps;
GstCaps *caps = nullptr;
gobject_class->set_property = gst_fastspectrum_set_property;
gobject_class->get_property = gst_fastspectrum_get_property;
@@ -264,7 +265,7 @@ static void input_data_mixed_float(const guint8* _in, double* out, guint len, do
Q_UNUSED(max_value);
guint j, ip = 0;
guint j = 0, ip = 0;
const gfloat *in = reinterpret_cast<const gfloat*>(_in);
for (j = 0; j < len; j++) {
@@ -278,7 +279,7 @@ static void input_data_mixed_double (const guint8 * _in, double* out, guint len,
Q_UNUSED(max_value);
guint j, ip = 0;
guint j = 0, ip = 0;
const gdouble *in = reinterpret_cast<const gdouble*>(_in);
for (j = 0; j < len; j++) {
@@ -290,7 +291,7 @@ static void input_data_mixed_double (const guint8 * _in, double* out, guint len,
static void input_data_mixed_int32_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) {
guint j, ip = 0;
guint j = 0, ip = 0;
const gint32 *in = reinterpret_cast<const gint32*>(_in);
for (j = 0; j < len; j++) {
@@ -302,7 +303,7 @@ static void input_data_mixed_int32_max (const guint8 * _in, double* out, guint l
static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) {
guint j;
guint j = 0;
for (j = 0; j < len; j++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
@@ -322,7 +323,7 @@ static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint l
static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint len, double max_value, guint op, guint nfft) {
guint j, ip = 0;
guint j = 0, ip = 0;
const gint16 *in = reinterpret_cast<const gint16*>(_in);
for (j = 0; j < len; j++) {
@@ -369,7 +370,7 @@ static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInf
static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_pos) {
guint i;
guint i = 0;
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
@@ -379,7 +380,7 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
// Should be safe to execute the same plan multiple times in parallel.
fftw_execute(spectrum->plan);
gdouble val;
gdouble val = 0.0;
/* Calculate magnitude in db */
for (i = 0; i < bands; i++) {
val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0];
@@ -399,13 +400,13 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
double max_value = (1UL << ((bps << 3) - 1)) - 1;
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
guint input_pos;
guint input_pos = 0;
GstMapInfo map;
const guint8 *data;
gsize size;
guint fft_todo, msg_todo, block_size;
gboolean have_full_interval;
GstFastSpectrumInputData input_data;
const guint8 *data = nullptr;
gsize size = 0;
guint fft_todo = 0, msg_todo = 0, block_size = 0;
gboolean have_full_interval = false;
GstFastSpectrumInputData input_data = nullptr;
g_mutex_lock (&spectrum->lock);
gst_buffer_map (buffer, &map, GST_MAP_READ);

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -278,7 +279,7 @@ QString CXXDemangle(const QString &mangled_function);
QString CXXDemangle(const QString &mangled_function) {
int status;
int status = 0;
char* demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
if (status == 0) {
QString ret = QString::fromLatin1(demangled_function);

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -82,7 +83,7 @@ template <typename MT>
class AbstractMessageHandler : public _MessageHandlerBase {
public:
AbstractMessageHandler(QIODevice *device, QObject *parent);
~AbstractMessageHandler() override { AbortAll(); }
~AbstractMessageHandler() override { AbstractMessageHandler::AbortAll(); }
typedef MT MessageType;
typedef MessageReply<MT> ReplyType;

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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
@@ -278,9 +279,9 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
// Create a server, find an unused name and start listening
forever {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
const int unique_number = QRandomGenerator::global()->bounded((int)(quint64(this) & 0xFFFFFFFF));
const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#else
const int unique_number = qrand() ^ ((int)(quint64(this) & 0xFFFFFFFF));
const quint32 unique_number = qrand() ^ (static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#endif
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);

View File

@@ -16,10 +16,10 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-tagreader PRIVATE
${TAGLIB_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common

View File

@@ -1,6 +1,6 @@
/* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
Copyright 2018-2021, 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

View File

@@ -1,6 +1,6 @@
/* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
Copyright 2018-2021, 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

View File

@@ -22,10 +22,10 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_include_directories(strawberry-tagreader PRIVATE
${TAGLIB_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader
${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, 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

View File

@@ -1126,6 +1126,7 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
${GOBJECT_INCLUDE_DIRS}
${GNUTLS_INCLUDE_DIRS}
${SQLITE_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_include_directories(strawberry_lib PUBLIC
@@ -1138,7 +1139,6 @@ target_include_directories(strawberry_lib PUBLIC
${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader
${SINGLEAPPLICATION_INCLUDE_DIRS}
${SINGLECOREAPPLICATION_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_link_libraries(strawberry_lib PUBLIC

View File

@@ -141,7 +141,7 @@ int Analyzer::Base::resizeExponent(int exp) {
int Analyzer::Base::resizeForBands(int bands) {
int exp;
int exp = 0;
if (bands <= 8)
exp = 4;
else if (bands <= 16)

View File

@@ -191,10 +191,11 @@ void AnalyzerContainer::Load() {
}
// Framerate
QList<QAction*> actions = group_framerate_->actions();
for (int i = 0; i < framerate_list_.count(); ++i) {
if (current_framerate_ == framerate_list_[i]) {
ChangeFramerate(current_framerate_);
group_framerate_->actions()[i]->setChecked(true);
actions[i]->setChecked(true);
break;
}
}

View File

@@ -35,12 +35,12 @@
#include "analyzerbase.h"
#include "fht.h"
const uint BlockAnalyzer::kHeight = 2;
const uint BlockAnalyzer::kWidth = 4;
const uint BlockAnalyzer::kMinRows = 3; // arbituary
const uint BlockAnalyzer::kMinColumns = 32; // arbituary
const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const uint BlockAnalyzer::kFadeSize = 90;
const int BlockAnalyzer::kHeight = 2;
const int BlockAnalyzer::kWidth = 4;
const int BlockAnalyzer::kMinRows = 3; // arbituary
const int BlockAnalyzer::kMinColumns = 32; // arbituary
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const int BlockAnalyzer::kFadeSize = 90;
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
@@ -74,11 +74,11 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
background_ = QPixmap(size());
canvas_ = QPixmap(size());
const uint oldRows = rows_;
const int oldRows = rows_;
// all is explained in analyze()..
// +1 to counter -1 in maxSizes, trust me we need this!
columns_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
columns_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
// this is the y-offset for drawing from the top of the widget
@@ -94,9 +94,9 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
yscale_.resize(rows_ + 1);
const uint PRE = 1, PRO = 1; // PRE and PRO allow us to restrict the range somewhat
const int PRE = 1, PRO = 1; // PRE and PRO allow us to restrict the range somewhat
for (uint z = 0; z < rows_; ++z)
for (int z = 0; z < rows_; ++z)
yscale_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
yscale_[rows_] = 0;
@@ -115,7 +115,7 @@ void BlockAnalyzer::determineStep() {
// I calculated the value 30 based on some trial and error
// the fall time of 30 is too slow on framerates above 50fps
const double fallTime = timeout() < 20 ? 20 * rows_ : 30 * rows_;
const double fallTime = static_cast<double>(timeout() < 20 ? 20 * rows_ : 30 * rows_);
step_ = double(rows_ * timeout()) / fallTime;
@@ -164,12 +164,12 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
// Paint the background
canvas_painter.drawPixmap(0, 0, background_);
for (uint y, x = 0; x < scope_.size(); ++x) {
for (int x = 0, y = 0; x < static_cast<int>(scope_.size()); ++x) {
// determine y
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
if (static_cast<float>(y) > store_[x])
if (static_cast<double>(y) > store_[x])
y = static_cast<int>(store_[x] += step_);
else
store_[x] = y;
@@ -182,8 +182,8 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
}
if (fade_intensity_[x] > 0) {
const uint offset = --fade_intensity_[x];
const uint y2 = y_ + (fade_pos_[x] * (kHeight + 1));
const int offset = --fade_intensity_[x];
const int y2 = y_ + (fade_pos_[x] * (kHeight + 1));
canvas_painter.drawPixmap(x * (kWidth + 1), y2, fade_bars_[offset], 0, 0, kWidth, height() - y2);
}
@@ -200,7 +200,7 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
}
static inline void adjustToLimits(int &b, int &f, uint &amount) {
static inline void adjustToLimits(int &b, int &f, int &amount) {
// with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
@@ -235,14 +235,14 @@ static inline void adjustToLimits(int &b, int &f, uint &amount) {
* It won't modify the hue of fg unless absolutely necessary
* @return the adjusted form of fg
*/
QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount = 150);
QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
QColor ensureContrast(const QColor &bg, const QColor &fg, int amount = 150);
QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
class OutputOnExit {
public:
explicit OutputOnExit(const QColor &color) : c(color) {}
~OutputOnExit() {
int h, s, v;
int h = 0, s = 0, v = 0;
c.getHsv(&h, &s, &v);
}
@@ -252,8 +252,8 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
OutputOnExit allocateOnTheStack(fg);
int bh, bs, bv;
int fh, fs, fv;
int bh = 0, bs = 0, bv = 0;
int fh = 0, fs = 0, fv = 0;
bg.getHsv(&bh, &bs, &bv);
fg.getHsv(&fh, &fs, &fv);
@@ -278,9 +278,9 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
// check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast
if (ds > static_cast<int>(amount) / 2 && (bs > 125 && fs > 125))
if (ds > amount / 2 && (bs > 125 && fs > 125))
return fg;
else if (dv > static_cast<int>(amount) / 2 && (bv > 125 && fv > 125))
else if (dv > amount / 2 && (bv > 125 && fv > 125))
return fg;
}
@@ -295,11 +295,11 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
}
// test that there is available value to honor our contrast requirement
if (255 - dv < static_cast<int>(amount)) {
if (255 - dv < amount) {
// we have to modify the value and saturation of fg
// adjustToLimits( bv, fv, amount );
// see if we need to adjust the saturation
if (static_cast<int>(amount) > 0) adjustToLimits(bs, fs, amount);
if (amount > 0) adjustToLimits(bs, fs, amount);
// see if we need to adjust the hue
if (static_cast<int>(amount) > 0)
@@ -312,13 +312,13 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
if (fv < bv && fv > static_cast<int>(amount))
return QColor::fromHsv(fh, fs, fv - static_cast<int>(amount));
return QColor::fromHsv(fh, fs, fv - amount);
if (fv > bv && (255 - fv > static_cast<int>(amount)))
return QColor::fromHsv(fh, fs, fv + static_cast<int>(amount));
return QColor::fromHsv(fh, fs, fv + amount);
if (fv < bv && (255 - bv > static_cast<int>(amount)))
return QColor::fromHsv(fh, fs, bv + static_cast<int>(amount));
return QColor::fromHsv(fh, fs, bv + amount);
return Qt::blue;
}
@@ -338,7 +338,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
bar()->fill(bg);
QPainter p(bar());
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
for (int y = 0; y < rows_; ++y)
// graduate the fg color
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
@@ -347,7 +347,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
// make a complimentary fadebar colour
// TODO dark is not always correct, dumbo!
int h, s, v;
int h = 0, s = 0, v = 0;
palette().color(QPalette::Window).darker(150).getHsv(&h, &s, &v);
const QColor fg2(QColor::fromHsv(h + 120, s, v));
@@ -360,7 +360,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
for (uint y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Window));
QPainter f(&fade_bars_[y]);
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
for (int z = 0; z < rows_; ++z) {
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
f.fillRect(0, z * (kHeight + 1), kWidth, kHeight, QColor(r2 + static_cast<int>(dr2 * Y), g2 + static_cast<int>(dg2 * Y), b2 + static_cast<int>(db2 * Y)));
}
@@ -386,8 +386,8 @@ void BlockAnalyzer::drawBackground() {
if (!p.paintEngine()) return;
for (int x = 0; static_cast<uint>(x) < columns_; ++x)
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
for (int x = 0; x < columns_; ++x)
for (int y = 0; y < rows_; ++y)
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
}

View File

@@ -43,12 +43,12 @@ class BlockAnalyzer : public Analyzer::Base {
public:
Q_INVOKABLE explicit BlockAnalyzer(QWidget*);
static const uint kHeight;
static const uint kWidth;
static const uint kMinRows;
static const uint kMinColumns;
static const uint kMaxColumns;
static const uint kFadeSize;
static const int kHeight;
static const int kWidth;
static const int kMinRows;
static const int kMinColumns;
static const int kMaxColumns;
static const int kFadeSize;
static const char *kName;
@@ -65,21 +65,21 @@ class BlockAnalyzer : public Analyzer::Base {
private:
QPixmap *bar() { return &barpixmap_; }
uint columns_, rows_; // number of rows and columns of blocks
uint y_; // y-offset from top of widget
int columns_, rows_; // number of rows and columns of blocks
int y_; // y-offset from top of widget
QPixmap barpixmap_;
QPixmap topbarpixmap_;
QPixmap background_;
QPixmap canvas_;
Analyzer::Scope scope_; // so we don't create a vector every frame
QVector<float> store_; // current bar heights
QVector<float> yscale_;
QVector<double> store_; // current bar heights
QVector<double> yscale_;
QVector<QPixmap> fade_bars_;
QVector<uint> fade_pos_;
QVector<int> fade_pos_;
QVector<int> fade_intensity_;
float step_; // rows to fall per frame
double step_; // rows to fall per frame
};
#endif // BLOCKANALYZER_H

View File

@@ -39,9 +39,9 @@
using Analyzer::Scope;
const uint BoomAnalyzer::kColumnWidth = 4;
const uint BoomAnalyzer::kMaxBandCount = 256;
const uint BoomAnalyzer::kMinBandCount = 32;
const int BoomAnalyzer::kColumnWidth = 4;
const int BoomAnalyzer::kMaxBandCount = 256;
const int BoomAnalyzer::kMinBandCount = 32;
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
@@ -80,10 +80,10 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
bands_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_);
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
F_ = double(HEIGHT) / (log10(256) * double(1.1) /*<- max. amplitude*/);
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());
@@ -106,7 +106,7 @@ void BoomAnalyzer::transform(Scope& s) {
fht_->spectrum(s.data());
fht_->scale(s.data(), 1.0 / 50);
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2 : scope_.size());
s.resize(scope_.size() <= static_cast<quint64>(kMaxBandCount) / 2 ? kMaxBandCount / 2 : scope_.size());
}
@@ -116,7 +116,7 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
p.drawPixmap(0, 0, canvas_);
return;
}
float h;
double h = 0.0;
const uint MAX_HEIGHT = height() - 1;
QPainter canvas_painter(&canvas_);
@@ -124,7 +124,7 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
Analyzer::interpolate(scope, scope_);
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
for (int i = 0, x = 0, y = 0; i < bands_; ++i, x += kColumnWidth + 1) {
h = log10(scope_[i] * 256.0) * F_;
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
@@ -157,13 +157,13 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
}
}
y = height() - uint(bar_height_[i]);
y = height() - static_cast<int>(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0)
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
y = height() - uint(peak_height_[i]);
y = height() - static_cast<int>(peak_height_[i]);
canvas_painter.setPen(palette().color(QPalette::Midlight));
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
}

View File

@@ -54,19 +54,19 @@ class BoomAnalyzer : public Analyzer::Base {
protected:
void resizeEvent(QResizeEvent *e) override;
static const uint kColumnWidth;
static const uint kMaxBandCount;
static const uint kMinBandCount;
static const int kColumnWidth;
static const int kMaxBandCount;
static const int kMinBandCount;
uint bands_;
int bands_;
Analyzer::Scope scope_;
QColor fg_;
double K_barHeight_, F_peakSpeed_, F_;
std::vector<float> bar_height_;
std::vector<float> peak_height_;
std::vector<float> peak_speed_;
std::vector<double> bar_height_;
std::vector<double> peak_height_;
std::vector<double> peak_speed_;
QPixmap barPixmap_;
QPixmap canvas_;

View File

@@ -68,7 +68,7 @@ void FHT::ewma(float* d, float* s, float w) {
void FHT::logSpectrum(float* out, float* p) {
int n = num_ / 2, i, j, k, *r;
int n = num_ / 2, i = 0, j = 0, k = 0, *r = nullptr;
if (log_vector_.size() < n) {
log_vector_.resize(n);
float f = n / log10(static_cast<double>(n));
@@ -135,8 +135,8 @@ void FHT::transform(float* p) {
void FHT::transform8(float* p) {
float a, b, c, d, e, f, g, h, b_f2, d_h2;
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
float a = 0.0, b = 0.0, c = 0.0, d = 0.0, e = 0.0, f = 0.0, g = 0.0, h = 0.0, b_f2 = 0.0, d_h2 = 0.0;
float a_c_eg = 0.0, a_ce_g = 0.0, ac_e_g = 0.0, aceg = 0.0, b_df_h = 0.0, bdfh = 0.0;
a = *p++, b = *p++, c = *p++, d = *p++;
e = *p++, f = *p++, g = *p++, h = *p;
@@ -169,8 +169,8 @@ void FHT::_transform(float* p, int n, int k) {
return;
}
int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
int i = 0, j = 0, ndiv2 = n / 2;
float a = 0.0, *t1 = nullptr, *t2 = nullptr, *t3 = nullptr, *t4 = nullptr, *ptab = nullptr, *pp = nullptr;
for (i = 0, t1 = buf_(), t2 = buf_() + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++;

View File

@@ -112,7 +112,7 @@ void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
// Discard the second half of the transform
const int scope_size = s.size() / 2;
const int scope_size = static_cast<int>(s.size() / 2);
if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
// Transform the music into rainbows!

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -597,7 +597,13 @@ void CollectionBackend::MarkSongsUnavailable(const SongList &songs, const bool u
}
transaction.Commit();
emit SongsDeleted(songs);
if (unavailable) {
emit SongsDeleted(songs);
}
else {
emit SongsDiscovered(songs);
}
UpdateTotalSongCountAsync();
UpdateTotalArtistCountAsync();
UpdateTotalAlbumCountAsync();
@@ -606,12 +612,14 @@ void CollectionBackend::MarkSongsUnavailable(const SongList &songs, const bool u
QStringList CollectionBackend::GetAll(const QString &column, const QueryOptions &opt) {
CollectionQuery query(opt);
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.SetColumnSpec("DISTINCT " + column);
query.AddCompilationRequirement(false);
QMutexLocker l(db_->Mutex());
if (!ExecQuery(&query)) return QStringList();
if (!query.Exec()) return QStringList();
QStringList ret;
while (query.Next()) {
@@ -628,24 +636,24 @@ QStringList CollectionBackend::GetAllArtists(const QueryOptions &opt) {
QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
// Albums with 'albumartist' field set:
CollectionQuery query(opt);
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.SetColumnSpec("DISTINCT albumartist");
query.AddCompilationRequirement(false);
query.AddWhere("album", "", "!=");
// Albums with no 'albumartist' (extract 'artist'):
CollectionQuery query2(opt);
CollectionQuery query2(db, songs_table_, fts_table_, opt);
query2.SetColumnSpec("DISTINCT artist");
query2.AddCompilationRequirement(false);
query2.AddWhere("album", "", "!=");
query2.AddWhere("albumartist", "", "=");
{
QMutexLocker l(db_->Mutex());
if (!ExecQuery(&query) || !ExecQuery(&query2)) {
return QStringList();
}
if (!query.Exec() || !query2.Exec()) {
return QStringList();
}
QSet<QString> artists;
@@ -671,28 +679,40 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbumsByArtist(const QString
SongList CollectionBackend::GetArtistSongs(const QString &effective_albumartist, const QueryOptions &opt) {
CollectionQuery query(opt);
QSqlDatabase db(db_->Connect());
QMutexLocker l(db_->Mutex());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.AddCompilationRequirement(false);
query.AddWhere("effective_albumartist", effective_albumartist);
return ExecCollectionQuery(&query);
}
SongList CollectionBackend::GetAlbumSongs(const QString &effective_albumartist, const QString &album, const QueryOptions &opt) {
CollectionQuery query(opt);
QSqlDatabase db(db_->Connect());
QMutexLocker l(db_->Mutex());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.AddCompilationRequirement(false);
query.AddWhere("effective_albumartist", effective_albumartist);
query.AddWhere("album", album);
return ExecCollectionQuery(&query);
}
SongList CollectionBackend::GetSongsByAlbum(const QString &album, const QueryOptions &opt) {
CollectionQuery query(opt);
QSqlDatabase db(db_->Connect());
QMutexLocker l(db_->Mutex());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.AddCompilationRequirement(false);
query.AddWhere("album", album);
return ExecCollectionQuery(&query);
}
@@ -700,8 +720,8 @@ SongList CollectionBackend::GetSongsByAlbum(const QString &album, const QueryOpt
SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
query->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
QMutexLocker l(db_->Mutex());
if (!ExecQuery(query)) return SongList();
if (!query->Exec()) return SongList();
SongList ret;
while (query->Next()) {
@@ -714,12 +734,15 @@ SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
}
Song CollectionBackend::GetSongById(const int id) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
return GetSongById(id, db);
}
SongList CollectionBackend::GetSongsById(const QList<int> &ids) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -729,13 +752,16 @@ SongList CollectionBackend::GetSongsById(const QList<int> &ids) {
}
return GetSongsById(str_ids, db);
}
SongList CollectionBackend::GetSongsById(const QStringList &ids) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
return GetSongsById(ids, db);
}
SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column) {
@@ -752,7 +778,7 @@ SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QS
QVector<Song> ret(ids.count());
while (q.next()) {
const QString foreign_id = q.value(Song::kColumns.count() + 1).toString();
const QString foreign_id = q.value(static_cast<int>(Song::kColumns.count()) + 1).toString();
const int index = ids.indexOf(foreign_id);
if (index == -1) continue;
@@ -763,9 +789,11 @@ SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QS
}
Song CollectionBackend::GetSongById(const int id, QSqlDatabase &db) {
SongList list = GetSongsById(QStringList() << QString::number(id), db);
if (list.isEmpty()) return Song();
return list.first();
}
SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &db) {
@@ -891,13 +919,15 @@ CollectionBackend::AlbumList CollectionBackend::GetCompilationAlbums(const Query
SongList CollectionBackend::GetCompilationSongs(const QString &album, const QueryOptions &opt) {
CollectionQuery query(opt);
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddCompilationRequirement(true);
query.AddWhere("album", album);
QMutexLocker l(db_->Mutex());
if (!ExecQuery(&query)) return SongList();
if (!query.Exec()) return SongList();
SongList ret;
while (query.Next()) {
@@ -1016,7 +1046,10 @@ void CollectionBackend::UpdateCompilations(QSqlQuery &find_song, QSqlQuery &upda
CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt) {
CollectionQuery query(opt);
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
CollectionQuery query(db, songs_table_, fts_table_, opt);
query.SetColumnSpec("url, effective_albumartist, album, compilation_effective, art_automatic, art_manual, filetype, cue_path");
query.SetOrderBy("effective_albumartist, album, url");
@@ -1028,10 +1061,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
query.AddWhere("effective_albumartist", artist);
}
{
QMutexLocker l(db_->Mutex());
if (!ExecQuery(&query)) return AlbumList();
}
if (!query.Exec()) return AlbumList();
QMap<QString, Album> albums;
while (query.Next()) {
@@ -1094,19 +1124,21 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
CollectionBackend::Album CollectionBackend::GetAlbumArt(const QString &effective_albumartist, const QString &album) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
Album ret;
ret.album = album;
ret.album_artist = effective_albumartist;
CollectionQuery query = CollectionQuery(QueryOptions());
CollectionQuery query(db, songs_table_, fts_table_, QueryOptions());
query.SetColumnSpec("art_automatic, art_manual, url");
if (!effective_albumartist.isEmpty()) {
query.AddWhere("effective_albumartist", effective_albumartist);
}
query.AddWhere("album", album);
QMutexLocker l(db_->Mutex());
if (!ExecQuery(&query)) return ret;
if (!query.Exec()) return ret;
if (query.Next()) {
ret.art_automatic = QUrl::fromEncoded(query.Value(0).toByteArray());
@@ -1130,12 +1162,12 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &effective_albumartis
QSqlDatabase db(db_->Connect());
// Get the songs before they're updated
CollectionQuery query;
CollectionQuery query(db, songs_table_, fts_table_);
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
query.AddWhere("effective_albumartist", effective_albumartist);
query.AddWhere("album", album);
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
SongList deleted_songs;
while (query.Next()) {
@@ -1161,7 +1193,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &effective_albumartis
db_->CheckErrors(q);
// Now get the updated songs
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
SongList added_songs;
while (query.Next()) {
@@ -1189,12 +1221,12 @@ void CollectionBackend::UpdateAutomaticAlbumArt(const QString &effective_albumar
QSqlDatabase db(db_->Connect());
// Get the songs before they're updated
CollectionQuery query;
CollectionQuery query(db, songs_table_, fts_table_);
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
query.AddWhere("effective_albumartist", effective_albumartist);
query.AddWhere("album", album);
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
SongList deleted_songs;
while (query.Next()) {
@@ -1216,7 +1248,7 @@ void CollectionBackend::UpdateAutomaticAlbumArt(const QString &effective_albumar
db_->CheckErrors(q);
// Now get the updated songs
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
SongList added_songs;
while (query.Next()) {
@@ -1240,12 +1272,12 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
for (const QString &artist : artists) {
// Get the songs before they're updated
CollectionQuery query;
CollectionQuery query(db, songs_table_, fts_table_);
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
query.AddWhere("album", album);
if (!artist.isEmpty()) query.AddWhere("artist", artist);
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
while (query.Next()) {
Song song(source_);
@@ -1268,7 +1300,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
db_->CheckErrors(q);
// Now get the updated songs
if (!ExecQuery(&query)) return;
if (!query.Exec()) return;
while (query.Next()) {
Song song(source_);
@@ -1284,10 +1316,6 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
}
bool CollectionBackend::ExecQuery(CollectionQuery *q) {
return !db_->CheckErrors(q->Exec(db_->Connect(), songs_table_, fts_table_));
}
void CollectionBackend::IncrementPlayCount(const int id) {
if (id == -1) return;
@@ -1478,7 +1506,7 @@ void CollectionBackend::UpdatePlayCount(const QString &artist, const QString &ti
}
void CollectionBackend::UpdateSongRating(const int id, const float rating) {
void CollectionBackend::UpdateSongRating(const int id, const double rating) {
if (id == -1) return;
@@ -1488,7 +1516,7 @@ void CollectionBackend::UpdateSongRating(const int id, const float rating) {
}
void CollectionBackend::UpdateSongsRating(const QList<int> &id_list, const float rating) {
void CollectionBackend::UpdateSongsRating(const QList<int> &id_list, const double rating) {
if (id_list.isEmpty()) return;
@@ -1510,10 +1538,10 @@ void CollectionBackend::UpdateSongsRating(const QList<int> &id_list, const float
}
void CollectionBackend::UpdateSongRatingAsync(const int id, const float rating) {
metaObject()->invokeMethod(this, "UpdateSongRating", Qt::QueuedConnection, Q_ARG(int, id), Q_ARG(float, rating));
void CollectionBackend::UpdateSongRatingAsync(const int id, const double rating) {
metaObject()->invokeMethod(this, "UpdateSongRating", Qt::QueuedConnection, Q_ARG(int, id), Q_ARG(double, rating));
}
void CollectionBackend::UpdateSongsRatingAsync(const QList<int>& ids, const float rating) {
metaObject()->invokeMethod(this, "UpdateSongsRating", Qt::QueuedConnection, Q_ARG(QList<int>, ids), Q_ARG(float, rating));
void CollectionBackend::UpdateSongsRatingAsync(const QList<int>& ids, const double rating) {
metaObject()->invokeMethod(this, "UpdateSongsRating", Qt::QueuedConnection, Q_ARG(QList<int>, ids), Q_ARG(double, rating));
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -71,6 +71,9 @@ class CollectionBackendInterface : public QObject {
typedef QList<Album> AlbumList;
virtual QString songs_table() const = 0;
virtual QString fts_table() const = 0;
virtual Database *db() const = 0;
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
virtual void LoadDirectoriesAsync() = 0;
@@ -111,8 +114,6 @@ class CollectionBackendInterface : public QObject {
virtual void AddDirectory(const QString &path) = 0;
virtual void RemoveDirectory(const Directory &dir) = 0;
virtual bool ExecQuery(CollectionQuery *q) = 0;
};
class CollectionBackend : public CollectionBackendInterface {
@@ -127,9 +128,10 @@ class CollectionBackend : public CollectionBackendInterface {
void ExitAsync();
Database *db() const { return db_; }
Database *db() const override { return db_; }
QString songs_table() const override { return songs_table_; }
QString fts_table() const override { return fts_table_; }
QString dirs_table() const { return dirs_table_; }
QString subdirs_table() const { return subdirs_table_; }
@@ -174,7 +176,6 @@ class CollectionBackend : public CollectionBackendInterface {
void AddDirectory(const QString &path) override;
void RemoveDirectory(const Directory &dir) override;
bool ExecQuery(CollectionQuery *q) override;
SongList ExecCollectionQuery(CollectionQuery *query);
void IncrementPlayCountAsync(const int id);
@@ -193,8 +194,8 @@ class CollectionBackend : public CollectionBackendInterface {
void AddOrUpdateSongsAsync(const SongList &songs);
void UpdateSongRatingAsync(const int id, const float rating);
void UpdateSongsRatingAsync(const QList<int> &ids, const float rating);
void UpdateSongRatingAsync(const int id, const double rating);
void UpdateSongsRatingAsync(const QList<int> &ids, const double rating);
public slots:
void Exit();
@@ -220,8 +221,8 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
void UpdatePlayCount(const QString &artist, const QString &title, const int playcount);
void UpdateSongRating(const int id, const float rating);
void UpdateSongsRating(const QList<int> &id_list, const float rating);
void UpdateSongRating(const int id, const double rating);
void UpdateSongsRating(const QList<int> &id_list, const double rating);
signals:
void DirectoryDiscovered(Directory, SubdirectoryList);

View File

@@ -303,7 +303,8 @@ void CollectionFilterWidget::SetCollectionModel(CollectionModel *model) {
QObject::disconnect(model_, nullptr, this, nullptr);
QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr);
QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr);
for (QAction *action : filter_ages_.keys()) {
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
}
}
@@ -315,7 +316,8 @@ void CollectionFilterWidget::SetCollectionModel(CollectionModel *model) {
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
for (QAction *action : filter_ages_.keys()) {
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
int age = filter_ages_[action];
QObject::connect(action, &QAction::triggered, [this, age]() { model_->SetFilterAge(age); } );
}
@@ -382,7 +384,9 @@ void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Groupin
}
// Check the advanced action
group_by_group_->actions().last()->setChecked(true);
QList<QAction*> actions = group_by_group_->actions();
QAction *action = actions.last();
action->setChecked(true);
}

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -70,9 +70,6 @@
#include "covermanager/albumcoverloaderresult.h"
#include "settings/collectionsettingspage.h"
using std::placeholders::_1;
using std::placeholders::_2;
const char *CollectionModel::kSavedGroupingsSettingsGroup = "SavedGroupings";
const int CollectionModel::kPrettyCoverSize = 32;
const char *CollectionModel::kPixmapDiskCacheDir = "pixmapcache";
@@ -103,6 +100,7 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
group_by_[2] = GroupBy_None;
cover_loader_options_.get_image_data_ = false;
cover_loader_options_.get_image_ = true;
cover_loader_options_.scale_output_image_ = true;
cover_loader_options_.pad_output_image_ = true;
cover_loader_options_.desired_height_ = kPrettyCoverSize;
@@ -113,7 +111,8 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
QIcon nocover = IconLoader::Load("cdcase");
if (!nocover.isNull()) {
no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QList<QSize> nocover_sizes = nocover.availableSizes();
no_cover_icon_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
if (app_ && !sIconCache) {
@@ -184,7 +183,7 @@ void CollectionModel::ReloadSettings() {
use_disk_cache_ = s.value(CollectionSettingsPage::kSettingsDiskCacheEnable, false).toBool();
QPixmapCache::setCacheLimit(MaximumCacheSize(&s, CollectionSettingsPage::kSettingsCacheSize, CollectionSettingsPage::kSettingsCacheSizeUnit, CollectionSettingsPage::kSettingsCacheSizeDefault) / 1024);
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&s, CollectionSettingsPage::kSettingsCacheSize, CollectionSettingsPage::kSettingsCacheSizeUnit, CollectionSettingsPage::kSettingsCacheSizeDefault) / 1024));
if (sIconCache) {
sIconCache->setMaximumCacheSize(MaximumCacheSize(&s, CollectionSettingsPage::kSettingsDiskCacheSize, CollectionSettingsPage::kSettingsDiskCacheSizeUnit, CollectionSettingsPage::kSettingsDiskCacheSizeDefault));
@@ -381,10 +380,10 @@ QString CollectionModel::ContainerKey(const GroupBy type, const Song &song) cons
}
else {
if (song.bitdepth() <= 0) {
key = QString("%1 (%2)").arg(song.TextForFiletype()).arg(QString::number(song.samplerate() / 1000.0, 'G', 5));
key = QString("%1 (%2)").arg(song.TextForFiletype(), QString::number(song.samplerate() / 1000.0, 'G', 5));
}
else {
key = QString("%1 (%2/%3)").arg(song.TextForFiletype()).arg(QString::number(song.samplerate() / 1000.0, 'G', 5)).arg(song.bitdepth());
key = QString("%1 (%2/%3)").arg(song.TextForFiletype(), QString::number(song.samplerate() / 1000.0, 'G', 5)).arg(song.bitdepth());
}
}
break;
@@ -420,7 +419,8 @@ QString CollectionModel::DividerKey(const GroupBy type, CollectionItem *item) co
if (c.isDigit()) return "0";
if (c == ' ') return QString();
if (c.decompositionTag() != QChar::NoDecomposition) {
return QChar(c.decomposition()[0]);
QString decomposition = c.decomposition();
return QChar(decomposition[0]);
}
return c;
}
@@ -586,12 +586,13 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
}
// Delete empty dividers
for (const QString &divider_key : divider_keys) {
for (const QString &divider_key : qAsConst(divider_keys)) {
if (!divider_nodes_.contains(divider_key)) continue;
// Look to see if there are any other items still under this divider
bool found = false;
for (CollectionItem *node : container_nodes_[0].values()) {
QList<CollectionItem*> container_nodes = container_nodes_[0].values();
for (CollectionItem *node : container_nodes) {
if (DividerKey(group_by_[0], node) == divider_key) {
found = true;
break;
@@ -814,14 +815,20 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const
}
bool CollectionModel::HasCompilations(const CollectionQuery &query) {
bool CollectionModel::HasCompilations(const QSqlDatabase &db, const CollectionQuery &query) {
CollectionQuery q = query;
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_);
q.SetColumnSpec(query.column_spec());
q.SetOrderBy(query.order_by());
q.SetWhereClauses(query.where_clauses());
q.SetBoundValues(query.bound_values());
q.SetIncludeUnavailable(query.include_unavailable());
q.SetDuplicatesOnly(query.duplicates_only());
q.AddCompilationRequirement(true);
q.SetLimit(1);
QMutexLocker l(backend_->db()->Mutex());
if (!backend_->ExecQuery(&q)) return false;
if (!q.Exec()) return false;
return q.Next();
@@ -836,33 +843,39 @@ CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) {
GroupBy child_type = child_level >= 3 ? GroupBy_None : group_by_[child_level];
// Initialize the query. child_type says what type of thing we want (artists, songs, etc.)
CollectionQuery q(query_options_);
InitQuery(child_type, &q);
// Walk up through the item's parents adding filters as necessary
CollectionItem *p = parent;
while (p && p->type == CollectionItem::Type_Container) {
FilterQuery(group_by_[p->container_level], p, &q);
p = p->parent;
}
{
QMutexLocker l(backend_->db()->Mutex());
QSqlDatabase db(backend_->db()->Connect());
// Artists GroupBy is special - we don't want compilation albums appearing
if (IsArtistGroupBy(child_type)) {
// Add the special Various artists node
if (show_various_artists_ && HasCompilations(q)) {
result.create_va = true;
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_);
InitQuery(child_type, &q);
// Walk up through the item's parents adding filters as necessary
CollectionItem *p = parent;
while (p && p->type == CollectionItem::Type_Container) {
FilterQuery(group_by_[p->container_level], p, &q);
p = p->parent;
}
// Don't show compilations again outside the Various artists node
q.AddCompilationRequirement(false);
}
// Artists GroupBy is special - we don't want compilation albums appearing
if (IsArtistGroupBy(child_type)) {
// Add the special Various artists node
if (show_various_artists_ && HasCompilations(db, q)) {
result.create_va = true;
}
// Execute the query
QMutexLocker l(backend_->db()->Mutex());
if (backend_->ExecQuery(&q)) {
while (q.Next()) {
result.rows << SqlRow(q);
// Don't show compilations again outside the Various artists node
q.AddCompilationRequirement(false);
}
// Execute the query
if (q.Exec()) {
while (q.Next()) {
result.rows << SqlRow(q);
}
}
}
if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) {
@@ -1769,7 +1782,7 @@ void CollectionModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls, Son
const_cast<CollectionModel*>(this)->LazyPopulate(item);
QList<CollectionItem*> children = item->children;
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, this, _1, _2));
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
for (CollectionItem *child : children)
GetChildSongs(child, urls, songs, song_ids);
@@ -1910,7 +1923,7 @@ QDataStream &operator<<(QDataStream &s, const CollectionModel::Grouping &g) {
QDataStream &operator>>(QDataStream &s, CollectionModel::Grouping &g) {
quint32 buf;
quint32 buf = 0;
s >> buf;
g.first = CollectionModel::GroupBy(buf);
s >> buf;

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -195,6 +195,9 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void ExpandAll(CollectionItem *item = nullptr) const;
const CollectionModel::Grouping &GetGroupBy() const { return group_by_; }
void SetGroupBy(const CollectionModel::Grouping &g);
signals:
void TotalSongCountUpdated(int count);
void TotalArtistCountUpdated(int count);
@@ -206,8 +209,6 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void SetFilterText(const QString &text);
void SetFilterQueryMode(QueryOptions::QueryMode query_mode);
void SetGroupBy(const CollectionModel::Grouping &g);
const CollectionModel::Grouping &GetGroupBy() const { return group_by_; }
void Init(const bool async = true);
void Reset();
void ResetAsync();
@@ -238,7 +239,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
QueryResult RunQuery(CollectionItem *parent);
void PostQuery(CollectionItem *parent, const QueryResult &result, const bool signal);
bool HasCompilations(const CollectionQuery &query);
bool HasCompilations(const QSqlDatabase &db, const CollectionQuery &query);
void BeginReset();

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -42,7 +42,7 @@ class CollectionPlaylistItem : public PlaylistItem {
Song Metadata() const override;
Song OriginalMetadata() const override { return song_; }
void SetMetadata(const Song &song) { song_ = song; }
void SetMetadata(const Song &song) override { song_ = song; }
QUrl Url() const override;

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
@@ -30,14 +31,23 @@
#include <QRegularExpression>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include "core/logging.h"
#include "core/song.h"
#include "collectionquery.h"
#include "core/song.h"
QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {}
CollectionQuery::CollectionQuery(const QueryOptions &options)
: include_unavailable_(false), join_with_fts_(false), limit_(-1) {
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const QueryOptions &options) :
QSqlQuery(db),
songs_table_(songs_table),
fts_table_(fts_table),
include_unavailable_(false),
join_with_fts_(false),
duplicates_only_(false),
limit_(-1) {
if (!options.filter().isEmpty()) {
// We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS5:
@@ -91,13 +101,13 @@ CollectionQuery::CollectionQuery(const QueryOptions &options)
}
if (options.max_age() != -1) {
int cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - options.max_age();
qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - options.max_age();
where_clauses_ << "ctime > ?";
bound_values_ << cutoff;
}
// TODO: Currently you cannot use any QueryMode other than All and fts at the same time.
// TODO: Currently you cannot use any QueryMode other than All and FTS at the same time.
// Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
// The query takes about 20 seconds on my machine then. Why?
// Untagged mode could work with additional filtering but I'm disabling it just to be consistent
@@ -122,7 +132,7 @@ QString CollectionQuery::GetInnerQuery() {
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
// ignore 'literal' for IN
// Ignore 'literal' for IN
if (!op.compare("IN", Qt::CaseInsensitive)) {
QStringList final;
for (const QString &single_value : value.toStringList()) {
@@ -167,7 +177,7 @@ void CollectionQuery::AddWhereArtist(const QVariant &value) {
}
void CollectionQuery::AddCompilationRequirement(bool compilation) {
void CollectionQuery::AddCompilationRequirement(const bool compilation) {
// The unary + is added to prevent sqlite from using the index idx_comp_artist.
// When joining with fts, sqlite 3.8 has a tendency to use this index and thereby nesting the tables in an order which gives very poor performance
@@ -175,15 +185,15 @@ void CollectionQuery::AddCompilationRequirement(bool compilation) {
}
QSqlQuery CollectionQuery::Exec(QSqlDatabase db, const QString &songs_table, const QString &fts_table) {
bool CollectionQuery::Exec() {
QString sql;
if (join_with_fts_) {
sql = QString("SELECT %1 FROM %2 INNER JOIN %3 AS fts ON %2.ROWID = fts.ROWID").arg(column_spec_, songs_table, fts_table);
sql = QString("SELECT %1 FROM %2 INNER JOIN %3 AS fts ON %2.ROWID = fts.ROWID").arg(column_spec_, songs_table_, fts_table_);
}
else {
sql = QString("SELECT %1 FROM %2 %3").arg(column_spec_, songs_table, GetInnerQuery());
sql = QString("SELECT %1 FROM %2 %3").arg(column_spec_, songs_table_, GetInnerQuery());
}
QStringList where_clauses(where_clauses_);
@@ -197,31 +207,40 @@ QSqlQuery CollectionQuery::Exec(QSqlDatabase db, const QString &songs_table, con
if (limit_ != -1) sql += " LIMIT " + QString::number(limit_);
sql.replace("%songs_table", songs_table);
sql.replace("%fts_table_noprefix", fts_table.section('.', -1, -1));
sql.replace("%fts_table", fts_table);
sql.replace("%songs_table", songs_table_);
sql.replace("%fts_table_noprefix", fts_table_.section('.', -1, -1));
sql.replace("%fts_table", fts_table_);
query_ = QSqlQuery(db);
query_.prepare(sql);
prepare(sql);
// Bind values
for (const QVariant &value : bound_values_) {
query_.addBindValue(value);
addBindValue(value);
}
query_.exec();
return query_;
const bool result = exec();
if (!result) {
QSqlError last_error = lastError();
if (last_error.isValid()) {
qLog(Error) << "DB error: " << last_error;
qLog(Error) << "Faulty query: " << lastQuery();
qLog(Error) << "Bound values: " << boundValues();
}
}
return result;
}
bool CollectionQuery::Next() { return query_.next(); }
bool CollectionQuery::Next() { return next(); }
QVariant CollectionQuery::Value(int column) const { return query_.value(column); }
QVariant CollectionQuery::Value(const int column) const { return value(column); }
bool QueryOptions::Matches(const Song &song) const {
if (max_age_ != -1) {
const uint cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
const qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
if (song.ctime() <= cutoff) return false;
}

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
@@ -38,7 +39,7 @@ struct QueryOptions {
// - use the all songs table
// - use the duplicated songs view; by duplicated we mean those songs for which the (artist, album, title) tuple is found more than once in the songs table
// - use the untagged songs view; by untagged we mean those for which at least one of the (artist, album, title) tags is empty
// Please note that additional filtering based on fts table (the filter attribute) won't work in Duplicates and Untagged modes.
// Please note that additional filtering based on FTS table (the filter attribute) won't work in Duplicates and Untagged modes.
enum QueryMode {
QueryMode_All,
QueryMode_Duplicates,
@@ -70,12 +71,13 @@ struct QueryOptions {
QueryMode query_mode_;
};
class CollectionQuery {
class CollectionQuery : public QSqlQuery {
public:
explicit CollectionQuery(const QueryOptions &options = QueryOptions());
explicit CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const QueryOptions &options = QueryOptions());
// Sets contents of SELECT clause on the query (list of columns to get).
void SetColumnSpec(const QString &spec) { column_spec_ = spec; }
// Sets an ORDER BY clause on the query.
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
@@ -84,29 +86,42 @@ class CollectionQuery {
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
void AddWhereArtist(const QVariant &value);
void AddCompilationRequirement(bool compilation);
void SetLimit(int limit) { limit_ = limit; }
void SetIncludeUnavailable(bool include_unavailable) { include_unavailable_ = include_unavailable; }
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
void SetBoundValues(const QVariantList &bound_values) { bound_values_ = bound_values; }
void SetDuplicatesOnly(const bool duplicates_only) { duplicates_only_ = duplicates_only; }
void SetIncludeUnavailable(const bool include_unavailable) { include_unavailable_ = include_unavailable; }
void SetLimit(const int limit) { limit_ = limit; }
void AddCompilationRequirement(const bool compilation);
QSqlQuery Exec(QSqlDatabase db, const QString &songs_table, const QString &fts_table);
bool Exec();
bool Next();
QVariant Value(int column) const;
QVariant Value(const int column) const;
operator const QSqlQuery &() const { return query_; }
QString column_spec() const { return column_spec_; }
QString order_by() const { return order_by_; }
QStringList where_clauses() const { return where_clauses_; }
QVariantList bound_values() const { return bound_values_; }
bool include_unavailable() const { return include_unavailable_; }
bool join_with_fts() const { return join_with_fts_; }
bool duplicates_only() const { return duplicates_only_; }
int limit() const { return limit_; }
private:
QString GetInnerQuery();
bool include_unavailable_;
bool join_with_fts_;
QSqlDatabase db_;
QString songs_table_;
QString fts_table_;
QString column_spec_;
QString order_by_;
QStringList where_clauses_;
QVariantList bound_values_;
int limit_;
bool duplicates_only_;
QSqlQuery query_;
bool include_unavailable_;
bool join_with_fts_;
bool duplicates_only_;
int limit_;
};
#endif // COLLECTIONQUERY_H

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
@@ -197,15 +198,15 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
case CollectionItem::Type_Divider: {
QString text = model()->data(current, CollectionModel::Role_SortText).toString();
if (!last_selected_container_.isEmpty() && last_selected_container_ == text) {
emit expand(current);
expand(current);
setCurrentIndex(current);
return true;
}
else if (last_selected_path_.contains(text)) {
emit expand(current);
expand(current);
// If a selected container or song were not found, we've got into a wrong subtree (happens with "unknown" all the time)
if (!RestoreLevelFocus(current)) {
emit collapse(current);
collapse(current);
}
else {
return true;
@@ -462,7 +463,8 @@ void CollectionView::SetShowInVarious(const bool on) {
// If we have only one album and we are putting it into Various Artists, check to see
// if there are other Artists in this album and prompt the user if they'd like them moved, too
if (on && albums.keys().count() == 1) {
const QString album = albums.keys().first();
const QStringList albums_list = albums.keys();
const QString album = albums_list.first();
QList<Song> all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
QSet<QString> other_artists;
for (const Song &s : all_of_album) {
@@ -480,10 +482,11 @@ void CollectionView::SetShowInVarious(const bool on) {
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
for (const QString &album : QSet<QString>(albums.keyBegin(), albums.keyEnd())) {
QSet<QString> albums_set = QSet<QString>(albums.keyBegin(), albums.keyEnd());
#else
for (const QString &album : QSet<QString>::fromList(albums.keys())) {
QSet<QString> albums_set = QSet<QString>::fromList(albums.keys());
#endif
for (const QString &album : albums_set) {
app_->collection_backend()->ForceCompilation(album, albums.values(album), on);
}

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -110,6 +110,45 @@ void CollectionWatcher::Exit() {
}
void CollectionWatcher::ReloadSettingsAsync() {
QMetaObject::invokeMethod(this, "ReloadSettings", Qt::QueuedConnection);
}
void CollectionWatcher::ReloadSettings() {
const bool was_monitoring_before = monitor_;
QSettings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
scan_on_startup_ = s.value("startup_scan", true).toBool();
monitor_ = s.value("monitor", true).toBool();
mark_songs_unavailable_ = s.value("mark_songs_unavailable", false).toBool();
QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList();
s.endGroup();
best_image_filters_.clear();
for (const QString &filter : filters) {
QString str = filter.trimmed();
if (!str.isEmpty()) best_image_filters_ << str;
}
if (!monitor_ && was_monitoring_before) {
fs_watcher_->Clear();
}
else if (monitor_ && !was_monitoring_before) {
// Add all directories to all QFileSystemWatchers again
QList<Directory> dirs = watched_dirs_.values();
for (const Directory &dir : dirs) {
SubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
for (const Subdirectory &subdir : subdirs) {
AddWatch(dir, subdir.path);
}
}
}
}
CollectionWatcher::ScanTransaction::ScanTransaction(CollectionWatcher *watcher, const int dir, const bool incremental, const bool ignores_mtime, const bool mark_songs_unavailable)
: progress_(0),
progress_max_(0),
@@ -119,15 +158,16 @@ CollectionWatcher::ScanTransaction::ScanTransaction(CollectionWatcher *watcher,
mark_songs_unavailable_(mark_songs_unavailable),
watcher_(watcher),
cached_songs_dirty_(true),
known_subdirs_dirty_(true)
{
known_subdirs_dirty_(true) {
QString description;
if (watcher_->device_name_.isEmpty())
if (watcher_->device_name_.isEmpty()) {
description = tr("Updating collection");
else
}
else {
description = tr("Updating %1").arg(watcher_->device_name_);
}
task_id_ = watcher_->task_manager_->StartTask(description);
emit watcher_->ScanStarted(task_id_);
@@ -310,7 +350,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Do not scan symlinked dirs that are already in collection
if (path_info.isSymLink()) {
QString real_path = path_info.symLinkTarget();
for (const Directory &dir : watched_dirs_) {
for (const Directory &dir : qAsConst(watched_dirs_)) {
if (real_path.startsWith(dir.path)) {
t->AddToProgress(1);
return;
@@ -347,6 +387,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// First we "quickly" get a list of the files in the directory that we think might be music. While we're here, we also look for new subdirectories and possible album artwork.
QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
while (it.hasNext()) {
if (stop_requested_) return;
QString child(it.next());
@@ -384,7 +425,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
for (const QString &file : files_on_disk) {
if (stop_requested_) return;
// associated cue
// Associated cue
QString matching_cue = NoExtensionPart(file) + ".cue";
Song matching_song(source_);
@@ -401,14 +442,14 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
continue;
}
// cue sheet's path from collection (if any)
// CUE sheet's path from collection (if any)
QString song_cue = matching_song.cue_path();
qint64 song_cue_mtime = GetMtimeForCue(song_cue);
bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue();
bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue();
// watch out for cue songs which have their mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
// Watch out for CUE songs which have their mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toSecsSinceEpoch(), song_cue_mtime)) || cue_deleted || cue_added;
// Also want to look to see whether the album art has changed
@@ -417,16 +458,14 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
changed = true;
}
// the song's changed - reread the metadata from file
// The song's changed - reread the metadata from file
if (t->ignores_mtime() || changed) {
qLog(Debug) << file << "changed";
// if cue associated...
if (!cue_deleted && (matching_song.has_cue() || cue_added)) {
if (!cue_deleted && (matching_song.has_cue() || cue_added)) { // If CUE associated.
UpdateCueAssociatedSongs(file, path, matching_cue, image, t);
// if no cue or it's about to lose it...
}
else {
else { // If no CUE or it's about to lose it.
UpdateNonCueAssociatedSong(file, matching_song, image, cue_deleted, t);
}
}
@@ -444,7 +483,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
}
qLog(Debug) << file << "created";
// choose an image for the song(s)
// Choose an image for the song(s)
QUrl image = ImageForSong(file, album_art);
for (Song song : song_list) {
@@ -509,7 +548,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
cue_song.set_directory_id(t->dir());
Song matching = sections_map[cue_song.beginning_nanosec()];
// a new section
// A new section
if (!matching.is_valid()) {
t->new_songs << cue_song;
// changed section
@@ -520,7 +559,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
}
}
// sections that are now missing
// Sections that are now missing
for (const Song &matching : old_sections) {
if (!used_ids.contains(matching.id())) {
t->deleted_songs << matching;
@@ -531,7 +570,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const Song &matching_song, const QUrl &image, bool cue_deleted, ScanTransaction *t) {
// If a cue got deleted, we turn it's first section into the new 'raw' (cueless) song and we just remove the rest of the sections from the collection
// If a CUE got deleted, we turn it's first section into the new 'raw' (cueless) song and we just remove the rest of the sections from the collection
if (cue_deleted) {
for (const Song &song : backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
if (!song.IsMetadataAndArtEqual(matching_song)) {
@@ -555,9 +594,8 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
SongList song_list;
quint64 matching_cue_mtime = GetMtimeForCue(matching_cue);
// If it's a cue - create virtual tracks
if (matching_cue_mtime) {
// don't process the same cue many times
if (matching_cue_mtime) { // If it's a CUE - create virtual tracks
// Don't process the same cue many times
if (cues_processed->contains(matching_cue)) return song_list;
QFile cue(matching_cue);
@@ -580,9 +618,8 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
*cues_processed << matching_cue;
}
// it's a normal media file
}
else {
else { // It's a normal media file
Song song(source_);
TagReaderClient::Instance()->ReadFileBlocking(file, &song);
if (song.is_valid()) {
@@ -653,7 +690,8 @@ void CollectionWatcher::AddWatch(const Directory &dir, const QString &path) {
void CollectionWatcher::RemoveWatch(const Directory &dir, const Subdirectory &subdir) {
for (const QString &subdir_path : subdir_mapping_.keys(dir)) {
QStringList subdir_paths = subdir_mapping_.keys(dir);
for (const QString &subdir_path : subdir_paths) {
if (subdir_path != subdir.path) continue;
fs_watcher_->RemovePath(subdir_path);
subdir_mapping_.remove(subdir_path);
@@ -668,7 +706,8 @@ void CollectionWatcher::RemoveDirectory(const Directory &dir) {
watched_dirs_.remove(dir.id);
// Stop watching the directory's subdirectories
for (const QString &subdir_path : subdir_mapping_.keys(dir)) {
QStringList subdir_paths = subdir_mapping_.keys(dir);
for (const QString &subdir_path : subdir_paths) {
fs_watcher_->RemovePath(subdir_path);
subdir_mapping_.remove(subdir_path);
}
@@ -708,7 +747,8 @@ void CollectionWatcher::DirectoryChanged(const QString &subdir) {
void CollectionWatcher::RescanPathsNow() {
for (int dir : rescan_queue_.keys()) {
QList<int> dirs = rescan_queue_.keys();
for (const int dir : dirs) {
if (stop_requested_) break;
ScanTransaction transaction(this, dir, false, false, mark_songs_unavailable_);
transaction.AddToProgressMax(rescan_queue_[dir].count());
@@ -793,44 +833,6 @@ QUrl CollectionWatcher::ImageForSong(const QString &path, QMap<QString, QStringL
}
void CollectionWatcher::ReloadSettingsAsync() {
QMetaObject::invokeMethod(this, "ReloadSettings", Qt::QueuedConnection);
}
void CollectionWatcher::ReloadSettings() {
const bool was_monitoring_before = monitor_;
QSettings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
scan_on_startup_ = s.value("startup_scan", true).toBool();
monitor_ = s.value("monitor", true).toBool();
mark_songs_unavailable_ = s.value("mark_songs_unavailable", false).toBool();
QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList();
s.endGroup();
best_image_filters_.clear();
for (const QString &filter : filters) {
QString str = filter.trimmed();
if (!str.isEmpty()) best_image_filters_ << str;
}
if (!monitor_ && was_monitoring_before) {
fs_watcher_->Clear();
}
else if (monitor_ && !was_monitoring_before) {
// Add all directories to all QFileSystemWatchers again
for (const Directory &dir : watched_dirs_.values()) {
SubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
for (const Subdirectory &subdir : subdirs) {
AddWatch(dir, subdir.path);
}
}
}
}
void CollectionWatcher::SetRescanPausedAsync(bool pause) {
QMetaObject::invokeMethod(this, "SetRescanPaused", Qt::QueuedConnection, Q_ARG(bool, pause));
@@ -902,11 +904,22 @@ void CollectionWatcher::PerformScan(bool incremental, bool ignore_mtimes) {
stop_requested_ = false;
for (const Directory &dir : watched_dirs_.values()) {
QList<Directory> dirs = watched_dirs_.values();
for (const Directory &dir : dirs) {
if (stop_requested_) break;
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes, mark_songs_unavailable_);
SubdirectoryList subdirs(transaction.GetAllSubdirs());
if (subdirs.isEmpty()) {
qLog(Debug) << "Collection directory wasn't in subdir list.";
Subdirectory subdir;
subdir.path = dir.path;
subdir.directory_id = dir.id;
subdirs << subdir;
}
transaction.AddToProgressMax(subdirs.count());
for (const Subdirectory &subdir : subdirs) {

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -155,7 +155,7 @@ class CollectionWatcher : public QObject {
void FullScanNow();
void RescanTracksNow();
void RescanPathsNow();
void ScanSubdirectory(const QString &path, const Subdirectory &subdir, ScanTransaction *t, bool force_noincremental = false);
void ScanSubdirectory(const QString &path, const Subdirectory &subdir, CollectionWatcher::ScanTransaction *t, bool force_noincremental = false);
private:
static bool FindSongByPath(const SongList &list, const QString &path, Song *out);
@@ -228,4 +228,3 @@ inline QString CollectionWatcher::DirectoryPart(const QString& fileName) {
}
#endif // COLLECTIONWATCHER_H

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
@@ -32,10 +33,6 @@
#include "groupbydialog.h"
#include "ui_groupbydialog.h"
// boost::multi_index still relies on these being in the global namespace.
using std::placeholders::_1;
using std::placeholders::_2;
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -1,7 +1,8 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2015, Nick Lanham <nick@afternight.org>
* Copyright 2019-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2015, Nick Lanham <nick@afternight.org>
* Copyright 2019-2021, 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

View File

@@ -30,8 +30,6 @@
SqlRow::SqlRow(const QSqlQuery &query) { Init(query); }
SqlRow::SqlRow(const CollectionQuery &query) { Init(query); }
void SqlRow::Init(const QSqlQuery &query) {
int rows = query.record().count();

View File

@@ -32,9 +32,7 @@ class CollectionQuery;
class SqlRow {
public:
// WARNING: Implicit construction from QSqlQuery and CollectionQuery.
SqlRow(const QSqlQuery &query);
SqlRow(const CollectionQuery &query);
const QVariant &value(int i) const { return columns_[i]; }

View File

@@ -1,22 +1,3 @@
/*
* Strawberry Music Player
* 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
* 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 CONFIG_H_IN
#define CONFIG_H_IN
@@ -24,7 +5,6 @@
#define CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}"
#define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}"
#cmakedefine DEBUG
#cmakedefine HAVE_BACKTRACE
#cmakedefine HAVE_GIO
#cmakedefine HAVE_DBUS

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2020-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2020-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013-2021, 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
@@ -54,9 +54,6 @@
#include "contextalbumsmodel.h"
using std::placeholders::_1;
using std::placeholders::_2;
const int ContextAlbumsModel::kPrettyCoverSize = 32;
ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent) :
@@ -75,7 +72,8 @@ ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *
QObject::connect(app_->album_cover_loader(), &AlbumCoverLoader::AlbumCoverLoaded, this, &ContextAlbumsModel::AlbumCoverLoaded);
QIcon nocover = IconLoader::Load("cdcase");
no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QList<QSize> nocover_sizes = nocover.availableSizes();
no_cover_icon_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
@@ -251,8 +249,11 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
ContextAlbumsModel::QueryResult ContextAlbumsModel::RunQuery(CollectionItem *parent) {
QMutexLocker l(backend_->db()->Mutex());
QSqlDatabase db(backend_->db()->Connect());
QueryResult result;
CollectionQuery q(query_options_);
CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_);
q.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
// Walk up through the item's parents adding filters as necessary
@@ -265,9 +266,7 @@ ContextAlbumsModel::QueryResult ContextAlbumsModel::RunQuery(CollectionItem *par
}
// Execute the query
QMutexLocker l(backend_->db()->Mutex());
if (!backend_->ExecQuery(&q)) return result;
if (!q.Exec()) return result;
while (q.Next()) {
result.rows << SqlRow(q);
@@ -463,7 +462,7 @@ void ContextAlbumsModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls,
const_cast<ContextAlbumsModel*>(this)->LazyPopulate(item);
QList<CollectionItem*> children = item->children;
std::sort(children.begin(), children.end(), std::bind(&ContextAlbumsModel::CompareItems, this, _1, _2));
std::sort(children.begin(), children.end(), std::bind(&ContextAlbumsModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
for (CollectionItem *child : children)
GetChildSongs(child, urls, songs, song_ids);

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* Copyright 2013-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
* Copyright 2013-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2013-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2013-2020, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013-2021, 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

View File

@@ -3,7 +3,7 @@
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -3,7 +3,7 @@
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -71,14 +71,15 @@ const char *CommandlineOptions::kHelpText =
" -o, --show-osd %27\n"
" -y, --toggle-pretty-osd %28\n"
" -g, --language <lang> %29\n"
" --quiet %30\n"
" --verbose %31\n"
" --log-levels <levels> %32\n"
" --version %33\n";
" -w, --resize-window <WxH> %30\n"
" --quiet %31\n"
" --verbose %32\n"
" --log-levels <levels> %33\n"
" --version %34\n";
const char *CommandlineOptions::kVersionText = "Strawberry %1";
CommandlineOptions::CommandlineOptions(int argc, char* *argv)
CommandlineOptions::CommandlineOptions(int argc, char **argv)
: argc_(argc),
argv_(argv),
url_list_action_(UrlList_None),
@@ -143,6 +144,7 @@ bool CommandlineOptions::Parse() {
{"show-osd", no_argument, nullptr, 'o'},
{"toggle-pretty-osd", no_argument, nullptr, 'y'},
{"language", required_argument, nullptr, 'g'},
{"resize-window", required_argument, nullptr, 'w'},
{"quiet", no_argument, nullptr, Quiet},
{"verbose", no_argument, nullptr, Verbose},
{"log-levels", required_argument, nullptr, LogLevels},
@@ -152,7 +154,7 @@ bool CommandlineOptions::Parse() {
// Parse the arguments
bool ok = false;
forever {
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:i:oyg:", kOptions, nullptr);
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:i:oyg:w:", kOptions, nullptr);
// End of the options
if (c == -1) break;
@@ -186,6 +188,7 @@ bool CommandlineOptions::Parse() {
.arg(tr("Other options"), tr("Display the on-screen-display"),
tr("Toggle visibility for the pretty on-screen-display"),
tr("Change the language"),
tr("Resize the window"),
tr("Equivalent to --log-levels *:1"),
tr("Equivalent to --log-levels *:3"),
tr("Comma separated list of class:level, level is 0-3"))
@@ -293,6 +296,11 @@ bool CommandlineOptions::Parse() {
if (!ok) play_track_at_ = -1;
break;
case 'w':
window_size_ = QString(optarg);
player_action_ = Player_ResizeWindow;
break;
case '?':
default:
return false;
@@ -370,7 +378,8 @@ QDataStream& operator<<(QDataStream &s, const CommandlineOptions &a) {
<< a.urls_
<< a.log_levels_
<< a.toggle_pretty_osd_
<< a.playlist_name_;
<< a.playlist_name_
<< a.window_size_;
return s;
@@ -380,6 +389,7 @@ QDataStream& operator>>(QDataStream &s, CommandlineOptions &a) {
quint32 player_action = 0;
quint32 url_list_action = 0;
s >> player_action
>> url_list_action
>> a.set_volume_
@@ -391,7 +401,9 @@ QDataStream& operator>>(QDataStream &s, CommandlineOptions &a) {
>> a.urls_
>> a.log_levels_
>> a.toggle_pretty_osd_
>> a.playlist_name_;
>> a.playlist_name_
>> a.window_size_;
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);

View File

@@ -58,6 +58,7 @@ class CommandlineOptions {
Player_RestartOrPrevious = 7,
Player_StopAfterCurrent = 8,
Player_PlayPlaylist = 9,
Player_ResizeWindow = 10
};
bool Parse();
@@ -78,13 +79,13 @@ class CommandlineOptions {
QString language() const { return language_; }
QString log_levels() const { return log_levels_; }
QString playlist_name() const { return playlist_name_; }
QString window_size() const { return window_size_; }
QByteArray Serialize() const;
void Load(const QByteArray &serialized);
private:
// These are "invalid" characters to pass to getopt_long for options that
// shouldn't have a short (single character) option.
// These are "invalid" characters to pass to getopt_long for options that shouldn't have a short (single character) option.
enum LongOptions {
VolumeUp = 256,
VolumeDown,
@@ -120,6 +121,7 @@ class CommandlineOptions {
QString language_;
QString log_levels_;
QString playlist_name_;
QString window_size_;
QList<QUrl> urls_;
};

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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
@@ -151,7 +151,7 @@ QSqlDatabase Database::Connect() {
UpdateDatabaseSchema(0, db);
}
{
if (SchemaVersion(&db) < 10) {
// Register unicode from unicode61 tokenizer to drop old FTS3 tables.
// We need it also to drop old devices later when loading devices.
// And that's done in a different thread after schemas are upgraded, so register it anyway.
@@ -160,8 +160,7 @@ QSqlDatabase Database::Connect() {
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) {
sqlite3 *handle = *static_cast<sqlite3**>(v.data());
if (handle) {
int result = sqlite3_db_config(handle, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, NULL);
if (result != SQLITE_OK) qLog(Fatal) << "Unable to enable FTS3 tokenizer";
(void)sqlite3_db_config(handle, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, nullptr);
}
else qLog(Fatal) << "Unable to enable FTS3 tokenizer";
}
@@ -184,7 +183,8 @@ QSqlDatabase Database::Connect() {
}
// Attach external databases
for (const QString &key : attached_databases_.keys()) {
QStringList keys = attached_databases_.keys();
for (const QString &key : keys) {
QString filename = attached_databases_[key].filename_;
if (!injected_database_name_.isNull()) filename = injected_database_name_;
@@ -204,7 +204,8 @@ QSqlDatabase Database::Connect() {
}
// We might have to initialize the schema in some attached databases now, if they were deleted and don't match up with the main schema version.
for (const QString &key : attached_databases_.keys()) {
keys = attached_databases_.keys();
for (const QString &key : keys) {
if (attached_databases_[key].is_temporary_ && attached_databases_[key].schema_.isEmpty())
continue;
// Find out if there are any tables in this database
@@ -453,7 +454,8 @@ QStringList Database::SongsTables(QSqlDatabase &db, int schema_version) const {
}
// look for the tables in attached dbs
for (const QString &key : attached_databases_.keys()) {
QStringList keys = attached_databases_.keys();
for (const QString &key : keys) {
QSqlQuery q(db);
q.prepare(QString("SELECT NAME FROM %1.sqlite_master WHERE type='table' AND name='songs' OR name LIKE '%songs'").arg(key));
if (q.exec()) {

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, 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

View File

@@ -33,11 +33,11 @@ FileSystemWatcherInterface::FileSystemWatcherInterface(QObject *parent)
: QObject(parent) {}
FileSystemWatcherInterface *FileSystemWatcherInterface::Create(QObject *parent) {
FileSystemWatcherInterface *ret;
#ifdef Q_OS_MACOS
ret = new MacFSListener(parent);
FileSystemWatcherInterface *ret = new MacFSListener(parent);
#else
ret = new QtFSListener(parent);
FileSystemWatcherInterface *ret = new QtFSListener(parent);
#endif
ret->Init();

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2013, 2017-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013, 2017-2021, 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
@@ -75,7 +75,7 @@ QIcon IconLoader::Load(const QString &name, const int fixed_size, const int min_
if (icon_prop.allow_system_icon) {
ret = QIcon::fromTheme(name);
if (ret.isNull()) {
for (QString alt_name : icon_prop.names) {
for (const QString &alt_name : icon_prop.names) {
ret = QIcon::fromTheme(alt_name);
if (!ret.isNull()) break;
}

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2013, 2017-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2013, 2017-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
@@ -147,8 +148,11 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
}
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
Q_UNUSED(aNotification);
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
key_tap_ = [ [SPMediaKeyTap alloc] initWithDelegate:self];
if ([SPMediaKeyTap usesGlobalMediaKeyTap]) {
if ([key_tap_ startWatchingMediaKeys]) {
@@ -186,6 +190,15 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
}
- (void)handleURLEvent:(NSAppleEventDescriptor*)theEvent withReplyEvent:(NSAppleEventDescriptor*)replyEvent {
#pragma unused(replyEvent)
NSString *url = [[theEvent paramDescriptorForKeyword:keyDirectObject] stringValue];
application_handler_->LoadUrl(QString::fromNSString(url));
}
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event {
#pragma unused(keyTap)
[self handleMediaEvent:event];

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2021, 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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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

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