Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
655c872ac9 | ||
|
|
cb0f2b48fd | ||
|
|
20a84bdefc | ||
|
|
83ad7d4935 | ||
|
|
ae2ca175d3 | ||
|
|
c855108b94 | ||
|
|
5e80b694c2 | ||
|
|
99640aa2ad | ||
|
|
4c0f7c3dd4 | ||
|
|
2a8490ef31 | ||
|
|
43dc694963 | ||
|
|
e03ee00554 | ||
|
|
01a07ec0e8 | ||
|
|
74b7738a9d | ||
|
|
10aaaebc38 | ||
|
|
a8d072150d | ||
|
|
7877bb7956 | ||
|
|
bd31e3df89 | ||
|
|
531e97b499 | ||
|
|
43876e967a | ||
|
|
efaf917939 | ||
|
|
7d3ceb7d8c | ||
|
|
201ac47943 | ||
|
|
5a70285c10 | ||
|
|
e13c27d32c | ||
|
|
9c9b673eeb | ||
|
|
7323fe0000 | ||
|
|
460d045cbe | ||
|
|
4983c1cfc9 | ||
|
|
c7bc9d471a | ||
|
|
a36b91ffe8 | ||
|
|
88da0db0f7 | ||
|
|
816c2b0ca8 | ||
|
|
d6ff68339b | ||
|
|
fdf483c98b | ||
|
|
eb020c1cb8 | ||
|
|
6fa331ad06 | ||
|
|
5a58ac2845 | ||
|
|
0c4edc4d17 | ||
|
|
b5cea4c27e | ||
|
|
b7de5d6df3 | ||
|
|
d7310ba307 | ||
|
|
cabb94b856 | ||
|
|
82b649f67d | ||
|
|
8753782a0c | ||
|
|
566a139edc | ||
|
|
39dda0f16b | ||
|
|
bddb371e31 | ||
|
|
128223a28a | ||
|
|
45b9d0553f | ||
|
|
a87f0e0475 | ||
|
|
beacea0482 | ||
|
|
85b59e79d3 | ||
|
|
9a947c5544 | ||
|
|
e4cebf4cbe | ||
|
|
f63e05b7d4 | ||
|
|
84b7fa02bb | ||
|
|
36e597a045 | ||
|
|
f760b3f271 | ||
|
|
d4c8fa6a24 | ||
|
|
c6604734c9 | ||
|
|
91ab8e22b7 | ||
|
|
14fb647647 | ||
|
|
8a39a43ad5 | ||
|
|
a3d23a6f57 | ||
|
|
ae5da12afb | ||
|
|
a3042b8f79 | ||
|
|
29cbfe7c1a | ||
|
|
1ddc3292cc | ||
|
|
658e1d122e | ||
|
|
d936a080db | ||
|
|
2a92af1e09 | ||
|
|
9cde537066 | ||
|
|
ffc5446914 | ||
|
|
5f70b32795 | ||
|
|
6b6117653a | ||
|
|
59bffed47f | ||
|
|
f91a679cdf | ||
|
|
66b8d27d66 | ||
|
|
1219f504ea | ||
|
|
ee5a191e39 | ||
|
|
f577aa8d4f | ||
|
|
2436c87372 | ||
|
|
e90b5e63e5 | ||
|
|
78588d8cdf | ||
|
|
20c1c1d4be | ||
|
|
329dfb21b9 | ||
|
|
02c30211a7 | ||
|
|
645da2713d | ||
|
|
20c5a79efa | ||
|
|
b224aeb0d8 | ||
|
|
73eebd6162 | ||
|
|
99a1851f4d | ||
|
|
d9d89d0927 | ||
|
|
54f2aa5f77 | ||
|
|
d022e9dd00 | ||
|
|
17cf8ec1c6 | ||
|
|
c4a6d81cda | ||
|
|
66ed485803 | ||
|
|
6de585d1c8 | ||
|
|
9498638988 | ||
|
|
87dad82210 | ||
|
|
e37aec16ac | ||
|
|
6fb48af598 | ||
|
|
8193be36e5 | ||
|
|
a7df2bc4fb | ||
|
|
5eda028af3 | ||
|
|
f77475dbfe | ||
|
|
96c1a35c8e | ||
|
|
0ef26be03f | ||
|
|
f5bb15f72e | ||
|
|
d84aebd8f4 | ||
|
|
7d8d9f4c4d | ||
|
|
61b201810d | ||
|
|
c75db8dc60 | ||
|
|
e0bf4091dd |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,2 +1,3 @@
|
||||
github: jonaski
|
||||
patreon: jonaskvinge
|
||||
custom: https://paypal.me/jonaskvinge
|
||||
|
||||
799
.github/workflows/ccpp.yml
vendored
799
.github/workflows/ccpp.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
10
3rdparty/README.md
vendored
@@ -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
7
3rdparty/macdeployqt/CMakeLists.txt
vendored
Normal 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
298
3rdparty/macdeployqt/main.cpp
vendored
Normal 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
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
141
3rdparty/macdeployqt/shared.h
vendored
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
37
Changelog
37
Changelog
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||

|
||||
|
||||
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:
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
BIN
data/screenshot/screenshot.png
Normal file
BIN
data/screenshot/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 957 KiB |
13
dist/macos/Info.plist.in
vendored
13
dist/macos/Info.plist.in
vendored
@@ -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>
|
||||
|
||||
514
dist/macos/macdeploy.py
vendored
514
dist/macos/macdeploy.py
vendored
@@ -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()
|
||||
34
dist/windows/strawberry.nsi.in
vendored
34
dist/windows/strawberry.nsi.in
vendored
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ÷r_key : divider_keys) {
|
||||
for (const QString ÷r_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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user