Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fd29c6dcc | ||
|
|
4429e9973f | ||
|
|
1572d241d5 | ||
|
|
eae7e2a9e6 | ||
|
|
251e5b379b | ||
|
|
0db082fca0 | ||
|
|
2799e55076 | ||
|
|
440ee43a91 | ||
|
|
98c72ec1f8 | ||
|
|
759488ae1a | ||
|
|
055cb413c9 | ||
|
|
f760b87b58 | ||
|
|
39f228f862 | ||
|
|
a683a279f5 | ||
|
|
e800926f57 | ||
|
|
dd9f80d539 | ||
|
|
8484cac4ed | ||
|
|
e24097582f | ||
|
|
58fc8c82bb | ||
|
|
02bb875bb3 | ||
|
|
5db01482eb | ||
|
|
719fa6ffb3 | ||
|
|
159be5d79e | ||
|
|
911237e281 | ||
|
|
ae89ca8123 | ||
|
|
b832675893 | ||
|
|
b4cfe636c9 | ||
|
|
e6a0945dfa | ||
|
|
726c105ed6 | ||
|
|
d73cbc3a1d | ||
|
|
121f45d3b6 | ||
|
|
3a9ea81929 | ||
|
|
b919472241 | ||
|
|
e8c8b39410 | ||
|
|
6904efef47 | ||
|
|
fafa89baff | ||
|
|
d9062446f5 | ||
|
|
9256b92d8f | ||
|
|
f66459f3cb | ||
|
|
f8ea9631ca | ||
|
|
ab558f87b5 | ||
|
|
ab73eda2be | ||
|
|
92f34ff36e | ||
|
|
d0bf2d7a9c | ||
|
|
b2cd3afe55 | ||
|
|
decd0a1dc6 | ||
|
|
3e0a9fa388 | ||
|
|
e5b6c5959f | ||
|
|
8a9db5440d | ||
|
|
28a25c5763 | ||
|
|
ea49fbcbee | ||
|
|
d9807b358e | ||
|
|
3e53d2b237 | ||
|
|
9122881f74 | ||
|
|
9c36d7fd43 | ||
|
|
a4a365cbee | ||
|
|
00f06b22b8 | ||
|
|
e2d8838fca | ||
|
|
9427691f39 | ||
|
|
bebdcc4e7f | ||
|
|
041f761921 | ||
|
|
1435ae6dc0 | ||
|
|
33ae53a90f | ||
|
|
01c28867b7 | ||
|
|
08fb2ae331 | ||
|
|
99970f9e52 | ||
|
|
bc206f43b4 | ||
|
|
018448159c | ||
|
|
81fe90bdef | ||
|
|
319558c535 | ||
|
|
9095b0d6b2 | ||
|
|
558eae1ca1 | ||
|
|
2c64f05cea | ||
|
|
c80e7071a1 | ||
|
|
038e679000 | ||
|
|
72447fecfb | ||
|
|
c7830f6f05 | ||
|
|
af525e42b6 | ||
|
|
eb83f23125 | ||
|
|
7527d2ea9a | ||
|
|
69d38879d2 | ||
|
|
cbce9f7191 | ||
|
|
f938129d81 | ||
|
|
415a40ea04 | ||
|
|
6e7aaed4ee | ||
|
|
7afae70bb0 | ||
|
|
be8097919b | ||
|
|
1990a42e1d | ||
|
|
8fcee4511d | ||
|
|
24af1be666 | ||
|
|
8302a95bc1 | ||
|
|
36a8ab49a0 | ||
|
|
5c64dc9c4d | ||
|
|
c271743208 | ||
|
|
ee49b1ddc8 | ||
|
|
bf98633f16 | ||
|
|
e2a928f2dc | ||
|
|
47d3312a6b | ||
|
|
4f97325953 | ||
|
|
91e8fe0943 | ||
|
|
dc5894b38a | ||
|
|
52ee50a2a4 | ||
|
|
f545b028ee | ||
|
|
a96627c5a9 | ||
|
|
a13fc31f83 | ||
|
|
9ee5c8dc17 | ||
|
|
82cd425ece | ||
|
|
3b02d364ba | ||
|
|
73e7947487 | ||
|
|
cfc9a43b88 | ||
|
|
969500023a | ||
|
|
53c72d4f8e | ||
|
|
f971c92f32 | ||
|
|
82156e8a13 | ||
|
|
6a6285861e | ||
|
|
dae8c8730b | ||
|
|
20b47eae8f | ||
|
|
2ce8220d88 | ||
|
|
35b0b5df57 | ||
|
|
63631d6b0c | ||
|
|
21ab2ef1a7 | ||
|
|
a3f96d2b85 | ||
|
|
e2c1cb0116 | ||
|
|
07e295776b | ||
|
|
8604a39d94 | ||
|
|
315240fec7 | ||
|
|
fbc6d326f8 | ||
|
|
c082377e7a | ||
|
|
448445a38a | ||
|
|
18f835d7e5 | ||
|
|
fd427dac29 | ||
|
|
e1afe03d51 | ||
|
|
1b49653974 | ||
|
|
d66126f998 | ||
|
|
0fff5f672a | ||
|
|
2726f01fb3 | ||
|
|
9a74fce53d | ||
|
|
b3be8387f1 | ||
|
|
d396cb515d | ||
|
|
2548b4648e | ||
|
|
df0ec6b709 | ||
|
|
376af26f0e | ||
|
|
9bff55e1ee | ||
|
|
8f7e29f503 | ||
|
|
c3aa885a0f | ||
|
|
77ea5729c3 |
156
.github/workflows/build.yml
vendored
156
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
|
|
||||||
build-opensuse:
|
build-opensuse:
|
||||||
name: Build openSUSE
|
name: Build openSUSE
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -21,18 +21,18 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Refresh repositories
|
- name: Refresh repositories
|
||||||
run: zypper -n --gpg-auto-import-keys ref
|
run: zypper -n --gpg-auto-import-keys ref
|
||||||
- name: Upgrade packages
|
- name: Upgrade packages (Tumbleweed)
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version == 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys dup
|
run: zypper -n --gpg-auto-import-keys dup
|
||||||
- name: Upgrade packages
|
- name: Upgrade packages (Leap)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version != 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys up
|
run: zypper -n --gpg-auto-import-keys up
|
||||||
- name: Install gcc
|
- name: Install gcc (Tumbleweed)
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version == 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys in gcc gcc-c++
|
run: zypper -n --gpg-auto-import-keys in gcc gcc-c++
|
||||||
- name: Install gcc 13
|
- name: Install gcc (Leap)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version != 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys in gcc13 gcc13-c++
|
run: zypper -n --gpg-auto-import-keys in gcc14 gcc14-c++
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: >
|
run: >
|
||||||
zypper -n --gpg-auto-import-keys in
|
zypper -n --gpg-auto-import-keys in
|
||||||
@@ -68,6 +68,7 @@ jobs:
|
|||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
qt6-core-devel
|
qt6-core-devel
|
||||||
qt6-gui-devel
|
qt6-gui-devel
|
||||||
|
qt6-gui-private-devel
|
||||||
qt6-widgets-devel
|
qt6-widgets-devel
|
||||||
qt6-concurrent-devel
|
qt6-concurrent-devel
|
||||||
qt6-network-devel
|
qt6-network-devel
|
||||||
@@ -77,6 +78,8 @@ jobs:
|
|||||||
qt6-base-common-devel
|
qt6-base-common-devel
|
||||||
qt6-sql-sqlite
|
qt6-sql-sqlite
|
||||||
qt6-linguist-devel
|
qt6-linguist-devel
|
||||||
|
gtest
|
||||||
|
gmock
|
||||||
- name: Install kdsingleapplication-qt6-devel
|
- name: Install kdsingleapplication-qt6-devel
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version == 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys in kdsingleapplication-qt6-devel
|
run: zypper -n --gpg-auto-import-keys in kdsingleapplication-qt6-devel
|
||||||
@@ -101,15 +104,18 @@ jobs:
|
|||||||
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||||
- name: Build RPM (Tumbleweed)
|
- name: Build RPM (Tumbleweed)
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version == 'tumbleweed'
|
||||||
|
env:
|
||||||
|
RPM_BUILD_NCPUS: 4
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Build RPM (Leap)
|
- name: Build RPM (Leap)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version != 'tumbleweed'
|
||||||
working-directory: build
|
|
||||||
env:
|
env:
|
||||||
CC: gcc-13
|
RPM_BUILD_NCPUS: 4
|
||||||
CXX: g++-13
|
CC: gcc-14
|
||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
CXX: g++-14
|
||||||
|
working-directory: build
|
||||||
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Set subdir
|
- name: Set subdir
|
||||||
id: set-subdir
|
id: set-subdir
|
||||||
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
|
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
|
||||||
@@ -132,7 +138,7 @@ jobs:
|
|||||||
|
|
||||||
build-fedora:
|
build-fedora:
|
||||||
name: Build Fedora
|
name: Build Fedora
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -149,7 +155,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
dnf -y install
|
dnf -y install
|
||||||
@development-tools
|
@development-tools
|
||||||
redhat-lsb-core
|
lsb_release
|
||||||
which
|
which
|
||||||
git
|
git
|
||||||
glibc
|
glibc
|
||||||
@@ -185,6 +191,8 @@ jobs:
|
|||||||
libappstream-glib
|
libappstream-glib
|
||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
kdsingleapplication-qt6-devel
|
kdsingleapplication-qt6-devel
|
||||||
|
gtest-devel
|
||||||
|
gmock-devel
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -207,9 +215,9 @@ jobs:
|
|||||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
env:
|
env:
|
||||||
RPM_BUILD_NCPUS: "2"
|
RPM_BUILD_NCPUS: 4
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -221,7 +229,7 @@ jobs:
|
|||||||
|
|
||||||
build-openmandriva:
|
build-openmandriva:
|
||||||
name: Build OpenMandriva
|
name: Build OpenMandriva
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && false
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master' && false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -300,9 +308,9 @@ jobs:
|
|||||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
env:
|
env:
|
||||||
RPM_BUILD_NCPUS: "2"
|
RPM_BUILD_NCPUS: 4
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: matrix.openmandriva_version != 'cooker'
|
if: matrix.openmandriva_version != 'cooker'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -315,7 +323,7 @@ jobs:
|
|||||||
|
|
||||||
build-mageia:
|
build-mageia:
|
||||||
name: Build Mageia
|
name: Build Mageia
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -374,6 +382,7 @@ jobs:
|
|||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
appstream-util
|
appstream-util
|
||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
|
gtest
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -396,9 +405,9 @@ jobs:
|
|||||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
env:
|
env:
|
||||||
RPM_BUILD_NCPUS: "2"
|
RPM_BUILD_NCPUS: 4
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -410,7 +419,7 @@ jobs:
|
|||||||
|
|
||||||
build-debian:
|
build-debian:
|
||||||
name: Build Debian
|
name: Build Debian
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -456,7 +465,9 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
|
libxkbcommon-dev
|
||||||
qt6-base-dev
|
qt6-base-dev
|
||||||
|
qt6-base-private-dev
|
||||||
qt6-base-dev-tools
|
qt6-base-dev-tools
|
||||||
qt6-tools-dev
|
qt6-tools-dev
|
||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
@@ -471,21 +482,24 @@ jobs:
|
|||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_WERROR=ON
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WERROR=ON
|
||||||
|
- name: Delete build directory
|
||||||
|
run: rm -rf build
|
||||||
- name: make deb
|
- name: make deb
|
||||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
run: dpkg-buildpackage -b -d -uc -us -nc -j4
|
||||||
- name: Copy deb
|
- name: Copy deb
|
||||||
run: cp ../*.deb .
|
run: cp ../*.deb .
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: debian-${{matrix.debian_version}}
|
name: debian-${{matrix.debian_version}}
|
||||||
path: "*.deb"
|
path: |
|
||||||
|
*.deb
|
||||||
|
|
||||||
|
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
name: Build Ubuntu
|
name: Build Ubuntu
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -534,7 +548,9 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
|
libxkbcommon-dev
|
||||||
qt6-base-dev
|
qt6-base-dev
|
||||||
|
qt6-base-private-dev
|
||||||
qt6-base-dev-tools
|
qt6-base-dev-tools
|
||||||
qt6-tools-dev
|
qt6-tools-dev
|
||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
@@ -549,9 +565,11 @@ jobs:
|
|||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_WERROR=ON
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WERROR=ON
|
||||||
|
- name: Delete build directory
|
||||||
|
run: rm -rf build
|
||||||
- name: make deb
|
- name: make deb
|
||||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
run: dpkg-buildpackage -b -d -uc -us -nc -j4
|
||||||
- name: Copy deb
|
- name: Copy deb
|
||||||
run: cp ../*.deb ../*.ddeb .
|
run: cp ../*.deb ../*.ddeb .
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
@@ -565,7 +583,7 @@ jobs:
|
|||||||
|
|
||||||
upload-ubuntu-ppa:
|
upload-ubuntu-ppa:
|
||||||
name: Upload Ubuntu PPA
|
name: Upload Ubuntu PPA
|
||||||
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && (github.event_name == 'release' || (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/ci' || github.ref == 'refs/heads/1.1'))) && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && (github.event_name == 'release' || (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/ci' || github.ref == 'refs/heads/1.1')))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -612,7 +630,9 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
|
libxkbcommon-dev
|
||||||
qt6-base-dev
|
qt6-base-dev
|
||||||
|
qt6-base-private-dev
|
||||||
qt6-base-dev-tools
|
qt6-base-dev-tools
|
||||||
qt6-tools-dev
|
qt6-tools-dev
|
||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
@@ -634,7 +654,7 @@ jobs:
|
|||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_WERROR=ON
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WERROR=ON
|
||||||
- name: Delete build directory
|
- name: Delete build directory
|
||||||
run: rm -rf build
|
run: rm -rf build
|
||||||
- name: Import Ubuntu PPA GPG private key
|
- name: Import Ubuntu PPA GPG private key
|
||||||
@@ -651,9 +671,60 @@ jobs:
|
|||||||
run: dput ppa:jonaski/strawberry ../*_source.changes
|
run: dput ppa:jonaski/strawberry ../*_source.changes
|
||||||
|
|
||||||
|
|
||||||
|
build-freebsd:
|
||||||
|
name: Build FreeBSD
|
||||||
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
- name: Build FreeBSD
|
||||||
|
id: build-freebsd
|
||||||
|
uses: vmactions/freebsd-vm@v1.1.8
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
mem: 4096
|
||||||
|
prepare: pkg install -y git cmake pkgconf boost-libs alsa-lib glib qt6-base qt6-tools sqlite gstreamer1 gstreamer1-plugins chromaprint libebur128 taglib libcdio libmtp gdk-pixbuf2 libgpod fftw3 icu kdsingleapplication googletest pulseaudio
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
git config --global --add safe.directory ${GITHUB_WORKSPACE}
|
||||||
|
cmake -E make_directory build
|
||||||
|
cmake -S . -B build -DCMAKE_BUILD_TYPE="Debug"
|
||||||
|
cmake --build build --config Debug --parallel 4
|
||||||
|
|
||||||
|
|
||||||
|
build-openbsd:
|
||||||
|
name: Build OpenBSD
|
||||||
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
- name: Build OpenBSD
|
||||||
|
id: build-openbsd
|
||||||
|
uses: vmactions/openbsd-vm@v1.1.6
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
mem: 4096
|
||||||
|
prepare: pkg_add git cmake pkgconf boost glib2 qt6-qtbase qt6-qttools sqlite gstreamer1 gstreamer1-plugins-base chromaprint libebur128 taglib libcdio libmtp gdk-pixbuf libgpod fftw3 icu4c kdsingleapplication pulseaudio
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
export LDFLAGS="-L/usr/local/lib"
|
||||||
|
git config --global --add safe.directory ${GITHUB_WORKSPACE}
|
||||||
|
cmake -E make_directory build
|
||||||
|
cmake -S . -B build -DCMAKE_BUILD_TYPE="Debug" -DENABLE_ALSA=OFF
|
||||||
|
cmake --build build --config Debug --parallel 4
|
||||||
|
|
||||||
|
|
||||||
build-macos-public:
|
build-macos-public:
|
||||||
name: Build macOS Public
|
name: Build macOS Public
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -737,10 +808,12 @@ jobs:
|
|||||||
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
||||||
-DBUILD_WERROR=ON
|
-DBUILD_WERROR=ON
|
||||||
-DUSE_BUNDLE=ON
|
-DUSE_BUNDLE=ON
|
||||||
-DENABLE_DBUS=OFF
|
|
||||||
-DICU_ROOT="${{env.prefix_path}}"
|
-DICU_ROOT="${{env.prefix_path}}"
|
||||||
-DAPPLE_DEVELOPER_ID=$(test '${{github.repository}}' = 'strawberrymusicplayer/strawberry' && test '${{github.event.pull_request.base.repo.full_name}}' = '${{github.event.pull_request.head.repo.full_name}}' && echo "383J84DVB6" || echo "")
|
-DAPPLE_DEVELOPER_ID=$(test '${{github.repository}}' = 'strawberrymusicplayer/strawberry' && test '${{github.event.pull_request.base.repo.full_name}}' = '${{github.event.pull_request.head.repo.full_name}}' && echo "383J84DVB6" || echo "")
|
||||||
|
-DARCH="${{env.arch}}"
|
||||||
-DENABLE_SPOTIFY=$(test -f "${{env.prefix_path}}/lib/gstreamer-1.0/libgstspotify.dylib" && echo "ON" || echo "OFF")
|
-DENABLE_SPOTIFY=$(test -f "${{env.prefix_path}}/lib/gstreamer-1.0/libgstspotify.dylib" && echo "ON" || echo "OFF")
|
||||||
|
-DENABLE_SPARKLE=ON
|
||||||
|
-DENABLE_QTSPARKLE=OFF
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --config Release --parallel 4
|
run: cmake --build build --config Release --parallel 4
|
||||||
@@ -876,10 +949,12 @@ jobs:
|
|||||||
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
||||||
-DBUILD_WERROR=ON
|
-DBUILD_WERROR=ON
|
||||||
-DUSE_BUNDLE=ON
|
-DUSE_BUNDLE=ON
|
||||||
-DENABLE_DBUS=OFF
|
|
||||||
-DICU_ROOT="${{env.prefix_path}}"
|
-DICU_ROOT="${{env.prefix_path}}"
|
||||||
-DAPPLE_DEVELOPER_ID="383J84DVB6"
|
-DAPPLE_DEVELOPER_ID="383J84DVB6"
|
||||||
|
-DARCH="${{env.arch}}"
|
||||||
-DENABLE_SPOTIFY=$(test -f "${{env.prefix_path}}/lib/gstreamer-1.0/libgstspotify.dylib" && echo "ON" || echo "OFF")
|
-DENABLE_SPOTIFY=$(test -f "${{env.prefix_path}}/lib/gstreamer-1.0/libgstspotify.dylib" && echo "ON" || echo "OFF")
|
||||||
|
-DENABLE_SPARKLE=ON
|
||||||
|
-DENABLE_QTSPARKLE=OFF
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --config Release --parallel 4
|
run: cmake --build build --config Release --parallel 4
|
||||||
@@ -931,7 +1006,7 @@ jobs:
|
|||||||
|
|
||||||
build-windows-mingw:
|
build-windows-mingw:
|
||||||
name: Build Windows MinGW
|
name: Build Windows MinGW
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -974,7 +1049,7 @@ jobs:
|
|||||||
-DBUILD_WERROR=ON
|
-DBUILD_WERROR=ON
|
||||||
-DARCH="${{matrix.arch}}"
|
-DARCH="${{matrix.arch}}"
|
||||||
-DENABLE_WIN32_CONSOLE=$(test "${{matrix.buildtype}}" = "debug" && echo "ON" || echo "OFF")
|
-DENABLE_WIN32_CONSOLE=$(test "${{matrix.buildtype}}" = "debug" && echo "ON" || echo "OFF")
|
||||||
-DENABLE_DBUS=OFF
|
-DENABLE_GIO=OFF
|
||||||
-DENABLE_AUDIOCD=OFF
|
-DENABLE_AUDIOCD=OFF
|
||||||
-DENABLE_MTP=OFF
|
-DENABLE_MTP=OFF
|
||||||
-DENABLE_GPOD=OFF
|
-DENABLE_GPOD=OFF
|
||||||
@@ -1120,7 +1195,7 @@ jobs:
|
|||||||
|
|
||||||
build-windows-msvc:
|
build-windows-msvc:
|
||||||
name: Build Windows MSVC
|
name: Build Windows MSVC
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && (!(github.event.pusher.name == 'strawbsbot' && contains(github.event.head_commit.message, 'New translations')))
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -1262,6 +1337,10 @@ jobs:
|
|||||||
-DENABLE_WIN32_CONSOLE=${{env.win32_console}}
|
-DENABLE_WIN32_CONSOLE=${{env.win32_console}}
|
||||||
-DPKG_CONFIG_EXECUTABLE="${{env.prefix_path_forwardslash}}/bin/pkg-config.exe"
|
-DPKG_CONFIG_EXECUTABLE="${{env.prefix_path_forwardslash}}/bin/pkg-config.exe"
|
||||||
-DICU_ROOT="${{env.prefix_path_forwardslash}}"
|
-DICU_ROOT="${{env.prefix_path_forwardslash}}"
|
||||||
|
-DENABLE_GIO=OFF
|
||||||
|
-DENABLE_AUDIOCD=OFF
|
||||||
|
-DENABLE_MTP=OFF
|
||||||
|
-DENABLE_GPOD=OFF
|
||||||
-DENABLE_SPOTIFY=ON
|
-DENABLE_SPOTIFY=ON
|
||||||
|
|
||||||
- name: Run Make
|
- name: Run Make
|
||||||
@@ -1396,6 +1475,11 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Download MSVC runtime
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: curl -f -O -L https://aka.ms/vs/17/release/vc_redist.$(test "${{matrix.arch}}" = "x86_64" && echo "x64" || echo "${{matrix.arch}}").exe
|
||||||
|
|
||||||
- name: Create nsis installer
|
- name: Create nsis installer
|
||||||
shell: cmd
|
shell: cmd
|
||||||
working-directory: build
|
working-directory: build
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,6 +11,4 @@
|
|||||||
/out
|
/out
|
||||||
/CMakeSettings.json
|
/CMakeSettings.json
|
||||||
/dist/scripts/maketarball.sh
|
/dist/scripts/maketarball.sh
|
||||||
/dist/unix/strawberry.spec
|
|
||||||
/debian/changelog
|
/debian/changelog
|
||||||
/dist/macos/Info.plist
|
|
||||||
|
|||||||
178
CMakeLists.txt
178
CMakeLists.txt
@@ -18,24 +18,19 @@ include(CheckCXXSourceRuns)
|
|||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
include(cmake/Version.cmake)
|
include(cmake/Version.cmake)
|
||||||
include(cmake/Summary.cmake)
|
include(cmake/OptionalComponent.cmake)
|
||||||
include(cmake/OptionalSource.cmake)
|
include(cmake/OptionalSource.cmake)
|
||||||
include(cmake/ParseArguments.cmake)
|
include(cmake/ParseArguments.cmake)
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
set(LINUX ON)
|
set(LINUX ON)
|
||||||
endif()
|
endif()
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
|
||||||
set(FREEBSD ON)
|
|
||||||
endif()
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
|
||||||
set(OPENBSD ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(LINUX)
|
if(LINUX)
|
||||||
include(cmake/Rpm.cmake)
|
include(cmake/Rpm.cmake)
|
||||||
include(cmake/Deb.cmake)
|
include(cmake/Deb.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
include(cmake/Dmg.cmake)
|
include(cmake/Dmg.cmake)
|
||||||
endif()
|
endif()
|
||||||
@@ -72,7 +67,6 @@ option(USE_BUNDLE "Bundle dependencies" ${USE_BUNDLE_DEFAULT})
|
|||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" ${ENABLE_WIN32_CONSOLE_DEFAULT})
|
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" ${ENABLE_WIN32_CONSOLE_DEFAULT})
|
||||||
option(USE_QTSPARKLE "Use Qt Sparkle updater" ON)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -134,7 +128,10 @@ add_definitions(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_definitions(-DUNICODE)
|
add_definitions(
|
||||||
|
-DUNICODE
|
||||||
|
-DNOMINMAX
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_WERROR)
|
if(BUILD_WERROR)
|
||||||
@@ -167,17 +164,17 @@ if(NOT Boost_FOUND)
|
|||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
||||||
if(LINUX)
|
|
||||||
find_package(ALSA REQUIRED)
|
|
||||||
else()
|
|
||||||
find_package(ALSA)
|
|
||||||
endif()
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
|
if(LINUX)
|
||||||
|
find_package(ALSA REQUIRED)
|
||||||
|
else()
|
||||||
|
find_package(ALSA)
|
||||||
|
endif()
|
||||||
find_package(X11 COMPONENTS X11_xcb)
|
find_package(X11 COMPONENTS X11_xcb)
|
||||||
endif()
|
endif()
|
||||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||||
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
|
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
|
||||||
if(UNIX AND NOT APPLE)
|
if(NOT APPLE)
|
||||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
||||||
if(GIO_FOUND AND UNIX)
|
if(GIO_FOUND AND UNIX)
|
||||||
pkg_check_modules(GIO_UNIX IMPORTED_TARGET gio-unix-2.0)
|
pkg_check_modules(GIO_UNIX IMPORTED_TARGET gio-unix-2.0)
|
||||||
@@ -191,7 +188,9 @@ pkg_check_modules(GSTREAMER_APP REQUIRED IMPORTED_TARGET gstreamer-app-1.0)
|
|||||||
pkg_check_modules(GSTREAMER_TAG REQUIRED IMPORTED_TARGET gstreamer-tag-1.0)
|
pkg_check_modules(GSTREAMER_TAG REQUIRED IMPORTED_TARGET gstreamer-tag-1.0)
|
||||||
pkg_check_modules(GSTREAMER_PBUTILS REQUIRED IMPORTED_TARGET gstreamer-pbutils-1.0)
|
pkg_check_modules(GSTREAMER_PBUTILS REQUIRED IMPORTED_TARGET gstreamer-pbutils-1.0)
|
||||||
pkg_check_modules(SQLITE REQUIRED IMPORTED_TARGET sqlite3>=3.9)
|
pkg_check_modules(SQLITE REQUIRED IMPORTED_TARGET sqlite3>=3.9)
|
||||||
pkg_check_modules(LIBPULSE IMPORTED_TARGET libpulse)
|
if(UNIX AND NOT APPLE)
|
||||||
|
pkg_check_modules(LIBPULSE IMPORTED_TARGET libpulse)
|
||||||
|
endif()
|
||||||
pkg_check_modules(CHROMAPRINT IMPORTED_TARGET libchromaprint>=1.4)
|
pkg_check_modules(CHROMAPRINT IMPORTED_TARGET libchromaprint>=1.4)
|
||||||
pkg_check_modules(FFTW3 IMPORTED_TARGET fftw3)
|
pkg_check_modules(FFTW3 IMPORTED_TARGET fftw3)
|
||||||
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
||||||
@@ -215,30 +214,23 @@ set(QT_VERSION_MAJOR 6)
|
|||||||
set(QT_MIN_VERSION 6.4.0)
|
set(QT_MIN_VERSION 6.4.0)
|
||||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
||||||
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
||||||
set(QT_OPTIONAL_COMPONENTS LinguistTools Test)
|
set(QT_OPTIONAL_COMPONENTS GuiPrivate LinguistTools Test)
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
list(APPEND QT_OPTIONAL_COMPONENTS DBus)
|
list(APPEND QT_OPTIONAL_COMPONENTS DBus)
|
||||||
endif()
|
endif()
|
||||||
|
set(QT_NO_PRIVATE_MODULE_WARNING ON)
|
||||||
|
|
||||||
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
||||||
|
|
||||||
|
if(TARGET "Qt${QT_VERSION_MAJOR}::GuiPrivate")
|
||||||
|
set(QT_GUI_PRIVATE_FOUND ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||||
set(DBUS_FOUND ON)
|
set(DBUS_FOUND ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(X11_FOUND)
|
if(X11_FOUND)
|
||||||
|
|
||||||
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_VERSION_MAJOR}Gui_INCLUDE_DIRS} ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
if(NOT QPA_QPLATFORMNATIVEINTERFACE_H)
|
|
||||||
find_path(QPA_QPLATFORMNATIVEINTERFACE_H ${Qt${QT_VERSION_MAJOR}Gui_VERSION}/QtGui/qpa/qplatformnativeinterface.h PATHS ${Qt${QT_VERSION_MAJOR}Gui_INCLUDE_DIRS} ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
if(QPA_QPLATFORMNATIVEINTERFACE_H)
|
|
||||||
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
|
|
||||||
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
|
|
||||||
else()
|
|
||||||
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Check for QX11Application (Qt 6 compiled with XCB).
|
# Check for QX11Application (Qt 6 compiled with XCB).
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
|
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
|
||||||
@@ -253,7 +245,6 @@ if(X11_FOUND)
|
|||||||
)
|
)
|
||||||
unset(CMAKE_REQUIRED_FLAGS)
|
unset(CMAKE_REQUIRED_FLAGS)
|
||||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SingleApplication
|
# SingleApplication
|
||||||
@@ -271,33 +262,36 @@ else()
|
|||||||
add_definitions(-DKDSINGLEAPPLICATION_STATIC_BUILD)
|
add_definitions(-DKDSINGLEAPPLICATION_STATIC_BUILD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# if(APPLE)
|
if(APPLE)
|
||||||
# find_package(SPMediaKeyTap REQUIRED)
|
find_library(SPARKLE Sparkle)
|
||||||
# endif()
|
#find_package(SPMediaKeyTap REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
find_package(getopt-win REQUIRED)
|
find_package(getopt-win REQUIRED)
|
||||||
if(USE_QTSPARKLE)
|
endif()
|
||||||
pkg_check_modules(QTSPARKLE REQUIRED IMPORTED_TARGET qtsparkle-qt${QT_VERSION_MAJOR})
|
|
||||||
set(HAVE_QTSPARKLE ON)
|
if(APPLE OR WIN32)
|
||||||
|
find_package(qtsparkle-qt${QT_VERSION_MAJOR})
|
||||||
|
if(TARGET "qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle")
|
||||||
|
set(QTSPARKLE_FOUND ON)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
optional_component(ALSA ON "ALSA integration"
|
if(UNIX AND NOT APPLE)
|
||||||
DEPENDS "alsa" ALSA_FOUND
|
optional_component(ALSA ON "ALSA integration"
|
||||||
)
|
DEPENDS "alsa" ALSA_FOUND
|
||||||
|
)
|
||||||
optional_component(PULSE ON "PulseAudio integration"
|
optional_component(PULSE ON "PulseAudio integration"
|
||||||
DEPENDS "libpulse" LIBPULSE_FOUND
|
DEPENDS "libpulse" LIBPULSE_FOUND
|
||||||
)
|
)
|
||||||
|
optional_component(DBUS ON "D-Bus support"
|
||||||
optional_component(DBUS ON "D-Bus support"
|
DEPENDS "Qt D-Bus" DBUS_FOUND
|
||||||
DEPENDS "Qt D-Bus" DBUS_FOUND
|
)
|
||||||
)
|
optional_component(MPRIS2 ON "MPRIS2 D-Bus Interface"
|
||||||
|
DEPENDS "D-Bus support" HAVE_DBUS
|
||||||
optional_component(MPRIS2 ON "MPRIS2 D-Bus Interface"
|
)
|
||||||
DEPENDS "D-Bus support" HAVE_DBUS
|
endif()
|
||||||
)
|
|
||||||
|
|
||||||
optional_component(SONGFINGERPRINTING ON "Song fingerprinting and tracking"
|
optional_component(SONGFINGERPRINTING ON "Song fingerprinting and tracking"
|
||||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||||
@@ -307,26 +301,29 @@ optional_component(MUSICBRAINZ ON "MusicBrainz integration"
|
|||||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
if(UNIX AND NOT APPLE)
|
||||||
DEPENDS "X11" X11_FOUND
|
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
||||||
DEPENDS "QX11Application" HAVE_QX11APPLICATION
|
DEPENDS "X11" X11_FOUND
|
||||||
)
|
DEPENDS "QX11Application" HAVE_QX11APPLICATION
|
||||||
|
)
|
||||||
|
optional_component(KGLOBALACCEL_GLOBALSHORTCUTS ON "KGlobalAccel global shortcuts"
|
||||||
|
DEPENDS "D-Bus support" HAVE_DBUS
|
||||||
|
)
|
||||||
|
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
||||||
|
DEPENDS "D-Bus support" HAVE_DBUS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
optional_component(KGLOBALACCEL_GLOBALSHORTCUTS ON "KGlobalAccel global shortcuts"
|
if(NOT APPLE)
|
||||||
DEPENDS "D-Bus support" HAVE_DBUS
|
optional_component(GIO ON "Devices: GIO device backend"
|
||||||
)
|
DEPENDS "libgio" GIO_FOUND
|
||||||
|
)
|
||||||
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
if(UNIX)
|
||||||
DEPENDS "D-Bus support" HAVE_DBUS
|
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
|
||||||
)
|
DEPENDS "libgio-unix" GIO_UNIX_FOUND
|
||||||
|
)
|
||||||
optional_component(GIO ON "Devices: GIO device backend"
|
endif()
|
||||||
DEPENDS "libgio" GIO_FOUND
|
endif()
|
||||||
)
|
|
||||||
|
|
||||||
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
|
|
||||||
DEPENDS "libgio-unix" GIO_UNIX_FOUND
|
|
||||||
)
|
|
||||||
|
|
||||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||||
@@ -358,6 +355,22 @@ optional_component(EBUR128 ON "EBU R 128 loudness normalization"
|
|||||||
DEPENDS "libebur128" LIBEBUR128_FOUND
|
DEPENDS "libebur128" LIBEBUR128_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
optional_component(SPARKLE ON "Sparkle integration"
|
||||||
|
DEPENDS "Sparkle" SPARKLE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE OR WIN32)
|
||||||
|
optional_component(QTSPARKLE ON "QtSparkle integration"
|
||||||
|
DEPENDS "QtSparkle" QTSPARKLE_FOUND
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
optional_component(QPA_QPLATFORMNATIVEINTERFACE ON "QPA Platform Native Interface"
|
||||||
|
DEPENDS "Qt Gui Private" QT_GUI_PRIVATE_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||||
set(HAVE_CHROMAPRINT ON)
|
set(HAVE_CHROMAPRINT ON)
|
||||||
endif()
|
endif()
|
||||||
@@ -392,7 +405,7 @@ add_executable(strawberry)
|
|||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE TRUE)
|
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/macos/Info.plist")
|
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/dist/macos/Info.plist")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 AND NOT ENABLE_WIN32_CONSOLE)
|
if(WIN32 AND NOT ENABLE_WIN32_CONSOLE)
|
||||||
@@ -443,6 +456,7 @@ set(SOURCES
|
|||||||
src/core/enginemetadata.cpp
|
src/core/enginemetadata.cpp
|
||||||
src/core/songmimedata.cpp
|
src/core/songmimedata.cpp
|
||||||
src/core/platforminterface.cpp
|
src/core/platforminterface.cpp
|
||||||
|
src/core/standardpaths.cpp
|
||||||
|
|
||||||
src/utilities/strutils.cpp
|
src/utilities/strutils.cpp
|
||||||
src/utilities/envutils.cpp
|
src/utilities/envutils.cpp
|
||||||
@@ -1202,6 +1216,7 @@ if(APPLE)
|
|||||||
src/osd/osdmac.h
|
src/osd/osdmac.h
|
||||||
src/device/macosdevicelister.h
|
src/device/macosdevicelister.h
|
||||||
)
|
)
|
||||||
|
optional_source(HAVE_SPARKLE SOURCES src/core/sparkleupdater.mm HEADERS src/core/sparkleupdater.h)
|
||||||
else()
|
else()
|
||||||
list(APPEND SOURCES src/systemtrayicon/qtsystemtrayicon.cpp src/widgets/searchfield_qt.cpp src/widgets/searchfield_qt_private.cpp)
|
list(APPEND SOURCES src/systemtrayicon/qtsystemtrayicon.cpp src/widgets/searchfield_qt.cpp src/widgets/searchfield_qt_private.cpp)
|
||||||
list(APPEND HEADERS src/systemtrayicon/qtsystemtrayicon.h src/widgets/searchfield_qt_private.h)
|
list(APPEND HEADERS src/systemtrayicon/qtsystemtrayicon.h src/widgets/searchfield_qt_private.h)
|
||||||
@@ -1306,9 +1321,7 @@ optional_source(HAVE_MOODBAR
|
|||||||
src/settings/moodbarsettingspage.ui
|
src/settings/moodbarsettingspage.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
if(UNIX)
|
optional_source(HAVE_GIO SOURCES src/device/giolister.cpp HEADERS src/device/giolister.h)
|
||||||
optional_source(HAVE_GIO SOURCES src/device/giolister.cpp HEADERS src/device/giolister.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_UDISKS2)
|
if(HAVE_UDISKS2)
|
||||||
optional_source(HAVE_UDISKS2 SOURCES src/device/udisks2lister.cpp HEADERS src/device/udisks2lister.h)
|
optional_source(HAVE_UDISKS2 SOURCES src/device/udisks2lister.cpp HEADERS src/device/udisks2lister.h)
|
||||||
@@ -1456,7 +1469,7 @@ endif()
|
|||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(dist)
|
add_subdirectory(dist)
|
||||||
|
|
||||||
if(TARGET GTest::GTest AND Qt${QT_VERSION_MAJOR}Test_FOUND)
|
if(TARGET GTest::gtest AND TARGET GTest::gmock AND Qt${QT_VERSION_MAJOR}Test_FOUND)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -1485,12 +1498,9 @@ if(SINGLEAPPLICATION_INCLUDE_DIRS)
|
|||||||
target_include_directories(strawberry_lib SYSTEM PUBLIC ${SINGLEAPPLICATION_INCLUDE_DIRS})
|
target_include_directories(strawberry_lib SYSTEM PUBLIC ${SINGLEAPPLICATION_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
|
|
||||||
target_include_directories(strawberry_lib SYSTEM PUBLIC ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(strawberry_lib PUBLIC
|
target_link_libraries(strawberry_lib PUBLIC
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
$<$<BOOL:${HAVE_BACKTRACE}>:${Backtrace_LIBRARIES}>
|
||||||
PkgConfig::GLIB
|
PkgConfig::GLIB
|
||||||
PkgConfig::GOBJECT
|
PkgConfig::GOBJECT
|
||||||
PkgConfig::SQLITE
|
PkgConfig::SQLITE
|
||||||
@@ -1508,6 +1518,7 @@ target_link_libraries(strawberry_lib PUBLIC
|
|||||||
Qt${QT_VERSION_MAJOR}::Network
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
Qt${QT_VERSION_MAJOR}::Sql
|
Qt${QT_VERSION_MAJOR}::Sql
|
||||||
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
||||||
|
$<$<BOOL:${HAVE_QPA_QPLATFORMNATIVEINTERFACE}>:Qt${QT_VERSION_MAJOR}::GuiPrivate>
|
||||||
ICU::uc
|
ICU::uc
|
||||||
ICU::i18n
|
ICU::i18n
|
||||||
$<$<BOOL:${HAVE_ALSA}>:ALSA::ALSA>
|
$<$<BOOL:${HAVE_ALSA}>:ALSA::ALSA>
|
||||||
@@ -1521,8 +1532,7 @@ target_link_libraries(strawberry_lib PUBLIC
|
|||||||
$<$<BOOL:${HAVE_AUDIOCD}>:PkgConfig::LIBCDIO>
|
$<$<BOOL:${HAVE_AUDIOCD}>:PkgConfig::LIBCDIO>
|
||||||
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
|
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
|
||||||
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
||||||
$<$<BOOL:${HAVE_QTSPARKLE}>:PkgConfig::QTSPARKLE>
|
$<$<BOOL:${HAVE_QTSPARKLE}>:qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle>
|
||||||
$<$<BOOL:${FREEBSD}>:execinfo>
|
|
||||||
$<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt>
|
$<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt>
|
||||||
$<$<BOOL:${MSVC}>:WindowsApp>
|
$<$<BOOL:${MSVC}>:WindowsApp>
|
||||||
${SINGLEAPPLICATION_LIBRARIES}
|
${SINGLEAPPLICATION_LIBRARIES}
|
||||||
@@ -1538,6 +1548,10 @@ if(APPLE)
|
|||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
"-framework ScriptingBridge"
|
"-framework ScriptingBridge"
|
||||||
)
|
)
|
||||||
|
if(HAVE_SPARKLE)
|
||||||
|
target_include_directories(strawberry_lib SYSTEM PRIVATE ${SPARKLE}/Headers)
|
||||||
|
target_link_libraries(strawberry_lib PRIVATE ${SPARKLE})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(strawberry PUBLIC strawberry_lib)
|
target_link_libraries(strawberry PUBLIC strawberry_lib)
|
||||||
@@ -1553,7 +1567,7 @@ endif()
|
|||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
||||||
add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
summary_show()
|
optional_component_summary_show()
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING AND NOT QT_SQLITE_TEST)
|
if(NOT CMAKE_CROSSCOMPILING AND NOT QT_SQLITE_TEST)
|
||||||
message(WARNING "The Qt sqlite driver test failed.")
|
message(WARNING "The Qt sqlite driver test failed.")
|
||||||
|
|||||||
59
Changelog
59
Changelog
@@ -2,6 +2,65 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
|
Version 1.2.7 (2025.01.31):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed strawberry exiting when clicking tray icon.
|
||||||
|
* Fixed Clementine import script errors.
|
||||||
|
* Disabled OSD Pretty on Wayland since it's not working properly.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Only maximize error dialog if Strawberry is the active window (#1627).
|
||||||
|
* Added QPA Platform Native Interface as optional component.
|
||||||
|
|
||||||
|
Version 1.2.6 (2025.01.17):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed dragging songs from playlist to queue.
|
||||||
|
|
||||||
|
Version 1.2.5 (2025.01.17):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed crash when saving playcount or rating to file (#1633).
|
||||||
|
* Fixed QFile::open failing in unit tests.
|
||||||
|
* Fixed playlist sequence settings saved to wrong configuration file (#1649).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Fixed use of deprecated GIO functions with GLib 2.84 and newer.
|
||||||
|
* (macOS) Added back Sparkle updater to check for new releases.
|
||||||
|
|
||||||
|
Version 1.2.4 (2025.01.10):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed Spotify songs not being available for scrobbling.
|
||||||
|
* Fixed leading "A" and "The" articles being skipped for album sort text.
|
||||||
|
* Fixed thread safety issue when validating playlist songs on startup.
|
||||||
|
* Fixed filter search not ignoring space after colon when using column based search.
|
||||||
|
* Fixed KGlobalAccel to use capitalized application name.
|
||||||
|
* Fixed slash not properly handled when saving a playlist (#1624).
|
||||||
|
* (Unix) Fixed collection scanner so it ignores special filesystem paths (/sys, /proc, /run, etc) (#1615).
|
||||||
|
* (Windows) Fixed smart playlist wizard not respecting dark mode with Windows 11 style (#1639).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Use XSPF "title" as playlist name when loading and saving playlists (#1624).
|
||||||
|
* Added support for using album ID when receving album covers for Subsonic songs (#1636).
|
||||||
|
* Added option for preserving directory structure when trascoding songs (#1637).
|
||||||
|
* (Windows) Always run MSVC runtime installer to possible fix issues when there is an older runtime installed.
|
||||||
|
|
||||||
|
Version 1.2.3 (2024.12.08):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed libcdio NULL related compilation error on FreeBSD (#1610).
|
||||||
|
* Fixed missing seek when starting playback of a CUE song (#1568).
|
||||||
|
* Fixed "QDBusObjectPath: invalid path" error.
|
||||||
|
|
||||||
|
Version 1.2.2 (2024.11.23):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Fixed crash when creating a new smart playlist (#1609).
|
||||||
|
* Fixed last playlist column being added when dragging a song and switching playlists.
|
||||||
|
|
||||||
Version 1.2.1 (2024.11.21):
|
Version 1.2.1 (2024.11.21):
|
||||||
|
|
||||||
This release features major restructuring of the codebase, moving source files,
|
This release features major restructuring of the codebase, moving source files,
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ find_program(LSB_RELEASE_EXEC lsb_release)
|
|||||||
find_program(DPKG_BUILDPACKAGE dpkg-buildpackage)
|
find_program(DPKG_BUILDPACKAGE dpkg-buildpackage)
|
||||||
|
|
||||||
if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
||||||
add_custom_target(deb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us)
|
add_custom_target(deb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us -nc -j4)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ if(MACDEPLOYQT_EXECUTABLE)
|
|||||||
|
|
||||||
add_custom_target(deploy
|
add_custom_target(deploy
|
||||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
||||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
COMMAND cp -v ${CMAKE_BINARY_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
||||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/strawberry.app
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/strawberry.app
|
||||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
|
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
set(summary_willbuild "")
|
set(summary_willbuild "")
|
||||||
set(summary_willnotbuild "")
|
set(summary_willnotbuild "")
|
||||||
|
|
||||||
macro(summary_add name test)
|
macro(optional_component_summary_add name test)
|
||||||
if (${test})
|
if (${test})
|
||||||
list(APPEND summary_willbuild ${name})
|
list(APPEND summary_willbuild ${name})
|
||||||
else (${test})
|
else (${test})
|
||||||
list(APPEND summary_willnotbuild "${name}")
|
list(APPEND summary_willnotbuild "${name}")
|
||||||
endif (${test})
|
endif (${test})
|
||||||
endmacro(summary_add)
|
endmacro(optional_component_summary_add)
|
||||||
|
|
||||||
macro(summary_show_part variable title)
|
macro(optional_component_summary_show_part variable title)
|
||||||
list(LENGTH ${variable} _len)
|
list(LENGTH ${variable} _len)
|
||||||
if (_len)
|
if (_len)
|
||||||
message("")
|
message("")
|
||||||
@@ -18,19 +18,20 @@ macro(summary_show_part variable title)
|
|||||||
message(" ${_item}")
|
message(" ${_item}")
|
||||||
endforeach (_item)
|
endforeach (_item)
|
||||||
endif (_len)
|
endif (_len)
|
||||||
endmacro(summary_show_part)
|
endmacro(optional_component_summary_show_part)
|
||||||
|
|
||||||
macro(summary_show)
|
macro(optional_component_summary_show)
|
||||||
list(SORT summary_willbuild)
|
list(SORT summary_willbuild)
|
||||||
list(SORT summary_willnotbuild)
|
list(SORT summary_willnotbuild)
|
||||||
message("")
|
message("")
|
||||||
message("Building strawberry version: ${STRAWBERRY_VERSION_DISPLAY}, Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
|
message("Building strawberry version: ${STRAWBERRY_VERSION_DISPLAY}, Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
|
||||||
summary_show_part(summary_willbuild "The following components will be built:")
|
optional_component_summary_show_part(summary_willbuild "The following components will be built:")
|
||||||
summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
|
optional_component_summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
|
||||||
message("")
|
message("")
|
||||||
endmacro(summary_show)
|
endmacro(optional_component_summary_show)
|
||||||
|
|
||||||
function(optional_component name default description)
|
function(optional_component name default description)
|
||||||
|
|
||||||
set(option_variable "ENABLE_${name}")
|
set(option_variable "ENABLE_${name}")
|
||||||
set(have_variable "HAVE_${name}")
|
set(have_variable "HAVE_${name}")
|
||||||
set(${have_variable} OFF)
|
set(${have_variable} OFF)
|
||||||
@@ -79,6 +80,9 @@ function(optional_component name default description)
|
|||||||
set(text "${description} (missing ${deplist_text})")
|
set(text "${description} (missing ${deplist_text})")
|
||||||
|
|
||||||
set(summary_willnotbuild "${summary_willnotbuild};${text}" PARENT_SCOPE)
|
set(summary_willnotbuild "${summary_willnotbuild};${text}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
message(FATAL_ERROR "${text}, to disable this optional feature, pass -D${option_variable}=OFF to CMake")
|
||||||
|
|
||||||
else()
|
else()
|
||||||
set(${have_variable} ON PARENT_SCOPE)
|
set(${have_variable} ON PARENT_SCOPE)
|
||||||
set(summary_willbuild "${summary_willbuild};${description}" PARENT_SCOPE)
|
set(summary_willbuild "${summary_willbuild};${description}" PARENT_SCOPE)
|
||||||
@@ -64,7 +64,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
|||||||
add_custom_target(rpm
|
add_custom_target(rpm
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
||||||
COMMAND ${RPMBUILD_EXEC} -ba ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
COMMAND ${RPMBUILD_EXEC} -ba ${CMAKE_BINARY_DIR}/strawberry.spec
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||||
set(STRAWBERRY_VERSION_MINOR 2)
|
set(STRAWBERRY_VERSION_MINOR 2)
|
||||||
set(STRAWBERRY_VERSION_PATCH 1)
|
set(STRAWBERRY_VERSION_PATCH 7)
|
||||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||||
|
|
||||||
set(INCLUDE_GIT_REVISION OFF)
|
set(INCLUDE_GIT_REVISION OFF)
|
||||||
|
|||||||
3
debian/clean
vendored
Normal file
3
debian/clean
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dist/scripts/maketarball.sh
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
|||||||
11
|
|
||||||
6
debian/control
vendored
6
debian/control
vendored
@@ -2,7 +2,7 @@ Source: strawberry
|
|||||||
Section: sound
|
Section: sound
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Jonas Kvinge <jonas@jkvinge.net>
|
Maintainer: Jonas Kvinge <jonas@jkvinge.net>
|
||||||
Build-Depends: debhelper (>= 11),
|
Build-Depends: debhelper-compat (= 12),
|
||||||
git,
|
git,
|
||||||
make,
|
make,
|
||||||
cmake,
|
cmake,
|
||||||
@@ -15,7 +15,9 @@ Build-Depends: debhelper (>= 11),
|
|||||||
libpulse-dev,
|
libpulse-dev,
|
||||||
libtag1-dev,
|
libtag1-dev,
|
||||||
libicu-dev,
|
libicu-dev,
|
||||||
|
libxkbcommon-dev,
|
||||||
qt6-base-dev,
|
qt6-base-dev,
|
||||||
|
qt6-base-private-dev,
|
||||||
qt6-base-dev-tools,
|
qt6-base-dev-tools,
|
||||||
qt6-tools-dev,
|
qt6-tools-dev,
|
||||||
qt6-tools-dev-tools,
|
qt6-tools-dev-tools,
|
||||||
@@ -28,7 +30,7 @@ Build-Depends: debhelper (>= 11),
|
|||||||
libchromaprint-dev,
|
libchromaprint-dev,
|
||||||
libfftw3-dev,
|
libfftw3-dev,
|
||||||
libebur128-dev
|
libebur128-dev
|
||||||
Standards-Version: 4.6.1
|
Standards-Version: 4.7.0
|
||||||
|
|
||||||
Package: strawberry
|
Package: strawberry
|
||||||
Architecture: any
|
Architecture: any
|
||||||
|
|||||||
15
debian/rules
vendored
15
debian/rules
vendored
@@ -1,17 +1,10 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
%:
|
export DH_VERBOSE=1
|
||||||
dh $@ --buildsystem=cmake -builddirectory=build
|
export DEB_BUILD_MAINT_OPTIONS=hardening=+all
|
||||||
|
|
||||||
override_dh_auto_clean:
|
|
||||||
rm -f dist/macos/Info.plist
|
|
||||||
rm -f dist/unix/strawberry.spec
|
|
||||||
rm -f dist/scripts/maketarball.sh
|
|
||||||
rm -f dist/windows/strawberry.nsi
|
|
||||||
rm -f src/translations/translations.pot
|
|
||||||
dh_auto_clean
|
|
||||||
|
|
||||||
override_dh_installchangelogs:
|
override_dh_installchangelogs:
|
||||||
dh_installchangelogs Changelog
|
dh_installchangelogs Changelog
|
||||||
|
|
||||||
override_dh_auto_test:
|
%:
|
||||||
|
dh $@
|
||||||
|
|||||||
14
dist/CMakeLists.txt
vendored
14
dist/CMakeLists.txt
vendored
@@ -1,7 +1,7 @@
|
|||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
|
||||||
if(RPM_DISTRO AND RPM_DATE)
|
if(RPM_DISTRO AND RPM_DATE)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_BINARY_DIR}/strawberry.spec @ONLY)
|
||||||
endif(RPM_DISTRO AND RPM_DATE)
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
|
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
|
||||||
@@ -9,13 +9,13 @@ if(APPLE)
|
|||||||
else()
|
else()
|
||||||
set(LSMinimumSystemVersion 12.0)
|
set(LSMinimumSystemVersion 12.0)
|
||||||
endif()
|
endif()
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist)
|
||||||
endif(APPLE)
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/windres.rc.in ${CMAKE_BINARY_DIR}/windres.rc)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/windres.rc.in ${CMAKE_BINARY_DIR}/windres.rc)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_BINARY_DIR}/strawberry.nsi @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_BINARY_DIR}/strawberry.nsi @ONLY)
|
||||||
endif(WIN32)
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)
|
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)
|
||||||
@@ -24,9 +24,9 @@ if(UNIX AND NOT APPLE)
|
|||||||
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
|
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
|
||||||
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
|
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
|
||||||
install(FILES unix/strawberry.1 DESTINATION share/man/man1)
|
install(FILES unix/strawberry.1 DESTINATION share/man/man1)
|
||||||
endif(UNIX AND NOT APPLE)
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
4
dist/macos/Info.plist.in
vendored
4
dist/macos/Info.plist.in
vendored
@@ -35,9 +35,9 @@
|
|||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>@LSMinimumSystemVersion@</string>
|
<string>@LSMinimumSystemVersion@</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
<string>https://www.strawberrymusicplayer.org/sparkle-macos-@ARCH@</string>
|
||||||
<key>SUPublicEDKey</key>
|
<key>SUPublicEDKey</key>
|
||||||
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
|
<string>/OydhYVfypuO2Mf7G6DUqVZWW9G19eFV74qaDCBTOUk=</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|||||||
29
dist/scripts/import-from-clementine.sh
vendored
29
dist/scripts/import-from-clementine.sh
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Strawberry Music Player
|
# Strawberry Music Player
|
||||||
# Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
# Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
# 2021 Alexey Vazhnov
|
# Copyright 2021, Alexey Vazhnov
|
||||||
#
|
#
|
||||||
# Strawberry is free software: you can redistribute it and/or modify
|
# Strawberry is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-only
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
|
||||||
# Based on https://github.com/strawberrymusicplayer/strawberry/wiki/Import-collection-library-and-playlists-data-from-Clementine
|
# Based on https://wiki.strawberrymusicplayer.org/wiki/Import_collection_library_and_playlists_from_Clementine
|
||||||
|
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o errexit
|
set -o errexit
|
||||||
@@ -35,8 +35,8 @@ test -f "$FILE_DST" || { echo "No such file: $FILE_DST"; exit 1; }
|
|||||||
|
|
||||||
echo "Will try to copy information from $FILE_SRC to $FILE_DST."
|
echo "Will try to copy information from $FILE_SRC to $FILE_DST."
|
||||||
echo
|
echo
|
||||||
echo 'This script will **delete all information** from Strawberry database!'
|
echo 'This script will **delete all data** from the Strawberry database!'
|
||||||
read -r -p 'Do you want to continue? (the only YES is accepted) ' answer
|
read -r -p 'Do you want to continue? (Only YES is accepted) ' answer
|
||||||
if [ "$answer" != "YES" ]; then exit 1; fi
|
if [ "$answer" != "YES" ]; then exit 1; fi
|
||||||
|
|
||||||
# 'heredoc' with substitution of variables, see `man bash`, "Here Documents":
|
# 'heredoc' with substitution of variables, see `man bash`, "Here Documents":
|
||||||
@@ -62,9 +62,9 @@ INSERT INTO strawberry.subdirectories (directory_id, path, mtime) SELECT directo
|
|||||||
INSERT INTO strawberry.songs (ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path, rating)
|
INSERT INTO strawberry.songs (ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path, rating)
|
||||||
SELECT ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, directory, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, sampler, forced_compilation_on, forced_compilation_off, effective_compilation, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path, rating FROM clementine.songs WHERE unavailable = 0;
|
SELECT ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, directory, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, sampler, forced_compilation_on, forced_compilation_off, effective_compilation, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path, rating FROM clementine.songs WHERE unavailable = 0;
|
||||||
UPDATE strawberry.songs SET source = 2;
|
UPDATE strawberry.songs SET source = 2;
|
||||||
UPDATE strawberry.songs SET artist_id = "";
|
UPDATE strawberry.songs SET artist_id = '';
|
||||||
UPDATE strawberry.songs SET album_id = "";
|
UPDATE strawberry.songs SET album_id = '';
|
||||||
UPDATE strawberry.songs SET song_id = "";
|
UPDATE strawberry.songs SET song_id = '';
|
||||||
|
|
||||||
/* Import playlists */
|
/* Import playlists */
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ SELECT ROWID,
|
|||||||
bitrate,
|
bitrate,
|
||||||
samplerate,
|
samplerate,
|
||||||
directory,
|
directory,
|
||||||
filename,
|
CASE WHEN filename IS NULL THEN '' ELSE filename END,
|
||||||
filetype,
|
filetype,
|
||||||
filesize,
|
filesize,
|
||||||
mtime,
|
mtime,
|
||||||
@@ -162,16 +162,9 @@ SELECT ROWID,
|
|||||||
|
|
||||||
UPDATE strawberry.playlist_items SET source = 2;
|
UPDATE strawberry.playlist_items SET source = 2;
|
||||||
UPDATE strawberry.playlist_items SET type = 2;
|
UPDATE strawberry.playlist_items SET type = 2;
|
||||||
UPDATE strawberry.playlist_items SET artist_id = "";
|
UPDATE strawberry.playlist_items SET artist_id = '';
|
||||||
UPDATE strawberry.playlist_items SET album_id = "";
|
UPDATE strawberry.playlist_items SET album_id = '';
|
||||||
UPDATE strawberry.playlist_items SET song_id = "";
|
UPDATE strawberry.playlist_items SET song_id = '';
|
||||||
|
|
||||||
/* Recreate the FTS tables */
|
|
||||||
|
|
||||||
DELETE FROM strawberry.songs_fts;
|
|
||||||
INSERT INTO strawberry.songs_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
|
|
||||||
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment
|
|
||||||
FROM strawberry.songs;
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,12 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.2.7" date="2025-01-31"/>
|
||||||
|
<release version="1.2.6" date="2025-01-17"/>
|
||||||
|
<release version="1.2.5" date="2025-01-17"/>
|
||||||
|
<release version="1.2.4" date="2025-01-10"/>
|
||||||
|
<release version="1.2.3" date="2024-12-08"/>
|
||||||
|
<release version="1.2.2" date="2024-11-23"/>
|
||||||
<release version="1.2.1" date="2024-11-21"/>
|
<release version="1.2.1" date="2024-11-21"/>
|
||||||
<release version="1.1.3" date="2024-09-21"/>
|
<release version="1.1.3" date="2024-09-21"/>
|
||||||
<release version="1.1.2" date="2024-09-12"/>
|
<release version="1.1.2" date="2024-09-12"/>
|
||||||
|
|||||||
18
dist/unix/strawberry.spec.in
vendored
18
dist/unix/strawberry.spec.in
vendored
@@ -63,6 +63,8 @@ BuildRequires: pkgconfig(libcdio)
|
|||||||
BuildRequires: pkgconfig(libebur128)
|
BuildRequires: pkgconfig(libebur128)
|
||||||
BuildRequires: pkgconfig(libgpod-1.0)
|
BuildRequires: pkgconfig(libgpod-1.0)
|
||||||
BuildRequires: pkgconfig(libmtp)
|
BuildRequires: pkgconfig(libmtp)
|
||||||
|
BuildRequires: cmake(GTest)
|
||||||
|
BuildRequires: pkgconfig(gmock)
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
Requires: qt6-sql-sqlite
|
Requires: qt6-sql-sqlite
|
||||||
@@ -103,13 +105,13 @@ Features:
|
|||||||
|
|
||||||
%build
|
%build
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
export CXXFLAGS="-fPIC -Wno-maybe-uninitialized $RPM_OPT_FLAGS"
|
||||||
%endif
|
%endif
|
||||||
%if "%{?_vendor}" == "openmandriva"
|
%if "%{?_vendor}" == "openmandriva"
|
||||||
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@ -DENABLE_TRANSLATIONS=OFF
|
%{cmake} -DBUILD_WERROR=ON
|
||||||
%make_build
|
%make_build
|
||||||
%else
|
%else
|
||||||
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
|
%{cmake} -DBUILD_WERROR=ON
|
||||||
%cmake_build
|
%cmake_build
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
@@ -120,11 +122,13 @@ Features:
|
|||||||
%cmake_install
|
%cmake_install
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
|
||||||
%suse_update_desktop_file org.strawberrymusicplayer.strawberry Qt AudioVideo Audio Player
|
|
||||||
%endif
|
|
||||||
|
|
||||||
%check
|
%check
|
||||||
|
export QT_QPA_PLATFORM="offscreen"
|
||||||
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
|
%{cmake_build} -t strawberry_tests
|
||||||
|
%else
|
||||||
|
%{make_build} -j $(nproc) -C build strawberry_tests
|
||||||
|
%endif
|
||||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||||
|
|||||||
10
dist/windows/strawberry.nsi.in
vendored
10
dist/windows/strawberry.nsi.in
vendored
@@ -208,14 +208,16 @@ FunctionEnd
|
|||||||
!ifdef msvc
|
!ifdef msvc
|
||||||
!define vc_redist_file "vc_redist.${arch}.exe"
|
!define vc_redist_file "vc_redist.${arch}.exe"
|
||||||
Function InstallMSVCRuntime
|
Function InstallMSVCRuntime
|
||||||
${registry::Read} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${arch}" "Version" $R0 $R1
|
SetOutPath "$TEMP"
|
||||||
${If} $R0 == ""
|
File "${vc_redist_file}"
|
||||||
|
; ${registry::Read} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${arch}" "Version" $R0 $R1
|
||||||
|
; ${If} $R0 == ""
|
||||||
SetDetailsView hide
|
SetDetailsView hide
|
||||||
inetc::get /caption "Downloading..." "https://aka.ms/vs/17/release/${vc_redist_file}" "$TEMP\${vc_redist_file}" /end
|
; inetc::get /caption "Downloading..." "https://aka.ms/vs/17/release/${vc_redist_file}" "$TEMP\${vc_redist_file}" /end
|
||||||
ExecWait '"$TEMP\${vc_redist_file}" /install /passive'
|
ExecWait '"$TEMP\${vc_redist_file}" /install /passive'
|
||||||
Delete "$TEMP\${vc_redist_file}"
|
Delete "$TEMP\${vc_redist_file}"
|
||||||
SetDetailsView show
|
SetDetailsView show
|
||||||
${EndIf}
|
; ${EndIf}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,9 @@
|
|||||||
// Make an INSTRUCTIONS file
|
// Make an INSTRUCTIONS file
|
||||||
// can't mod scope in analyze you have to use transform for 2D use setErasePixmap Qt function insetead of m_background
|
// can't mod scope in analyze you have to use transform for 2D use setErasePixmap Qt function insetead of m_background
|
||||||
|
|
||||||
AnalyzerBase::AnalyzerBase(QWidget *parent, const uint scopeSize)
|
AnalyzerBase::AnalyzerBase(QWidget *parent, const uint scope_size)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
fht_(new FHT(scopeSize)),
|
fht_(new FHT(scope_size)),
|
||||||
engine_(nullptr),
|
engine_(nullptr),
|
||||||
lastscope_(512),
|
lastscope_(512),
|
||||||
new_frame_(false),
|
new_frame_(false),
|
||||||
@@ -211,28 +211,28 @@ void AnalyzerBase::demo(QPainter &p) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerBase::interpolate(const Scope &inVec, Scope &outVec) {
|
void AnalyzerBase::interpolate(const Scope &in_scope, Scope &out_scope) {
|
||||||
|
|
||||||
double pos = 0.0;
|
double pos = 0.0;
|
||||||
const double step = static_cast<double>(inVec.size()) / static_cast<double>(outVec.size());
|
const double step = static_cast<double>(in_scope.size()) / static_cast<double>(out_scope.size());
|
||||||
|
|
||||||
for (uint i = 0; i < outVec.size(); ++i, pos += step) {
|
for (uint i = 0; i < out_scope.size(); ++i, pos += step) {
|
||||||
const double error = pos - std::floor(pos);
|
const double error = pos - std::floor(pos);
|
||||||
const uint64_t offset = static_cast<uint64_t>(pos);
|
const uint64_t offset = static_cast<uint64_t>(pos);
|
||||||
|
|
||||||
uint64_t indexLeft = offset + 0;
|
uint64_t indexLeft = offset + 0;
|
||||||
|
|
||||||
if (indexLeft >= inVec.size()) {
|
if (indexLeft >= in_scope.size()) {
|
||||||
indexLeft = inVec.size() - 1;
|
indexLeft = in_scope.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t indexRight = offset + 1;
|
uint64_t indexRight = offset + 1;
|
||||||
|
|
||||||
if (indexRight >= inVec.size()) {
|
if (indexRight >= in_scope.size()) {
|
||||||
indexRight = inVec.size() - 1;
|
indexRight = in_scope.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
outVec[i] = inVec[indexLeft] * (1.0F - static_cast<float>(error)) + inVec[indexRight] * static_cast<float>(error);
|
out_scope[i] = in_scope[indexLeft] * (1.0F - static_cast<float>(error)) + in_scope[indexRight] * static_cast<float>(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class AnalyzerBase : public QWidget {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Scope = std::vector<float>;
|
using Scope = std::vector<float>;
|
||||||
explicit AnalyzerBase(QWidget*, const uint scopeSize = 7);
|
explicit AnalyzerBase(QWidget *parent, const uint scope_size = 7);
|
||||||
|
|
||||||
void hideEvent(QHideEvent *e) override;
|
void hideEvent(QHideEvent *e) override;
|
||||||
void showEvent(QShowEvent *e) override;
|
void showEvent(QShowEvent *e) override;
|
||||||
@@ -71,12 +71,12 @@ class AnalyzerBase : public QWidget {
|
|||||||
int resizeExponent(int exp);
|
int resizeExponent(int exp);
|
||||||
int resizeForBands(const int bands);
|
int resizeForBands(const int bands);
|
||||||
virtual void init() {}
|
virtual void init() {}
|
||||||
virtual void transform(Scope&);
|
virtual void transform(Scope &scope);
|
||||||
virtual void analyze(QPainter &p, const Scope&, const bool new_frame) = 0;
|
virtual void analyze(QPainter &p, const Scope &s, const bool new_frame) = 0;
|
||||||
virtual void demo(QPainter &p);
|
virtual void demo(QPainter &p);
|
||||||
|
|
||||||
void interpolate(const Scope&, Scope&);
|
void interpolate(const Scope &in_scope, Scope &out_scope);
|
||||||
void initSin(Scope&, const uint = 6000);
|
void initSin(Scope &v, const uint size = 6000);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QBasicTimer timer_;
|
QBasicTimer timer_;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ CollectionBackend::~CollectionBackend() {
|
|||||||
|
|
||||||
void CollectionBackend::Init(SharedPtr<Database> db, SharedPtr<TaskManager> task_manager, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table) {
|
void CollectionBackend::Init(SharedPtr<Database> db, SharedPtr<TaskManager> task_manager, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table) {
|
||||||
|
|
||||||
setObjectName(source == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source), QLatin1String(metaObject()->className())));
|
setObjectName(source == Song::Source::Collection ? QLatin1String(QObject::metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source), QLatin1String(QObject::metaObject()->className())));
|
||||||
|
|
||||||
db_ = db;
|
db_ = db;
|
||||||
task_manager_ = task_manager;
|
task_manager_ = task_manager;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ CollectionLibrary::CollectionLibrary(const SharedPtr<Database> database,
|
|||||||
save_playcounts_to_files_(false),
|
save_playcounts_to_files_(false),
|
||||||
save_ratings_to_files_(false) {
|
save_ratings_to_files_(false) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@
|
|||||||
#include <QPixmapCache>
|
#include <QPixmapCache>
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "includes/scoped_ptr.h"
|
#include "includes/scoped_ptr.h"
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/logging.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "core/songmimedata.h"
|
#include "core/songmimedata.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
@@ -98,7 +98,7 @@ CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, con
|
|||||||
loading_(false),
|
loading_(false),
|
||||||
icon_disk_cache_(new QNetworkDiskCache(this)) {
|
icon_disk_cache_(new QNetworkDiskCache(this)) {
|
||||||
|
|
||||||
setObjectName(backend_->source() == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(backend_->source()), QLatin1String(metaObject()->className())));
|
setObjectName(backend_->source() == Song::Source::Collection ? QLatin1String(QObject::metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(backend_->source()), QLatin1String(QObject::metaObject()->className())));
|
||||||
|
|
||||||
filter_->setSourceModel(this);
|
filter_->setSourceModel(this);
|
||||||
filter_->setSortRole(Role_SortText);
|
filter_->setSortRole(Role_SortText);
|
||||||
@@ -114,7 +114,7 @@ CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, con
|
|||||||
pixmap_no_cover_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
pixmap_no_cover_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
icon_disk_cache_->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir) + u'-' + Song::TextForSource(backend_->source()));
|
icon_disk_cache_->setCacheDirectory(StandardPaths::WritableLocation(StandardPaths::StandardLocation::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir) + u'-' + Song::TextForSource(backend_->source()));
|
||||||
|
|
||||||
QObject::connect(&*backend_, &CollectionBackend::SongsAdded, this, &CollectionModel::AddReAddOrUpdate);
|
QObject::connect(&*backend_, &CollectionBackend::SongsAdded, this, &CollectionModel::AddReAddOrUpdate);
|
||||||
QObject::connect(&*backend_, &CollectionBackend::SongsChanged, this, &CollectionModel::AddReAddOrUpdate);
|
QObject::connect(&*backend_, &CollectionBackend::SongsChanged, this, &CollectionModel::AddReAddOrUpdate);
|
||||||
@@ -288,29 +288,11 @@ void CollectionModel::SetFilterMaxAge(const int filter_max_age) {
|
|||||||
|
|
||||||
QVariant CollectionModel::data(const QModelIndex &idx, const int role) const {
|
QVariant CollectionModel::data(const QModelIndex &idx, const int role) const {
|
||||||
|
|
||||||
const CollectionItem *item = IndexToItem(idx);
|
return data(IndexToItem(idx), role);
|
||||||
|
|
||||||
// Handle a special case for returning album artwork instead of a generic CD icon.
|
|
||||||
// this is here instead of in the other data() function to let us use the
|
|
||||||
// QModelIndex& version of GetChildSongs, which satisfies const-ness, instead
|
|
||||||
// of the CollectionItem *version, which doesn't.
|
|
||||||
if (options_active_.show_pretty_covers) {
|
|
||||||
bool is_album_node = false;
|
|
||||||
if (role == Qt::DecorationRole && item->type == CollectionItem::Type::Container) {
|
|
||||||
GroupBy container_group_by = options_active_.group_by[item->container_level];
|
|
||||||
is_album_node = IsAlbumGroupBy(container_group_by);
|
|
||||||
}
|
|
||||||
if (is_album_node) {
|
|
||||||
// It has const behaviour some of the time - that's ok right?
|
|
||||||
return const_cast<CollectionModel*>(this)->AlbumIcon(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data(item, role);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CollectionModel::data(const CollectionItem *item, const int role) const {
|
QVariant CollectionModel::data(CollectionItem *item, const int role) const {
|
||||||
|
|
||||||
GroupBy container_group_by = item->type == CollectionItem::Type::Container ? options_active_.group_by[item->container_level] : GroupBy::None;
|
GroupBy container_group_by = item->type == CollectionItem::Type::Container ? options_active_.group_by[item->container_level] : GroupBy::None;
|
||||||
|
|
||||||
@@ -329,7 +311,7 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const
|
|||||||
case GroupBy::YearAlbumDisc:
|
case GroupBy::YearAlbumDisc:
|
||||||
case GroupBy::OriginalYearAlbum:
|
case GroupBy::OriginalYearAlbum:
|
||||||
case GroupBy::OriginalYearAlbumDisc:
|
case GroupBy::OriginalYearAlbumDisc:
|
||||||
return QVariant();
|
return options_active_.show_pretty_covers ? const_cast<CollectionModel*>(this)->AlbumIcon(item) : QVariant();
|
||||||
case GroupBy::Artist:
|
case GroupBy::Artist:
|
||||||
case GroupBy::AlbumArtist:
|
case GroupBy::AlbumArtist:
|
||||||
return icon_artist_;
|
return icon_artist_;
|
||||||
@@ -408,17 +390,17 @@ QMimeData *CollectionModel::mimeData(const QModelIndexList &indexes) const {
|
|||||||
|
|
||||||
if (indexes.isEmpty()) return nullptr;
|
if (indexes.isEmpty()) return nullptr;
|
||||||
|
|
||||||
SongMimeData *data = new SongMimeData;
|
SongList songs;
|
||||||
QList<QUrl> urls;
|
|
||||||
QSet<int> song_ids;
|
QSet<int> song_ids;
|
||||||
|
QList<QUrl> urls;
|
||||||
data->backend = backend_;
|
|
||||||
|
|
||||||
for (const QModelIndex &idx : indexes) {
|
for (const QModelIndex &idx : indexes) {
|
||||||
GetChildSongs(IndexToItem(idx), &urls, &data->songs, &song_ids);
|
GetChildSongs(IndexToItem(idx), songs, song_ids, urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SongMimeData *data = new SongMimeData;
|
||||||
data->setUrls(urls);
|
data->setUrls(urls);
|
||||||
|
data->backend = backend_;
|
||||||
|
data->songs = songs;
|
||||||
data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
|
data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -854,9 +836,9 @@ void CollectionModel::LoadSongsFromSqlAsyncFinished() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionModel::AlbumIconPixmapCacheKey(const QModelIndex &idx) const {
|
QString CollectionModel::AlbumIconPixmapCacheKey(const CollectionItem *item) const {
|
||||||
|
|
||||||
return Song::TextForSource(backend_->source()) + QLatin1Char('/') + idx.data(Role_ContainerKey).toString();
|
return Song::TextForSource(backend_->source()) + QLatin1Char('/') + item->container_key;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,7 +851,7 @@ QUrl CollectionModel::AlbumIconPixmapDiskCacheKey(const QString &cache_key) {
|
|||||||
void CollectionModel::ClearItemPixmapCache(CollectionItem *item) {
|
void CollectionModel::ClearItemPixmapCache(CollectionItem *item) {
|
||||||
|
|
||||||
// Remove from pixmap cache
|
// Remove from pixmap cache
|
||||||
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(item));
|
const QString cache_key = AlbumIconPixmapCacheKey(item);
|
||||||
QPixmapCache::remove(cache_key);
|
QPixmapCache::remove(cache_key);
|
||||||
if (use_disk_cache_ && icon_disk_cache_) icon_disk_cache_->remove(AlbumIconPixmapDiskCacheKey(cache_key));
|
if (use_disk_cache_ && icon_disk_cache_) icon_disk_cache_->remove(AlbumIconPixmapDiskCacheKey(cache_key));
|
||||||
if (pending_cache_keys_.contains(cache_key)) {
|
if (pending_cache_keys_.contains(cache_key)) {
|
||||||
@@ -888,13 +870,12 @@ void CollectionModel::ClearItemPixmapCache(CollectionItem *item) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CollectionModel::AlbumIcon(const QModelIndex &idx) {
|
QVariant CollectionModel::AlbumIcon(CollectionItem *item) {
|
||||||
|
|
||||||
CollectionItem *item = IndexToItem(idx);
|
|
||||||
if (!item) return pixmap_no_cover_;
|
if (!item) return pixmap_no_cover_;
|
||||||
|
|
||||||
// Check the cache for a pixmap we already loaded.
|
// Check the cache for a pixmap we already loaded.
|
||||||
const QString cache_key = AlbumIconPixmapCacheKey(idx);
|
const QString cache_key = AlbumIconPixmapCacheKey(item);
|
||||||
|
|
||||||
QPixmap cached_pixmap;
|
QPixmap cached_pixmap;
|
||||||
if (QPixmapCache::find(cache_key, &cached_pixmap)) {
|
if (QPixmapCache::find(cache_key, &cached_pixmap)) {
|
||||||
@@ -919,7 +900,7 @@ QVariant CollectionModel::AlbumIcon(const QModelIndex &idx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No art is cached and we're not loading it already. Load art for the first song in the album.
|
// No art is cached and we're not loading it already. Load art for the first song in the album.
|
||||||
SongList songs = GetChildSongs(idx);
|
const SongList songs = GetChildSongs(item);
|
||||||
if (!songs.isEmpty()) {
|
if (!songs.isEmpty()) {
|
||||||
AlbumCoverLoaderOptions cover_loader_options(AlbumCoverLoaderOptions::Option::ScaledImage | AlbumCoverLoaderOptions::Option::PadScaledImage);
|
AlbumCoverLoaderOptions cover_loader_options(AlbumCoverLoaderOptions::Option::ScaledImage | AlbumCoverLoaderOptions::Option::PadScaledImage);
|
||||||
cover_loader_options.desired_scaled_size = QSize(kPrettyCoverSize, kPrettyCoverSize);
|
cover_loader_options.desired_scaled_size = QSize(kPrettyCoverSize, kPrettyCoverSize);
|
||||||
@@ -1095,7 +1076,7 @@ QString CollectionModel::SortText(const GroupBy group_by, const Song &song, cons
|
|||||||
case GroupBy::Artist:
|
case GroupBy::Artist:
|
||||||
return SortTextForArtist(song.artist(), sort_skips_articles);
|
return SortTextForArtist(song.artist(), sort_skips_articles);
|
||||||
case GroupBy::Album:
|
case GroupBy::Album:
|
||||||
return SortTextForArtist(song.album(), sort_skips_articles);
|
return SortText(song.album());
|
||||||
case GroupBy::AlbumDisc:
|
case GroupBy::AlbumDisc:
|
||||||
return song.album() + SortTextForNumber(std::max(0, song.disc()));
|
return song.album() + SortTextForNumber(std::max(0, song.disc()));
|
||||||
case GroupBy::YearAlbum:
|
case GroupBy::YearAlbum:
|
||||||
@@ -1412,7 +1393,7 @@ QString CollectionModel::DividerDisplayText(const GroupBy group_by, const QStrin
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollectionModel::CompareItems(const CollectionItem *a, const CollectionItem *b) const {
|
bool CollectionModel::CompareItems(CollectionItem *a, CollectionItem *b) const {
|
||||||
|
|
||||||
QVariant left = data(a, CollectionModel::Role_SortText);
|
QVariant left = data(a, CollectionModel::Role_SortText);
|
||||||
QVariant right = data(b, CollectionModel::Role_SortText);
|
QVariant right = data(b, CollectionModel::Role_SortText);
|
||||||
@@ -1453,24 +1434,23 @@ qint64 CollectionModel::MaximumCacheSize(Settings *s, const char *size_id, const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const {
|
void CollectionModel::GetChildSongs(CollectionItem *item, SongList &songs, QSet<int> &song_ids, QList<QUrl> &urls) const {
|
||||||
|
|
||||||
switch (item->type) {
|
switch (item->type) {
|
||||||
case CollectionItem::Type::Container: {
|
case CollectionItem::Type::Container: {
|
||||||
QList<CollectionItem*> children = item->children;
|
QList<CollectionItem*> children = item->children;
|
||||||
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
|
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
for (CollectionItem *child : children) {
|
for (CollectionItem *child : children) {
|
||||||
GetChildSongs(child, urls, songs, song_ids);
|
GetChildSongs(child, songs, song_ids, urls);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CollectionItem::Type::Song:
|
case CollectionItem::Type::Song:
|
||||||
urls->append(item->metadata.url());
|
urls << item->metadata.url();
|
||||||
if (!song_ids->contains(item->metadata.id())) {
|
if (!song_ids.contains(item->metadata.id())) {
|
||||||
songs->append(item->metadata);
|
songs << item->metadata;
|
||||||
song_ids->insert(item->metadata.id());
|
song_ids << item->metadata.id();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1480,16 +1460,33 @@ void CollectionModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls, Son
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SongList CollectionModel::GetChildSongs(const QList<CollectionItem*> items) const {
|
||||||
|
|
||||||
|
SongList songs;
|
||||||
|
QSet<int> song_ids;
|
||||||
|
QList<QUrl> urls;
|
||||||
|
for (CollectionItem *item : items) {
|
||||||
|
GetChildSongs(item, songs, song_ids, urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
return songs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SongList CollectionModel::GetChildSongs(CollectionItem *item) const {
|
||||||
|
return GetChildSongs(QList<CollectionItem*>() << item);
|
||||||
|
}
|
||||||
|
|
||||||
SongList CollectionModel::GetChildSongs(const QModelIndexList &indexes) const {
|
SongList CollectionModel::GetChildSongs(const QModelIndexList &indexes) const {
|
||||||
|
|
||||||
QList<QUrl> dontcare;
|
SongList songs;
|
||||||
SongList ret;
|
|
||||||
QSet<int> song_ids;
|
QSet<int> song_ids;
|
||||||
|
QList<QUrl> urls;
|
||||||
for (const QModelIndex &idx : indexes) {
|
for (const QModelIndex &idx : indexes) {
|
||||||
GetChildSongs(IndexToItem(idx), &dontcare, &ret, &song_ids);
|
GetChildSongs(IndexToItem(idx), songs, song_ids, urls);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
return songs;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,11 +194,13 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
QString ContainerKey(const GroupBy group_by, const Song &song, bool &has_unique_album_identifier) const;
|
QString ContainerKey(const GroupBy group_by, const Song &song, bool &has_unique_album_identifier) const;
|
||||||
|
|
||||||
// Get information about the collection
|
// Get information about the collection
|
||||||
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
|
void GetChildSongs(CollectionItem *item, SongList &songs, QSet<int> &song_ids, QList<QUrl> &urls) const;
|
||||||
|
SongList GetChildSongs(const QList<CollectionItem*> items) const;
|
||||||
|
SongList GetChildSongs(CollectionItem *item) const;
|
||||||
SongList GetChildSongs(const QModelIndex &idx) const;
|
SongList GetChildSongs(const QModelIndex &idx) const;
|
||||||
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
||||||
|
|
||||||
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
bool CompareItems(CollectionItem *a, CollectionItem *b) const;
|
||||||
|
|
||||||
bool HasParentAlbumGroupBy(CollectionItem *item) const;
|
bool HasParentAlbumGroupBy(CollectionItem *item) const;
|
||||||
|
|
||||||
@@ -224,7 +226,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
void BeginReset();
|
void BeginReset();
|
||||||
void EndReset();
|
void EndReset();
|
||||||
|
|
||||||
QVariant data(const CollectionItem *item, const int role) const;
|
QVariant data(CollectionItem *item, const int role) const;
|
||||||
|
|
||||||
void ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs);
|
void ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs);
|
||||||
void ScheduleAddSongs(const SongList &songs);
|
void ScheduleAddSongs(const SongList &songs);
|
||||||
@@ -250,9 +252,9 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; }
|
static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; }
|
||||||
QString AlbumIconPixmapCacheKey(const QModelIndex &idx) const;
|
QString AlbumIconPixmapCacheKey(const CollectionItem *item) const;
|
||||||
static QUrl AlbumIconPixmapDiskCacheKey(const QString &cache_key);
|
static QUrl AlbumIconPixmapDiskCacheKey(const QString &cache_key);
|
||||||
QVariant AlbumIcon(const QModelIndex &idx);
|
QVariant AlbumIcon(CollectionItem *item);
|
||||||
void ClearItemPixmapCache(CollectionItem *item);
|
void ClearItemPixmapCache(CollectionItem *item);
|
||||||
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
#include "collectionitem.h"
|
#include "collectionitem.h"
|
||||||
#include "collectionitemdelegate.h"
|
#include "collectionitemdelegate.h"
|
||||||
#include "collectionview.h"
|
#include "collectionview.h"
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
# include "device/devicemanager.h"
|
# include "device/devicemanager.h"
|
||||||
# include "device/devicestatefiltermodel.h"
|
# include "device/devicestatefiltermodel.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -95,7 +95,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
action_open_in_new_playlist_(nullptr),
|
action_open_in_new_playlist_(nullptr),
|
||||||
action_organize_(nullptr),
|
action_organize_(nullptr),
|
||||||
action_search_for_this_(nullptr),
|
action_search_for_this_(nullptr),
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
action_copy_to_device_(nullptr),
|
action_copy_to_device_(nullptr),
|
||||||
#endif
|
#endif
|
||||||
action_edit_track_(nullptr),
|
action_edit_track_(nullptr),
|
||||||
@@ -108,7 +108,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
is_in_keyboard_search_(false),
|
is_in_keyboard_search_(false),
|
||||||
delete_files_(false) {
|
delete_files_(false) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
setItemDelegate(new CollectionItemDelegate(this));
|
setItemDelegate(new CollectionItemDelegate(this));
|
||||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
@@ -417,7 +417,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
action_organize_ = context_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &CollectionView::Organize);
|
action_organize_ = context_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &CollectionView::Organize);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
||||||
#endif
|
#endif
|
||||||
action_delete_files_ = context_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &CollectionView::Delete);
|
action_delete_files_ = context_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &CollectionView::Delete);
|
||||||
@@ -439,7 +439,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
context_menu_->addMenu(filter_widget_->menu());
|
context_menu_->addMenu(filter_widget_->menu());
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
|
action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
|
||||||
QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
|
QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
|
||||||
#endif
|
#endif
|
||||||
@@ -481,7 +481,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
action_rescan_songs_->setEnabled(regular_editable > 0);
|
action_rescan_songs_->setEnabled(regular_editable > 0);
|
||||||
|
|
||||||
action_organize_->setVisible(regular_elements == regular_editable);
|
action_organize_->setVisible(regular_elements == regular_editable);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
action_copy_to_device_->setVisible(regular_elements == regular_editable);
|
action_copy_to_device_->setVisible(regular_elements == regular_editable);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -492,7 +492,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
// only when all selected items are editable
|
// only when all selected items are editable
|
||||||
action_organize_->setEnabled(regular_elements == regular_editable);
|
action_organize_->setEnabled(regular_elements == regular_editable);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
action_copy_to_device_->setEnabled(regular_elements == regular_editable);
|
action_copy_to_device_->setEnabled(regular_elements == regular_editable);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -759,7 +759,7 @@ void CollectionView::RescanSongs() {
|
|||||||
|
|
||||||
void CollectionView::CopyToDevice() {
|
void CollectionView::CopyToDevice() {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
if (!organize_dialog_) {
|
if (!organize_dialog_) {
|
||||||
organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, nullptr, this);
|
organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, nullptr, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
QAction *action_organize_;
|
QAction *action_organize_;
|
||||||
QAction *action_search_for_this_;
|
QAction *action_search_for_this_;
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
QAction *action_copy_to_device_;
|
QAction *action_copy_to_device_;
|
||||||
#endif
|
#endif
|
||||||
QAction *action_edit_track_;
|
QAction *action_edit_track_;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
|
#include <QStorageInfo>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "utilities/imageutils.h"
|
#include "utilities/imageutils.h"
|
||||||
#include "constants/timeconstants.h"
|
#include "constants/timeconstants.h"
|
||||||
|
#include "constants/filesystemconstants.h"
|
||||||
#include "tagreader/tagreaderclient.h"
|
#include "tagreader/tagreaderclient.h"
|
||||||
#include "collectiondirectory.h"
|
#include "collectiondirectory.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
@@ -104,7 +106,7 @@ CollectionWatcher::CollectionWatcher(const Song::Source source,
|
|||||||
cue_parser_(new CueParser(tagreader_client, backend, this)),
|
cue_parser_(new CueParser(tagreader_client, backend, this)),
|
||||||
last_scan_time_(0) {
|
last_scan_time_(0) {
|
||||||
|
|
||||||
setObjectName(source_ == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source_), QLatin1String(metaObject()->className())));
|
setObjectName(source_ == Song::Source::Collection ? QLatin1String(QObject::metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source_), QLatin1String(QObject::metaObject()->className())));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
@@ -464,6 +466,24 @@ CollectionSubdirectoryList CollectionWatcher::ScanTransaction::GetAllSubdirs() {
|
|||||||
|
|
||||||
void CollectionWatcher::AddDirectory(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdirs) {
|
void CollectionWatcher::AddDirectory(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdirs) {
|
||||||
|
|
||||||
|
{
|
||||||
|
const QFileInfo path_info(dir.path);
|
||||||
|
if (path_info.isSymbolicLink()) {
|
||||||
|
const QStorageInfo storage_info(path_info.symLinkTarget());
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
qLog(Warning) << "Ignoring collection directory path" << dir.path << "which is a symbolic link to path" << path_info.symLinkTarget() << "with rejected filesystem type" << storage_info.fileSystemType();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const QStorageInfo storage_info(dir.path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
qLog(Warning) << "Ignoring collection directory path" << dir.path << "with rejected filesystem type" << storage_info.fileSystemType();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CancelStop();
|
CancelStop();
|
||||||
|
|
||||||
watched_dirs_[dir.id] = dir;
|
watched_dirs_[dir.id] = dir;
|
||||||
@@ -506,17 +526,29 @@ void CollectionWatcher::AddDirectory(const CollectionDirectory &dir, const Colle
|
|||||||
|
|
||||||
void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSubdirectory &subdir, const quint64 files_count, ScanTransaction *t, const bool force_noincremental) {
|
void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSubdirectory &subdir, const quint64 files_count, ScanTransaction *t, const bool force_noincremental) {
|
||||||
|
|
||||||
QFileInfo path_info(path);
|
const QFileInfo path_info(path);
|
||||||
|
|
||||||
// Do not scan symlinked dirs that are already in collection
|
|
||||||
if (path_info.isSymLink()) {
|
if (path_info.isSymLink()) {
|
||||||
QString real_path = path_info.symLinkTarget();
|
const QString real_path = path_info.symLinkTarget();
|
||||||
|
const QStorageInfo storage_info(real_path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
qLog(Warning) << "Ignoring symbolic link" << path << "which links to" << real_path << "with rejected filesystem type" << storage_info.fileSystemType();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do not scan symlinked dirs that are already in collection
|
||||||
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
if (real_path.startsWith(dir.path)) {
|
if (real_path.startsWith(dir.path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const QStorageInfo storage_info(path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
qLog(Warning) << "Ignoring path" << path << "with rejected filesystem type" << storage_info.fileSystemType();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool songs_missing_fingerprint = false;
|
bool songs_missing_fingerprint = false;
|
||||||
bool songs_missing_loudness_characteristics = false;
|
bool songs_missing_loudness_characteristics = false;
|
||||||
@@ -556,32 +588,40 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
|
|
||||||
if (stop_or_abort_requested()) return;
|
if (stop_or_abort_requested()) return;
|
||||||
|
|
||||||
QString child(it.next());
|
const QString child_filepath = it.next();
|
||||||
QFileInfo child_info(child);
|
const QFileInfo child_fileinfo(child_filepath);
|
||||||
|
|
||||||
if (child_info.isDir()) {
|
if (child_fileinfo.isSymLink()) {
|
||||||
if (!t->HasSeenSubdir(child)) {
|
QStorageInfo storage_info(child_fileinfo.symLinkTarget());
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
qLog(Warning) << "Ignoring symbolic link" << child_filepath << "which links to" << child_fileinfo.symLinkTarget() << "with rejected filesystem type" << storage_info.fileSystemType();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child_fileinfo.isDir()) {
|
||||||
|
if (!t->HasSeenSubdir(child_filepath)) {
|
||||||
// We haven't seen this subdirectory before - add it to a list, and later we'll tell the backend about it and scan it.
|
// We haven't seen this subdirectory before - add it to a list, and later we'll tell the backend about it and scan it.
|
||||||
CollectionSubdirectory new_subdir;
|
CollectionSubdirectory new_subdir;
|
||||||
new_subdir.directory_id = -1;
|
new_subdir.directory_id = -1;
|
||||||
new_subdir.path = child;
|
new_subdir.path = child_filepath;
|
||||||
new_subdir.mtime = child_info.lastModified().toSecsSinceEpoch();
|
new_subdir.mtime = child_fileinfo.lastModified().toSecsSinceEpoch();
|
||||||
my_new_subdirs << new_subdir;
|
my_new_subdirs << new_subdir;
|
||||||
}
|
}
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
QString ext_part(ExtensionPart(child));
|
QString ext_part(ExtensionPart(child_filepath));
|
||||||
QString dir_part(DirectoryPart(child));
|
QString dir_part(DirectoryPart(child_filepath));
|
||||||
if (Song::kRejectedExtensions.contains(child_info.suffix(), Qt::CaseInsensitive) || child_info.baseName() == "qt_temp"_L1) {
|
if (Song::kRejectedExtensions.contains(child_fileinfo.suffix(), Qt::CaseInsensitive) || child_fileinfo.baseName() == "qt_temp"_L1) {
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
}
|
}
|
||||||
else if (sValidImages.contains(ext_part)) {
|
else if (sValidImages.contains(ext_part)) {
|
||||||
album_art[dir_part] << child;
|
album_art[dir_part] << child_filepath;
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
}
|
}
|
||||||
else if (tagreader_client_->IsMediaFileBlocking(child)) {
|
else if (tagreader_client_->IsMediaFileBlocking(child_filepath)) {
|
||||||
files_on_disk << child;
|
files_on_disk << child_filepath;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
@@ -828,7 +868,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file,
|
|||||||
qLog(Error) << "Could not open CUE file" << matching_cue << "for reading:" << cue_file.errorString();
|
qLog(Error) << "Could not open CUE file" << matching_cue << "for reading:" << cue_file.errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const SongList songs = cue_parser_->Load(&cue_file, matching_cue, path, false);
|
const SongList songs = cue_parser_->Load(&cue_file, matching_cue, path, false).songs;
|
||||||
cue_file.close();
|
cue_file.close();
|
||||||
|
|
||||||
// Update every song that's in the CUE and collection
|
// Update every song that's in the CUE and collection
|
||||||
@@ -915,7 +955,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
|
|||||||
// Also, watch out for incorrect media files.
|
// Also, watch out for incorrect media files.
|
||||||
// Playlist parser for CUEs considers every entry in sheet valid, and we don't want invalid media getting into collection!
|
// Playlist parser for CUEs considers every entry in sheet valid, and we don't want invalid media getting into collection!
|
||||||
QString file_nfd = file.normalized(QString::NormalizationForm_D);
|
QString file_nfd = file.normalized(QString::NormalizationForm_D);
|
||||||
SongList cue_songs = cue_parser_->Load(&cue_file, matching_cue, path, false);
|
SongList cue_songs = cue_parser_->Load(&cue_file, matching_cue, path, false).songs;
|
||||||
cue_file.close();
|
cue_file.close();
|
||||||
songs.reserve(cue_songs.count());
|
songs.reserve(cue_songs.count());
|
||||||
for (Song &cue_song : cue_songs) {
|
for (Song &cue_song : cue_songs) {
|
||||||
@@ -1153,7 +1193,7 @@ void CollectionWatcher::RescanPathsNow() {
|
|||||||
|
|
||||||
QMap<QString, quint64> subdir_files_count;
|
QMap<QString, quint64> subdir_files_count;
|
||||||
for (const QString &path : paths) {
|
for (const QString &path : paths) {
|
||||||
quint64 files_count = FilesCountForPath(&transaction, path);
|
const quint64 files_count = FilesCountForPath(&transaction, path);
|
||||||
subdir_files_count[path] = files_count;
|
subdir_files_count[path] = files_count;
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
}
|
}
|
||||||
@@ -1316,18 +1356,45 @@ void CollectionWatcher::PerformScan(const bool incremental, const bool ignore_mt
|
|||||||
|
|
||||||
quint64 CollectionWatcher::FilesCountForPath(ScanTransaction *t, const QString &path) {
|
quint64 CollectionWatcher::FilesCountForPath(ScanTransaction *t, const QString &path) {
|
||||||
|
|
||||||
|
const QFileInfo path_info(path);
|
||||||
|
if (path_info.isSymLink()) {
|
||||||
|
const QString real_path = path_info.symLinkTarget();
|
||||||
|
const QStorageInfo storage_info(real_path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
|
if (real_path.startsWith(dir.path)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const QStorageInfo storage_info(path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
quint64 i = 0;
|
quint64 i = 0;
|
||||||
QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
|
|
||||||
if (stop_or_abort_requested()) break;
|
if (stop_or_abort_requested()) break;
|
||||||
|
|
||||||
QString child = it.next();
|
const QString child_filepath = it.next();
|
||||||
QFileInfo path_info(child);
|
const QFileInfo child_fileinfo(child_filepath);
|
||||||
|
|
||||||
|
if (child_fileinfo.isDir()) {
|
||||||
|
if (child_fileinfo.isSymLink()) {
|
||||||
|
|
||||||
|
const QString real_path = child_fileinfo.symLinkTarget();
|
||||||
|
|
||||||
|
QStorageInfo storage_info(real_path);
|
||||||
|
if (kRejectedFileSystems.contains(storage_info.fileSystemType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (path_info.isDir()) {
|
|
||||||
if (path_info.isSymLink()) {
|
|
||||||
QString real_path = path_info.symLinkTarget();
|
|
||||||
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
if (real_path.startsWith(dir.path)) {
|
if (real_path.startsWith(dir.path)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1335,9 +1402,9 @@ quint64 CollectionWatcher::FilesCountForPath(ScanTransaction *t, const QString &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!t->HasSeenSubdir(child) && !path_info.isHidden()) {
|
if (!t->HasSeenSubdir(child_filepath) && !child_fileinfo.isHidden()) {
|
||||||
// We haven't seen this subdirectory before, so we need to include the file count for this directory too.
|
// We haven't seen this subdirectory before, so we need to include the file count for this directory too.
|
||||||
i += FilesCountForPath(t, child);
|
i += FilesCountForPath(t, child_filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1385,7 +1452,7 @@ void CollectionWatcher::RescanSongs(const SongList &songs) {
|
|||||||
if (stop_or_abort_requested()) break;
|
if (stop_or_abort_requested()) break;
|
||||||
if (subdir.path != song_path) continue;
|
if (subdir.path != song_path) continue;
|
||||||
qLog(Debug) << "Rescan for directory ID" << song.directory_id() << "directory" << subdir.path;
|
qLog(Debug) << "Rescan for directory ID" << song.directory_id() << "directory" << subdir.path;
|
||||||
quint64 files_count = FilesCountForPath(&transaction, subdir.path);
|
const quint64 files_count = FilesCountForPath(&transaction, subdir.path);
|
||||||
ScanSubdirectory(song_path, subdir, files_count, &transaction);
|
ScanSubdirectory(song_path, subdir, files_count, &transaction);
|
||||||
scanned_paths << subdir.path;
|
scanned_paths << subdir.path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#cmakedefine HAVE_AUDIOCD
|
#cmakedefine HAVE_AUDIOCD
|
||||||
#cmakedefine HAVE_MTP
|
#cmakedefine HAVE_MTP
|
||||||
#cmakedefine HAVE_GPOD
|
#cmakedefine HAVE_GPOD
|
||||||
|
#cmakedefine HAVE_SPARKLE
|
||||||
#cmakedefine HAVE_QTSPARKLE
|
#cmakedefine HAVE_QTSPARKLE
|
||||||
#cmakedefine HAVE_SONGFINGERPRINTING
|
#cmakedefine HAVE_SONGFINGERPRINTING
|
||||||
#cmakedefine HAVE_MUSICBRAINZ
|
#cmakedefine HAVE_MUSICBRAINZ
|
||||||
@@ -40,8 +41,8 @@
|
|||||||
#cmakedefine INSTALL_TRANSLATIONS
|
#cmakedefine INSTALL_TRANSLATIONS
|
||||||
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/translations"
|
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/translations"
|
||||||
|
|
||||||
|
#cmakedefine HAVE_QPA_QPLATFORMNATIVEINTERFACE
|
||||||
#cmakedefine HAVE_QX11APPLICATION
|
#cmakedefine HAVE_QX11APPLICATION
|
||||||
#cmakedefine HAVE_QPA_QPLATFORMNATIVEINTERFACE_H
|
|
||||||
|
|
||||||
#cmakedefine ENABLE_WIN32_CONSOLE
|
#cmakedefine ENABLE_WIN32_CONSOLE
|
||||||
|
|
||||||
|
|||||||
31
src/constants/filesystemconstants.h
Normal file
31
src/constants/filesystemconstants.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILESYSTEMCONSTANTS_H
|
||||||
|
#define FILESYSTEMCONSTANTS_H
|
||||||
|
|
||||||
|
#include <QByteArrayList>
|
||||||
|
|
||||||
|
const QByteArrayList kRejectedFileSystems = QByteArrayList() << "proc"
|
||||||
|
<< "sysfs"
|
||||||
|
<< "tmpfs"
|
||||||
|
<< "devtmpfs";
|
||||||
|
|
||||||
|
|
||||||
|
#endif // FILESYSTEMCONSTANTS_H
|
||||||
@@ -37,6 +37,7 @@ constexpr char kPassword[] = "password";
|
|||||||
constexpr char kHTTP2[] = "http2";
|
constexpr char kHTTP2[] = "http2";
|
||||||
constexpr char kVerifyCertificate[] = "verifycertificate";
|
constexpr char kVerifyCertificate[] = "verifycertificate";
|
||||||
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
||||||
|
constexpr char kUseAlbumIdForAlbumCovers[] = "usealbumidforalbumcovers";
|
||||||
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
|
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
|
||||||
constexpr char kAuthMethod[] = "authmethod";
|
constexpr char kAuthMethod[] = "authmethod";
|
||||||
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ Application::Application(QObject *parent)
|
|||||||
p_(new ApplicationImpl(this)),
|
p_(new ApplicationImpl(this)),
|
||||||
g_thread_(nullptr) {
|
g_thread_(nullptr) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
const QMetaObject *mo = QAbstractEventDispatcher::instance(QCoreApplication::instance()->thread())->metaObject();
|
const QMetaObject *mo = QAbstractEventDispatcher::instance(QCoreApplication::instance()->thread())->metaObject();
|
||||||
if (mo && strcmp(mo->className(), "QEventDispatcherGlib") != 0 && strcmp(mo->superClass()->className(), "QEventDispatcherGlib") != 0) {
|
if (mo && strcmp(mo->className(), "QEventDispatcherGlib") != 0 && strcmp(mo->superClass()->className(), "QEventDispatcherGlib") != 0) {
|
||||||
|
|||||||
@@ -39,10 +39,10 @@
|
|||||||
#include <QSqlDriver>
|
#include <QSqlDriver>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "logging.h"
|
||||||
|
#include "standardpaths.h"
|
||||||
#include "taskmanager.h"
|
#include "taskmanager.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "sqlquery.h"
|
#include "sqlquery.h"
|
||||||
@@ -69,7 +69,7 @@ Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const Q
|
|||||||
startup_schema_version_(-1),
|
startup_schema_version_(-1),
|
||||||
original_thread_(nullptr) {
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const Q
|
|||||||
connection_id_ = sNextConnectionId++;
|
connection_id_ = sNextConnectionId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
directory_ = QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
|
directory_ = QDir::toNativeSeparators(StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation)).replace(u"Strawberry"_s, u"strawberry"_s);
|
||||||
|
|
||||||
QMutexLocker l(&mutex_);
|
QMutexLocker l(&mutex_);
|
||||||
Connect();
|
Connect();
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "standardpaths.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "includes/iconmapper.h"
|
#include "includes/iconmapper.h"
|
||||||
#include "iconloader.h"
|
#include "iconloader.h"
|
||||||
@@ -43,7 +43,7 @@ bool IconLoader::custom_icons_ = false;
|
|||||||
|
|
||||||
void IconLoader::Init() {
|
void IconLoader::Init() {
|
||||||
|
|
||||||
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
|
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN32)
|
||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(AppearanceSettings::kSettingsGroup);
|
s.beginGroup(AppearanceSettings::kSettingsGroup);
|
||||||
system_icons_ = s.value("system_icons", false).toBool();
|
system_icons_ = s.value("system_icons", false).toBool();
|
||||||
@@ -51,7 +51,7 @@ void IconLoader::Init() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
QDir dir;
|
QDir dir;
|
||||||
if (dir.exists(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/icons"_s)) {
|
if (dir.exists(StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/icons"_s)) {
|
||||||
custom_icons_ = true;
|
custom_icons_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ QIcon IconLoader::Load(const QString &name, const bool system_icon, const int fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (custom_icons_) {
|
if (custom_icons_) {
|
||||||
QString custom_icon_path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/icons/%1x%2/%3.png"_s;
|
QString custom_icon_path = StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/icons/%1x%2/%3.png"_s;
|
||||||
for (int s : std::as_const(sizes)) {
|
for (int s : std::as_const(sizes)) {
|
||||||
QString filename(custom_icon_path.arg(s).arg(s).arg(name));
|
QString filename(custom_icon_path.arg(s).arg(s).arg(name));
|
||||||
if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
|
if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
|
||||||
|
|||||||
@@ -156,7 +156,7 @@
|
|||||||
#include "lyrics/lyricsproviders.h"
|
#include "lyrics/lyricsproviders.h"
|
||||||
#include "device/devicemanager.h"
|
#include "device/devicemanager.h"
|
||||||
#include "device/devicestatefiltermodel.h"
|
#include "device/devicestatefiltermodel.h"
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
# include "device/deviceview.h"
|
# include "device/deviceview.h"
|
||||||
# include "device/deviceviewcontainer.h"
|
# include "device/deviceviewcontainer.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
|
|
||||||
#include "organize/organizeerrordialog.h"
|
#include "organize/organizeerrordialog.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
# include "core/windows7thumbbar.h"
|
# include "core/windows7thumbbar.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -220,6 +220,10 @@
|
|||||||
# include "systemtrayicon/qtsystemtrayicon.h"
|
# include "systemtrayicon/qtsystemtrayicon.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPARKLE
|
||||||
|
#include "core/sparkleupdater.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_QTSPARKLE
|
#ifdef HAVE_QTSPARKLE
|
||||||
# include <qtsparkle-qt6/Updater>
|
# include <qtsparkle-qt6/Updater>
|
||||||
#endif // HAVE_QTSPARKLE
|
#endif // HAVE_QTSPARKLE
|
||||||
@@ -235,25 +239,46 @@ const int kTrackPositionUpdateTimeMs = 1000;
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#ifdef HAVE_QTSPARKLE
|
#ifdef HAVE_QTSPARKLE
|
||||||
# ifdef _MSC_VER
|
namespace {
|
||||||
# ifdef _M_X64
|
|
||||||
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-msvc-x64";
|
# if defined(__APPLE__)
|
||||||
|
# if defined(__x86_64__)
|
||||||
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-macos-x86_64";
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-macos-arm64";
|
||||||
# else
|
# else
|
||||||
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-msvc-x86";
|
# error "Unsupported macOS arch for QtSparkle"
|
||||||
# endif
|
# endif
|
||||||
# else
|
|
||||||
# ifdef __x86_64__
|
# elif defined(__MINGW32__)
|
||||||
|
# if defined(__x86_64__)
|
||||||
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-mingw-x64";
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-mingw-x64";
|
||||||
# else
|
# elif defined(__i686__)
|
||||||
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-mingw-x86";
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-mingw-x86";
|
||||||
|
# else
|
||||||
|
# error "Unsupported MinGW arch for QtSparkle"
|
||||||
# endif
|
# endif
|
||||||
# endif
|
|
||||||
#endif
|
# elif defined(_MSC_VER)
|
||||||
|
# if defined(_WIN64)
|
||||||
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-msvc-x64";
|
||||||
|
# elif defined(_WIN32)
|
||||||
|
constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-windows-msvc-x86";
|
||||||
|
# else
|
||||||
|
# error "Unsupported MSVC arch for QtSparkle"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# else
|
||||||
|
# error "Unsupported OS for QtSparkle"
|
||||||
|
# endif // OS
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
#endif // HAVE_QTSPARKLE
|
||||||
|
|
||||||
MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent)
|
MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent)
|
||||||
: QMainWindow(parent),
|
: QMainWindow(parent),
|
||||||
ui_(new Ui_MainWindow),
|
ui_(new Ui_MainWindow),
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
thumbbar_(new Windows7ThumbBar(this)),
|
thumbbar_(new Windows7ThumbBar(this)),
|
||||||
#endif
|
#endif
|
||||||
app_(app),
|
app_(app),
|
||||||
@@ -272,7 +297,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
context_view_(new ContextView(this)),
|
context_view_(new ContextView(this)),
|
||||||
collection_view_(new CollectionViewContainer(this)),
|
collection_view_(new CollectionViewContainer(this)),
|
||||||
file_view_(new FileView(this)),
|
file_view_(new FileView(this)),
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
device_view_(new DeviceViewContainer(this)),
|
device_view_(new DeviceViewContainer(this)),
|
||||||
#endif
|
#endif
|
||||||
playlist_list_(new PlaylistListContainer(this)),
|
playlist_list_(new PlaylistListContainer(this)),
|
||||||
@@ -337,7 +362,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
playlist_move_to_collection_(nullptr),
|
playlist_move_to_collection_(nullptr),
|
||||||
playlist_open_in_browser_(nullptr),
|
playlist_open_in_browser_(nullptr),
|
||||||
playlist_organize_(nullptr),
|
playlist_organize_(nullptr),
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
playlist_copy_to_device_(nullptr),
|
playlist_copy_to_device_(nullptr),
|
||||||
#endif
|
#endif
|
||||||
playlist_delete_(nullptr),
|
playlist_delete_(nullptr),
|
||||||
@@ -391,7 +416,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
ui_->tabs->AddTab(smartplaylists_view_, u"smartplaylists"_s, IconLoader::Load(u"view-media-playlist"_s, true, 0, 32), tr("Smart playlists"));
|
ui_->tabs->AddTab(smartplaylists_view_, u"smartplaylists"_s, IconLoader::Load(u"view-media-playlist"_s, true, 0, 32), tr("Smart playlists"));
|
||||||
ui_->tabs->AddTab(file_view_, u"files"_s, IconLoader::Load(u"document-open"_s, true, 0, 32), tr("Files"));
|
ui_->tabs->AddTab(file_view_, u"files"_s, IconLoader::Load(u"document-open"_s, true, 0, 32), tr("Files"));
|
||||||
ui_->tabs->AddTab(radio_view_, u"radios"_s, IconLoader::Load(u"radio"_s, true, 0, 32), tr("Radios"));
|
ui_->tabs->AddTab(radio_view_, u"radios"_s, IconLoader::Load(u"radio"_s, true, 0, 32), tr("Radios"));
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
ui_->tabs->AddTab(device_view_, u"devices"_s, IconLoader::Load(u"device"_s, true, 0, 32), tr("Devices"));
|
ui_->tabs->AddTab(device_view_, u"devices"_s, IconLoader::Load(u"device"_s, true, 0, 32), tr("Devices"));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
@@ -441,7 +466,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
|
|
||||||
collection_view_->view()->setModel(app_->collection()->model()->filter());
|
collection_view_->view()->setModel(app_->collection()->model()->filter());
|
||||||
collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
|
collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
|
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
|
||||||
#endif
|
#endif
|
||||||
playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
|
playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
|
||||||
@@ -515,7 +540,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
QObject::connect(file_view_, &FileView::CopyToCollection, this, &MainWindow::CopyFilesToCollection);
|
QObject::connect(file_view_, &FileView::CopyToCollection, this, &MainWindow::CopyFilesToCollection);
|
||||||
QObject::connect(file_view_, &FileView::MoveToCollection, this, &MainWindow::MoveFilesToCollection);
|
QObject::connect(file_view_, &FileView::MoveToCollection, this, &MainWindow::MoveFilesToCollection);
|
||||||
QObject::connect(file_view_, &FileView::EditTags, this, &MainWindow::EditFileTags);
|
QObject::connect(file_view_, &FileView::EditTags, this, &MainWindow::EditFileTags);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
QObject::connect(file_view_, &FileView::CopyToDevice, this, &MainWindow::CopyFilesToDevice);
|
QObject::connect(file_view_, &FileView::CopyToDevice, this, &MainWindow::CopyFilesToDevice);
|
||||||
#endif
|
#endif
|
||||||
file_view_->SetTaskManager(app_->task_manager());
|
file_view_->SetTaskManager(app_->task_manager());
|
||||||
@@ -679,7 +704,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::triggered, this, &MainWindow::SearchCoverAutomatically);
|
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::triggered, this, &MainWindow::SearchCoverAutomatically);
|
||||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::toggled, this, &MainWindow::ToggleSearchCoverAuto);
|
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::toggled, this, &MainWindow::ToggleSearchCoverAuto);
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
// Devices connections
|
// Devices connections
|
||||||
QObject::connect(device_view_->view(), &DeviceView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
QObject::connect(device_view_->view(), &DeviceView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
||||||
#endif
|
#endif
|
||||||
@@ -785,7 +810,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
playlist_organize_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &MainWindow::PlaylistMoveToCollection);
|
playlist_organize_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &MainWindow::PlaylistMoveToCollection);
|
||||||
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Copy to collection..."), this, &MainWindow::PlaylistCopyToCollection);
|
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Copy to collection..."), this, &MainWindow::PlaylistCopyToCollection);
|
||||||
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"go-jump"_s), tr("Move to collection..."), this, &MainWindow::PlaylistMoveToCollection);
|
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"go-jump"_s), tr("Move to collection..."), this, &MainWindow::PlaylistMoveToCollection);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &MainWindow::PlaylistCopyToDevice);
|
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &MainWindow::PlaylistCopyToDevice);
|
||||||
#endif
|
#endif
|
||||||
playlist_delete_ = playlist_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &MainWindow::PlaylistDelete);
|
playlist_delete_ = playlist_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &MainWindow::PlaylistDelete);
|
||||||
@@ -829,13 +854,13 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::ChangeVolume, this, &MainWindow::VolumeWheelEvent);
|
QObject::connect(&*tray_icon_, &SystemTrayIcon::ChangeVolume, this, &MainWindow::VolumeWheelEvent);
|
||||||
|
|
||||||
// Windows 7 thumbbar buttons
|
// Windows 7 thumbbar buttons
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
thumbbar_->SetActions(QList<QAction*>() << ui_->action_previous_track << ui_->action_play_pause << ui_->action_stop << ui_->action_next_track << nullptr << ui_->action_love);
|
thumbbar_->SetActions(QList<QAction*>() << ui_->action_previous_track << ui_->action_play_pause << ui_->action_stop << ui_->action_next_track << nullptr << ui_->action_love);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_QTSPARKLE)
|
#if defined(HAVE_SPARKLE) || defined(HAVE_QTSPARKLE)
|
||||||
QAction *check_updates = ui_->menu_tools->addAction(tr("Check for updates..."));
|
QAction *action_check_updates = ui_->menu_tools->addAction(tr("Check for updates..."));
|
||||||
check_updates->setMenuRole(QAction::ApplicationSpecificRole);
|
action_check_updates->setMenuRole(QAction::ApplicationSpecificRole);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_GLOBALSHORTCUTS
|
#ifdef HAVE_GLOBALSHORTCUTS
|
||||||
@@ -941,7 +966,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
|
||||||
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
|
||||||
|
|
||||||
#if !defined(HAVE_AUDIOCD) || defined(Q_OS_WIN)
|
#if !defined(HAVE_AUDIOCD) || defined(Q_OS_WIN32)
|
||||||
ui_->action_open_cd->setEnabled(false);
|
ui_->action_open_cd->setEnabled(false);
|
||||||
ui_->action_open_cd->setVisible(false);
|
ui_->action_open_cd->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -1046,13 +1071,18 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
app_->scrobbler()->Submit();
|
app_->scrobbler()->Submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SPARKLE
|
||||||
|
SparkleUpdater *sparkle_updater = new SparkleUpdater(action_check_updates, this);
|
||||||
|
QObject::connect(action_check_updates, &QAction::triggered, sparkle_updater, &SparkleUpdater::CheckForUpdates);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_QTSPARKLE
|
#ifdef HAVE_QTSPARKLE
|
||||||
QUrl sparkle_url(QString::fromLatin1(QTSPARKLE_URL));
|
QUrl sparkle_url(QString::fromLatin1(QTSPARKLE_URL));
|
||||||
if (!sparkle_url.isEmpty()) {
|
if (!sparkle_url.isEmpty()) {
|
||||||
qLog(Debug) << "Creating Qt Sparkle updater";
|
qLog(Debug) << "Creating Qt Sparkle updater";
|
||||||
qtsparkle::Updater *updater = new qtsparkle::Updater(sparkle_url, this);
|
qtsparkle::Updater *updater = new qtsparkle::Updater(sparkle_url, this);
|
||||||
updater->SetVersion(QStringLiteral(STRAWBERRY_VERSION_PACKAGE));
|
updater->SetVersion(QStringLiteral(STRAWBERRY_VERSION_PACKAGE));
|
||||||
QObject::connect(check_updates, &QAction::triggered, updater, &qtsparkle::Updater::CheckNow);
|
QObject::connect(action_check_updates, &QAction::triggered, updater, &qtsparkle::Updater::CheckNow);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1636,6 +1666,11 @@ void MainWindow::StopAfterCurrent() {
|
|||||||
|
|
||||||
void MainWindow::showEvent(QShowEvent *e) {
|
void MainWindow::showEvent(QShowEvent *e) {
|
||||||
|
|
||||||
|
if (error_dialog_ && error_dialog_->isVisible() && error_dialog_->isMinimized()) {
|
||||||
|
error_dialog_->raise();
|
||||||
|
error_dialog_->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
QMainWindow::showEvent(e);
|
QMainWindow::showEvent(e);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1665,7 +1700,12 @@ void MainWindow::closeEvent(QCloseEvent *e) {
|
|||||||
void MainWindow::SetHiddenInTray(const bool hidden) {
|
void MainWindow::SetHiddenInTray(const bool hidden) {
|
||||||
|
|
||||||
if (hidden && isVisible()) {
|
if (hidden && isVisible()) {
|
||||||
close();
|
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible() && keep_running_) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showMinimized();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!hidden && isHidden()) {
|
else if (!hidden && isHidden()) {
|
||||||
if (was_minimized_) {
|
if (was_minimized_) {
|
||||||
@@ -1962,7 +2002,7 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
playlist_show_in_collection_->setVisible(false);
|
playlist_show_in_collection_->setVisible(false);
|
||||||
playlist_copy_to_collection_->setVisible(false);
|
playlist_copy_to_collection_->setVisible(false);
|
||||||
playlist_move_to_collection_->setVisible(false);
|
playlist_move_to_collection_->setVisible(false);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
playlist_copy_to_device_->setVisible(false);
|
playlist_copy_to_device_->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
playlist_organize_->setVisible(false);
|
playlist_organize_->setVisible(false);
|
||||||
@@ -2037,7 +2077,7 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
playlist_move_to_collection_->setVisible(local_songs > 0);
|
playlist_move_to_collection_->setVisible(local_songs > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
playlist_copy_to_device_->setVisible(local_songs > 0);
|
playlist_copy_to_device_->setVisible(local_songs > 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2676,7 +2716,7 @@ void MainWindow::MoveFilesToCollection(const QList<QUrl> &urls) {
|
|||||||
|
|
||||||
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
|
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
||||||
organize_dialog_->SetCopy(true);
|
organize_dialog_->SetCopy(true);
|
||||||
if (organize_dialog_->SetUrls(urls)) {
|
if (organize_dialog_->SetUrls(urls)) {
|
||||||
@@ -2816,7 +2856,7 @@ void MainWindow::PlaylistSkip() {
|
|||||||
|
|
||||||
void MainWindow::PlaylistCopyToDevice() {
|
void MainWindow::PlaylistCopyToDevice() {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
|
|
||||||
SongList songs;
|
SongList songs;
|
||||||
|
|
||||||
@@ -2992,7 +3032,7 @@ void MainWindow::Raise() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
|
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
|
||||||
|
|
||||||
if (exit_count_ == 0 && message) {
|
if (exit_count_ == 0 && message) {
|
||||||
@@ -3001,7 +3041,7 @@ bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr
|
|||||||
}
|
}
|
||||||
return QMainWindow::nativeEvent(eventType, message, result);
|
return QMainWindow::nativeEvent(eventType, message, result);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN32
|
||||||
|
|
||||||
void MainWindow::AutoCompleteTags() {
|
void MainWindow::AutoCompleteTags() {
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class CollectionViewContainer;
|
|||||||
class CollectionFilter;
|
class CollectionFilter;
|
||||||
class AlbumCoverChoiceController;
|
class AlbumCoverChoiceController;
|
||||||
class CommandlineOptions;
|
class CommandlineOptions;
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
class DeviceViewContainer;
|
class DeviceViewContainer;
|
||||||
#endif
|
#endif
|
||||||
class EditTagDialog;
|
class EditTagDialog;
|
||||||
@@ -92,7 +92,7 @@ class Ui_MainWindow;
|
|||||||
class StreamingSongsView;
|
class StreamingSongsView;
|
||||||
class StreamingTabsView;
|
class StreamingTabsView;
|
||||||
class SmartPlaylistsViewContainer;
|
class SmartPlaylistsViewContainer;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
class Windows7ThumbBar;
|
class Windows7ThumbBar;
|
||||||
#endif
|
#endif
|
||||||
class AddStreamDialog;
|
class AddStreamDialog;
|
||||||
@@ -114,7 +114,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
void hideEvent(QHideEvent *e) override;
|
void hideEvent(QHideEvent *e) override;
|
||||||
void closeEvent(QCloseEvent *e) override;
|
void closeEvent(QCloseEvent *e) override;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui_MainWindow *ui_;
|
Ui_MainWindow *ui_;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
Windows7ThumbBar *thumbbar_;
|
Windows7ThumbBar *thumbbar_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
ContextView *context_view_;
|
ContextView *context_view_;
|
||||||
CollectionViewContainer *collection_view_;
|
CollectionViewContainer *collection_view_;
|
||||||
FileView *file_view_;
|
FileView *file_view_;
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
DeviceViewContainer *device_view_;
|
DeviceViewContainer *device_view_;
|
||||||
#endif
|
#endif
|
||||||
PlaylistListContainer *playlist_list_;
|
PlaylistListContainer *playlist_list_;
|
||||||
@@ -359,7 +359,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
QAction *playlist_move_to_collection_;
|
QAction *playlist_move_to_collection_;
|
||||||
QAction *playlist_open_in_browser_;
|
QAction *playlist_open_in_browser_;
|
||||||
QAction *playlist_organize_;
|
QAction *playlist_organize_;
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN32
|
||||||
QAction *playlist_copy_to_device_;
|
QAction *playlist_copy_to_device_;
|
||||||
#endif
|
#endif
|
||||||
QAction *playlist_delete_;
|
QAction *playlist_delete_;
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Player::Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHan
|
|||||||
volume_increment_(5),
|
volume_increment_(5),
|
||||||
play_offset_nanosec_(0) {
|
play_offset_nanosec_(0) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
timer_save_volume_->setSingleShot(true);
|
timer_save_volume_->setSingleShot(true);
|
||||||
timer_save_volume_->setInterval(5s);
|
timer_save_volume_->setInterval(5s);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2024-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,13 +18,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QVariant>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
Settings::Settings(QObject *parent)
|
Settings::Settings(QObject *parent)
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
|
: QSettings(QCoreApplication::organizationName().toLower(), QCoreApplication::applicationName().toLower(), parent) {}
|
||||||
|
#else
|
||||||
: QSettings(parent) {}
|
: QSettings(parent) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Settings::Settings(const QSettings::Scope scope, QObject *parent)
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
|
: QSettings(scope, QCoreApplication::organizationName().toLower(), QCoreApplication::applicationName().toLower(), parent) {}
|
||||||
|
#else
|
||||||
|
: QSettings(scope, parent) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
Settings::Settings(const QString &filename, const Format format, QObject *parent)
|
Settings::Settings(const QString &filename, const Format format, QObject *parent)
|
||||||
: QSettings(filename, format, parent) {}
|
: QSettings(filename, format, parent) {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2024-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,14 +21,14 @@
|
|||||||
#define SETTINGS_H
|
#define SETTINGS_H
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QObject>
|
#include <QString>
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
class Settings : public QSettings {
|
class Settings : public QSettings {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Settings(QObject *parent = nullptr);
|
explicit Settings(QObject *parent = nullptr);
|
||||||
|
explicit Settings(const QSettings::Scope scope, QObject *parent = nullptr);
|
||||||
explicit Settings(const QString &filename, const Format format, QObject *parent = nullptr);
|
explicit Settings(const QString &filename, const Format format, QObject *parent = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QSettings>
|
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
class SettingsProvider {
|
class SettingsProvider {
|
||||||
public:
|
public:
|
||||||
@@ -60,7 +61,7 @@ class DefaultSettingsProvider : public SettingsProvider {
|
|||||||
void endArray() override;
|
void endArray() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSettings backend_;
|
Settings backend_;
|
||||||
|
|
||||||
Q_DISABLE_COPY(DefaultSettingsProvider)
|
Q_DISABLE_COPY(DefaultSettingsProvider)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,11 +45,11 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QSqlRecord>
|
#include <QSqlRecord>
|
||||||
|
|
||||||
#include <taglib/tstring.h>
|
#include <taglib/tstring.h>
|
||||||
|
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/enginemetadata.h"
|
#include "core/enginemetadata.h"
|
||||||
#include "utilities/strutils.h"
|
#include "utilities/strutils.h"
|
||||||
@@ -249,7 +249,8 @@ const QStringList Song::kRejectedExtensions = QStringList() << u"tmp"_s
|
|||||||
<< u"z"_s
|
<< u"z"_s
|
||||||
<< u"zip"_s
|
<< u"zip"_s
|
||||||
<< u"rar"_s
|
<< u"rar"_s
|
||||||
<< u"wvc"_s;
|
<< u"wvc"_s
|
||||||
|
<< u"zst"_s;
|
||||||
|
|
||||||
struct Song::Private : public QSharedData {
|
struct Song::Private : public QSharedData {
|
||||||
|
|
||||||
@@ -1334,24 +1335,24 @@ QString Song::ImageCacheDir(const Source source) {
|
|||||||
|
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case Source::Collection:
|
case Source::Collection:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/collectionalbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/collectionalbumcovers"_s;
|
||||||
case Source::Subsonic:
|
case Source::Subsonic:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/subsonicalbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/subsonicalbumcovers"_s;
|
||||||
case Source::Tidal:
|
case Source::Tidal:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/tidalalbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/tidalalbumcovers"_s;
|
||||||
case Source::Spotify:
|
case Source::Spotify:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/spotifyalbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/spotifyalbumcovers"_s;
|
||||||
case Source::Qobuz:
|
case Source::Qobuz:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/qobuzalbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/qobuzalbumcovers"_s;
|
||||||
case Source::Device:
|
case Source::Device:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/devicealbumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/devicealbumcovers"_s;
|
||||||
case Source::LocalFile:
|
case Source::LocalFile:
|
||||||
case Source::CDDA:
|
case Source::CDDA:
|
||||||
case Source::Stream:
|
case Source::Stream:
|
||||||
case Source::SomaFM:
|
case Source::SomaFM:
|
||||||
case Source::RadioParadise:
|
case Source::RadioParadise:
|
||||||
case Source::Unknown:
|
case Source::Unknown:
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/albumcovers"_s;
|
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/albumcovers"_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
|
|||||||
// It's a CUE - create virtual tracks
|
// It's a CUE - create virtual tracks
|
||||||
QFile cue(matching_cue);
|
QFile cue(matching_cue);
|
||||||
if (cue.open(QIODevice::ReadOnly)) {
|
if (cue.open(QIODevice::ReadOnly)) {
|
||||||
const SongList songs = cue_parser_->Load(&cue, matching_cue, QDir(filename.section(u'/', 0, -2)));
|
const SongList songs = cue_parser_->Load(&cue, matching_cue, QDir(filename.section(u'/', 0, -2))).songs;
|
||||||
cue.close();
|
cue.close();
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
if (song.is_valid()) songs_ << song;
|
if (song.is_valid()) songs_ << song;
|
||||||
@@ -348,7 +348,9 @@ void SongLoader::LoadPlaylist(ParserBase *parser, const QString &filename) {
|
|||||||
|
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
songs_ = parser->Load(&file, filename, QFileInfo(filename).path());
|
const ParserBase::LoadResult result = parser->Load(&file, filename, QFileInfo(filename).path());
|
||||||
|
songs_ = result.songs;
|
||||||
|
playlist_name_ = result.playlist_name;
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -424,7 +426,9 @@ void SongLoader::StopTypefind() {
|
|||||||
// Parse the playlist
|
// Parse the playlist
|
||||||
QBuffer buf(&buffer_);
|
QBuffer buf(&buffer_);
|
||||||
if (buf.open(QIODevice::ReadOnly)) {
|
if (buf.open(QIODevice::ReadOnly)) {
|
||||||
songs_ = parser_->Load(&buf);
|
const ParserBase::LoadResult result = parser_->Load(&buf);
|
||||||
|
songs_ = result.songs;
|
||||||
|
playlist_name_ = result.playlist_name;
|
||||||
buf.close();
|
buf.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class SongLoader : public QObject {
|
|||||||
|
|
||||||
const QUrl &url() const { return url_; }
|
const QUrl &url() const { return url_; }
|
||||||
const SongList &songs() const { return songs_; }
|
const SongList &songs() const { return songs_; }
|
||||||
|
const QString &playlist_name() const { return playlist_name_; }
|
||||||
|
|
||||||
int timeout() const { return timeout_; }
|
int timeout() const { return timeout_; }
|
||||||
void set_timeout(int msec) { timeout_ = msec; }
|
void set_timeout(int msec) { timeout_ = msec; }
|
||||||
@@ -141,6 +142,7 @@ class SongLoader : public QObject {
|
|||||||
|
|
||||||
QUrl url_;
|
QUrl url_;
|
||||||
SongList songs_;
|
SongList songs_;
|
||||||
|
QString playlist_name_;
|
||||||
|
|
||||||
const SharedPtr<UrlHandlers> url_handlers_;
|
const SharedPtr<UrlHandlers> url_handlers_;
|
||||||
const SharedPtr<CollectionBackendInterface> collection_backend_;
|
const SharedPtr<CollectionBackendInterface> collection_backend_;
|
||||||
|
|||||||
48
src/core/sparkleupdater.h
Normal file
48
src/core/sparkleupdater.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPARKLEUPDATER_H
|
||||||
|
#define SPARKLEUPDATER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@class AppUpdaterDelegate;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SparkleUpdater : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SparkleUpdater(QAction *action_check_updates, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void CheckForUpdates();
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef __OBJC__
|
||||||
|
AppUpdaterDelegate *updater_delegate_;
|
||||||
|
#else
|
||||||
|
void *updater_delegate_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPARKLEUPDATER_H
|
||||||
79
src/core/sparkleupdater.mm
Normal file
79
src/core/sparkleupdater.mm
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Sparkle/Sparkle.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
#include "sparkleupdater.h"
|
||||||
|
|
||||||
|
@interface AppUpdaterDelegate : NSObject <SPUUpdaterDelegate>
|
||||||
|
|
||||||
|
@property(nonatomic, assign) SPUStandardUpdaterController *updater_controller;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AppUpdaterDelegate
|
||||||
|
|
||||||
|
- (void)observeCanCheckForUpdatesWithAction:(QAction*)action_check_updates {
|
||||||
|
[_updater_controller.updater addObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates)) options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:(void*)action_check_updates];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id>*)change context:(void*)context {
|
||||||
|
|
||||||
|
if ([keyPath isEqualToString:NSStringFromSelector(@selector(canCheckForUpdates))]) {
|
||||||
|
QAction *action = reinterpret_cast<QAction*>(context);
|
||||||
|
action->setEnabled(_updater_controller.updater.canCheckForUpdates);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
[_updater_controller.updater removeObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))];
|
||||||
|
}
|
||||||
|
|
||||||
|
[super dealloc];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
SparkleUpdater::SparkleUpdater(QAction *action_check_updates, QObject *parent) : QObject(parent) {
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
updater_delegate_ = [[AppUpdaterDelegate alloc] init];
|
||||||
|
updater_delegate_.updater_controller = [[SPUStandardUpdaterController alloc] initWithStartingUpdater:YES updaterDelegate:updater_delegate_ userDriverDelegate:nil];
|
||||||
|
[updater_delegate_ observeCanCheckForUpdatesWithAction:action_check_updates];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SparkleUpdater::CheckForUpdates() {
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
[updater_delegate_.updater_controller checkForUpdates:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ void StandardItemIconLoader::SetModel(QAbstractItemModel *model) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StandardItemIconLoader::LoadIcon(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item) {
|
void StandardItemIconLoader::LoadAlbumCover(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item) {
|
||||||
|
|
||||||
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
|
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
|
||||||
cover_options.desired_scaled_size = QSize(16, 16);
|
cover_options.desired_scaled_size = QSize(16, 16);
|
||||||
@@ -64,7 +64,7 @@ void StandardItemIconLoader::LoadIcon(const QUrl &art_automatic, const QUrl &art
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StandardItemIconLoader::LoadIcon(const Song &song, QStandardItem *for_item) {
|
void StandardItemIconLoader::LoadAlbumCover(const Song &song, QStandardItem *for_item) {
|
||||||
|
|
||||||
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
|
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
|
||||||
cover_options.desired_scaled_size = QSize(16, 16);
|
cover_options.desired_scaled_size = QSize(16, 16);
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class StandardItemIconLoader : public QObject {
|
|||||||
|
|
||||||
void SetModel(QAbstractItemModel *model);
|
void SetModel(QAbstractItemModel *model);
|
||||||
|
|
||||||
void LoadIcon(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item);
|
void LoadAlbumCover(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item);
|
||||||
void LoadIcon(const Song &song, QStandardItem *for_item);
|
void LoadAlbumCover(const Song &song, QStandardItem *for_item);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
||||||
|
|||||||
82
src/core/standardpaths.cpp
Normal file
82
src/core/standardpaths.cpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "standardpaths.h"
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
void StandardPaths::AppendOrganizationAndApplication(QString &path) {
|
||||||
|
|
||||||
|
const QString organization_name = QCoreApplication::organizationName().toLower();
|
||||||
|
if (!organization_name.isEmpty()) {
|
||||||
|
path += u'/' + organization_name;
|
||||||
|
}
|
||||||
|
const QString application_name = QCoreApplication::applicationName().toLower();
|
||||||
|
if (!application_name.isEmpty()) {
|
||||||
|
path += u'/' + application_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StandardPaths::WritableLocation(const StandardLocation type) {
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
|
switch (type) {
|
||||||
|
case StandardLocation::CacheLocation:
|
||||||
|
case StandardLocation::GenericCacheLocation:{
|
||||||
|
QString cache_location = qEnvironmentVariable("XDG_CACHE_HOME");
|
||||||
|
if (!cache_location.startsWith(u'/')) {
|
||||||
|
cache_location.clear();
|
||||||
|
}
|
||||||
|
if (cache_location.isEmpty()) {
|
||||||
|
cache_location = QDir::homePath() + "/.cache"_L1;
|
||||||
|
}
|
||||||
|
if (type == QStandardPaths::CacheLocation) {
|
||||||
|
AppendOrganizationAndApplication(cache_location);
|
||||||
|
}
|
||||||
|
return cache_location;
|
||||||
|
}
|
||||||
|
case StandardLocation::AppDataLocation:
|
||||||
|
case StandardLocation::AppLocalDataLocation:
|
||||||
|
case StandardLocation::GenericDataLocation:{
|
||||||
|
QString data_location = qEnvironmentVariable("XDG_DATA_HOME");
|
||||||
|
if (!data_location.startsWith(u'/')) {
|
||||||
|
data_location.clear();
|
||||||
|
}
|
||||||
|
if (data_location.isEmpty()) {
|
||||||
|
data_location = QDir::homePath() + "/.local/share"_L1;
|
||||||
|
}
|
||||||
|
if (type == StandardLocation::AppDataLocation || type == StandardLocation::AppLocalDataLocation) {
|
||||||
|
AppendOrganizationAndApplication(data_location);
|
||||||
|
}
|
||||||
|
return data_location;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return QStandardPaths::writableLocation(type);
|
||||||
|
|
||||||
|
}
|
||||||
36
src/core/standardpaths.h
Normal file
36
src/core/standardpaths.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STANDARDPATHS_H
|
||||||
|
#define STANDARDPATHS_H
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
class StandardPaths {
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
public:
|
||||||
|
using StandardLocation = QStandardPaths::StandardLocation;
|
||||||
|
static QString WritableLocation(const StandardLocation type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void AppendOrganizationAndApplication(QString &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STANDARDPATHS_H
|
||||||
@@ -34,7 +34,7 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
|
|
||||||
TaskManager::TaskManager(QObject *parent) : QObject(parent), next_task_id_(1) {
|
TaskManager::TaskManager(QObject *parent) : QObject(parent), next_task_id_(1) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
@@ -32,6 +31,7 @@
|
|||||||
#include <QAbstractNetworkCache>
|
#include <QAbstractNetworkCache>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "standardpaths.h"
|
||||||
#include "threadsafenetworkdiskcache.h"
|
#include "threadsafenetworkdiskcache.h"
|
||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
@@ -48,9 +48,9 @@ ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstr
|
|||||||
if (!sCache) {
|
if (!sCache) {
|
||||||
sCache = new QNetworkDiskCache;
|
sCache = new QNetworkDiskCache;
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u"/strawberry/networkcache"_s);
|
sCache->setCacheDirectory(StandardPaths::WritableLocation(StandardPaths::StandardLocation::TempLocation) + u"/strawberry/networkcache"_s);
|
||||||
#else
|
#else
|
||||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u"/networkcache"_s);
|
sCache->setCacheDirectory(StandardPaths::WritableLocation(StandardPaths::StandardLocation::CacheLocation) + u"/networkcache"_s);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ AlbumCoverLoader::AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_cl
|
|||||||
load_image_async_id_(1),
|
load_image_async_id_(1),
|
||||||
original_thread_(nullptr) {
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/temporaryfile.h"
|
#include "core/temporaryfile.h"
|
||||||
#include "albumcoverloader.h"
|
#include "albumcoverloader.h"
|
||||||
@@ -42,10 +42,10 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
CurrentAlbumCoverLoader::CurrentAlbumCoverLoader(const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent)
|
CurrentAlbumCoverLoader::CurrentAlbumCoverLoader(const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
albumcover_loader_(albumcover_loader),
|
albumcover_loader_(albumcover_loader),
|
||||||
temp_file_pattern_(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u"/strawberry-cover-XXXXXX.jpg"_s),
|
temp_file_pattern_(StandardPaths::WritableLocation(StandardPaths::StandardLocation::TempLocation) + u"/strawberry-cover-XXXXXX.jpg"_s),
|
||||||
id_(0) {
|
id_(0) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
options_.options = AlbumCoverLoaderOptions::Option::RawImageData | AlbumCoverLoaderOptions::Option::OriginalImage | AlbumCoverLoaderOptions::Option::ScaledImage;
|
options_.options = AlbumCoverLoaderOptions::Option::RawImageData | AlbumCoverLoaderOptions::Option::OriginalImage | AlbumCoverLoaderOptions::Option::ScaledImage;
|
||||||
options_.desired_scaled_size = QSize(120, 120);
|
options_.desired_scaled_size = QSize(120, 120);
|
||||||
|
|||||||
@@ -52,10 +52,12 @@
|
|||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
|
||||||
const char *DiscogsCoverProvider::kUrlSearch = "https://api.discogs.com/database/search";
|
namespace {
|
||||||
const char *DiscogsCoverProvider::kAccessKeyB64 = "dGh6ZnljUGJlZ1NEeXBuSFFxSVk=";
|
constexpr char kUrlSearch[] = "https://api.discogs.com/database/search";
|
||||||
const char *DiscogsCoverProvider::kSecretKeyB64 = "ZkFIcmlaSER4aHhRSlF2U3d0bm5ZVmdxeXFLWUl0UXI=";
|
constexpr char kAccessKeyB64[] = "dGh6ZnljUGJlZ1NEeXBuSFFxSVk=";
|
||||||
const int DiscogsCoverProvider::kRequestsDelay = 1000;
|
constexpr char kSecretKeyB64[] = "ZkFIcmlaSER4aHhRSlF2U3d0bm5ZVmdxeXFLWUl0UXI=";
|
||||||
|
constexpr int kRequestsDelay = 1000;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
DiscogsCoverProvider::DiscogsCoverProvider(const SharedPtr<NetworkAccessManager> network, QObject *parent)
|
DiscogsCoverProvider::DiscogsCoverProvider(const SharedPtr<NetworkAccessManager> network, QObject *parent)
|
||||||
: JsonCoverProvider(u"Discogs"_s, false, false, 0.0, false, false, network, parent),
|
: JsonCoverProvider(u"Discogs"_s, false, false, 0.0, false, false, network, parent),
|
||||||
|
|||||||
@@ -89,11 +89,6 @@ class DiscogsCoverProvider : public JsonCoverProvider {
|
|||||||
void HandleReleaseReply(QNetworkReply *reply, const int search_id, const quint64 release_id);
|
void HandleReleaseReply(QNetworkReply *reply, const int search_id, const quint64 release_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char *kUrlSearch;
|
|
||||||
static const char *kAccessKeyB64;
|
|
||||||
static const char *kSecretKeyB64;
|
|
||||||
static const int kRequestsDelay;
|
|
||||||
|
|
||||||
QTimer *timer_flush_requests_;
|
QTimer *timer_flush_requests_;
|
||||||
QQueue<SharedPtr<DiscogsCoverSearchContext>> queue_search_requests_;
|
QQueue<SharedPtr<DiscogsCoverSearchContext>> queue_search_requests_;
|
||||||
QQueue<DiscogsCoverReleaseContext> queue_release_requests_;
|
QQueue<DiscogsCoverReleaseContext> queue_release_requests_;
|
||||||
|
|||||||
@@ -24,9 +24,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cdio/cdio.h>
|
|
||||||
#include <gst/audio/gstaudiocdsrc.h>
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include <cdio/cdio.h>
|
||||||
|
#include <cdio/device.h>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@@ -30,10 +35,6 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
// This must come after Qt includes
|
|
||||||
#include <cdio/cdio.h>
|
|
||||||
#include <cdio/device.h>
|
|
||||||
|
|
||||||
#include "cddalister.h"
|
#include "cddalister.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,16 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gtypes.h>
|
#include <glib/gtypes.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include <cdio/cdio.h>
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/tag/tag.h>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
@@ -35,10 +41,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include <cdio/cdio.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/tag/tag.h>
|
|
||||||
|
|
||||||
#include "cddasongloader.h"
|
#include "cddasongloader.h"
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|||||||
@@ -24,17 +24,19 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include <cdio/types.h>
|
||||||
|
#include <cdio/cdio.h>
|
||||||
|
|
||||||
|
#include <gst/gstelement.h>
|
||||||
|
#include <gst/audio/gstaudiocdsrc.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
// These must come after Qt includes
|
|
||||||
#include <cdio/types.h>
|
|
||||||
#include <cdio/cdio.h>
|
|
||||||
#include <gst/gstelement.h>
|
|
||||||
#include <gst/audio/gstaudiocdsrc.h>
|
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#ifdef HAVE_MUSICBRAINZ
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ DeviceDatabaseBackend::DeviceDatabaseBackend(QObject *parent)
|
|||||||
db_(nullptr),
|
db_(nullptr),
|
||||||
original_thread_(nullptr) {
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const DeviceInfo::Backend *DeviceInfo::BestBackend() const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceInfo::LoadIcon(const QVariantList &icons, const QString &name_hint) {
|
void DeviceInfo::SetIcon(const QVariantList &icons, const QString &name_hint) {
|
||||||
|
|
||||||
icon_name_ = "device"_L1;
|
icon_name_ = "device"_L1;
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class DeviceInfo : public SimpleTreeItem<DeviceInfo> {
|
|||||||
DeviceDatabaseBackend::Device SaveToDb() const;
|
DeviceDatabaseBackend::Device SaveToDb() const;
|
||||||
|
|
||||||
// Tries to load a good icon for the device. Sets icon_name_ and icon_.
|
// Tries to load a good icon for the device. Sets icon_name_ and icon_.
|
||||||
void LoadIcon(const QVariantList &icons, const QString &name_hint);
|
void SetIcon(const QVariantList &icons, const QString &name_hint);
|
||||||
|
|
||||||
// Gets the best backend available (the one with the highest priority)
|
// Gets the best backend available (the one with the highest priority)
|
||||||
const Backend *BestBackend() const;
|
const Backend *BestBackend() const;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ DeviceLister::DeviceLister(QObject *parent)
|
|||||||
original_thread_(nullptr),
|
original_thread_(nullptr),
|
||||||
next_mount_request_id_(0) {
|
next_mount_request_id_(0) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ DeviceManager::DeviceManager(const SharedPtr<TaskManager> task_manager,
|
|||||||
albumcover_loader_(albumcover_loader),
|
albumcover_loader_(albumcover_loader),
|
||||||
not_connected_overlay_(IconLoader::Load(u"edit-delete"_s)) {
|
not_connected_overlay_(IconLoader::Load(u"edit-delete"_s)) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
thread_pool_.setMaxThreadCount(1);
|
thread_pool_.setMaxThreadCount(1);
|
||||||
QObject::connect(&*task_manager, &TaskManager::TasksChanged, this, &DeviceManager::TasksChanged);
|
QObject::connect(&*task_manager, &TaskManager::TasksChanged, this, &DeviceManager::TasksChanged);
|
||||||
@@ -256,7 +256,7 @@ void DeviceManager::AddDeviceFromDB(DeviceInfo *info) {
|
|||||||
for (const QString &icon_name : icon_names) {
|
for (const QString &icon_name : icon_names) {
|
||||||
icons << icon_name;
|
icons << icon_name;
|
||||||
}
|
}
|
||||||
info->LoadIcon(icons, info->friendly_name_);
|
info->SetIcon(icons, info->friendly_name_);
|
||||||
|
|
||||||
DeviceInfo *existing = FindEquivalentDevice(info);
|
DeviceInfo *existing = FindEquivalentDevice(info);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@@ -477,7 +477,7 @@ void DeviceManager::PhysicalDeviceAdded(const QString &id) {
|
|||||||
if (info->database_id_ == -1 && info->BestBackend() && info->BestBackend()->lister_ == lister) {
|
if (info->database_id_ == -1 && info->BestBackend() && info->BestBackend()->lister_ == lister) {
|
||||||
info->friendly_name_ = lister->MakeFriendlyName(id);
|
info->friendly_name_ = lister->MakeFriendlyName(id);
|
||||||
info->size_ = lister->DeviceCapacity(id);
|
info->size_ = lister->DeviceCapacity(id);
|
||||||
info->LoadIcon(lister->DeviceIcons(id), info->friendly_name_);
|
info->SetIcon(lister->DeviceIcons(id), info->friendly_name_);
|
||||||
}
|
}
|
||||||
QModelIndex idx = ItemToIndex(info);
|
QModelIndex idx = ItemToIndex(info);
|
||||||
if (idx.isValid()) Q_EMIT dataChanged(idx, idx);
|
if (idx.isValid()) Q_EMIT dataChanged(idx, idx);
|
||||||
@@ -488,7 +488,7 @@ void DeviceManager::PhysicalDeviceAdded(const QString &id) {
|
|||||||
info->backends_ << DeviceInfo::Backend(lister, id);
|
info->backends_ << DeviceInfo::Backend(lister, id);
|
||||||
info->friendly_name_ = lister->MakeFriendlyName(id);
|
info->friendly_name_ = lister->MakeFriendlyName(id);
|
||||||
info->size_ = lister->DeviceCapacity(id);
|
info->size_ = lister->DeviceCapacity(id);
|
||||||
info->LoadIcon(lister->DeviceIcons(id), info->friendly_name_);
|
info->SetIcon(lister->DeviceIcons(id), info->friendly_name_);
|
||||||
beginInsertRows(ItemToIndex(root_), static_cast<int>(devices_.count()), static_cast<int>(devices_.count()));
|
beginInsertRows(ItemToIndex(root_), static_cast<int>(devices_.count()), static_cast<int>(devices_.count()));
|
||||||
devices_ << info;
|
devices_ << info;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
@@ -808,7 +808,7 @@ void DeviceManager::RemoveFromDB(DeviceInfo *info, const QModelIndex &idx) {
|
|||||||
const QString id = info->BestBackend()->unique_id_;
|
const QString id = info->BestBackend()->unique_id_;
|
||||||
|
|
||||||
info->friendly_name_ = info->BestBackend()->lister_->MakeFriendlyName(id);
|
info->friendly_name_ = info->BestBackend()->lister_->MakeFriendlyName(id);
|
||||||
info->LoadIcon(info->BestBackend()->lister_->DeviceIcons(id), info->friendly_name_);
|
info->SetIcon(info->BestBackend()->lister_->DeviceIcons(id), info->friendly_name_);
|
||||||
Q_EMIT dataChanged(idx, idx);
|
Q_EMIT dataChanged(idx, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,7 +822,7 @@ void DeviceManager::SetDeviceOptions(const QModelIndex &idx, const QString &frie
|
|||||||
if (!info) return;
|
if (!info) return;
|
||||||
|
|
||||||
info->friendly_name_ = friendly_name;
|
info->friendly_name_ = friendly_name;
|
||||||
info->LoadIcon(QVariantList() << icon_name, friendly_name);
|
info->SetIcon(QVariantList() << icon_name, friendly_name);
|
||||||
info->transcode_mode_ = mode;
|
info->transcode_mode_ = mode;
|
||||||
info->transcode_format_ = format;
|
info->transcode_format_ = format;
|
||||||
|
|
||||||
|
|||||||
@@ -484,16 +484,22 @@ void GioLister::DeviceInfo::ReadMountInfo(GMount *mount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_GIO_UNIX
|
#ifdef HAVE_GIO_UNIX
|
||||||
|
#ifdef GLIB_VERSION_2_84
|
||||||
|
GUnixMountEntry *unix_mount = g_unix_mount_entry_for(g_file_get_path(root), nullptr);
|
||||||
|
#else
|
||||||
GUnixMountEntry *unix_mount = g_unix_mount_for(g_file_get_path(root), nullptr);
|
GUnixMountEntry *unix_mount = g_unix_mount_for(g_file_get_path(root), nullptr);
|
||||||
|
#endif
|
||||||
if (unix_mount) {
|
if (unix_mount) {
|
||||||
// the GIO's definition of system internal mounts include filesystems like
|
// The GIO's definition of system internal mounts include filesystems like autofs, tmpfs, sysfs, etc,
|
||||||
// autofs, tmpfs, sysfs, etc, and various system directories, including the root,
|
// and various system directories, including the root, /boot, /var, /home, etc.
|
||||||
// /boot, /var, /home, etc.
|
#ifdef GLIB_VERSION_2_84
|
||||||
|
is_system_internal = g_unix_mount_entry_is_system_internal(unix_mount);
|
||||||
|
g_unix_mount_entry_free(unix_mount);
|
||||||
|
#else
|
||||||
is_system_internal = g_unix_mount_is_system_internal(unix_mount);
|
is_system_internal = g_unix_mount_is_system_internal(unix_mount);
|
||||||
g_unix_mount_free(unix_mount);
|
g_unix_mount_free(unix_mount);
|
||||||
// Although checking most of the internal mounts is safe,
|
#endif
|
||||||
// we really don't want to touch autofs filesystems, as that would
|
// Although checking most of the internal mounts is safe, we really don't want to touch autofs filesystems, as that would trigger automounting.
|
||||||
// trigger automounting.
|
|
||||||
if (is_system_internal) return;
|
if (is_system_internal) return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/temporaryfile.h"
|
#include "core/temporaryfile.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
@@ -211,9 +211,9 @@ bool GPodDevice::CopyToStorage(const CopyJob &job, QString &error_text) {
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
if (!job.cover_image_.isNull()) {
|
if (!job.cover_image_.isNull()) {
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
QString temp_path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u"/organize"_s;
|
QString temp_path = StandardPaths::WritableLocation(StandardPaths::StandardLocation::CacheLocation) + u"/organize"_s;
|
||||||
#else
|
#else
|
||||||
QString temp_path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
QString temp_path = StandardPaths::WritableLocation(StandardPaths::StandardLocation::TempLocation);
|
||||||
#endif
|
#endif
|
||||||
if (!QDir(temp_path).exists()) QDir().mkpath(temp_path);
|
if (!QDir(temp_path).exists()) QDir().mkpath(temp_path);
|
||||||
SharedPtr<TemporaryFile> cover_file = make_shared<TemporaryFile>(temp_path + u"/track-albumcover-XXXXXX.jpg"_s);
|
SharedPtr<TemporaryFile> cover_file = make_shared<TemporaryFile>(temp_path + u"/track-albumcover-XXXXXX.jpg"_s);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
|
|
||||||
ErrorDialog::ErrorDialog(QWidget *parent)
|
ErrorDialog::ErrorDialog(QWidget *parent)
|
||||||
: QDialog(parent),
|
: QDialog(parent),
|
||||||
|
parent_(parent),
|
||||||
ui_(new Ui_ErrorDialog) {
|
ui_(new Ui_ErrorDialog) {
|
||||||
|
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
@@ -66,8 +67,11 @@ void ErrorDialog::ShowMessage(const QString &message) {
|
|||||||
UpdateContent();
|
UpdateContent();
|
||||||
|
|
||||||
show();
|
show();
|
||||||
raise();
|
|
||||||
activateWindow();
|
if (parent_ && parent_->isMaximized()) {
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class ErrorDialog : public QDialog {
|
|||||||
private:
|
private:
|
||||||
void UpdateContent();
|
void UpdateContent();
|
||||||
|
|
||||||
|
QWidget *parent_;
|
||||||
Ui_ErrorDialog *ui_;
|
Ui_ErrorDialog *ui_;
|
||||||
|
|
||||||
QStringList current_messages_;
|
QStringList current_messages_;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
|
|
||||||
DeviceFinders::DeviceFinders(QObject *parent) : QObject(parent) {
|
DeviceFinders::DeviceFinders(QObject *parent) : QObject(parent) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ EngineBase::EngineBase(QObject *parent)
|
|||||||
exclusive_mode_(false),
|
exclusive_mode_(false),
|
||||||
volume_control_(true),
|
volume_control_(true),
|
||||||
volume_(100),
|
volume_(100),
|
||||||
beginning_nanosec_(0),
|
beginning_offset_nanosec_(0),
|
||||||
end_nanosec_(0),
|
end_offset_nanosec_(0),
|
||||||
ebur128_loudness_normalizing_gain_db_(0.0),
|
ebur128_loudness_normalizing_gain_db_(0.0),
|
||||||
scope_(kScopeSize),
|
scope_(kScopeSize),
|
||||||
buffering_(false),
|
buffering_(false),
|
||||||
@@ -84,15 +84,15 @@ EngineBase::EngineBase(QObject *parent)
|
|||||||
|
|
||||||
EngineBase::~EngineBase() = default;
|
EngineBase::~EngineBase() = default;
|
||||||
|
|
||||||
bool EngineBase::Load(const QUrl &media_url, const QUrl &stream_url, const TrackChangeFlags track_change_flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
bool EngineBase::Load(const QUrl &media_url, const QUrl &stream_url, const TrackChangeFlags track_change_flags, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
||||||
|
|
||||||
Q_UNUSED(track_change_flags)
|
Q_UNUSED(track_change_flags)
|
||||||
Q_UNUSED(force_stop_at_end);
|
Q_UNUSED(force_stop_at_end);
|
||||||
|
|
||||||
media_url_ = media_url;
|
media_url_ = media_url;
|
||||||
stream_url_ = stream_url;
|
stream_url_ = stream_url;
|
||||||
beginning_nanosec_ = beginning_nanosec;
|
beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||||
end_nanosec_ = end_nanosec;
|
end_offset_nanosec_ = end_offset_nanosec;
|
||||||
|
|
||||||
ebur128_loudness_normalizing_gain_db_ = 0.0;
|
ebur128_loudness_normalizing_gain_db_ = 0.0;
|
||||||
if (ebur128_loudness_normalization_ && ebur128_integrated_loudness_lufs) {
|
if (ebur128_loudness_normalization_ && ebur128_integrated_loudness_lufs) {
|
||||||
@@ -112,9 +112,9 @@ bool EngineBase::Load(const QUrl &media_url, const QUrl &stream_url, const Track
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EngineBase::Play(const QUrl &media_url, const QUrl &stream_url, const bool pause, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const quint64 offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
bool EngineBase::Play(const QUrl &media_url, const QUrl &stream_url, const bool pause, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const quint64 offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
||||||
|
|
||||||
if (!Load(media_url, stream_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec, ebur128_integrated_loudness_lufs)) {
|
if (!Load(media_url, stream_url, flags, force_stop_at_end, beginning_offset_nanosec, end_offset_nanosec, ebur128_integrated_loudness_lufs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class EngineBase : public QObject {
|
|||||||
virtual bool Init() = 0;
|
virtual bool Init() = 0;
|
||||||
virtual State state() const = 0;
|
virtual State state() const = 0;
|
||||||
virtual void StartPreloading(const QUrl&, const QUrl&, const bool, const qint64, const qint64) {}
|
virtual void StartPreloading(const QUrl&, const QUrl&, const bool, const qint64, const qint64) {}
|
||||||
virtual bool Load(const QUrl &media_url, const QUrl &stream_url, const TrackChangeFlags track_change_flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs);
|
virtual bool Load(const QUrl &media_url, const QUrl &stream_url, const TrackChangeFlags track_change_flags, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs);
|
||||||
virtual bool Play(const bool pause, const quint64 offset_nanosec) = 0;
|
virtual bool Play(const bool pause, const quint64 offset_nanosec) = 0;
|
||||||
virtual void Stop(const bool stop_after = false) = 0;
|
virtual void Stop(const bool stop_after = false) = 0;
|
||||||
virtual void Pause() = 0;
|
virtual void Pause() = 0;
|
||||||
@@ -106,9 +106,9 @@ class EngineBase : public QObject {
|
|||||||
|
|
||||||
// Sets new values for the beginning and end markers of the currently playing song.
|
// Sets new values for the beginning and end markers of the currently playing song.
|
||||||
// This doesn't change the state of engine or the stream's current position.
|
// This doesn't change the state of engine or the stream's current position.
|
||||||
virtual void RefreshMarkers(const quint64 beginning_nanosec, const qint64 end_nanosec) {
|
virtual void RefreshMarkers(const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) {
|
||||||
beginning_nanosec_ = beginning_nanosec;
|
beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||||
end_nanosec_ = end_nanosec;
|
end_offset_nanosec_ = end_offset_nanosec;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual OutputDetailsList GetOutputsList() const = 0;
|
virtual OutputDetailsList GetOutputsList() const = 0;
|
||||||
@@ -120,7 +120,7 @@ class EngineBase : public QObject {
|
|||||||
|
|
||||||
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
|
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
|
||||||
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
|
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
|
||||||
bool Play(const QUrl &media_url, const QUrl &stream_url, const bool pause, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const quint64 offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs);
|
bool Play(const QUrl &media_url, const QUrl &stream_url, const bool pause, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const quint64 offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs);
|
||||||
void SetVolume(const uint volume);
|
void SetVolume(const uint volume);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
@@ -179,8 +179,8 @@ class EngineBase : public QObject {
|
|||||||
bool exclusive_mode_;
|
bool exclusive_mode_;
|
||||||
bool volume_control_;
|
bool volume_control_;
|
||||||
uint volume_;
|
uint volume_;
|
||||||
quint64 beginning_nanosec_;
|
quint64 beginning_offset_nanosec_;
|
||||||
qint64 end_nanosec_;
|
qint64 end_offset_nanosec_;
|
||||||
QUrl media_url_;
|
QUrl media_url_;
|
||||||
QUrl stream_url_;
|
QUrl stream_url_;
|
||||||
double ebur128_loudness_normalizing_gain_db_;
|
double ebur128_loudness_normalizing_gain_db_;
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
#include "gstbufferconsumer.h"
|
#include "gstbufferconsumer.h"
|
||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
using std::make_shared;
|
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# pragma clang diagnostic push
|
# pragma clang diagnostic push
|
||||||
@@ -177,13 +176,13 @@ EngineBase::State GstEngine::state() const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &stream_url, const bool force_stop_at_end, const qint64 beginning_nanosec, const qint64 end_nanosec) {
|
void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &stream_url, const bool force_stop_at_end, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) {
|
||||||
|
|
||||||
const QByteArray gst_url = FixupUrl(stream_url);
|
const QByteArray gst_url = FixupUrl(stream_url);
|
||||||
|
|
||||||
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
|
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
|
||||||
if (current_pipeline_) {
|
if (current_pipeline_) {
|
||||||
current_pipeline_->PrepareNextUrl(media_url, stream_url, gst_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
|
current_pipeline_->PrepareNextUrl(media_url, stream_url, gst_url, beginning_offset_nanosec, force_stop_at_end ? end_offset_nanosec : 0);
|
||||||
// Add request to discover the stream
|
// Add request to discover the stream
|
||||||
if (discoverer_ && media_url.scheme() != u"spotify"_s) {
|
if (discoverer_ && media_url.scheme() != u"spotify"_s) {
|
||||||
if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.constData())) {
|
if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.constData())) {
|
||||||
@@ -194,9 +193,9 @@ void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &stream_url, c
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const EngineBase::TrackChangeFlags change, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const EngineBase::TrackChangeFlags change, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) {
|
||||||
|
|
||||||
EngineBase::Load(media_url, stream_url, change, force_stop_at_end, beginning_nanosec, end_nanosec, ebur128_integrated_loudness_lufs);
|
EngineBase::Load(media_url, stream_url, change, force_stop_at_end, beginning_offset_nanosec, end_offset_nanosec, ebur128_integrated_loudness_lufs);
|
||||||
|
|
||||||
const QByteArray gst_url = FixupUrl(stream_url);
|
const QByteArray gst_url = FixupUrl(stream_url);
|
||||||
|
|
||||||
@@ -215,7 +214,7 @@ bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GstEnginePipelinePtr pipeline = CreatePipeline(media_url, stream_url, gst_url, force_stop_at_end ? end_nanosec : 0, ebur128_loudness_normalizing_gain_db_);
|
GstEnginePipelinePtr pipeline = CreatePipeline(media_url, stream_url, gst_url, static_cast<qint64>(beginning_offset_nanosec), force_stop_at_end ? end_offset_nanosec : 0, ebur128_loudness_normalizing_gain_db_);
|
||||||
if (!pipeline) return false;
|
if (!pipeline) return false;
|
||||||
|
|
||||||
GstEnginePipelinePtr old_pipeline = current_pipeline_;
|
GstEnginePipelinePtr old_pipeline = current_pipeline_;
|
||||||
@@ -264,7 +263,17 @@ bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const Engine
|
|||||||
|
|
||||||
bool GstEngine::Play(const bool pause, const quint64 offset_nanosec) {
|
bool GstEngine::Play(const bool pause, const quint64 offset_nanosec) {
|
||||||
|
|
||||||
if (!current_pipeline_ || current_pipeline_->is_buffering() || current_pipeline_->state() == GstState::GST_STATE_PLAYING) return false;
|
if (!current_pipeline_ || current_pipeline_->is_buffering()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_pipeline_->state() == GstState::GST_STATE_PLAYING) {
|
||||||
|
if (offset_nanosec != 0 || beginning_offset_nanosec_ != 0) {
|
||||||
|
Seek(offset_nanosec);
|
||||||
|
PlayDone(GST_STATE_CHANGE_SUCCESS, false, offset_nanosec, current_pipeline_->id());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (OldExclusivePipelineActive()) {
|
if (OldExclusivePipelineActive()) {
|
||||||
qLog(Debug) << "Delaying play because a exclusive pipeline is already active...";
|
qLog(Debug) << "Delaying play because a exclusive pipeline is already active...";
|
||||||
@@ -289,7 +298,7 @@ bool GstEngine::Play(const bool pause, const quint64 offset_nanosec) {
|
|||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
PlayDone(ret, pause, offset_nanosec, pipeline_id);
|
PlayDone(ret, pause, offset_nanosec, pipeline_id);
|
||||||
});
|
});
|
||||||
QFuture<GstStateChangeReturn> future = current_pipeline_->Play(pause, beginning_nanosec_ + offset_nanosec);
|
QFuture<GstStateChangeReturn> future = current_pipeline_->Play(pause, beginning_offset_nanosec_ + offset_nanosec);
|
||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -306,7 +315,7 @@ void GstEngine::Stop(const bool stop_after) {
|
|||||||
|
|
||||||
media_url_.clear();
|
media_url_.clear();
|
||||||
stream_url_.clear(); // To ensure we return Empty from state()
|
stream_url_.clear(); // To ensure we return Empty from state()
|
||||||
beginning_nanosec_ = end_nanosec_ = 0;
|
beginning_offset_nanosec_ = end_offset_nanosec_ = 0;
|
||||||
|
|
||||||
// Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback.
|
// Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback.
|
||||||
if (fadeout_pause_pipeline_) {
|
if (fadeout_pause_pipeline_) {
|
||||||
@@ -384,7 +393,7 @@ void GstEngine::Seek(const quint64 offset_nanosec) {
|
|||||||
|
|
||||||
if (!current_pipeline_) return;
|
if (!current_pipeline_) return;
|
||||||
|
|
||||||
seek_pos_ = beginning_nanosec_ + offset_nanosec;
|
seek_pos_ = beginning_offset_nanosec_ + offset_nanosec;
|
||||||
waiting_to_seek_ = true;
|
waiting_to_seek_ = true;
|
||||||
|
|
||||||
if (!seek_timer_->isActive()) {
|
if (!seek_timer_->isActive()) {
|
||||||
@@ -402,7 +411,7 @@ qint64 GstEngine::position_nanosec() const {
|
|||||||
|
|
||||||
if (!current_pipeline_) return 0;
|
if (!current_pipeline_) return 0;
|
||||||
|
|
||||||
const qint64 result = current_pipeline_->position() - static_cast<qint64>(beginning_nanosec_);
|
const qint64 result = current_pipeline_->position() - static_cast<qint64>(beginning_offset_nanosec_);
|
||||||
return std::max(0LL, result);
|
return std::max(0LL, result);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -411,7 +420,7 @@ qint64 GstEngine::length_nanosec() const {
|
|||||||
|
|
||||||
if (!current_pipeline_) return 0;
|
if (!current_pipeline_) return 0;
|
||||||
|
|
||||||
const qint64 result = end_nanosec_ - static_cast<qint64>(beginning_nanosec_);
|
const qint64 result = end_offset_nanosec_ - static_cast<qint64>(beginning_offset_nanosec_);
|
||||||
|
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
return result;
|
return result;
|
||||||
@@ -742,7 +751,7 @@ void GstEngine::PlayDone(const GstStateChangeReturn ret, const bool pause, const
|
|||||||
stream_url = old_pipeline->stream_url();
|
stream_url = old_pipeline->stream_url();
|
||||||
stream_url.detach();
|
stream_url.detach();
|
||||||
}
|
}
|
||||||
current_pipeline_ = CreatePipeline(media_url, stream_url, redirect_url, end_nanosec_, old_pipeline->ebur128_loudness_normalizing_gain_db());
|
current_pipeline_ = CreatePipeline(media_url, stream_url, redirect_url, beginning_offset_nanosec_, end_offset_nanosec_, old_pipeline->ebur128_loudness_normalizing_gain_db());
|
||||||
FinishPipeline(old_pipeline);
|
FinishPipeline(old_pipeline);
|
||||||
Play(pause, offset_nanosec);
|
Play(pause, offset_nanosec);
|
||||||
return;
|
return;
|
||||||
@@ -761,7 +770,7 @@ void GstEngine::PlayDone(const GstStateChangeReturn ret, const bool pause, const
|
|||||||
|
|
||||||
Q_EMIT StateChanged(pause ? State::Paused : State::Playing);
|
Q_EMIT StateChanged(pause ? State::Paused : State::Playing);
|
||||||
|
|
||||||
// We've successfully started playing a media stream with this url
|
// We've successfully started playing a media stream with this URL
|
||||||
Q_EMIT ValidSongRequested(stream_url_);
|
Q_EMIT ValidSongRequested(stream_url_);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -887,7 +896,7 @@ void GstEngine::StopTimers() {
|
|||||||
|
|
||||||
GstEnginePipelinePtr GstEngine::CreatePipeline() {
|
GstEnginePipelinePtr GstEngine::CreatePipeline() {
|
||||||
|
|
||||||
GstEnginePipelinePtr pipeline = make_shared<GstEnginePipeline>();
|
GstEnginePipelinePtr pipeline = GstEnginePipelinePtr(new GstEnginePipeline);
|
||||||
pipeline->set_output_device(output_, device_);
|
pipeline->set_output_device(output_, device_);
|
||||||
pipeline->set_exclusive_mode(exclusive_mode_);
|
pipeline->set_exclusive_mode(exclusive_mode_);
|
||||||
pipeline->set_volume_enabled(volume_control_);
|
pipeline->set_volume_enabled(volume_control_);
|
||||||
@@ -926,11 +935,11 @@ GstEnginePipelinePtr GstEngine::CreatePipeline() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GstEnginePipelinePtr GstEngine::CreatePipeline(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 end_nanosec, const double ebur128_loudness_normalizing_gain_db) {
|
GstEnginePipelinePtr GstEngine::CreatePipeline(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const double ebur128_loudness_normalizing_gain_db) {
|
||||||
|
|
||||||
GstEnginePipelinePtr ret = CreatePipeline();
|
GstEnginePipelinePtr ret = CreatePipeline();
|
||||||
QString error;
|
QString error;
|
||||||
if (!ret->InitFromUrl(media_url, stream_url, gst_url, end_nanosec, ebur128_loudness_normalizing_gain_db, error)) {
|
if (!ret->InitFromUrl(media_url, stream_url, gst_url, beginning_offset_nanosec, end_offset_nanosec, ebur128_loudness_normalizing_gain_db, error)) {
|
||||||
ret.reset();
|
ret.reset();
|
||||||
Q_EMIT Error(error);
|
Q_EMIT Error(error);
|
||||||
Q_EMIT StateChanged(State::Error);
|
Q_EMIT StateChanged(State::Error);
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ class GstEngine : public EngineBase, public GstBufferConsumer {
|
|||||||
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
State state() const override;
|
State state() const override;
|
||||||
void StartPreloading(const QUrl &media_url, const QUrl &stream_url, const bool force_stop_at_end, const qint64 beginning_nanosec, const qint64 end_nanosec) override;
|
void StartPreloading(const QUrl &media_url, const QUrl &stream_url, const bool force_stop_at_end, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) override;
|
||||||
bool Load(const QUrl &media_url, const QUrl &stream_url, const EngineBase::TrackChangeFlags change, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) override;
|
bool Load(const QUrl &media_url, const QUrl &stream_url, const EngineBase::TrackChangeFlags change, const bool force_stop_at_end, const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const std::optional<double> ebur128_integrated_loudness_lufs) override;
|
||||||
bool Play(const bool pause, const quint64 offset_nanosec) override;
|
bool Play(const bool pause, const quint64 offset_nanosec) override;
|
||||||
void Stop(const bool stop_after = false) override;
|
void Stop(const bool stop_after = false) override;
|
||||||
void Pause() override;
|
void Pause() override;
|
||||||
@@ -133,7 +133,7 @@ class GstEngine : public EngineBase, public GstBufferConsumer {
|
|||||||
void StopTimers();
|
void StopTimers();
|
||||||
|
|
||||||
GstEnginePipelinePtr CreatePipeline();
|
GstEnginePipelinePtr CreatePipeline();
|
||||||
GstEnginePipelinePtr CreatePipeline(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 end_nanosec, const double ebur128_loudness_normalizing_gain_db);
|
GstEnginePipelinePtr CreatePipeline(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const double ebur128_loudness_normalizing_gain_db);
|
||||||
|
|
||||||
void FinishPipeline(GstEnginePipelinePtr pipeline);
|
void FinishPipeline(GstEnginePipelinePtr pipeline);
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
|||||||
ebur128_loudness_normalizing_gain_db_(0.0),
|
ebur128_loudness_normalizing_gain_db_(0.0),
|
||||||
segment_start_(0),
|
segment_start_(0),
|
||||||
segment_start_received_(false),
|
segment_start_received_(false),
|
||||||
|
beginning_offset_nanosec_(-1),
|
||||||
end_offset_nanosec_(-1),
|
end_offset_nanosec_(-1),
|
||||||
next_beginning_offset_nanosec_(-1),
|
next_beginning_offset_nanosec_(-1),
|
||||||
next_end_offset_nanosec_(-1),
|
next_end_offset_nanosec_(-1),
|
||||||
@@ -173,7 +174,11 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
|||||||
logged_unsupported_analyzer_format_(false),
|
logged_unsupported_analyzer_format_(false),
|
||||||
about_to_finish_(false),
|
about_to_finish_(false),
|
||||||
finish_requested_(false),
|
finish_requested_(false),
|
||||||
finished_(false) {
|
finished_(false),
|
||||||
|
set_state_in_progress_(0),
|
||||||
|
set_state_async_in_progress_(0),
|
||||||
|
last_set_state_in_progress_(GST_STATE_VOID_PENDING),
|
||||||
|
last_set_state_async_in_progress_(GST_STATE_VOID_PENDING) {
|
||||||
|
|
||||||
eq_band_gains_.reserve(kEqBandCount);
|
eq_band_gains_.reserve(kEqBandCount);
|
||||||
for (int i = 0; i < kEqBandCount; ++i) eq_band_gains_ << 0;
|
for (int i = 0; i < kEqBandCount; ++i) eq_band_gains_ << 0;
|
||||||
@@ -418,18 +423,23 @@ bool GstEnginePipeline::Finish() {
|
|||||||
|
|
||||||
Disconnect();
|
Disconnect();
|
||||||
|
|
||||||
if (IsStateNull()) {
|
if (IsStateNull() && set_state_async_in_progress_ == 0 && set_state_in_progress_ == 0) {
|
||||||
finished_ = true;
|
finished_ = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SetState(GST_STATE_NULL);
|
if (set_state_async_in_progress_ > 0 && last_set_state_async_in_progress_ != GST_STATE_NULL) {
|
||||||
|
SetStateAsync(GST_STATE_NULL);
|
||||||
|
}
|
||||||
|
else if ((!IsStateNull() || set_state_in_progress_ > 0) && last_set_state_in_progress_ != GST_STATE_NULL) {
|
||||||
|
SetState(GST_STATE_NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return finished_.value();
|
return finished_.value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEnginePipeline::InitFromUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 end_nanosec, const double ebur128_loudness_normalizing_gain_db, QString &error) {
|
bool GstEnginePipeline::InitFromUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const double ebur128_loudness_normalizing_gain_db, QString &error) {
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker l(&mutex_url_);
|
QMutexLocker l(&mutex_url_);
|
||||||
@@ -438,7 +448,8 @@ bool GstEnginePipeline::InitFromUrl(const QUrl &media_url, const QUrl &stream_ur
|
|||||||
gst_url_ = gst_url;
|
gst_url_ = gst_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
end_offset_nanosec_ = end_nanosec;
|
beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||||
|
end_offset_nanosec_ = end_offset_nanosec;
|
||||||
ebur128_loudness_normalizing_gain_db_ = ebur128_loudness_normalizing_gain_db;
|
ebur128_loudness_normalizing_gain_db_ = ebur128_loudness_normalizing_gain_db;
|
||||||
|
|
||||||
guint version_major = 0, version_minor = 0, version_micro = 0, version_nano = 0;
|
guint version_major = 0, version_minor = 0, version_micro = 0, version_nano = 0;
|
||||||
@@ -1328,6 +1339,7 @@ GstPadProbeReturn GstEnginePipeline::BufferProbeCallback(GstPad *pad, GstPadProb
|
|||||||
if (instance->end_offset_nanosec_.value() > 0 && end_time > instance->end_offset_nanosec_.value()) {
|
if (instance->end_offset_nanosec_.value() > 0 && end_time > instance->end_offset_nanosec_.value()) {
|
||||||
if (instance->HasMatchingNextUrl() && instance->next_beginning_offset_nanosec_.value() == instance->end_offset_nanosec_.value()) {
|
if (instance->HasMatchingNextUrl() && instance->next_beginning_offset_nanosec_.value() == instance->end_offset_nanosec_.value()) {
|
||||||
// The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on.
|
// The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on.
|
||||||
|
instance->beginning_offset_nanosec_ = instance->next_beginning_offset_nanosec_;
|
||||||
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
|
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
|
||||||
instance->next_media_url_.clear();
|
instance->next_media_url_.clear();
|
||||||
instance->next_stream_url_.clear();
|
instance->next_stream_url_.clear();
|
||||||
@@ -1477,6 +1489,7 @@ void GstEnginePipeline::StreamStartMessageReceived() {
|
|||||||
next_media_url_.clear();
|
next_media_url_.clear();
|
||||||
next_gst_url_.clear();
|
next_gst_url_.clear();
|
||||||
}
|
}
|
||||||
|
beginning_offset_nanosec_ = next_beginning_offset_nanosec_;
|
||||||
end_offset_nanosec_ = next_end_offset_nanosec_;
|
end_offset_nanosec_ = next_end_offset_nanosec_;
|
||||||
next_beginning_offset_nanosec_ = 0;
|
next_beginning_offset_nanosec_ = 0;
|
||||||
next_end_offset_nanosec_ = 0;
|
next_end_offset_nanosec_ = 0;
|
||||||
@@ -1554,7 +1567,7 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
// Ignore non-error received for directsoundsink: "IDirectSoundBuffer_GetStatus The operation completed successfully"
|
// Ignore non-error received for directsoundsink: "IDirectSoundBuffer_GetStatus The operation completed successfully"
|
||||||
if (code == GST_RESOURCE_ERROR_OPEN_WRITE && message.contains(QLatin1String("IDirectSoundBuffer_GetStatus The operation completed successfully."))) {
|
if (code == GST_RESOURCE_ERROR_OPEN_WRITE && message.contains(QLatin1String("IDirectSoundBuffer_GetStatus The operation completed successfully."))) {
|
||||||
return;
|
return;
|
||||||
@@ -1788,7 +1801,19 @@ bool GstEnginePipeline::IsStateNull() const {
|
|||||||
|
|
||||||
void GstEnginePipeline::SetStateAsync(const GstState state) {
|
void GstEnginePipeline::SetStateAsync(const GstState state) {
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "SetState", Qt::QueuedConnection, Q_ARG(GstState, state));
|
last_set_state_async_in_progress_ = state;
|
||||||
|
++set_state_async_in_progress_;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "SetStateAsyncSlot", Qt::QueuedConnection, Q_ARG(GstState, state));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GstEnginePipeline::SetStateAsyncSlot(const GstState state) {
|
||||||
|
|
||||||
|
last_set_state_async_in_progress_ = GST_STATE_VOID_PENDING;
|
||||||
|
--set_state_async_in_progress_;
|
||||||
|
|
||||||
|
SetState(state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1796,6 +1821,9 @@ QFuture<GstStateChangeReturn> GstEnginePipeline::SetState(const GstState state)
|
|||||||
|
|
||||||
qLog(Debug) << "Setting pipeline" << id() << "state to" << GstStateText(state);
|
qLog(Debug) << "Setting pipeline" << id() << "state to" << GstStateText(state);
|
||||||
|
|
||||||
|
last_set_state_in_progress_ = state;
|
||||||
|
++set_state_in_progress_;
|
||||||
|
|
||||||
QFutureWatcher<GstStateChangeReturn> *watcher = new QFutureWatcher<GstStateChangeReturn>();
|
QFutureWatcher<GstStateChangeReturn> *watcher = new QFutureWatcher<GstStateChangeReturn>();
|
||||||
QObject::connect(watcher, &QFutureWatcher<GstStateChangeReturn>::finished, this, [this, watcher, state]() {
|
QObject::connect(watcher, &QFutureWatcher<GstStateChangeReturn>::finished, this, [this, watcher, state]() {
|
||||||
const GstStateChangeReturn state_change_return = watcher->result();
|
const GstStateChangeReturn state_change_return = watcher->result();
|
||||||
@@ -1811,13 +1839,16 @@ QFuture<GstStateChangeReturn> GstEnginePipeline::SetState(const GstState state)
|
|||||||
|
|
||||||
void GstEnginePipeline::SetStateFinishedSlot(const GstState state, const GstStateChangeReturn state_change_return) {
|
void GstEnginePipeline::SetStateFinishedSlot(const GstState state, const GstStateChangeReturn state_change_return) {
|
||||||
|
|
||||||
|
last_set_state_in_progress_ = GST_STATE_VOID_PENDING;
|
||||||
|
--set_state_in_progress_;
|
||||||
|
|
||||||
switch (state_change_return) {
|
switch (state_change_return) {
|
||||||
case GST_STATE_CHANGE_SUCCESS:
|
case GST_STATE_CHANGE_SUCCESS:
|
||||||
case GST_STATE_CHANGE_ASYNC:
|
case GST_STATE_CHANGE_ASYNC:
|
||||||
case GST_STATE_CHANGE_NO_PREROLL:
|
case GST_STATE_CHANGE_NO_PREROLL:
|
||||||
qLog(Debug) << "Pipeline" << id() << "state successfully set to" << GstStateText(state);
|
qLog(Debug) << "Pipeline" << id() << "state successfully set to" << GstStateText(state);
|
||||||
Q_EMIT SetStateFinished(state_change_return);
|
Q_EMIT SetStateFinished(state_change_return);
|
||||||
if (!finished_.value() && finish_requested_.value()) {
|
if (!finished_.value() && finish_requested_.value() && set_state_async_in_progress_ == 0 && set_state_in_progress_ == 0) {
|
||||||
finished_ = true;
|
finished_ = true;
|
||||||
Q_EMIT Finished();
|
Q_EMIT Finished();
|
||||||
}
|
}
|
||||||
@@ -2137,7 +2168,7 @@ bool GstEnginePipeline::HasMatchingNextUrl() const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GstEnginePipeline::PrepareNextUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_nanosec, const qint64 end_nanosec) {
|
void GstEnginePipeline::PrepareNextUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) {
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker l(&mutex_next_url_);
|
QMutexLocker l(&mutex_next_url_);
|
||||||
@@ -2146,8 +2177,8 @@ void GstEnginePipeline::PrepareNextUrl(const QUrl &media_url, const QUrl &stream
|
|||||||
next_gst_url_ = gst_url;
|
next_gst_url_ = gst_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_beginning_offset_nanosec_ = beginning_nanosec;
|
next_beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||||
next_end_offset_nanosec_ = end_nanosec;
|
next_end_offset_nanosec_ = end_offset_nanosec;
|
||||||
|
|
||||||
if (about_to_finish_.value()) {
|
if (about_to_finish_.value()) {
|
||||||
SetNextUrl();
|
SetNextUrl();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "includes/mutex_protected.h"
|
#include "includes/mutex_protected.h"
|
||||||
@@ -83,7 +84,7 @@ class GstEnginePipeline : public QObject {
|
|||||||
bool Finish();
|
bool Finish();
|
||||||
|
|
||||||
// Creates the pipeline, returns false on error
|
// Creates the pipeline, returns false on error
|
||||||
bool InitFromUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 end_nanosec, const double ebur128_loudness_normalizing_gain_db, QString &error);
|
bool InitFromUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec, const double ebur128_loudness_normalizing_gain_db, QString &error);
|
||||||
|
|
||||||
// GstBufferConsumers get fed audio data. Thread-safe.
|
// GstBufferConsumers get fed audio data. Thread-safe.
|
||||||
void AddBufferConsumer(GstBufferConsumer *consumer);
|
void AddBufferConsumer(GstBufferConsumer *consumer);
|
||||||
@@ -105,7 +106,7 @@ class GstEnginePipeline : public QObject {
|
|||||||
// If this is set then it will be loaded automatically when playback finishes for gapless playback
|
// If this is set then it will be loaded automatically when playback finishes for gapless playback
|
||||||
bool HasNextUrl() const;
|
bool HasNextUrl() const;
|
||||||
bool HasMatchingNextUrl() const;
|
bool HasMatchingNextUrl() const;
|
||||||
void PrepareNextUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_nanosec, const qint64 end_nanosec);
|
void PrepareNextUrl(const QUrl &media_url, const QUrl &stream_url, const QByteArray &gst_url, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec);
|
||||||
void SetNextUrl();
|
void SetNextUrl();
|
||||||
|
|
||||||
void SetSourceDevice(const QString &device);
|
void SetSourceDevice(const QString &device);
|
||||||
@@ -197,6 +198,7 @@ class GstEnginePipeline : public QObject {
|
|||||||
void ProcessPendingSeek(const GstState state);
|
void ProcessPendingSeek(const GstState state);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void SetStateAsyncSlot(const GstState state);
|
||||||
void SetStateFinishedSlot(const GstState state, const GstStateChangeReturn state_change_return);
|
void SetStateFinishedSlot(const GstState state, const GstStateChangeReturn state_change_return);
|
||||||
void SetFaderVolume(const qreal volume);
|
void SetFaderVolume(const qreal volume);
|
||||||
void FaderTimelineStateChanged(const QTimeLine::State state);
|
void FaderTimelineStateChanged(const QTimeLine::State state);
|
||||||
@@ -287,6 +289,7 @@ class GstEnginePipeline : public QObject {
|
|||||||
mutex_protected<bool> segment_start_received_;
|
mutex_protected<bool> segment_start_received_;
|
||||||
GstSegment last_playbin_segment_{};
|
GstSegment last_playbin_segment_{};
|
||||||
|
|
||||||
|
mutex_protected<qint64> beginning_offset_nanosec_;
|
||||||
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
|
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
|
||||||
mutex_protected<qint64> end_offset_nanosec_;
|
mutex_protected<qint64> end_offset_nanosec_;
|
||||||
|
|
||||||
@@ -367,8 +370,14 @@ class GstEnginePipeline : public QObject {
|
|||||||
mutex_protected<bool> about_to_finish_;
|
mutex_protected<bool> about_to_finish_;
|
||||||
mutex_protected<bool> finish_requested_;
|
mutex_protected<bool> finish_requested_;
|
||||||
mutex_protected<bool> finished_;
|
mutex_protected<bool> finished_;
|
||||||
|
|
||||||
|
mutex_protected<int> set_state_in_progress_;
|
||||||
|
mutex_protected<int> set_state_async_in_progress_;
|
||||||
|
|
||||||
|
mutex_protected<GstState> last_set_state_in_progress_;
|
||||||
|
mutex_protected<GstState> last_set_state_async_in_progress_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using GstEnginePipelinePtr = SharedPtr<GstEnginePipeline>;
|
using GstEnginePipelinePtr = QSharedPointer<GstEnginePipeline>;
|
||||||
|
|
||||||
#endif // GSTENGINEPIPELINE_H
|
#endif // GSTENGINEPIPELINE_H
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
#include <gst/pbutils/pbutils.h>
|
#include <gst/pbutils/pbutils.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "utilities/envutils.h"
|
#include "utilities/envutils.h"
|
||||||
|
|
||||||
#ifdef HAVE_MOODBAR
|
#ifdef HAVE_MOODBAR
|
||||||
@@ -145,7 +145,7 @@ void SetEnvironment() {
|
|||||||
#endif // USE_BUNDLE
|
#endif // USE_BUNDLE
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_WIN32) || defined(Q_OS_MACOS)
|
||||||
QString gst_registry_filename = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QStringLiteral("/gst-registry-%1-bin").arg(QCoreApplication::applicationVersion());
|
QString gst_registry_filename = StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + QStringLiteral("/gst-registry-%1-bin").arg(QCoreApplication::applicationVersion());
|
||||||
qLog(Debug) << "Setting GStreamer registry file to" << gst_registry_filename;
|
qLog(Debug) << "Setting GStreamer registry file to" << gst_registry_filename;
|
||||||
Utilities::SetEnv("GST_REGISTRY", gst_registry_filename);
|
Utilities::SetEnv("GST_REGISTRY", gst_registry_filename);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -188,8 +188,15 @@ FilterTree *FilterParser::parseSearchTerm() {
|
|||||||
QString value;
|
QString value;
|
||||||
|
|
||||||
bool in_quotes = false;
|
bool in_quotes = false;
|
||||||
|
bool previous_char_operator = false;
|
||||||
|
|
||||||
for (; iter_ != end_; ++iter_) {
|
for (; iter_ != end_; ++iter_) {
|
||||||
|
if (previous_char_operator) {
|
||||||
|
if (iter_->isSpace()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
previous_char_operator = false;
|
||||||
|
}
|
||||||
if (in_quotes) {
|
if (in_quotes) {
|
||||||
if (*iter_ == u'"') {
|
if (*iter_ == u'"') {
|
||||||
in_quotes = false;
|
in_quotes = false;
|
||||||
@@ -206,6 +213,7 @@ FilterTree *FilterParser::parseSearchTerm() {
|
|||||||
column = buf_.toLower();
|
column = buf_.toLower();
|
||||||
buf_.clear();
|
buf_.clear();
|
||||||
prefix.clear(); // Prefix isn't allowed here - let's ignore it
|
prefix.clear(); // Prefix isn't allowed here - let's ignore it
|
||||||
|
previous_char_operator = true;
|
||||||
}
|
}
|
||||||
else if (iter_->isSpace() || *iter_ == u'(' || *iter_ == u')' || *iter_ == u'-') {
|
else if (iter_->isSpace() || *iter_ == u'(' || *iter_ == u')' || *iter_ == u'-') {
|
||||||
break;
|
break;
|
||||||
@@ -214,9 +222,11 @@ FilterTree *FilterParser::parseSearchTerm() {
|
|||||||
// We don't know whether there is a column part in this search term thus we assume the latter and just try and read a prefix
|
// We don't know whether there is a column part in this search term thus we assume the latter and just try and read a prefix
|
||||||
if (prefix.isEmpty() && (*iter_ == u'>' || *iter_ == u'<' || *iter_ == u'=' || *iter_ == u'!')) {
|
if (prefix.isEmpty() && (*iter_ == u'>' || *iter_ == u'<' || *iter_ == u'=' || *iter_ == u'!')) {
|
||||||
prefix += *iter_;
|
prefix += *iter_;
|
||||||
|
previous_char_operator = true;
|
||||||
}
|
}
|
||||||
else if (prefix != u'=' && *iter_ == u'=') {
|
else if (prefix != u'=' && *iter_ == u'=') {
|
||||||
prefix += *iter_;
|
prefix += *iter_;
|
||||||
|
previous_char_operator = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buf_ += *iter_;
|
buf_ += *iter_;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
# include "globalshortcutsbackend-x11.h"
|
# include "globalshortcutsbackend-x11.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
# include "globalshortcutsbackend-win.h"
|
# include "globalshortcutsbackend-win.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ GlobalShortcutsManager::GlobalShortcutsManager(QWidget *parent) : QWidget(parent
|
|||||||
backends_ << new GlobalShortcutsBackendMacOS(this, this);
|
backends_ << new GlobalShortcutsBackendMacOS(this, this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
backends_ << new GlobalShortcutsBackendWin(this, this);
|
backends_ << new GlobalShortcutsBackendWin(this, this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ void GlobalShortcutsManager::ReloadSettings() {
|
|||||||
backends_enabled_ << GlobalShortcutsBackend::Type::macOS;
|
backends_enabled_ << GlobalShortcutsBackend::Type::macOS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN32
|
||||||
backends_enabled_ << GlobalShortcutsBackend::Type::Win;
|
backends_enabled_ << GlobalShortcutsBackend::Type::Win;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ class Lazy {
|
|||||||
|
|
||||||
T* operator->() const { return get(); }
|
T* operator->() const { return get(); }
|
||||||
|
|
||||||
// Returns true if the object is not yet initialized.
|
// Returns true if the object is initialized.
|
||||||
explicit operator bool() const { return ptr_; }
|
explicit operator bool() const { return ptr_ != nullptr; }
|
||||||
|
|
||||||
// Deletes the underlying object and will re-run the initialization function if the object is requested again.
|
// Deletes the underlying object and will re-run the initialization function if the object is requested again.
|
||||||
void reset() { ptr_.reset(); }
|
void reset() { ptr_.reset(); }
|
||||||
|
|||||||
@@ -47,6 +47,56 @@ class mutex_protected : public boost::noncopyable {
|
|||||||
return value == value_;
|
return value == value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator!=(const mutex_protected &value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value.value() != value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const T value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value != value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const mutex_protected &value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ > value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const T value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ > value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const mutex_protected &value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ >= value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const T value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ >= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const mutex_protected &value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ < value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const T value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ < value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const mutex_protected &value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ <= value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const T value) const {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
return value_ <= value;
|
||||||
|
}
|
||||||
|
|
||||||
void operator=(const mutex_protected &value) {
|
void operator=(const mutex_protected &value) {
|
||||||
QMutexLocker l(&mutex_);
|
QMutexLocker l(&mutex_);
|
||||||
value_ = value.value();
|
value_ = value.value();
|
||||||
@@ -57,6 +107,36 @@ class mutex_protected : public boost::noncopyable {
|
|||||||
value_ = value;
|
value_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator++() {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
++value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator+=(const T value) {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
value_ += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator+=(const mutex_protected value) {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
value_ += value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator--() {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
--value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator-=(const T value) {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
value_ -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator-=(const mutex_protected value) {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
value_ -= value.value();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T value_;
|
T value_;
|
||||||
mutable QMutex mutex_;
|
mutable QMutex mutex_;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ using std::make_shared;
|
|||||||
|
|
||||||
LyricsProviders::LyricsProviders(QObject *parent) : QObject(parent), thread_(new QThread(this)), network_(make_shared<NetworkAccessManager>()) {
|
LyricsProviders::LyricsProviders(QObject *parent) : QObject(parent), thread_(new QThread(this)), network_(make_shared<NetworkAccessManager>()) {
|
||||||
|
|
||||||
setObjectName(QLatin1String(metaObject()->className()));
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
thread_->setObjectName(objectName());
|
thread_->setObjectName(objectName());
|
||||||
network_->moveToThread(thread_);
|
network_->moveToThread(thread_);
|
||||||
thread_->start();
|
thread_->start();
|
||||||
|
|||||||
15
src/main.cpp
15
src/main.cpp
@@ -48,7 +48,6 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QFileDevice>
|
#include <QFileDevice>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
@@ -70,6 +69,7 @@
|
|||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
#include "utilities/envutils.h"
|
#include "utilities/envutils.h"
|
||||||
@@ -131,13 +131,8 @@ int main(int argc, char *argv[]) {
|
|||||||
mac::MacMain();
|
mac::MacMain();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32) || defined(Q_OS_MACOS)
|
|
||||||
QCoreApplication::setApplicationName(u"Strawberry"_s);
|
QCoreApplication::setApplicationName(u"Strawberry"_s);
|
||||||
QCoreApplication::setOrganizationName(u"Strawberry"_s);
|
QCoreApplication::setOrganizationName(u"Strawberry"_s);
|
||||||
#else
|
|
||||||
QCoreApplication::setApplicationName(u"strawberry"_s);
|
|
||||||
QCoreApplication::setOrganizationName(u"strawberry"_s);
|
|
||||||
#endif
|
|
||||||
QCoreApplication::setApplicationVersion(QStringLiteral(STRAWBERRY_VERSION_DISPLAY));
|
QCoreApplication::setApplicationVersion(QStringLiteral(STRAWBERRY_VERSION_DISPLAY));
|
||||||
QCoreApplication::setOrganizationDomain(u"strawberrymusicplayer.org"_s);
|
QCoreApplication::setOrganizationDomain(u"strawberrymusicplayer.org"_s);
|
||||||
|
|
||||||
@@ -157,7 +152,7 @@ int main(int argc, char *argv[]) {
|
|||||||
// Only start a core application now, so we can check if there's another instance without requiring an X server.
|
// Only start a core application now, so we can check if there's another instance without requiring an X server.
|
||||||
// This MUST be done before parsing the commandline options so QTextCodec gets the right system locale for filenames.
|
// This MUST be done before parsing the commandline options so QTextCodec gets the right system locale for filenames.
|
||||||
QCoreApplication core_app(argc, argv);
|
QCoreApplication core_app(argc, argv);
|
||||||
KDSingleApplication single_app(QCoreApplication::applicationName(), KDSingleApplication::Option::IncludeUsernameInSocketName);
|
KDSingleApplication single_app(QCoreApplication::applicationName().toLower(), KDSingleApplication::Option::IncludeUsernameInSocketName);
|
||||||
// Parse commandline options - need to do this before starting the full QApplication, so it works without an X server
|
// Parse commandline options - need to do this before starting the full QApplication, so it works without an X server
|
||||||
if (!options.Parse()) return 1;
|
if (!options.Parse()) return 1;
|
||||||
logging::SetLevels(options.log_levels());
|
logging::SetLevels(options.log_levels());
|
||||||
@@ -174,7 +169,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
// Must happen after QCoreApplication::setOrganizationName().
|
// Must happen after QCoreApplication::setOrganizationName().
|
||||||
Utilities::SetEnv("XDG_CONFIG_HOME", QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
Utilities::SetEnv("XDG_CONFIG_HOME", StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppConfigLocation));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Output the version, so when people attach log output to bug reports they don't have to tell us which version they're using.
|
// Output the version, so when people attach log output to bug reports they don't have to tell us which version they're using.
|
||||||
@@ -194,7 +189,7 @@ int main(int argc, char *argv[]) {
|
|||||||
QGuiApplication::setQuitOnLastWindowClosed(false);
|
QGuiApplication::setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
KDSingleApplication single_app(QCoreApplication::applicationName(), KDSingleApplication::Option::IncludeUsernameInSocketName);
|
KDSingleApplication single_app(QCoreApplication::applicationName().toLower(), KDSingleApplication::Option::IncludeUsernameInSocketName);
|
||||||
if (!single_app.isPrimaryInstance()) {
|
if (!single_app.isPrimaryInstance()) {
|
||||||
if (options.is_empty()) {
|
if (options.is_empty()) {
|
||||||
qLog(Info) << "Strawberry is already running - activating existing window (2)";
|
qLog(Info) << "Strawberry is already running - activating existing window (2)";
|
||||||
@@ -295,7 +290,7 @@ int main(int argc, char *argv[]) {
|
|||||||
translations->LoadTranslation(u"strawberry"_s, QDir::currentPath(), language);
|
translations->LoadTranslation(u"strawberry"_s, QDir::currentPath(), language);
|
||||||
|
|
||||||
# ifdef HAVE_QTSPARKLE
|
# ifdef HAVE_QTSPARKLE
|
||||||
//qtsparkle::LoadTranslations(language);
|
qtsparkle::LoadTranslations(language);
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "core/player.h"
|
#include "core/player.h"
|
||||||
@@ -33,6 +34,8 @@
|
|||||||
#include "moodbarloader.h"
|
#include "moodbarloader.h"
|
||||||
#include "moodbarpipeline.h"
|
#include "moodbarpipeline.h"
|
||||||
|
|
||||||
|
using std::make_shared;
|
||||||
|
|
||||||
MoodbarController::MoodbarController(const SharedPtr<Player> player, const SharedPtr<MoodbarLoader> moodbar_loader, QObject *parent)
|
MoodbarController::MoodbarController(const SharedPtr<Player> player, const SharedPtr<MoodbarLoader> moodbar_loader, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
player_(player),
|
player_(player),
|
||||||
@@ -56,25 +59,27 @@ void MoodbarController::CurrentSongChanged(const Song &song) {
|
|||||||
|
|
||||||
if (!enabled_) return;
|
if (!enabled_) return;
|
||||||
|
|
||||||
QByteArray data;
|
const MoodbarLoader::LoadResult load_result = moodbar_loader_->Load(song.url(), song.has_cue());
|
||||||
MoodbarPipeline *pipeline = nullptr;
|
switch (load_result.status) {
|
||||||
const MoodbarLoader::Result result = moodbar_loader_->Load(song.url(), song.has_cue(), &data, &pipeline);
|
case MoodbarLoader::LoadStatus::CannotLoad:
|
||||||
|
Q_EMIT CurrentMoodbarDataChanged();
|
||||||
switch (result) {
|
|
||||||
case MoodbarLoader::Result::CannotLoad:
|
|
||||||
Q_EMIT CurrentMoodbarDataChanged(QByteArray());
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MoodbarLoader::Result::Loaded:
|
case MoodbarLoader::LoadStatus::Loaded:
|
||||||
Q_EMIT CurrentMoodbarDataChanged(data);
|
Q_EMIT CurrentMoodbarDataChanged(load_result.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MoodbarLoader::Result::WillLoadAsync:
|
case MoodbarLoader::LoadStatus::WillLoadAsync:
|
||||||
// Emit an empty array for now so the GUI reverts to a normal progress
|
// Emit an empty array for now so the GUI reverts to a normal progressbar. Our slot will be called when the data is actually loaded.
|
||||||
// bar. Our slot will be called when the data is actually loaded.
|
Q_EMIT CurrentMoodbarDataChanged();
|
||||||
Q_EMIT CurrentMoodbarDataChanged(QByteArray());
|
|
||||||
|
|
||||||
QObject::connect(pipeline, &MoodbarPipeline::Finished, this, [this, pipeline, song]() { AsyncLoadComplete(pipeline, song.url()); });
|
MoodbarPipelinePtr pipeline = load_result.pipeline;
|
||||||
|
Q_ASSERT(pipeline);
|
||||||
|
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||||
|
*connection = QObject::connect(&*pipeline, &MoodbarPipeline::Finished, this, [this, connection, pipeline, song]() {
|
||||||
|
AsyncLoadComplete(pipeline, song.url());
|
||||||
|
QObject::disconnect(*connection);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,12 +88,12 @@ void MoodbarController::CurrentSongChanged(const Song &song) {
|
|||||||
void MoodbarController::PlaybackStopped() {
|
void MoodbarController::PlaybackStopped() {
|
||||||
|
|
||||||
if (enabled_) {
|
if (enabled_) {
|
||||||
Q_EMIT CurrentMoodbarDataChanged(QByteArray());
|
Q_EMIT CurrentMoodbarDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarController::AsyncLoadComplete(MoodbarPipeline *pipeline, const QUrl &url) {
|
void MoodbarController::AsyncLoadComplete(MoodbarPipelinePtr pipeline, const QUrl &url) {
|
||||||
|
|
||||||
// Is this song still playing?
|
// Is this song still playing?
|
||||||
PlaylistItemPtr current_item = player_->GetCurrentItem();
|
PlaylistItemPtr current_item = player_->GetCurrentItem();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,9 +28,9 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
|
#include "moodbarpipeline.h"
|
||||||
|
|
||||||
class MoodbarLoader;
|
class MoodbarLoader;
|
||||||
class MoodbarPipeline;
|
|
||||||
class Song;
|
class Song;
|
||||||
class Player;
|
class Player;
|
||||||
|
|
||||||
@@ -43,15 +43,15 @@ class MoodbarController : public QObject {
|
|||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void CurrentMoodbarDataChanged(const QByteArray &data);
|
void CurrentMoodbarDataChanged(const QByteArray &data = QByteArray());
|
||||||
void StyleChanged();
|
void StyleChanged();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void CurrentSongChanged(const Song &song);
|
void CurrentSongChanged(const Song &song);
|
||||||
void PlaybackStopped();
|
void PlaybackStopped();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private:
|
||||||
void AsyncLoadComplete(MoodbarPipeline *pipeline, const QUrl &url);
|
void AsyncLoadComplete(MoodbarPipelinePtr pipeline, const QUrl &url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const SharedPtr<Player> player_;
|
const SharedPtr<Player> player_;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QRect>
|
#include <QRect>
|
||||||
|
|
||||||
|
#include "includes/shared_ptr.h"
|
||||||
|
#include "core/logging.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "playlist/playlist.h"
|
#include "playlist/playlist.h"
|
||||||
#include "playlist/playlistview.h"
|
#include "playlist/playlistview.h"
|
||||||
@@ -48,6 +50,8 @@
|
|||||||
|
|
||||||
#include "constants/moodbarsettings.h"
|
#include "constants/moodbarsettings.h"
|
||||||
|
|
||||||
|
using std::make_shared;
|
||||||
|
|
||||||
MoodbarItemDelegate::Data::Data() : state_(State::None) {}
|
MoodbarItemDelegate::Data::Data() : state_(State::None) {}
|
||||||
|
|
||||||
MoodbarItemDelegate::MoodbarItemDelegate(const SharedPtr<MoodbarLoader> moodbar_loader, PlaylistView *playlist_view, QObject *parent)
|
MoodbarItemDelegate::MoodbarItemDelegate(const SharedPtr<MoodbarLoader> moodbar_loader, PlaylistView *playlist_view, QObject *parent)
|
||||||
@@ -113,7 +117,10 @@ QPixmap MoodbarItemDelegate::PixmapForIndex(const QModelIndex &idx, const QSize
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data = new Data;
|
data = new Data;
|
||||||
if (!data_.insert(url, data)) return QPixmap();
|
if (!data_.insert(url, data)) {
|
||||||
|
qLog(Error) << "Could not insert moodbar data for URL" << url << "into cache";
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->indexes_.insert(idx);
|
data->indexes_.insert(idx);
|
||||||
@@ -150,21 +157,24 @@ void MoodbarItemDelegate::StartLoadingData(const QUrl &url, const bool has_cue,
|
|||||||
data->state_ = Data::State::LoadingData;
|
data->state_ = Data::State::LoadingData;
|
||||||
|
|
||||||
// Load a mood file for this song and generate some colors from it
|
// Load a mood file for this song and generate some colors from it
|
||||||
QByteArray bytes;
|
const MoodbarLoader::LoadResult load_result = moodbar_loader_->Load(url, has_cue);
|
||||||
MoodbarPipeline *pipeline = nullptr;
|
switch (load_result.status) {
|
||||||
switch (moodbar_loader_->Load(url, has_cue, &bytes, &pipeline)) {
|
case MoodbarLoader::LoadStatus::CannotLoad:
|
||||||
case MoodbarLoader::Result::CannotLoad:
|
|
||||||
data->state_ = Data::State::CannotLoad;
|
data->state_ = Data::State::CannotLoad;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MoodbarLoader::Result::Loaded:
|
case MoodbarLoader::LoadStatus::Loaded:
|
||||||
// We got the data immediately.
|
StartLoadingColors(url, load_result.data, data);
|
||||||
StartLoadingColors(url, bytes, data);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MoodbarLoader::Result::WillLoadAsync:
|
case MoodbarLoader::LoadStatus::WillLoadAsync:
|
||||||
// Maybe in a little while.
|
MoodbarPipelinePtr pipeline = load_result.pipeline;
|
||||||
QObject::connect(pipeline, &MoodbarPipeline::Finished, this, [this, url, pipeline]() { DataLoaded(url, pipeline); });
|
Q_ASSERT(pipeline);
|
||||||
|
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||||
|
*connection = QObject::connect(&*pipeline, &MoodbarPipeline::Finished, this, [this, connection, url, pipeline]() {
|
||||||
|
DataLoaded(url, pipeline);
|
||||||
|
QObject::disconnect(*connection);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +204,7 @@ void MoodbarItemDelegate::ReloadAllColors() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarItemDelegate::DataLoaded(const QUrl &url, MoodbarPipeline *pipeline) {
|
void MoodbarItemDelegate::DataLoaded(const QUrl &url, MoodbarPipelinePtr pipeline) {
|
||||||
|
|
||||||
if (!data_.contains(url)) return;
|
if (!data_.contains(url)) return;
|
||||||
|
|
||||||
@@ -291,8 +301,7 @@ void MoodbarItemDelegate::ImageLoaded(const QUrl &url, const QImage &image) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (source_index.model() != playlist) {
|
if (source_index.model() != playlist) {
|
||||||
// The pixmap was for an index in a different playlist, maybe the user
|
// The pixmap was for an index in a different playlist, maybe the user switched to a different one.
|
||||||
// switched to a different one.
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "constants/moodbarsettings.h"
|
#include "constants/moodbarsettings.h"
|
||||||
|
#include "moodbarpipeline.h"
|
||||||
|
|
||||||
class QPainter;
|
class QPainter;
|
||||||
class MoodbarLoader;
|
class MoodbarLoader;
|
||||||
class MoodbarPipeline;
|
|
||||||
class PlaylistView;
|
class PlaylistView;
|
||||||
|
|
||||||
class MoodbarItemDelegate : public QItemDelegate {
|
class MoodbarItemDelegate : public QItemDelegate {
|
||||||
@@ -55,13 +55,14 @@ class MoodbarItemDelegate : public QItemDelegate {
|
|||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
void DataLoaded(const QUrl &url, MoodbarPipeline *pipeline);
|
|
||||||
void ColorsLoaded(const QUrl &url, const ColorVector &colors);
|
|
||||||
void ImageLoaded(const QUrl &url, const QImage &image);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void StyleChanged();
|
void StyleChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DataLoaded(const QUrl &url, MoodbarPipelinePtr pipeline);
|
||||||
|
void ColorsLoaded(const QUrl &url, const ColorVector &colors);
|
||||||
|
void ImageLoaded(const QUrl &url, const QImage &image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Data {
|
struct Data {
|
||||||
Data();
|
Data();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,21 +28,21 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QAbstractNetworkCache>
|
#include <QAbstractNetworkCache>
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
#include <QTimer>
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "includes/scoped_ptr.h"
|
#include "includes/scoped_ptr.h"
|
||||||
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/standardpaths.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
#include "moodbarpipeline.h"
|
#include "moodbarpipeline.h"
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
using std::make_shared;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@@ -63,7 +64,10 @@ MoodbarLoader::MoodbarLoader(QObject *parent)
|
|||||||
kMaxActiveRequests(qMax(1, QThread::idealThreadCount() / 2)),
|
kMaxActiveRequests(qMax(1, QThread::idealThreadCount() / 2)),
|
||||||
save_(false) {
|
save_(false) {
|
||||||
|
|
||||||
cache_->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u"/moodbar"_s);
|
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||||
|
thread_->setObjectName(objectName());
|
||||||
|
|
||||||
|
cache_->setCacheDirectory(StandardPaths::WritableLocation(StandardPaths::StandardLocation::CacheLocation) + u"/moodbar"_s);
|
||||||
cache_->setMaximumCacheSize(60LL * 1024LL * 1024LL); // 60MB - enough for 20,000 moodbars
|
cache_->setMaximumCacheSize(60LL * 1024LL * 1024LL); // 60MB - enough for 20,000 moodbars
|
||||||
|
|
||||||
ReloadSettings();
|
ReloadSettings();
|
||||||
@@ -104,16 +108,15 @@ QUrl MoodbarLoader::CacheUrlEntry(const QString &filename) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MoodbarLoader::Result MoodbarLoader::Load(const QUrl &url, const bool has_cue, QByteArray *data, MoodbarPipeline **async_pipeline) {
|
MoodbarLoader::LoadResult MoodbarLoader::Load(const QUrl &url, const bool has_cue) {
|
||||||
|
|
||||||
if (!url.isLocalFile() || has_cue) {
|
if (!url.isLocalFile() || has_cue) {
|
||||||
return Result::CannotLoad;
|
return LoadStatus::CannotLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we in the middle of loading this moodbar already?
|
// Are we in the middle of loading this moodbar already?
|
||||||
if (requests_.contains(url)) {
|
if (requests_.contains(url)) {
|
||||||
*async_pipeline = requests_.value(url);
|
return LoadResult(LoadStatus::WillLoadAsync, requests_.value(url));
|
||||||
return Result::WillLoadAsync;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a mood file exists for this file already
|
// Check if a mood file exists for this file already
|
||||||
@@ -121,16 +124,18 @@ MoodbarLoader::Result MoodbarLoader::Load(const QUrl &url, const bool has_cue, Q
|
|||||||
|
|
||||||
const QStringList possible_mood_files = MoodFilenames(filename);
|
const QStringList possible_mood_files = MoodFilenames(filename);
|
||||||
for (const QString &possible_mood_file : possible_mood_files) {
|
for (const QString &possible_mood_file : possible_mood_files) {
|
||||||
QFile f(possible_mood_file);
|
QFile file(possible_mood_file);
|
||||||
if (f.exists()) {
|
if (file.exists()) {
|
||||||
if (f.open(QIODevice::ReadOnly)) {
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
qLog(Info) << "Loading moodbar data from" << possible_mood_file;
|
qLog(Info) << "Loading moodbar data from" << possible_mood_file;
|
||||||
*data = f.readAll();
|
const QByteArray data = file.readAll();
|
||||||
f.close();
|
file.close();
|
||||||
return Result::Loaded;
|
if (!data.isEmpty()) {
|
||||||
|
return LoadResult(LoadStatus::Loaded, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Error) << "Failed to load moodbar data from" << possible_mood_file << f.errorString();
|
qLog(Error) << "Failed to load moodbar data from" << possible_mood_file << file.errorString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,9 +147,9 @@ MoodbarLoader::Result MoodbarLoader::Load(const QUrl &url, const bool has_cue, Q
|
|||||||
ScopedPtr<QIODevice> device_cache_file(cache_->data(disk_cache_metadata.url()));
|
ScopedPtr<QIODevice> device_cache_file(cache_->data(disk_cache_metadata.url()));
|
||||||
if (device_cache_file) {
|
if (device_cache_file) {
|
||||||
qLog(Info) << "Loading cached moodbar data for" << filename;
|
qLog(Info) << "Loading cached moodbar data for" << filename;
|
||||||
*data = device_cache_file->readAll();
|
const QByteArray data = device_cache_file->readAll();
|
||||||
if (!data->isEmpty()) {
|
if (!data.isEmpty()) {
|
||||||
return Result::Loaded;
|
return LoadResult(LoadStatus::Loaded, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,17 +157,20 @@ MoodbarLoader::Result MoodbarLoader::Load(const QUrl &url, const bool has_cue, Q
|
|||||||
if (!thread_->isRunning()) thread_->start(QThread::IdlePriority);
|
if (!thread_->isRunning()) thread_->start(QThread::IdlePriority);
|
||||||
|
|
||||||
// There was no existing file, analyze the audio file and create one.
|
// There was no existing file, analyze the audio file and create one.
|
||||||
MoodbarPipeline *pipeline = new MoodbarPipeline(url);
|
MoodbarPipelinePtr pipeline = MoodbarPipelinePtr(new MoodbarPipeline(url));
|
||||||
pipeline->moveToThread(thread_);
|
pipeline->moveToThread(thread_);
|
||||||
QObject::connect(pipeline, &MoodbarPipeline::Finished, this, [this, pipeline, url]() { RequestFinished(pipeline, url); });
|
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||||
|
*connection = QObject::connect(&*pipeline, &MoodbarPipeline::Finished, this, [this, connection, pipeline, url]() {
|
||||||
|
RequestFinished(pipeline, url);
|
||||||
|
QObject::disconnect(*connection);
|
||||||
|
});
|
||||||
|
|
||||||
requests_[url] = pipeline;
|
requests_[url] = pipeline;
|
||||||
queued_requests_ << url;
|
queued_requests_ << url;
|
||||||
|
|
||||||
MaybeTakeNextRequest();
|
MaybeTakeNextRequest();
|
||||||
|
|
||||||
*async_pipeline = pipeline;
|
return LoadResult(LoadStatus::WillLoadAsync, pipeline);
|
||||||
return Result::WillLoadAsync;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,15 +186,17 @@ void MoodbarLoader::MaybeTakeNextRequest() {
|
|||||||
active_requests_ << url;
|
active_requests_ << url;
|
||||||
|
|
||||||
qLog(Info) << "Creating moodbar data for" << url.toLocalFile();
|
qLog(Info) << "Creating moodbar data for" << url.toLocalFile();
|
||||||
QMetaObject::invokeMethod(requests_.value(url), &MoodbarPipeline::Start, Qt::QueuedConnection);
|
|
||||||
|
MoodbarPipelinePtr pipeline = requests_.value(url);
|
||||||
|
QMetaObject::invokeMethod(&*pipeline, &MoodbarPipeline::Start, Qt::QueuedConnection);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarLoader::RequestFinished(MoodbarPipeline *request, const QUrl &url) {
|
void MoodbarLoader::RequestFinished(MoodbarPipelinePtr pipeline, const QUrl &url) {
|
||||||
|
|
||||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||||
|
|
||||||
if (request->success()) {
|
if (pipeline->success()) {
|
||||||
|
|
||||||
const QString filename = url.toLocalFile();
|
const QString filename = url.toLocalFile();
|
||||||
|
|
||||||
@@ -201,7 +211,7 @@ void MoodbarLoader::RequestFinished(MoodbarPipeline *request, const QUrl &url) {
|
|||||||
|
|
||||||
QIODevice *device_cache_file = cache_->prepare(disk_cache_metadata);
|
QIODevice *device_cache_file = cache_->prepare(disk_cache_metadata);
|
||||||
if (device_cache_file) {
|
if (device_cache_file) {
|
||||||
const qint64 data_written = device_cache_file->write(request->data());
|
const qint64 data_written = device_cache_file->write(pipeline->data());
|
||||||
if (data_written > 0) {
|
if (data_written > 0) {
|
||||||
cache_->insert(device_cache_file);
|
cache_->insert(device_cache_file);
|
||||||
}
|
}
|
||||||
@@ -213,7 +223,7 @@ void MoodbarLoader::RequestFinished(MoodbarPipeline *request, const QUrl &url) {
|
|||||||
const QString mood_filename(mood_filenames[0]);
|
const QString mood_filename(mood_filenames[0]);
|
||||||
QFile mood_file(mood_filename);
|
QFile mood_file(mood_filename);
|
||||||
if (mood_file.open(QIODevice::WriteOnly)) {
|
if (mood_file.open(QIODevice::WriteOnly)) {
|
||||||
if (mood_file.write(request->data()) <= 0) {
|
if (mood_file.write(pipeline->data()) <= 0) {
|
||||||
qLog(Error) << "Error writing to mood file" << mood_filename << mood_file.errorString();
|
qLog(Error) << "Error writing to mood file" << mood_filename << mood_file.errorString();
|
||||||
}
|
}
|
||||||
mood_file.close();
|
mood_file.close();
|
||||||
@@ -233,8 +243,6 @@ void MoodbarLoader::RequestFinished(MoodbarPipeline *request, const QUrl &url) {
|
|||||||
requests_.remove(url);
|
requests_.remove(url);
|
||||||
active_requests_.remove(url);
|
active_requests_.remove(url);
|
||||||
|
|
||||||
QTimer::singleShot(1s, request, &MoodbarLoader::deleteLater);
|
|
||||||
|
|
||||||
MaybeTakeNextRequest();
|
MaybeTakeNextRequest();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -30,10 +30,11 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "moodbarpipeline.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QNetworkDiskCache;
|
class QNetworkDiskCache;
|
||||||
class MoodbarPipeline;
|
|
||||||
|
|
||||||
class MoodbarLoader : public QObject {
|
class MoodbarLoader : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -42,7 +43,7 @@ class MoodbarLoader : public QObject {
|
|||||||
explicit MoodbarLoader(QObject *parent = nullptr);
|
explicit MoodbarLoader(QObject *parent = nullptr);
|
||||||
~MoodbarLoader() override;
|
~MoodbarLoader() override;
|
||||||
|
|
||||||
enum class Result {
|
enum class LoadStatus {
|
||||||
// The URL isn't a local file or the moodbar plugin was not available -
|
// The URL isn't a local file or the moodbar plugin was not available -
|
||||||
// moodbar data can never be loaded.
|
// moodbar data can never be loaded.
|
||||||
CannotLoad,
|
CannotLoad,
|
||||||
@@ -55,17 +56,25 @@ class MoodbarLoader : public QObject {
|
|||||||
WillLoadAsync
|
WillLoadAsync
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LoadResult {
|
||||||
|
public:
|
||||||
|
LoadResult(const LoadStatus _status) : status(_status) {}
|
||||||
|
LoadResult(const LoadStatus _status, MoodbarPipelinePtr _pipeline) : status(_status), pipeline(_pipeline) {}
|
||||||
|
LoadResult(const LoadStatus _status, const QByteArray &_data) : status(_status), data(_data) {}
|
||||||
|
LoadStatus status;
|
||||||
|
MoodbarPipelinePtr pipeline;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
Result Load(const QUrl &url, const bool has_cue, QByteArray *data, MoodbarPipeline **async_pipeline);
|
LoadResult Load(const QUrl &url, const bool has_cue);
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void RequestFinished(MoodbarPipeline *request, const QUrl &url);
|
|
||||||
void MaybeTakeNextRequest();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QStringList MoodFilenames(const QString &song_filename);
|
static QStringList MoodFilenames(const QString &song_filename);
|
||||||
static QUrl CacheUrlEntry(const QString &filename);
|
static QUrl CacheUrlEntry(const QString &filename);
|
||||||
|
void RequestFinished(MoodbarPipelinePtr pipeline, const QUrl &url);
|
||||||
|
void MaybeTakeNextRequest();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void MoodbarEnabled(const bool enabled);
|
void MoodbarEnabled(const bool enabled);
|
||||||
@@ -78,7 +87,7 @@ class MoodbarLoader : public QObject {
|
|||||||
|
|
||||||
const int kMaxActiveRequests;
|
const int kMaxActiveRequests;
|
||||||
|
|
||||||
QMap<QUrl, MoodbarPipeline*> requests_;
|
QMap<QUrl, MoodbarPipelinePtr> requests_;
|
||||||
QList<QUrl> queued_requests_;
|
QList<QUrl> queued_requests_;
|
||||||
QSet<QUrl> active_requests_;
|
QSet<QUrl> active_requests_;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -56,20 +56,24 @@ MoodbarPipeline::MoodbarPipeline(const QUrl &url, QObject *parent)
|
|||||||
success_(false),
|
success_(false),
|
||||||
running_(false) {}
|
running_(false) {}
|
||||||
|
|
||||||
MoodbarPipeline::~MoodbarPipeline() { Cleanup(); }
|
MoodbarPipeline::~MoodbarPipeline() {
|
||||||
|
|
||||||
GstElement *MoodbarPipeline::CreateElement(const QString &factory_name) {
|
Cleanup();
|
||||||
|
|
||||||
GstElement *ret = gst_element_factory_make(factory_name.toLatin1().constData(), nullptr);
|
}
|
||||||
|
|
||||||
if (ret) {
|
GstElement *MoodbarPipeline::CreateElement(const QByteArray &factory_name) {
|
||||||
gst_bin_add(GST_BIN(pipeline_), ret);
|
|
||||||
|
GstElement *element = gst_element_factory_make(factory_name.constData(), nullptr);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
gst_bin_add(GST_BIN(pipeline_), element);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Warning) << "Unable to create gstreamer element" << factory_name;
|
qLog(Warning) << "Unable to create gstreamer element" << factory_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return element;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +90,7 @@ QByteArray MoodbarPipeline::ToGstUrl(const QUrl &url) {
|
|||||||
|
|
||||||
void MoodbarPipeline::Start() {
|
void MoodbarPipeline::Start() {
|
||||||
|
|
||||||
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
||||||
|
|
||||||
Utilities::SetThreadIOPriority(Utilities::IoPriority::IOPRIO_CLASS_IDLE);
|
Utilities::SetThreadIOPriority(Utilities::IoPriority::IOPRIO_CLASS_IDLE);
|
||||||
@@ -96,10 +101,10 @@ void MoodbarPipeline::Start() {
|
|||||||
|
|
||||||
pipeline_ = gst_pipeline_new("moodbar-pipeline");
|
pipeline_ = gst_pipeline_new("moodbar-pipeline");
|
||||||
|
|
||||||
GstElement *decodebin = CreateElement(u"uridecodebin"_s);
|
GstElement *decodebin = CreateElement("uridecodebin");
|
||||||
convert_element_ = CreateElement(u"audioconvert"_s);
|
convert_element_ = CreateElement("audioconvert");
|
||||||
GstElement *spectrum = CreateElement(u"strawberry-fastspectrum"_s);
|
GstElement *spectrum = CreateElement("strawberry-fastspectrum");
|
||||||
GstElement *fakesink = CreateElement(u"fakesink"_s);
|
GstElement *fakesink = CreateElement("fakesink");
|
||||||
|
|
||||||
if (!decodebin || !convert_element_ || !spectrum || !fakesink) {
|
if (!decodebin || !convert_element_ || !spectrum || !fakesink) {
|
||||||
gst_object_unref(GST_OBJECT(pipeline_));
|
gst_object_unref(GST_OBJECT(pipeline_));
|
||||||
@@ -204,6 +209,8 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus *bus, GstMessage *messag
|
|||||||
|
|
||||||
MoodbarPipeline *instance = reinterpret_cast<MoodbarPipeline*>(self);
|
MoodbarPipeline *instance = reinterpret_cast<MoodbarPipeline*>(self);
|
||||||
|
|
||||||
|
if (!instance->running_) return GST_BUS_PASS;
|
||||||
|
|
||||||
switch (GST_MESSAGE_TYPE(message)) {
|
switch (GST_MESSAGE_TYPE(message)) {
|
||||||
case GST_MESSAGE_EOS:
|
case GST_MESSAGE_EOS:
|
||||||
instance->Stop(true);
|
instance->Stop(true);
|
||||||
@@ -224,23 +231,34 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus *bus, GstMessage *messag
|
|||||||
|
|
||||||
void MoodbarPipeline::Stop(const bool success) {
|
void MoodbarPipeline::Stop(const bool success) {
|
||||||
|
|
||||||
success_ = success;
|
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "Finish", Qt::QueuedConnection, Q_ARG(bool, success));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoodbarPipeline::Finish(const bool success) {
|
||||||
|
|
||||||
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
||||||
|
|
||||||
|
success_ = success;
|
||||||
|
|
||||||
if (builder_) {
|
if (builder_) {
|
||||||
data_ = builder_->Finish(1000);
|
data_ = builder_->Finish(1000);
|
||||||
builder_.reset();
|
builder_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
Q_EMIT Finished(success);
|
Q_EMIT Finished(success);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarPipeline::Cleanup() {
|
void MoodbarPipeline::Cleanup() {
|
||||||
|
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
|
||||||
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
|
||||||
|
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
||||||
if (pipeline_) {
|
if (pipeline_) {
|
||||||
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
|
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
|
||||||
if (bus) {
|
if (bus) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
@@ -46,18 +47,18 @@ class MoodbarPipeline : public QObject {
|
|||||||
bool success() const { return success_; }
|
bool success() const { return success_; }
|
||||||
const QByteArray &data() const { return data_; }
|
const QByteArray &data() const { return data_; }
|
||||||
|
|
||||||
public Q_SLOTS:
|
Q_INVOKABLE void Start();
|
||||||
void Start();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void Finished(const bool success);
|
void Finished(const bool success);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GstElement *CreateElement(const QString &factory_name);
|
GstElement *CreateElement(const QByteArray &factory_name);
|
||||||
|
|
||||||
QByteArray ToGstUrl(const QUrl &url);
|
QByteArray ToGstUrl(const QUrl &url);
|
||||||
void ReportError(GstMessage *msg);
|
void ReportError(GstMessage *msg);
|
||||||
void Stop(const bool success);
|
void Stop(const bool success);
|
||||||
|
Q_INVOKABLE void Finish(const bool success);
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
|
||||||
static void NewPadCallback(GstElement *element, GstPad *pad, gpointer self);
|
static void NewPadCallback(GstElement *element, GstPad *pad, gpointer self);
|
||||||
@@ -75,4 +76,6 @@ class MoodbarPipeline : public QObject {
|
|||||||
QByteArray data_;
|
QByteArray data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using MoodbarPipelinePtr = QSharedPointer<MoodbarPipeline>;
|
||||||
|
|
||||||
#endif // MOODBARPIPELINE_H
|
#endif // MOODBARPIPELINE_H
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ void MoodbarProxyStyle::drawComplexControl(ComplexControl control, const QStyleO
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarProxyStyle::Render(ComplexControl control, const QStyleOptionSlider *option, QPainter *painter, const QWidget *widget) {
|
void MoodbarProxyStyle::Render(const ComplexControl control, const QStyleOptionSlider *option, QPainter *painter, const QWidget *widget) {
|
||||||
|
|
||||||
const qreal fade_value = fade_timeline_->currentValue();
|
const qreal fade_value = fade_timeline_->currentValue();
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class MoodbarProxyStyle : public QProxyStyle {
|
|||||||
private:
|
private:
|
||||||
void NextState();
|
void NextState();
|
||||||
|
|
||||||
void Render(ComplexControl control, const QStyleOptionSlider *option, QPainter *painter, const QWidget *widget);
|
void Render(const ComplexControl control, const QStyleOptionSlider *option, QPainter *painter, const QWidget *widget);
|
||||||
void EnsureMoodbarRendered(const QStyleOptionSlider *opt);
|
void EnsureMoodbarRendered(const QStyleOptionSlider *opt);
|
||||||
void DrawArrow(const QStyleOptionSlider *option, QPainter *painter) const;
|
void DrawArrow(const QStyleOptionSlider *option, QPainter *painter) const;
|
||||||
void ShowContextMenu(const QPoint pos);
|
void ShowContextMenu(const QPoint pos);
|
||||||
@@ -92,7 +92,7 @@ class MoodbarProxyStyle : public QProxyStyle {
|
|||||||
static QPixmap MoodbarPixmap(const ColorVector &colors, const QSize size, const QPalette &palette, const QStyleOptionSlider *opt);
|
static QPixmap MoodbarPixmap(const ColorVector &colors, const QSize size, const QPalette &palette, const QStyleOptionSlider *opt);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void FaderValueChanged(qreal value);
|
void FaderValueChanged(const qreal value);
|
||||||
void SetStyle(QAction *action);
|
void SetStyle(QAction *action);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|||||||
@@ -106,8 +106,7 @@ Mpris2::Mpris2(const SharedPtr<Player> player,
|
|||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
player_(player),
|
player_(player),
|
||||||
playlist_manager_(playlist_manager),
|
playlist_manager_(playlist_manager),
|
||||||
current_albumcover_loader_(current_albumcover_loader),
|
current_albumcover_loader_(current_albumcover_loader) {
|
||||||
app_name_(QCoreApplication::applicationName()) {
|
|
||||||
|
|
||||||
new Mpris2Root(this);
|
new Mpris2Root(this);
|
||||||
new Mpris2TrackList(this);
|
new Mpris2TrackList(this);
|
||||||
@@ -135,8 +134,6 @@ Mpris2::Mpris2(const SharedPtr<Player> player,
|
|||||||
QObject::connect(&*playlist_manager_, &PlaylistManager::PlaylistChanged, this, &Mpris2::PlaylistChangedSlot);
|
QObject::connect(&*playlist_manager_, &PlaylistManager::PlaylistChanged, this, &Mpris2::PlaylistChangedSlot);
|
||||||
QObject::connect(&*playlist_manager_, &PlaylistManager::CurrentChanged, this, &Mpris2::PlaylistCollectionChanged);
|
QObject::connect(&*playlist_manager_, &PlaylistManager::CurrentChanged, this, &Mpris2::PlaylistCollectionChanged);
|
||||||
|
|
||||||
app_name_[0] = app_name_[0].toUpper();
|
|
||||||
|
|
||||||
QStringList data_dirs = QString::fromUtf8(qgetenv("XDG_DATA_DIRS")).split(u':');
|
QStringList data_dirs = QString::fromUtf8(qgetenv("XDG_DATA_DIRS")).split(u':');
|
||||||
|
|
||||||
if (!data_dirs.contains("/usr/local/share"_L1)) {
|
if (!data_dirs.contains("/usr/local/share"_L1)) {
|
||||||
@@ -238,7 +235,7 @@ bool Mpris2::CanRaise() const { return true; }
|
|||||||
|
|
||||||
bool Mpris2::HasTrackList() const { return true; }
|
bool Mpris2::HasTrackList() const { return true; }
|
||||||
|
|
||||||
QString Mpris2::Identity() const { return app_name_; }
|
QString Mpris2::Identity() const { return QCoreApplication::applicationName(); }
|
||||||
|
|
||||||
QString Mpris2::DesktopEntryAbsolutePath() const {
|
QString Mpris2::DesktopEntryAbsolutePath() const {
|
||||||
|
|
||||||
@@ -386,8 +383,12 @@ void Mpris2::SetRating(double rating) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusObjectPath Mpris2::current_track_id() const {
|
int Mpris2::current_playlist_row() const {
|
||||||
return QDBusObjectPath(QStringLiteral("/org/strawberrymusicplayer/strawberry/Track/%1").arg(QString::number(playlist_manager_->active()->current_row())));
|
return playlist_manager_->active()->current_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDBusObjectPath Mpris2::current_track_id(const int current_row) const {
|
||||||
|
return QDBusObjectPath(QStringLiteral("/org/strawberrymusicplayer/strawberry/Track/%1").arg(current_row));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We send Metadata change notification as soon as the process of changing song starts...
|
// We send Metadata change notification as soon as the process of changing song starts...
|
||||||
@@ -405,11 +406,14 @@ void Mpris2::CurrentSongChanged(const Song &song) {
|
|||||||
// ... and we add the cover information later, when it's available.
|
// ... and we add the cover information later, when it's available.
|
||||||
void Mpris2::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &result) {
|
void Mpris2::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &result) {
|
||||||
|
|
||||||
|
const int current_row = current_playlist_row();
|
||||||
|
if (current_row == -1) return;
|
||||||
|
|
||||||
last_metadata_ = QVariantMap();
|
last_metadata_ = QVariantMap();
|
||||||
song.ToXesam(&last_metadata_);
|
song.ToXesam(&last_metadata_);
|
||||||
|
|
||||||
using mpris::AddMetadata;
|
using mpris::AddMetadata;
|
||||||
AddMetadata(u"mpris:trackid"_s, current_track_id(), &last_metadata_);
|
AddMetadata(u"mpris:trackid"_s, current_track_id(current_row), &last_metadata_);
|
||||||
|
|
||||||
QUrl cover_url;
|
QUrl cover_url;
|
||||||
if (result.album_cover.cover_url.isValid() && result.album_cover.cover_url.isLocalFile() && QFile(result.album_cover.cover_url.toLocalFile()).exists()) {
|
if (result.album_cover.cover_url.isValid() && result.album_cover.cover_url.isLocalFile() && QFile(result.album_cover.cover_url.toLocalFile()).exists()) {
|
||||||
@@ -519,7 +523,11 @@ void Mpris2::Seek(qint64 offset) {
|
|||||||
|
|
||||||
void Mpris2::SetPosition(const QDBusObjectPath &trackId, qint64 offset) {
|
void Mpris2::SetPosition(const QDBusObjectPath &trackId, qint64 offset) {
|
||||||
|
|
||||||
if (CanSeek() && trackId == current_track_id() && offset >= 0) {
|
const int current_row = current_playlist_row();
|
||||||
|
|
||||||
|
if (current_row == -1) return;
|
||||||
|
|
||||||
|
if (CanSeek() && trackId == current_track_id(current_row) && offset >= 0) {
|
||||||
offset *= kNsecPerUsec;
|
offset *= kNsecPerUsec;
|
||||||
|
|
||||||
if (offset < player_->GetCurrentItem()->Metadata().length_nanosec()) {
|
if (offset < player_->GetCurrentItem()->Metadata().length_nanosec()) {
|
||||||
|
|||||||
@@ -230,7 +230,8 @@ class Mpris2 : public QObject {
|
|||||||
|
|
||||||
QString PlaybackStatus(EngineBase::State state) const;
|
QString PlaybackStatus(EngineBase::State state) const;
|
||||||
|
|
||||||
QDBusObjectPath current_track_id() const;
|
int current_playlist_row() const;
|
||||||
|
QDBusObjectPath current_track_id(const int current_row) const;
|
||||||
|
|
||||||
bool CanSeek(EngineBase::State state) const;
|
bool CanSeek(EngineBase::State state) const;
|
||||||
|
|
||||||
@@ -241,7 +242,6 @@ class Mpris2 : public QObject {
|
|||||||
const SharedPtr<PlaylistManager> playlist_manager_;
|
const SharedPtr<PlaylistManager> playlist_manager_;
|
||||||
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader_;
|
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader_;
|
||||||
|
|
||||||
QString app_name_;
|
|
||||||
QString desktopfilepath_;
|
QString desktopfilepath_;
|
||||||
QVariantMap last_metadata_;
|
QVariantMap last_metadata_;
|
||||||
|
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ void Organize::ProcessSomeFiles() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Song::FileType Organize::CheckTranscode(Song::FileType original_type) const {
|
Song::FileType Organize::CheckTranscode(const Song::FileType original_type) const {
|
||||||
|
|
||||||
if (original_type == Song::FileType::Stream) return Song::FileType::Unknown;
|
if (original_type == Song::FileType::Stream) return Song::FileType::Unknown;
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ Song::FileType Organize::CheckTranscode(Song::FileType original_type) const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Organize::SetSongProgress(float progress, bool transcoded) {
|
void Organize::SetSongProgress(const float progress, const bool transcoded) {
|
||||||
|
|
||||||
const int max = transcoded ? 50 : 100;
|
const int max = transcoded ? 50 : 100;
|
||||||
current_copy_progress_ = (transcoded ? 50 : 0) + qBound(0, static_cast<int>(progress * static_cast<float>(max)), max - 1);
|
current_copy_progress_ = (transcoded ? 50 : 0) + qBound(0, static_cast<int>(progress * static_cast<float>(max)), max - 1);
|
||||||
@@ -359,7 +359,7 @@ void Organize::UpdateProgress() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Organize::FileTranscoded(const QString &input, const QString &output, bool success) {
|
void Organize::FileTranscoded(const QString &input, const QString &output, const bool success) {
|
||||||
|
|
||||||
Q_UNUSED(output);
|
Q_UNUSED(output);
|
||||||
|
|
||||||
|
|||||||
@@ -87,13 +87,13 @@ class Organize : public QObject {
|
|||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void ProcessSomeFiles();
|
void ProcessSomeFiles();
|
||||||
void FileTranscoded(const QString &input, const QString &output, bool success);
|
void FileTranscoded(const QString &input, const QString &output, const bool success);
|
||||||
void LogLine(const QString &message);
|
void LogLine(const QString &message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetSongProgress(float progress, bool transcoded = false);
|
void SetSongProgress(const float progress, const bool transcoded = false);
|
||||||
void UpdateProgress();
|
void UpdateProgress();
|
||||||
Song::FileType CheckTranscode(Song::FileType original_type) const;
|
Song::FileType CheckTranscode(const Song::FileType original_type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Task {
|
struct Task {
|
||||||
|
|||||||
@@ -411,7 +411,6 @@ void OrganizeDialog::SetLoadingSongs(const bool loading) {
|
|||||||
SongList OrganizeDialog::LoadSongsBlocking(const QStringList &filenames) const {
|
SongList OrganizeDialog::LoadSongsBlocking(const QStringList &filenames) const {
|
||||||
|
|
||||||
SongList songs;
|
SongList songs;
|
||||||
Song song;
|
|
||||||
|
|
||||||
QStringList filenames_copy = filenames;
|
QStringList filenames_copy = filenames;
|
||||||
while (!filenames_copy.isEmpty()) {
|
while (!filenames_copy.isEmpty()) {
|
||||||
@@ -427,6 +426,7 @@ SongList OrganizeDialog::LoadSongsBlocking(const QStringList &filenames) const {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Song song;
|
||||||
const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, &song);
|
const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, &song);
|
||||||
if (result.success() && song.is_valid()) {
|
if (result.success() && song.is_valid()) {
|
||||||
songs << song;
|
songs << song;
|
||||||
@@ -476,6 +476,7 @@ Organize::NewSongInfoList OrganizeDialog::ComputeNewSongsFilenames(const SongLis
|
|||||||
}
|
}
|
||||||
new_songs_info << Organize::NewSongInfo(song, result.filename, result.unique_filename);
|
new_songs_info << Organize::NewSongInfo(song, result.filename, result.unique_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_songs_info;
|
return new_songs_info;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QGuiApplication>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
@@ -48,7 +49,6 @@ OSDBase::OSDBase(const SharedPtr<SystemTrayIcon> tray_icon, QObject *parent)
|
|||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
tray_icon_(tray_icon),
|
tray_icon_(tray_icon),
|
||||||
pretty_popup_(new OSDPretty(OSDPretty::Mode::Popup)),
|
pretty_popup_(new OSDPretty(OSDPretty::Mode::Popup)),
|
||||||
app_name_(QCoreApplication::applicationName()),
|
|
||||||
timeout_msec_(5000),
|
timeout_msec_(5000),
|
||||||
type_(OSDSettings::Type::Native),
|
type_(OSDSettings::Type::Native),
|
||||||
show_on_volume_change_(false),
|
show_on_volume_change_(false),
|
||||||
@@ -59,11 +59,7 @@ OSDBase::OSDBase(const SharedPtr<SystemTrayIcon> tray_icon, QObject *parent)
|
|||||||
use_custom_text_(false),
|
use_custom_text_(false),
|
||||||
force_show_next_(false),
|
force_show_next_(false),
|
||||||
ignore_next_stopped_(false),
|
ignore_next_stopped_(false),
|
||||||
playing_(false) {
|
playing_(false) {}
|
||||||
|
|
||||||
app_name_[0] = app_name_[0].toUpper();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
OSDBase::~OSDBase() {
|
OSDBase::~OSDBase() {
|
||||||
delete pretty_popup_;
|
delete pretty_popup_;
|
||||||
@@ -85,22 +81,48 @@ void OSDBase::ReloadSettings() {
|
|||||||
custom_text2_ = s.value("CustomText2").toString();
|
custom_text2_ = s.value("CustomText2").toString();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
if (!IsTypeSupported(type_)) {
|
||||||
if (!SupportsNativeNotifications() && !SupportsTrayPopups() && type_ == OSDSettings::Type::Native) {
|
type_ = GetSupportedType();
|
||||||
#else
|
|
||||||
if (!SupportsNativeNotifications() && type_ == OSDSettings::Type::Native) {
|
|
||||||
#endif
|
|
||||||
type_ = OSDSettings::Type::Pretty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SupportsTrayPopups() && type_ == OSDSettings::Type::TrayPopup) {
|
|
||||||
type_ = OSDSettings::Type::Disabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadPrettyOSDSettings();
|
ReloadPrettyOSDSettings();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OSDSettings::Type OSDBase::GetSupportedType() const {
|
||||||
|
|
||||||
|
if (SupportsNativeNotifications()) {
|
||||||
|
return OSDSettings::Type::Native;
|
||||||
|
}
|
||||||
|
if (SupportsOSDPretty()) {
|
||||||
|
return OSDSettings::Type::Pretty;
|
||||||
|
}
|
||||||
|
if (SupportsTrayPopups()) {
|
||||||
|
return OSDSettings::Type::TrayPopup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OSDSettings::Type::Disabled;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OSDBase::IsTypeSupported(const OSDSettings::Type type) const {
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case OSDSettings::Type::Native:
|
||||||
|
return SupportsNativeNotifications();
|
||||||
|
case OSDSettings::Type::TrayPopup:
|
||||||
|
return SupportsTrayPopups();
|
||||||
|
case OSDSettings::Type::Pretty:
|
||||||
|
return SupportsOSDPretty();
|
||||||
|
break;
|
||||||
|
case OSDSettings::Type::Disabled:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Reload just Pretty OSD settings, not everything
|
// Reload just Pretty OSD settings, not everything
|
||||||
void OSDBase::ReloadPrettyOSDSettings() {
|
void OSDBase::ReloadPrettyOSDSettings() {
|
||||||
|
|
||||||
@@ -264,7 +286,7 @@ void OSDBase::Stopped() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OSDBase::StopAfterToggle(const bool stop) {
|
void OSDBase::StopAfterToggle(const bool stop) {
|
||||||
ShowMessage(app_name_, tr("Stop playing after track: %1").arg(stop ? tr("On") : tr("Off")));
|
ShowMessage(QCoreApplication::applicationName(), tr("Stop playing after track: %1").arg(stop ? tr("On") : tr("Off")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSDBase::PlaylistFinished() {
|
void OSDBase::PlaylistFinished() {
|
||||||
@@ -272,7 +294,7 @@ void OSDBase::PlaylistFinished() {
|
|||||||
// We get a PlaylistFinished followed by a Stopped from the player
|
// We get a PlaylistFinished followed by a Stopped from the player
|
||||||
ignore_next_stopped_ = true;
|
ignore_next_stopped_ = true;
|
||||||
|
|
||||||
ShowMessage(app_name_, tr("Playlist finished"));
|
ShowMessage(QCoreApplication::applicationName(), tr("Playlist finished"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +312,7 @@ void OSDBase::VolumeChanged(const uint value) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ShowMessage(app_name_, message);
|
ShowMessage(QCoreApplication::applicationName(), message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +368,7 @@ void OSDBase::ShuffleModeChanged(const PlaylistSequence::ShuffleMode mode) {
|
|||||||
case PlaylistSequence::ShuffleMode::InsideAlbum: current_mode = tr("Shuffle tracks in this album"); break;
|
case PlaylistSequence::ShuffleMode::InsideAlbum: current_mode = tr("Shuffle tracks in this album"); break;
|
||||||
case PlaylistSequence::ShuffleMode::Albums: current_mode = tr("Shuffle albums"); break;
|
case PlaylistSequence::ShuffleMode::Albums: current_mode = tr("Shuffle albums"); break;
|
||||||
}
|
}
|
||||||
ShowMessage(app_name_, current_mode);
|
ShowMessage(QCoreApplication::applicationName(), current_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -363,7 +385,7 @@ void OSDBase::RepeatModeChanged(const PlaylistSequence::RepeatMode mode) {
|
|||||||
case PlaylistSequence::RepeatMode::OneByOne: current_mode = tr("Stop after every track"); break;
|
case PlaylistSequence::RepeatMode::OneByOne: current_mode = tr("Stop after every track"); break;
|
||||||
case PlaylistSequence::RepeatMode::Intro: current_mode = tr("Intro tracks"); break;
|
case PlaylistSequence::RepeatMode::Intro: current_mode = tr("Intro tracks"); break;
|
||||||
}
|
}
|
||||||
ShowMessage(app_name_, current_mode);
|
ShowMessage(QCoreApplication::applicationName(), current_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -437,14 +459,24 @@ void OSDBase::SetPrettyOSDToggleMode(const bool toggle) {
|
|||||||
pretty_popup_->set_toggle_mode(toggle);
|
pretty_popup_->set_toggle_mode(toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OSDBase::SupportsNativeNotifications() {
|
bool OSDBase::SupportsNativeNotifications() const {
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
return SupportsTrayPopups();
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OSDBase::SupportsTrayPopups() {
|
bool OSDBase::SupportsTrayPopups() const {
|
||||||
return tray_icon_->IsSystemTrayAvailable();
|
return tray_icon_->IsSystemTrayAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OSDBase::SupportsOSDPretty() {
|
||||||
|
return QGuiApplication::platformName() != "wayland"_L1;
|
||||||
|
}
|
||||||
|
|
||||||
void OSDBase::ShowMessageNative(const QString &summary, const QString &message, const QString &icon, const QImage &image) {
|
void OSDBase::ShowMessageNative(const QString &summary, const QString &message, const QString &icon, const QImage &image) {
|
||||||
|
|
||||||
Q_UNUSED(summary)
|
Q_UNUSED(summary)
|
||||||
|
|||||||
@@ -50,10 +50,11 @@ class OSDBase : public QObject {
|
|||||||
void ReloadPrettyOSDSettings();
|
void ReloadPrettyOSDSettings();
|
||||||
void SetPrettyOSDToggleMode(bool toggle);
|
void SetPrettyOSDToggleMode(bool toggle);
|
||||||
|
|
||||||
virtual bool SupportsNativeNotifications();
|
OSDSettings::Type GetSupportedType() const;
|
||||||
virtual bool SupportsTrayPopups();
|
bool IsTypeSupported(const OSDSettings::Type type) const;
|
||||||
|
virtual bool SupportsNativeNotifications() const;
|
||||||
QString app_name() { return app_name_; }
|
virtual bool SupportsTrayPopups() const;
|
||||||
|
static bool SupportsOSDPretty();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
@@ -88,7 +89,6 @@ class OSDBase : public QObject {
|
|||||||
const SharedPtr<SystemTrayIcon> tray_icon_;
|
const SharedPtr<SystemTrayIcon> tray_icon_;
|
||||||
OSDPretty *pretty_popup_;
|
OSDPretty *pretty_popup_;
|
||||||
|
|
||||||
QString app_name_;
|
|
||||||
int timeout_msec_;
|
int timeout_msec_;
|
||||||
OSDSettings::Type type_;
|
OSDSettings::Type type_;
|
||||||
bool show_on_volume_change_;
|
bool show_on_volume_change_;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user