Compare commits
136 Commits
1.2.1-rc1
...
refactorin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38d49ceb64 | ||
|
|
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 | ||
|
|
fac323a4a5 | ||
|
|
a26066d70f | ||
|
|
d500d38e63 | ||
|
|
295d4d9d05 | ||
|
|
01aaa0ba0b | ||
|
|
7b23118475 | ||
|
|
0c7806ab0a |
149
.github/workflows/build.yml
vendored
149
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -21,18 +21,18 @@ jobs:
|
||||
steps:
|
||||
- name: Refresh repositories
|
||||
run: zypper -n --gpg-auto-import-keys ref
|
||||
- name: Upgrade packages
|
||||
- name: Upgrade packages (Tumbleweed)
|
||||
if: matrix.opensuse_version == 'tumbleweed'
|
||||
run: zypper -n --gpg-auto-import-keys dup
|
||||
- name: Upgrade packages
|
||||
- name: Upgrade packages (Leap)
|
||||
if: matrix.opensuse_version != 'tumbleweed'
|
||||
run: zypper -n --gpg-auto-import-keys up
|
||||
- name: Install gcc
|
||||
- name: Install gcc (Tumbleweed)
|
||||
if: matrix.opensuse_version == 'tumbleweed'
|
||||
run: zypper -n --gpg-auto-import-keys in gcc gcc-c++
|
||||
- name: Install gcc 13
|
||||
- name: Install gcc (Leap)
|
||||
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
|
||||
run: >
|
||||
zypper -n --gpg-auto-import-keys in
|
||||
@@ -77,6 +77,8 @@ jobs:
|
||||
qt6-base-common-devel
|
||||
qt6-sql-sqlite
|
||||
qt6-linguist-devel
|
||||
gtest
|
||||
gmock
|
||||
- name: Install kdsingleapplication-qt6-devel
|
||||
if: matrix.opensuse_version == 'tumbleweed'
|
||||
run: zypper -n --gpg-auto-import-keys in kdsingleapplication-qt6-devel
|
||||
@@ -101,15 +103,18 @@ jobs:
|
||||
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||
- name: Build RPM (Tumbleweed)
|
||||
if: matrix.opensuse_version == 'tumbleweed'
|
||||
env:
|
||||
RPM_BUILD_NCPUS: 4
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
run: rpmbuild -ba strawberry.spec
|
||||
- name: Build RPM (Leap)
|
||||
if: matrix.opensuse_version != 'tumbleweed'
|
||||
working-directory: build
|
||||
env:
|
||||
CC: gcc-13
|
||||
CXX: g++-13
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
RPM_BUILD_NCPUS: 4
|
||||
CC: gcc-14
|
||||
CXX: g++-14
|
||||
working-directory: build
|
||||
run: rpmbuild -ba strawberry.spec
|
||||
- name: Set subdir
|
||||
id: set-subdir
|
||||
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
|
||||
@@ -132,7 +137,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -149,7 +154,7 @@ jobs:
|
||||
run: >
|
||||
dnf -y install
|
||||
@development-tools
|
||||
redhat-lsb-core
|
||||
lsb_release
|
||||
which
|
||||
git
|
||||
glibc
|
||||
@@ -185,6 +190,8 @@ jobs:
|
||||
libappstream-glib
|
||||
hicolor-icon-theme
|
||||
kdsingleapplication-qt6-devel
|
||||
gtest-devel
|
||||
gmock-devel
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -207,9 +214,9 @@ jobs:
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
env:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
RPM_BUILD_NCPUS: 4
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
run: rpmbuild -ba strawberry.spec
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -221,7 +228,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -300,9 +307,9 @@ jobs:
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
env:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
RPM_BUILD_NCPUS: 4
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
run: rpmbuild -ba strawberry.spec
|
||||
- name: Upload artifacts
|
||||
if: matrix.openmandriva_version != 'cooker'
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -315,7 +322,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -374,6 +381,7 @@ jobs:
|
||||
desktop-file-utils
|
||||
appstream-util
|
||||
hicolor-icon-theme
|
||||
gtest
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -396,9 +404,9 @@ jobs:
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
env:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
RPM_BUILD_NCPUS: 4
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
run: rpmbuild -ba strawberry.spec
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -410,7 +418,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -471,21 +479,24 @@ jobs:
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory build
|
||||
- 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
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j4
|
||||
- name: Copy deb
|
||||
run: cp ../*.deb .
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-${{matrix.debian_version}}
|
||||
path: "*.deb"
|
||||
path: |
|
||||
*.deb
|
||||
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -549,9 +560,11 @@ jobs:
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory build
|
||||
- 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
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j4
|
||||
- name: Copy deb
|
||||
run: cp ../*.deb ../*.ddeb .
|
||||
- name: Upload artifacts
|
||||
@@ -565,7 +578,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -634,7 +647,7 @@ jobs:
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory build
|
||||
- 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: Import Ubuntu PPA GPG private key
|
||||
@@ -651,9 +664,60 @@ jobs:
|
||||
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:
|
||||
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:
|
||||
fail-fast: false
|
||||
@@ -737,10 +801,12 @@ jobs:
|
||||
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
||||
-DBUILD_WERROR=ON
|
||||
-DUSE_BUNDLE=ON
|
||||
-DENABLE_DBUS=OFF
|
||||
-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 "")
|
||||
-DARCH="${{env.arch}}"
|
||||
-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
|
||||
run: cmake --build build --config Release --parallel 4
|
||||
@@ -876,10 +942,12 @@ jobs:
|
||||
-DCMAKE_PREFIX_PATH="${{env.prefix_path}}/lib/cmake"
|
||||
-DBUILD_WERROR=ON
|
||||
-DUSE_BUNDLE=ON
|
||||
-DENABLE_DBUS=OFF
|
||||
-DICU_ROOT="${{env.prefix_path}}"
|
||||
-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_SPARKLE=ON
|
||||
-DENABLE_QTSPARKLE=OFF
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --config Release --parallel 4
|
||||
@@ -931,7 +999,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -974,7 +1042,7 @@ jobs:
|
||||
-DBUILD_WERROR=ON
|
||||
-DARCH="${{matrix.arch}}"
|
||||
-DENABLE_WIN32_CONSOLE=$(test "${{matrix.buildtype}}" = "debug" && echo "ON" || echo "OFF")
|
||||
-DENABLE_DBUS=OFF
|
||||
-DENABLE_GIO=OFF
|
||||
-DENABLE_AUDIOCD=OFF
|
||||
-DENABLE_MTP=OFF
|
||||
-DENABLE_GPOD=OFF
|
||||
@@ -1120,7 +1188,7 @@ jobs:
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1262,6 +1330,10 @@ jobs:
|
||||
-DENABLE_WIN32_CONSOLE=${{env.win32_console}}
|
||||
-DPKG_CONFIG_EXECUTABLE="${{env.prefix_path_forwardslash}}/bin/pkg-config.exe"
|
||||
-DICU_ROOT="${{env.prefix_path_forwardslash}}"
|
||||
-DENABLE_GIO=OFF
|
||||
-DENABLE_AUDIOCD=OFF
|
||||
-DENABLE_MTP=OFF
|
||||
-DENABLE_GPOD=OFF
|
||||
-DENABLE_SPOTIFY=ON
|
||||
|
||||
- name: Run Make
|
||||
@@ -1396,6 +1468,11 @@ jobs:
|
||||
exit 1
|
||||
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
|
||||
shell: cmd
|
||||
working-directory: build
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,6 +11,4 @@
|
||||
/out
|
||||
/CMakeSettings.json
|
||||
/dist/scripts/maketarball.sh
|
||||
/dist/unix/strawberry.spec
|
||||
/debian/changelog
|
||||
/dist/macos/Info.plist
|
||||
|
||||
1254
CMakeLists.txt
1254
CMakeLists.txt
File diff suppressed because it is too large
Load Diff
52
Changelog
52
Changelog
@@ -2,7 +2,55 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
Version 1.2.1-rc1 (2024.11.16):
|
||||
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):
|
||||
|
||||
This release features major restructuring of the codebase, moving source files,
|
||||
rewriting CMake build files, dropping Qt 5 support, external tagreader,
|
||||
@@ -19,6 +67,8 @@ and dropping some unmaintained parts such as VLC.
|
||||
* Fixed crash when enabling Tidal, Spotify, Qobuz or Subsonic services.
|
||||
* Fixed passing filenames to strawberry on command line not resolving to absolute paths.
|
||||
* (macOS) Fixed program not starting for users with long usernames.
|
||||
* (macoS) Fixed crash when pressing caps lock (#1606).
|
||||
* (macOS) Remove "song progress on taskbar" option in behaviour settings.
|
||||
|
||||
Enhancements:
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ find_program(LSB_RELEASE_EXEC lsb_release)
|
||||
find_program(DPKG_BUILDPACKAGE 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()
|
||||
|
||||
@@ -31,7 +31,7 @@ if(MACDEPLOYQT_EXECUTABLE)
|
||||
|
||||
add_custom_target(deploy
|
||||
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 ${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}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
set(summary_willbuild "")
|
||||
set(summary_willnotbuild "")
|
||||
|
||||
macro(summary_add name test)
|
||||
macro(optional_component_summary_add name test)
|
||||
if (${test})
|
||||
list(APPEND summary_willbuild ${name})
|
||||
else (${test})
|
||||
list(APPEND summary_willnotbuild "${name}")
|
||||
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)
|
||||
if (_len)
|
||||
message("")
|
||||
@@ -18,19 +18,20 @@ macro(summary_show_part variable title)
|
||||
message(" ${_item}")
|
||||
endforeach (_item)
|
||||
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_willnotbuild)
|
||||
message("")
|
||||
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:")
|
||||
summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
|
||||
optional_component_summary_show_part(summary_willbuild "The following components will be built:")
|
||||
optional_component_summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
|
||||
message("")
|
||||
endmacro(summary_show)
|
||||
endmacro(optional_component_summary_show)
|
||||
|
||||
function(optional_component name default description)
|
||||
|
||||
set(option_variable "ENABLE_${name}")
|
||||
set(have_variable "HAVE_${name}")
|
||||
set(${have_variable} OFF)
|
||||
@@ -79,6 +80,9 @@ function(optional_component name default description)
|
||||
set(text "${description} (missing ${deplist_text})")
|
||||
|
||||
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()
|
||||
set(${have_variable} ON 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
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
||||
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()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||
set(STRAWBERRY_VERSION_MINOR 2)
|
||||
set(STRAWBERRY_VERSION_PATCH 1)
|
||||
set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
set(STRAWBERRY_VERSION_PATCH 6)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
set(INCLUDE_GIT_REVISION ON)
|
||||
|
||||
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
|
||||
|
||||
|
||||
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
|
||||
4
debian/control
vendored
4
debian/control
vendored
@@ -2,7 +2,7 @@ Source: strawberry
|
||||
Section: sound
|
||||
Priority: optional
|
||||
Maintainer: Jonas Kvinge <jonas@jkvinge.net>
|
||||
Build-Depends: debhelper (>= 11),
|
||||
Build-Depends: debhelper-compat (= 12),
|
||||
git,
|
||||
make,
|
||||
cmake,
|
||||
@@ -28,7 +28,7 @@ Build-Depends: debhelper (>= 11),
|
||||
libchromaprint-dev,
|
||||
libfftw3-dev,
|
||||
libebur128-dev
|
||||
Standards-Version: 4.6.1
|
||||
Standards-Version: 4.7.0
|
||||
|
||||
Package: strawberry
|
||||
Architecture: any
|
||||
|
||||
15
debian/rules
vendored
15
debian/rules
vendored
@@ -1,17 +1,10 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=cmake -builddirectory=build
|
||||
|
||||
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
|
||||
export DH_VERBOSE=1
|
||||
export DEB_BUILD_MAINT_OPTIONS=hardening=+all
|
||||
|
||||
override_dh_installchangelogs:
|
||||
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)
|
||||
if(RPM_DISTRO AND RPM_DATE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec @ONLY)
|
||||
endif(RPM_DISTRO AND RPM_DATE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_BINARY_DIR}/strawberry.spec @ONLY)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
|
||||
@@ -9,13 +9,13 @@ if(APPLE)
|
||||
else()
|
||||
set(LSMinimumSystemVersion 12.0)
|
||||
endif()
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
||||
endif(APPLE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
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)
|
||||
endif(WIN32)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
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.appdata.xml DESTINATION share/metainfo)
|
||||
install(FILES unix/strawberry.1 DESTINATION share/man/man1)
|
||||
endif(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
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")
|
||||
endif()
|
||||
|
||||
4
dist/macos/Info.plist.in
vendored
4
dist/macos/Info.plist.in
vendored
@@ -35,9 +35,9 @@
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>@LSMinimumSystemVersion@</string>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
||||
<string>https://www.strawberrymusicplayer.org/sparkle-macos-@ARCH@</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
|
||||
<string>/OydhYVfypuO2Mf7G6DUqVZWW9G19eFV74qaDCBTOUk=</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
</screenshots>
|
||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||
<releases>
|
||||
<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.1.3" date="2024-09-21"/>
|
||||
<release version="1.1.2" date="2024-09-12"/>
|
||||
<release version="1.1.1" date="2024-07-22"/>
|
||||
|
||||
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(libgpod-1.0)
|
||||
BuildRequires: pkgconfig(libmtp)
|
||||
BuildRequires: cmake(GTest)
|
||||
BuildRequires: pkgconfig(gmock)
|
||||
|
||||
%if 0%{?suse_version}
|
||||
Requires: qt6-sql-sqlite
|
||||
@@ -103,13 +105,13 @@ Features:
|
||||
|
||||
%build
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
||||
export CXXFLAGS="-fPIC -Wno-maybe-uninitialized $RPM_OPT_FLAGS"
|
||||
%endif
|
||||
%if "%{?_vendor}" == "openmandriva"
|
||||
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@ -DENABLE_TRANSLATIONS=OFF
|
||||
%{cmake} -DBUILD_WERROR=ON
|
||||
%make_build
|
||||
%else
|
||||
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
|
||||
%{cmake} -DBUILD_WERROR=ON
|
||||
%cmake_build
|
||||
%endif
|
||||
|
||||
@@ -120,11 +122,13 @@ Features:
|
||||
%cmake_install
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%suse_update_desktop_file org.strawberrymusicplayer.strawberry Qt AudioVideo Audio Player
|
||||
%endif
|
||||
|
||||
%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
|
||||
%if 0%{?suse_version}
|
||||
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
|
||||
!define vc_redist_file "vc_redist.${arch}.exe"
|
||||
Function InstallMSVCRuntime
|
||||
${registry::Read} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${arch}" "Version" $R0 $R1
|
||||
${If} $R0 == ""
|
||||
SetOutPath "$TEMP"
|
||||
File "${vc_redist_file}"
|
||||
; ${registry::Read} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${arch}" "Version" $R0 $R1
|
||||
; ${If} $R0 == ""
|
||||
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'
|
||||
Delete "$TEMP\${vc_redist_file}"
|
||||
SetDetailsView show
|
||||
${EndIf}
|
||||
; ${EndIf}
|
||||
FunctionEnd
|
||||
!endif
|
||||
|
||||
|
||||
@@ -1,2 +1,71 @@
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
|
||||
add_subdirectory(utilities)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(mimedata)
|
||||
add_subdirectory(osd)
|
||||
add_subdirectory(tagreader)
|
||||
add_subdirectory(widgets)
|
||||
add_subdirectory(dialogs)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(lyrics)
|
||||
add_subdirectory(filterparser)
|
||||
add_subdirectory(analyzer)
|
||||
add_subdirectory(transcoder)
|
||||
add_subdirectory(collection)
|
||||
add_subdirectory(playlist)
|
||||
add_subdirectory(playlistparsers)
|
||||
add_subdirectory(equalizer)
|
||||
add_subdirectory(edittagdialog)
|
||||
add_subdirectory(smartplaylists)
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(device)
|
||||
add_subdirectory(covermanager)
|
||||
add_subdirectory(fileview)
|
||||
add_subdirectory(player)
|
||||
add_subdirectory(radios)
|
||||
add_subdirectory(streaming)
|
||||
add_subdirectory(scrobbler)
|
||||
add_subdirectory(organize)
|
||||
add_subdirectory(context)
|
||||
add_subdirectory(queue)
|
||||
add_subdirectory(providers)
|
||||
add_subdirectory(songloader)
|
||||
add_subdirectory(systemtrayicon)
|
||||
|
||||
if(HAVE_MUSICBRAINZ)
|
||||
add_subdirectory(musicbrainz)
|
||||
endif()
|
||||
|
||||
if(HAVE_GLOBALSHORTCUTS)
|
||||
add_subdirectory(globalshortcuts)
|
||||
endif()
|
||||
|
||||
if(HAVE_MOODBAR)
|
||||
add_subdirectory(moodbar)
|
||||
endif()
|
||||
|
||||
if(HAVE_MPRIS2)
|
||||
add_subdirectory(mpris2)
|
||||
endif()
|
||||
|
||||
if(HAVE_SUBSONIC)
|
||||
add_subdirectory(subsonic)
|
||||
endif()
|
||||
|
||||
if(HAVE_TIDAL)
|
||||
add_subdirectory(tidal)
|
||||
endif()
|
||||
|
||||
if(HAVE_SPOTIFY)
|
||||
add_subdirectory(spotify)
|
||||
endif()
|
||||
|
||||
if(HAVE_QOBUZ)
|
||||
add_subdirectory(qobuz)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(macstartup)
|
||||
endif()
|
||||
|
||||
41
src/analyzer/CMakeLists.txt
Normal file
41
src/analyzer/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
set(ANALYZER_SOURCES
|
||||
fht.cpp
|
||||
analyzerbase.cpp
|
||||
analyzercontainer.cpp
|
||||
blockanalyzer.cpp
|
||||
boomanalyzer.cpp
|
||||
turbineanalyzer.cpp
|
||||
sonogramanalyzer.cpp
|
||||
waverubberanalyzer.cpp
|
||||
rainbowanalyzer.cpp
|
||||
)
|
||||
|
||||
set(ANALYZER_HEADERS
|
||||
analyzerbase.h
|
||||
analyzercontainer.h
|
||||
blockanalyzer.h
|
||||
boomanalyzer.h
|
||||
turbineanalyzer.h
|
||||
sonogramanalyzer.h
|
||||
waverubberanalyzer.h
|
||||
rainbowanalyzer.h
|
||||
)
|
||||
|
||||
qt_wrap_cpp(ANALYZER_SOURCES ${ANALYZER_HEADERS})
|
||||
|
||||
add_library(strawberry_analyzer STATIC ${ANALYZER_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_analyzer PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_analyzer PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_core
|
||||
strawberry_engine
|
||||
)
|
||||
@@ -50,9 +50,9 @@
|
||||
// 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
|
||||
|
||||
AnalyzerBase::AnalyzerBase(QWidget *parent, const uint scopeSize)
|
||||
AnalyzerBase::AnalyzerBase(QWidget *parent, const uint scope_size)
|
||||
: QWidget(parent),
|
||||
fht_(new FHT(scopeSize)),
|
||||
fht_(new FHT(scope_size)),
|
||||
engine_(nullptr),
|
||||
lastscope_(512),
|
||||
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;
|
||||
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 uint64_t offset = static_cast<uint64_t>(pos);
|
||||
|
||||
uint64_t indexLeft = offset + 0;
|
||||
|
||||
if (indexLeft >= inVec.size()) {
|
||||
indexLeft = inVec.size() - 1;
|
||||
if (indexLeft >= in_scope.size()) {
|
||||
indexLeft = in_scope.size() - 1;
|
||||
}
|
||||
|
||||
uint64_t indexRight = offset + 1;
|
||||
|
||||
if (indexRight >= inVec.size()) {
|
||||
indexRight = inVec.size() - 1;
|
||||
if (indexRight >= in_scope.size()) {
|
||||
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:
|
||||
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 showEvent(QShowEvent *e) override;
|
||||
@@ -71,12 +71,12 @@ class AnalyzerBase : public QWidget {
|
||||
int resizeExponent(int exp);
|
||||
int resizeForBands(const int bands);
|
||||
virtual void init() {}
|
||||
virtual void transform(Scope&);
|
||||
virtual void analyze(QPainter &p, const Scope&, const bool new_frame) = 0;
|
||||
virtual void transform(Scope &scope);
|
||||
virtual void analyze(QPainter &p, const Scope &s, const bool new_frame) = 0;
|
||||
virtual void demo(QPainter &p);
|
||||
|
||||
void interpolate(const Scope&, Scope&);
|
||||
void initSin(Scope&, const uint = 6000);
|
||||
void interpolate(const Scope &in_scope, Scope &out_scope);
|
||||
void initSin(Scope &v, const uint size = 6000);
|
||||
|
||||
protected:
|
||||
QBasicTimer timer_;
|
||||
|
||||
82
src/collection/CMakeLists.txt
Normal file
82
src/collection/CMakeLists.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
set(COLLECTION_SOURCES
|
||||
collectionlibrary.cpp
|
||||
collectionmodel.cpp
|
||||
collectionbackend.cpp
|
||||
collectionwatcher.cpp
|
||||
collectionview.cpp
|
||||
collectionitemdelegate.cpp
|
||||
collectionviewcontainer.cpp
|
||||
collectiondirectorymodel.cpp
|
||||
collectionfilteroptions.cpp
|
||||
collectionfilterwidget.cpp
|
||||
collectionfilter.cpp
|
||||
collectionplaylistitem.cpp
|
||||
collectionquery.cpp
|
||||
savedgroupingmanager.cpp
|
||||
groupbydialog.cpp
|
||||
collectiontask.cpp
|
||||
collectionmodelupdate.cpp
|
||||
collectionitem.cpp
|
||||
)
|
||||
|
||||
set(COLLECTION_HEADERS
|
||||
collectionlibrary.h
|
||||
collectionmodel.h
|
||||
collectionbackend.h
|
||||
collectionwatcher.h
|
||||
collectionview.h
|
||||
collectionitemdelegate.h
|
||||
collectionviewcontainer.h
|
||||
collectiondirectorymodel.h
|
||||
collectionfilterwidget.h
|
||||
collectionfilter.h
|
||||
savedgroupingmanager.h
|
||||
groupbydialog.h
|
||||
)
|
||||
|
||||
set(COLLECTION_UI
|
||||
groupbydialog.ui
|
||||
collectionfilterwidget.ui
|
||||
collectionviewcontainer.ui
|
||||
savedgroupingmanager.ui
|
||||
)
|
||||
|
||||
qt_wrap_cpp(COLLECTION_SOURCES ${COLLECTION_HEADERS})
|
||||
qt_wrap_ui(COLLECTION_SOURCES ${COLLECTION_UI})
|
||||
|
||||
add_library(strawberry_collection STATIC ${COLLECTION_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_collection PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_collection PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
PkgConfig::GSTREAMER
|
||||
PkgConfig::GSTREAMER_BASE
|
||||
PkgConfig::GSTREAMER_AUDIO
|
||||
PkgConfig::GSTREAMER_APP
|
||||
PkgConfig::GSTREAMER_TAG
|
||||
PkgConfig::GSTREAMER_PBUTILS
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
strawberry_mimedata
|
||||
strawberry_engine
|
||||
strawberry_tagreader
|
||||
strawberry_covermanager
|
||||
strawberry_filterparser
|
||||
strawberry_dialogs
|
||||
strawberry_edittagdialog
|
||||
strawberry_organize
|
||||
strawberry_playlistparsers
|
||||
)
|
||||
@@ -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) {
|
||||
|
||||
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;
|
||||
task_manager_ = task_manager;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "core/songmimedata.h"
|
||||
#include "mimedata/songmimedata.h"
|
||||
#include "filterparser/filterparser.h"
|
||||
#include "filterparser/filtertree.h"
|
||||
#include "collectionbackend.h"
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
#include "savedgroupingmanager.h"
|
||||
#include "collectionfilterwidget.h"
|
||||
#include "groupbydialog.h"
|
||||
#include "ui_collectionfilterwidget.h"
|
||||
#include "collection/ui_collectionfilterwidget.h"
|
||||
#include "widgets/searchfield.h"
|
||||
#include "constants/collectionsettings.h"
|
||||
#include "constants/appearancesettings.h"
|
||||
|
||||
@@ -66,7 +66,7 @@ CollectionLibrary::CollectionLibrary(const SharedPtr<Database> database,
|
||||
save_playcounts_to_files_(false),
|
||||
save_ratings_to_files_(false) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
original_thread_ = thread();
|
||||
|
||||
|
||||
@@ -50,16 +50,16 @@
|
||||
#include <QPixmapCache>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
||||
#include "includes/scoped_ptr.h"
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/standardpaths.h"
|
||||
#include "core/database.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/songmimedata.h"
|
||||
#include "mimedata/songmimedata.h"
|
||||
#include "collectionfilteroptions.h"
|
||||
#include "collectionquery.h"
|
||||
#include "collectionbackend.h"
|
||||
@@ -98,7 +98,7 @@ CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, con
|
||||
loading_(false),
|
||||
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_->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);
|
||||
}
|
||||
|
||||
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::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 {
|
||||
|
||||
const CollectionItem *item = IndexToItem(idx);
|
||||
|
||||
// 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);
|
||||
return data(IndexToItem(idx), 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;
|
||||
|
||||
@@ -329,7 +311,7 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const
|
||||
case GroupBy::YearAlbumDisc:
|
||||
case GroupBy::OriginalYearAlbum:
|
||||
case GroupBy::OriginalYearAlbumDisc:
|
||||
return QVariant();
|
||||
return options_active_.show_pretty_covers ? const_cast<CollectionModel*>(this)->AlbumIcon(item) : QVariant();
|
||||
case GroupBy::Artist:
|
||||
case GroupBy::AlbumArtist:
|
||||
return icon_artist_;
|
||||
@@ -408,17 +390,17 @@ QMimeData *CollectionModel::mimeData(const QModelIndexList &indexes) const {
|
||||
|
||||
if (indexes.isEmpty()) return nullptr;
|
||||
|
||||
SongMimeData *data = new SongMimeData;
|
||||
QList<QUrl> urls;
|
||||
SongList songs;
|
||||
QSet<int> song_ids;
|
||||
|
||||
data->backend = backend_;
|
||||
|
||||
QList<QUrl> urls;
|
||||
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->backend = backend_;
|
||||
data->songs = songs;
|
||||
data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
|
||||
|
||||
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) {
|
||||
|
||||
// Remove from pixmap cache
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(item));
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(item);
|
||||
QPixmapCache::remove(cache_key);
|
||||
if (use_disk_cache_ && icon_disk_cache_) icon_disk_cache_->remove(AlbumIconPixmapDiskCacheKey(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_;
|
||||
|
||||
// Check the cache for a pixmap we already loaded.
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(idx);
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(item);
|
||||
|
||||
QPixmap 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.
|
||||
SongList songs = GetChildSongs(idx);
|
||||
const SongList songs = GetChildSongs(item);
|
||||
if (!songs.isEmpty()) {
|
||||
AlbumCoverLoaderOptions cover_loader_options(AlbumCoverLoaderOptions::Option::ScaledImage | AlbumCoverLoaderOptions::Option::PadScaledImage);
|
||||
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:
|
||||
return SortTextForArtist(song.artist(), sort_skips_articles);
|
||||
case GroupBy::Album:
|
||||
return SortTextForArtist(song.album(), sort_skips_articles);
|
||||
return SortText(song.album());
|
||||
case GroupBy::AlbumDisc:
|
||||
return song.album() + SortTextForNumber(std::max(0, song.disc()));
|
||||
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 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) {
|
||||
case CollectionItem::Type::Container: {
|
||||
QList<CollectionItem*> children = item->children;
|
||||
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
for (CollectionItem *child : children) {
|
||||
GetChildSongs(child, urls, songs, song_ids);
|
||||
GetChildSongs(child, songs, song_ids, urls);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CollectionItem::Type::Song:
|
||||
urls->append(item->metadata.url());
|
||||
if (!song_ids->contains(item->metadata.id())) {
|
||||
songs->append(item->metadata);
|
||||
song_ids->insert(item->metadata.id());
|
||||
urls << item->metadata.url();
|
||||
if (!song_ids.contains(item->metadata.id())) {
|
||||
songs << item->metadata;
|
||||
song_ids << item->metadata.id();
|
||||
}
|
||||
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 {
|
||||
|
||||
QList<QUrl> dontcare;
|
||||
SongList ret;
|
||||
SongList songs;
|
||||
QSet<int> song_ids;
|
||||
|
||||
QList<QUrl> urls;
|
||||
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;
|
||||
|
||||
// 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 QModelIndexList &indexes) const;
|
||||
|
||||
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
||||
bool CompareItems(CollectionItem *a, CollectionItem *b) const;
|
||||
|
||||
bool HasParentAlbumGroupBy(CollectionItem *item) const;
|
||||
|
||||
@@ -224,7 +226,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
void BeginReset();
|
||||
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 ScheduleAddSongs(const SongList &songs);
|
||||
@@ -250,9 +252,9 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
|
||||
// Helpers
|
||||
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);
|
||||
QVariant AlbumIcon(const QModelIndex &idx);
|
||||
QVariant AlbumIcon(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);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
#include "core/iconloader.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "mimedata/mimedata.h"
|
||||
#include "core/musicstorage.h"
|
||||
#include "core/deletefiles.h"
|
||||
#include "core/settings.h"
|
||||
@@ -69,7 +69,7 @@
|
||||
# include "device/devicemanager.h"
|
||||
# include "device/devicestatefiltermodel.h"
|
||||
#endif
|
||||
#include "dialogs/edittagdialog.h"
|
||||
#include "edittagdialog/edittagdialog.h"
|
||||
#include "dialogs/deleteconfirmationdialog.h"
|
||||
#include "organize/organizedialog.h"
|
||||
#include "organize/organizeerrordialog.h"
|
||||
@@ -108,7 +108,7 @@ CollectionView::CollectionView(QWidget *parent)
|
||||
is_in_keyboard_search_(false),
|
||||
delete_files_(false) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
setItemDelegate(new CollectionItemDelegate(this));
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "collectionfilterwidget.h"
|
||||
#include "collectionview.h"
|
||||
#include "collectionviewcontainer.h"
|
||||
#include "ui_collectionviewcontainer.h"
|
||||
#include "collection/ui_collectionviewcontainer.h"
|
||||
|
||||
CollectionViewContainer::CollectionViewContainer(QWidget *parent) : QWidget(parent), ui_(new Ui_CollectionViewContainer) {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QIODevice>
|
||||
#include <QStorageInfo>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
@@ -51,6 +52,7 @@
|
||||
#include "core/settings.h"
|
||||
#include "utilities/imageutils.h"
|
||||
#include "constants/timeconstants.h"
|
||||
#include "constants/filesystemconstants.h"
|
||||
#include "tagreader/tagreaderclient.h"
|
||||
#include "collectiondirectory.h"
|
||||
#include "collectionbackend.h"
|
||||
@@ -104,7 +106,7 @@ CollectionWatcher::CollectionWatcher(const Song::Source source,
|
||||
cue_parser_(new CueParser(tagreader_client, backend, this)),
|
||||
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();
|
||||
|
||||
@@ -464,6 +466,24 @@ CollectionSubdirectoryList CollectionWatcher::ScanTransaction::GetAllSubdirs() {
|
||||
|
||||
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();
|
||||
|
||||
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) {
|
||||
|
||||
QFileInfo path_info(path);
|
||||
const QFileInfo path_info(path);
|
||||
|
||||
// Do not scan symlinked dirs that are already in collection
|
||||
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_)) {
|
||||
if (real_path.startsWith(dir.path)) {
|
||||
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_loudness_characteristics = false;
|
||||
@@ -556,32 +588,40 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
||||
|
||||
if (stop_or_abort_requested()) return;
|
||||
|
||||
QString child(it.next());
|
||||
QFileInfo child_info(child);
|
||||
const QString child_filepath = it.next();
|
||||
const QFileInfo child_fileinfo(child_filepath);
|
||||
|
||||
if (child_info.isDir()) {
|
||||
if (!t->HasSeenSubdir(child)) {
|
||||
if (child_fileinfo.isSymLink()) {
|
||||
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.
|
||||
CollectionSubdirectory new_subdir;
|
||||
new_subdir.directory_id = -1;
|
||||
new_subdir.path = child;
|
||||
new_subdir.mtime = child_info.lastModified().toSecsSinceEpoch();
|
||||
new_subdir.path = child_filepath;
|
||||
new_subdir.mtime = child_fileinfo.lastModified().toSecsSinceEpoch();
|
||||
my_new_subdirs << new_subdir;
|
||||
}
|
||||
t->AddToProgress(1);
|
||||
}
|
||||
else {
|
||||
QString ext_part(ExtensionPart(child));
|
||||
QString dir_part(DirectoryPart(child));
|
||||
if (Song::kRejectedExtensions.contains(child_info.suffix(), Qt::CaseInsensitive) || child_info.baseName() == "qt_temp"_L1) {
|
||||
QString ext_part(ExtensionPart(child_filepath));
|
||||
QString dir_part(DirectoryPart(child_filepath));
|
||||
if (Song::kRejectedExtensions.contains(child_fileinfo.suffix(), Qt::CaseInsensitive) || child_fileinfo.baseName() == "qt_temp"_L1) {
|
||||
t->AddToProgress(1);
|
||||
}
|
||||
else if (sValidImages.contains(ext_part)) {
|
||||
album_art[dir_part] << child;
|
||||
album_art[dir_part] << child_filepath;
|
||||
t->AddToProgress(1);
|
||||
}
|
||||
else if (tagreader_client_->IsMediaFileBlocking(child)) {
|
||||
files_on_disk << child;
|
||||
else if (tagreader_client_->IsMediaFileBlocking(child_filepath)) {
|
||||
files_on_disk << child_filepath;
|
||||
}
|
||||
else {
|
||||
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();
|
||||
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();
|
||||
|
||||
// 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.
|
||||
// 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);
|
||||
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();
|
||||
songs.reserve(cue_songs.count());
|
||||
for (Song &cue_song : cue_songs) {
|
||||
@@ -1153,7 +1193,7 @@ void CollectionWatcher::RescanPathsNow() {
|
||||
|
||||
QMap<QString, quint64> subdir_files_count;
|
||||
for (const QString &path : paths) {
|
||||
quint64 files_count = FilesCountForPath(&transaction, path);
|
||||
const quint64 files_count = FilesCountForPath(&transaction, path);
|
||||
subdir_files_count[path] = 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) {
|
||||
|
||||
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;
|
||||
QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
while (it.hasNext()) {
|
||||
|
||||
if (stop_or_abort_requested()) break;
|
||||
|
||||
QString child = it.next();
|
||||
QFileInfo path_info(child);
|
||||
const QString child_filepath = it.next();
|
||||
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_)) {
|
||||
if (real_path.startsWith(dir.path)) {
|
||||
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.
|
||||
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 (subdir.path != song_path) continue;
|
||||
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);
|
||||
scanned_paths << subdir.path;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#cmakedefine HAVE_AUDIOCD
|
||||
#cmakedefine HAVE_MTP
|
||||
#cmakedefine HAVE_GPOD
|
||||
#cmakedefine HAVE_SPARKLE
|
||||
#cmakedefine HAVE_QTSPARKLE
|
||||
#cmakedefine HAVE_SONGFINGERPRINTING
|
||||
#cmakedefine HAVE_MUSICBRAINZ
|
||||
|
||||
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 kVerifyCertificate[] = "verifycertificate";
|
||||
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
||||
constexpr char kUseAlbumIdForAlbumCovers[] = "usealbumidforalbumcovers";
|
||||
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
|
||||
constexpr char kAuthMethod[] = "authmethod";
|
||||
|
||||
|
||||
32
src/context/CMakeLists.txt
Normal file
32
src/context/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
set(CONTEXT_SOURCES
|
||||
contextview.cpp
|
||||
contextalbum.cpp
|
||||
)
|
||||
|
||||
set(CONTEXT_HEADERS
|
||||
contextview.h
|
||||
contextalbum.h
|
||||
)
|
||||
|
||||
qt_wrap_cpp(CONTEXT_SOURCES ${CONTEXT_HEADERS})
|
||||
|
||||
add_library(strawberry_context STATIC ${CONTEXT_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_context PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_context PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
strawberry_collection
|
||||
strawberry_covermanager
|
||||
strawberry_lyrics
|
||||
strawberry_widgets
|
||||
)
|
||||
95
src/core/CMakeLists.txt
Normal file
95
src/core/CMakeLists.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
set(CORE_SOURCES
|
||||
logging.cpp
|
||||
commandlineoptions.cpp
|
||||
database.cpp
|
||||
memorydatabase.cpp
|
||||
sqlquery.cpp
|
||||
sqlrow.cpp
|
||||
deletefiles.cpp
|
||||
filesystemmusicstorage.cpp
|
||||
filesystemwatcherinterface.cpp
|
||||
mergedproxymodel.cpp
|
||||
multisortfilterproxy.cpp
|
||||
musicstorage.cpp
|
||||
networkaccessmanager.cpp
|
||||
threadsafenetworkdiskcache.cpp
|
||||
networktimeouts.cpp
|
||||
networkproxyfactory.cpp
|
||||
qtfslistener.cpp
|
||||
settings.cpp
|
||||
settingsprovider.cpp
|
||||
signalchecker.cpp
|
||||
song.cpp
|
||||
stylehelper.cpp
|
||||
stylesheetloader.cpp
|
||||
taskmanager.cpp
|
||||
thread.cpp
|
||||
urlhandlers.cpp
|
||||
urlhandler.cpp
|
||||
iconloader.cpp
|
||||
standarditemiconloader.cpp
|
||||
scopedtransaction.cpp
|
||||
localredirectserver.cpp
|
||||
temporaryfile.cpp
|
||||
enginemetadata.cpp
|
||||
platforminterface.cpp
|
||||
)
|
||||
|
||||
set(CORE_HEADERS
|
||||
logging.h
|
||||
database.h
|
||||
memorydatabase.h
|
||||
deletefiles.h
|
||||
filesystemwatcherinterface.h
|
||||
mergedproxymodel.h
|
||||
multisortfilterproxy.h
|
||||
networkaccessmanager.h
|
||||
threadsafenetworkdiskcache.h
|
||||
networktimeouts.h
|
||||
qtfslistener.h
|
||||
settings.h
|
||||
taskmanager.h
|
||||
thread.h
|
||||
urlhandlers.h
|
||||
urlhandler.h
|
||||
standarditemiconloader.h
|
||||
stylesheetloader.h
|
||||
localredirectserver.h
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND CORE_SOURCES scoped_nsautorelease_pool.mm)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND CORE_SOURCES windows7thumbbar.cpp)
|
||||
list(APPEND CORE_HEADERS windows7thumbbar.h)
|
||||
endif()
|
||||
|
||||
qt_wrap_cpp(CORE_SOURCES ${CORE_HEADERS})
|
||||
|
||||
add_library(strawberry_core STATIC ${CORE_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_core PRIVATE
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_core PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
PkgConfig::SQLITE
|
||||
${TAGLIB_LIBRARIES}
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
||||
$<$<BOOL:${WIN32}>:getopt-win::getopt>
|
||||
strawberry_utilities
|
||||
)
|
||||
@@ -39,10 +39,10 @@
|
||||
#include <QSqlDriver>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlError>
|
||||
#include <QStandardPaths>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "logging.h"
|
||||
#include "standardpaths.h"
|
||||
#include "taskmanager.h"
|
||||
#include "database.h"
|
||||
#include "sqlquery.h"
|
||||
@@ -69,7 +69,7 @@ Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const Q
|
||||
startup_schema_version_(-1),
|
||||
original_thread_(nullptr) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
original_thread_ = thread();
|
||||
|
||||
@@ -78,7 +78,7 @@ Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const Q
|
||||
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_);
|
||||
Connect();
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
#include <QSize>
|
||||
#include <QStandardPaths>
|
||||
#include <QSettings>
|
||||
|
||||
#include "logging.h"
|
||||
#include "standardpaths.h"
|
||||
#include "settings.h"
|
||||
#include "includes/iconmapper.h"
|
||||
#include "iconloader.h"
|
||||
@@ -51,7 +51,7 @@ void IconLoader::Init() {
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ QIcon IconLoader::Load(const QString &name, const bool system_icon, const int fi
|
||||
}
|
||||
|
||||
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)) {
|
||||
QString filename(custom_icon_path.arg(s).arg(s).arg(name));
|
||||
if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#import <QuartzCore/CALayer.h>
|
||||
|
||||
#import <SPMediaKeyTap.h>
|
||||
//#import <SPMediaKeyTap.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -145,18 +145,18 @@ QDebug operator<<(QDebug dbg, NSObject *object) {
|
||||
|
||||
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
|
||||
|
||||
key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self];
|
||||
if ([SPMediaKeyTap usesGlobalMediaKeyTap]) {
|
||||
if ([key_tap_ startWatchingMediaKeys]) {
|
||||
qLog(Debug) << "Media key monitoring started";
|
||||
}
|
||||
else {
|
||||
qLog(Warning) << "Failed to start media key monitoring";
|
||||
}
|
||||
}
|
||||
else {
|
||||
qLog(Warning) << "Media key monitoring disabled";
|
||||
}
|
||||
// key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self];
|
||||
// if ([SPMediaKeyTap usesGlobalMediaKeyTap]) {
|
||||
// if ([key_tap_ startWatchingMediaKeys]) {
|
||||
// qLog(Debug) << "Media key monitoring started";
|
||||
// }
|
||||
// else {
|
||||
// qLog(Warning) << "Failed to start media key monitoring";
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// qLog(Warning) << "Media key monitoring disabled";
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -192,10 +192,10 @@ QDebug operator<<(QDebug dbg, NSObject *object) {
|
||||
|
||||
}
|
||||
|
||||
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event {
|
||||
#pragma unused(keyTap)
|
||||
[self handleMediaEvent:event];
|
||||
}
|
||||
// - (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event {
|
||||
// #pragma unused(keyTap)
|
||||
// [self handleMediaEvent:event];
|
||||
// }
|
||||
|
||||
- (BOOL) handleMediaEvent:(NSEvent*)event {
|
||||
// if it is not a media key event, then ignore
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,13 +18,26 @@
|
||||
*/
|
||||
|
||||
#include <QSettings>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Settings::Settings(QObject *parent)
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
: QSettings(QCoreApplication::organizationName().toLower(), QCoreApplication::applicationName().toLower(), parent) {}
|
||||
#else
|
||||
: 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)
|
||||
: QSettings(filename, format, parent) {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,14 +21,14 @@
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
class Settings : public QSettings {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
class SettingsProvider {
|
||||
public:
|
||||
@@ -60,7 +61,7 @@ class DefaultSettingsProvider : public SettingsProvider {
|
||||
void endArray() override;
|
||||
|
||||
private:
|
||||
QSettings backend_;
|
||||
Settings backend_;
|
||||
|
||||
Q_DISABLE_COPY(DefaultSettingsProvider)
|
||||
};
|
||||
|
||||
@@ -45,11 +45,11 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QUrl>
|
||||
#include <QIcon>
|
||||
#include <QStandardPaths>
|
||||
#include <QSqlRecord>
|
||||
|
||||
#include <taglib/tstring.h>
|
||||
|
||||
#include "core/standardpaths.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/enginemetadata.h"
|
||||
#include "utilities/strutils.h"
|
||||
@@ -249,7 +249,8 @@ const QStringList Song::kRejectedExtensions = QStringList() << u"tmp"_s
|
||||
<< u"z"_s
|
||||
<< u"zip"_s
|
||||
<< u"rar"_s
|
||||
<< u"wvc"_s;
|
||||
<< u"wvc"_s
|
||||
<< u"zst"_s;
|
||||
|
||||
struct Song::Private : public QSharedData {
|
||||
|
||||
@@ -1334,24 +1335,24 @@ QString Song::ImageCacheDir(const Source source) {
|
||||
|
||||
switch (source) {
|
||||
case Source::Collection:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/collectionalbumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/collectionalbumcovers"_s;
|
||||
case Source::Subsonic:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/subsonicalbumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/subsonicalbumcovers"_s;
|
||||
case Source::Tidal:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/tidalalbumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/tidalalbumcovers"_s;
|
||||
case Source::Spotify:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/spotifyalbumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/spotifyalbumcovers"_s;
|
||||
case Source::Qobuz:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/qobuzalbumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/qobuzalbumcovers"_s;
|
||||
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::CDDA:
|
||||
case Source::Stream:
|
||||
case Source::SomaFM:
|
||||
case Source::RadioParadise:
|
||||
case Source::Unknown:
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/albumcovers"_s;
|
||||
return StandardPaths::WritableLocation(StandardPaths::StandardLocation::AppLocalDataLocation) + u"/albumcovers"_s;
|
||||
}
|
||||
|
||||
return QString();
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
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) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
#include <QNetworkDiskCache>
|
||||
@@ -32,6 +31,7 @@
|
||||
#include <QAbstractNetworkCache>
|
||||
#include <QUrl>
|
||||
|
||||
#include "standardpaths.h"
|
||||
#include "threadsafenetworkdiskcache.h"
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
@@ -48,9 +48,9 @@ ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstr
|
||||
if (!sCache) {
|
||||
sCache = new QNetworkDiskCache;
|
||||
#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
|
||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u"/networkcache"_s);
|
||||
sCache->setCacheDirectory(StandardPaths::WritableLocation(StandardPaths::StandardLocation::CacheLocation) + u"/networkcache"_s);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
119
src/covermanager/CMakeLists.txt
Normal file
119
src/covermanager/CMakeLists.txt
Normal file
@@ -0,0 +1,119 @@
|
||||
set(COVERMANAGER_SOURCES
|
||||
albumcovermanager.cpp
|
||||
albumcovermanagerlist.cpp
|
||||
albumcoverloader.cpp
|
||||
albumcoverloaderoptions.cpp
|
||||
albumcoverfetcher.cpp
|
||||
albumcoverfetchersearch.cpp
|
||||
albumcoversearcher.cpp
|
||||
albumcoverexport.cpp
|
||||
albumcoverexporter.cpp
|
||||
albumcoverchoicecontroller.cpp
|
||||
coverprovider.cpp
|
||||
coverproviders.cpp
|
||||
coversearchstatistics.cpp
|
||||
coversearchstatisticsdialog.cpp
|
||||
coverexportrunnable.cpp
|
||||
currentalbumcoverloader.cpp
|
||||
coverfromurldialog.cpp
|
||||
jsoncoverprovider.cpp
|
||||
lastfmcoverprovider.cpp
|
||||
musicbrainzcoverprovider.cpp
|
||||
discogscoverprovider.cpp
|
||||
deezercoverprovider.cpp
|
||||
musixmatchcoverprovider.cpp
|
||||
opentidalcoverprovider.cpp
|
||||
)
|
||||
|
||||
set(COVERMANAGER_HEADERS
|
||||
albumcovermanager.h
|
||||
albumcovermanagerlist.h
|
||||
albumcoverloader.h
|
||||
albumcoverfetcher.h
|
||||
albumcoverfetchersearch.h
|
||||
albumcoversearcher.h
|
||||
albumcoverexport.h
|
||||
albumcoverexporter.h
|
||||
albumcoverchoicecontroller.h
|
||||
coverprovider.h
|
||||
coverproviders.h
|
||||
coversearchstatisticsdialog.h
|
||||
coverexportrunnable.h
|
||||
currentalbumcoverloader.h
|
||||
coverfromurldialog.h
|
||||
jsoncoverprovider.h
|
||||
lastfmcoverprovider.h
|
||||
musicbrainzcoverprovider.h
|
||||
discogscoverprovider.h
|
||||
deezercoverprovider.h
|
||||
musixmatchcoverprovider.h
|
||||
opentidalcoverprovider.h
|
||||
)
|
||||
|
||||
set(COVERMANAGER_UI
|
||||
albumcoverexport.ui
|
||||
albumcovermanager.ui
|
||||
albumcoversearcher.ui
|
||||
coversearchstatisticsdialog.ui
|
||||
coverfromurldialog.ui
|
||||
)
|
||||
|
||||
if(HAVE_TIDAL)
|
||||
list(APPEND COVERMANAGER_SOURCES tidalcoverprovider.cpp)
|
||||
list(APPEND COVERMANAGER_HEADERS tidalcoverprovider.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_SPOTIFY)
|
||||
list(APPEND COVERMANAGER_SOURCES spotifycoverprovider.cpp)
|
||||
list(APPEND COVERMANAGER_HEADERS spotifycoverprovider.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_QOBUZ)
|
||||
list(APPEND COVERMANAGER_SOURCES qobuzcoverprovider.cpp)
|
||||
list(APPEND COVERMANAGER_HEADERS qobuzcoverprovider.h)
|
||||
endif()
|
||||
|
||||
qt_wrap_cpp(COVERMANAGER_SOURCES ${COVERMANAGER_HEADERS})
|
||||
qt_wrap_ui(COVERMANAGER_SOURCES ${COVERMANAGER_UI})
|
||||
|
||||
add_library(strawberry_covermanager STATIC ${COVERMANAGER_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_covermanager PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_covermanager PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
strawberry_tagreader
|
||||
strawberry_collection
|
||||
strawberry_streaming
|
||||
strawberry_widgets
|
||||
)
|
||||
|
||||
if(HAVE_SUBSONIC)
|
||||
target_link_libraries(strawberry_covermanager PRIVATE strawberry_subsonic)
|
||||
endif()
|
||||
|
||||
if(HAVE_TIDAL)
|
||||
target_link_libraries(strawberry_covermanager PRIVATE strawberry_tidal)
|
||||
endif()
|
||||
|
||||
if(HAVE_SPOTIFY)
|
||||
target_link_libraries(strawberry_covermanager PRIVATE strawberry_spotify)
|
||||
endif()
|
||||
|
||||
if(HAVE_QOBUZ)
|
||||
target_link_libraries(strawberry_covermanager PRIVATE strawberry_qobuz)
|
||||
endif()
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QGuiApplication>
|
||||
@@ -79,6 +80,7 @@
|
||||
#include "coverfromurldialog.h"
|
||||
#include "currentalbumcoverloader.h"
|
||||
|
||||
using std::make_shared;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
QSet<QString> *AlbumCoverChoiceController::sImageExtensions = nullptr;
|
||||
@@ -743,7 +745,11 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedToSong(const Song &song, const
|
||||
cover_save_tasks_.append(song);
|
||||
const bool art_embedded = !image_data.isNull();
|
||||
TagReaderReplyPtr reply = tagreader_client_->SaveCoverAsync(song.url().toLocalFile(), SaveTagCoverData(cover_filename, image_data, mime_type));
|
||||
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, song, art_embedded]() { SaveEmbeddedCoverFinished(reply, song, art_embedded); });
|
||||
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, song, art_embedded, connection]() {
|
||||
SaveEmbeddedCoverFinished(reply, song, art_embedded);
|
||||
QObject::disconnect(*connection);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ AlbumCoverLoader::AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_cl
|
||||
load_image_async_id_(1),
|
||||
original_thread_(nullptr) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
original_thread_ = thread();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMainWindow>
|
||||
@@ -68,7 +69,7 @@
|
||||
#include "core/settings.h"
|
||||
#include "core/database.h"
|
||||
#include "core/networkaccessmanager.h"
|
||||
#include "core/songmimedata.h"
|
||||
#include "mimedata/songmimedata.h"
|
||||
#include "utilities/strutils.h"
|
||||
#include "utilities/fileutils.h"
|
||||
#include "utilities/imageutils.h"
|
||||
@@ -96,6 +97,7 @@
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
using std::make_shared;
|
||||
|
||||
namespace {
|
||||
constexpr char kSettingsGroup[] = "CoverManager";
|
||||
@@ -855,8 +857,10 @@ void AlbumCoverManager::SaveImageToAlbums(Song *song, const AlbumCoverImageResul
|
||||
for (const QUrl &url : std::as_const(album_item->urls)) {
|
||||
const bool art_embedded = !result.image_data.isEmpty();
|
||||
TagReaderReplyPtr reply = tagreader_client_->SaveCoverAsync(url.toLocalFile(), SaveTagCoverData(result.image_data, result.mime_type));
|
||||
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, album_item, url, art_embedded]() {
|
||||
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, album_item, url, art_embedded, connection]() {
|
||||
SaveEmbeddedCoverFinished(reply, album_item, url, art_embedded);
|
||||
QObject::disconnect(*connection);
|
||||
});
|
||||
cover_save_tasks_.insert(album_item, url);
|
||||
}
|
||||
@@ -1005,8 +1009,10 @@ void AlbumCoverManager::SaveAndSetCover(AlbumItem *album_item, const AlbumCoverI
|
||||
for (const QUrl &url : urls) {
|
||||
const bool art_embedded = !result.image_data.isEmpty();
|
||||
TagReaderReplyPtr reply = tagreader_client_->SaveCoverAsync(url.toLocalFile(), SaveTagCoverData(result.cover_url.isValid() ? result.cover_url.toLocalFile() : QString(), result.image_data, result.mime_type));
|
||||
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, album_item, url, art_embedded]() {
|
||||
SharedPtr<QMetaObject::Connection> connection = std::make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, album_item, url, art_embedded, connection]() {
|
||||
SaveEmbeddedCoverFinished(reply, album_item, url, art_embedded);
|
||||
QObject::disconnect(*connection);
|
||||
});
|
||||
cover_save_tasks_.insert(album_item, url);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "includes/scoped_ptr.h"
|
||||
#include "core/song.h"
|
||||
#include "core/songmimedata.h"
|
||||
#include "mimedata/songmimedata.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "albumcovermanager.h"
|
||||
#include "albumcovermanagerlist.h"
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
#include <QImage>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/standardpaths.h"
|
||||
#include "core/song.h"
|
||||
#include "core/temporaryfile.h"
|
||||
#include "albumcoverloader.h"
|
||||
@@ -42,10 +42,10 @@ using namespace Qt::Literals::StringLiterals;
|
||||
CurrentAlbumCoverLoader::CurrentAlbumCoverLoader(const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent)
|
||||
: QObject(parent),
|
||||
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) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
options_.options = AlbumCoverLoaderOptions::Option::RawImageData | AlbumCoverLoaderOptions::Option::OriginalImage | AlbumCoverLoaderOptions::Option::ScaledImage;
|
||||
options_.desired_scaled_size = QSize(120, 120);
|
||||
|
||||
@@ -52,10 +52,12 @@
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
using std::make_shared;
|
||||
|
||||
const char *DiscogsCoverProvider::kUrlSearch = "https://api.discogs.com/database/search";
|
||||
const char *DiscogsCoverProvider::kAccessKeyB64 = "dGh6ZnljUGJlZ1NEeXBuSFFxSVk=";
|
||||
const char *DiscogsCoverProvider::kSecretKeyB64 = "ZkFIcmlaSER4aHhRSlF2U3d0bm5ZVmdxeXFLWUl0UXI=";
|
||||
const int DiscogsCoverProvider::kRequestsDelay = 1000;
|
||||
namespace {
|
||||
constexpr char kUrlSearch[] = "https://api.discogs.com/database/search";
|
||||
constexpr char kAccessKeyB64[] = "dGh6ZnljUGJlZ1NEeXBuSFFxSVk=";
|
||||
constexpr char kSecretKeyB64[] = "ZkFIcmlaSER4aHhRSlF2U3d0bm5ZVmdxeXFLWUl0UXI=";
|
||||
constexpr int kRequestsDelay = 1000;
|
||||
} // namespace
|
||||
|
||||
DiscogsCoverProvider::DiscogsCoverProvider(const SharedPtr<NetworkAccessManager> network, QObject *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);
|
||||
|
||||
private:
|
||||
static const char *kUrlSearch;
|
||||
static const char *kAccessKeyB64;
|
||||
static const char *kSecretKeyB64;
|
||||
static const int kRequestsDelay;
|
||||
|
||||
QTimer *timer_flush_requests_;
|
||||
QQueue<SharedPtr<DiscogsCoverSearchContext>> queue_search_requests_;
|
||||
QQueue<DiscogsCoverReleaseContext> queue_release_requests_;
|
||||
|
||||
115
src/device/CMakeLists.txt
Normal file
115
src/device/CMakeLists.txt
Normal file
@@ -0,0 +1,115 @@
|
||||
set(DEVICE_SOURCES
|
||||
connecteddevice.cpp
|
||||
devicedatabasebackend.cpp
|
||||
devicelister.cpp
|
||||
devicemanager.cpp
|
||||
devicestatefiltermodel.cpp
|
||||
filesystemdevice.cpp
|
||||
deviceviewcontainer.cpp
|
||||
deviceview.cpp
|
||||
deviceproperties.cpp
|
||||
deviceinfo.cpp
|
||||
)
|
||||
|
||||
set(DEVICE_HEADERS
|
||||
connecteddevice.h
|
||||
devicedatabasebackend.h
|
||||
devicelister.h
|
||||
devicemanager.h
|
||||
devicestatefiltermodel.h
|
||||
filesystemdevice.h
|
||||
deviceviewcontainer.h
|
||||
deviceview.h
|
||||
deviceproperties.h
|
||||
)
|
||||
|
||||
set(DEVICE_UI
|
||||
deviceproperties.ui
|
||||
deviceviewcontainer.ui
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND DEVICE_SOURCES macosdevicelister.mm)
|
||||
list(APPEND DEVICE_HEADERS macosdevicelister.h)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
list(APPEND DEVICE_SOURCES giolister.cpp)
|
||||
list(APPEND DEVICE_HEADERS giolister.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_UDISKS2)
|
||||
list(APPEND DEVICE_SOURCES udisks2lister.cpp)
|
||||
list(APPEND DEVICE_HEADERS udisks2lister.h)
|
||||
set_source_files_properties(org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE objectmanager INCLUDE includes/dbus_metatypes.h)
|
||||
set_source_files_properties(org.freedesktop.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE udisks2filesystem INCLUDE includes/dbus_metatypes.h)
|
||||
set_source_files_properties(org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE udisks2block INCLUDE includes/dbus_metatypes.h)
|
||||
set_source_files_properties(org.freedesktop.UDisks2.Drive.xml PROPERTIES NO_NAMESPACE udisks2drive INCLUDE includes/dbus_metatypes.h)
|
||||
set_source_files_properties(org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE udisks2job INCLUDE includes/dbus_metatypes.h)
|
||||
qt_add_dbus_interface(DEVICE_SOURCES org.freedesktop.DBus.ObjectManager.xml objectmanager)
|
||||
qt_add_dbus_interface(DEVICE_SOURCES org.freedesktop.UDisks2.Filesystem.xml udisks2filesystem)
|
||||
qt_add_dbus_interface(DEVICE_SOURCES org.freedesktop.UDisks2.Block.xml udisks2block)
|
||||
qt_add_dbus_interface(DEVICE_SOURCES org.freedesktop.UDisks2.Drive.xml udisks2drive)
|
||||
qt_add_dbus_interface(DEVICE_SOURCES org.freedesktop.UDisks2.Job.xml udisks2job)
|
||||
endif()
|
||||
|
||||
if(HAVE_MTP)
|
||||
list(APPEND DEVICE_SOURCES mtpconnection.cpp mtpdevice.cpp mtploader.cpp)
|
||||
list(APPEND DEVICE_HEADERS mtpconnection.h mtpdevice.h mtploader.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_AUDIOCD)
|
||||
list(APPEND DEVICE_SOURCES cddadevice.cpp cddalister.cpp cddasongloader.cpp)
|
||||
list(APPEND DEVICE_HEADERS cddadevice.h cddalister.h cddasongloader.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_GPOD)
|
||||
list(APPEND DEVICE_SOURCES gpoddevice.cpp gpodloader.cpp)
|
||||
list(APPEND DEVICE_HEADERS gpoddevice.h gpodloader.h)
|
||||
endif()
|
||||
|
||||
qt_wrap_cpp(DEVICE_SOURCES ${DEVICE_HEADERS})
|
||||
qt_wrap_ui(DEVICE_SOURCES ${DEVICE_UI})
|
||||
|
||||
add_library(strawberry_device STATIC ${DEVICE_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_device PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_device PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
PkgConfig::GSTREAMER
|
||||
PkgConfig::GSTREAMER_BASE
|
||||
PkgConfig::GSTREAMER_AUDIO
|
||||
PkgConfig::GSTREAMER_APP
|
||||
PkgConfig::GSTREAMER_TAG
|
||||
PkgConfig::GSTREAMER_PBUTILS
|
||||
$<$<BOOL:${HAVE_GIO}>:PkgConfig::GIO>
|
||||
$<$<BOOL:${HAVE_GIO_UNIX}>:PkgConfig::GIO_UNIX>
|
||||
$<$<BOOL:${HAVE_AUDIOCD}>:PkgConfig::LIBCDIO>
|
||||
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
|
||||
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
strawberry_collection
|
||||
strawberry_widgets
|
||||
strawberry_musicbrainz
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(strawberry_device PRIVATE
|
||||
"-framework IOKit"
|
||||
"-framework DiskArbitration"
|
||||
)
|
||||
endif()
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cdio/cdio.h>
|
||||
#include <gst/audio/gstaudiocdsrc.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <cdio/cdio.h>
|
||||
#include <cdio/device.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFileInfo>
|
||||
#include <QByteArray>
|
||||
@@ -30,10 +35,6 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QUrl>
|
||||
|
||||
// This must come after Qt includes
|
||||
#include <cdio/cdio.h>
|
||||
#include <cdio/device.h>
|
||||
|
||||
#include "cddalister.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
@@ -24,10 +24,16 @@
|
||||
#include <memory>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <cdio/cdio.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
@@ -35,10 +41,6 @@
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include <cdio/cdio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include "cddasongloader.h"
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
@@ -24,17 +24,19 @@
|
||||
|
||||
#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 <QMutex>
|
||||
#include <QString>
|
||||
#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 "core/song.h"
|
||||
#ifdef HAVE_MUSICBRAINZ
|
||||
|
||||
@@ -49,7 +49,7 @@ DeviceDatabaseBackend::DeviceDatabaseBackend(QObject *parent)
|
||||
db_(nullptr),
|
||||
original_thread_(nullptr) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
original_thread_ = thread();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ DeviceLister::DeviceLister(QObject *parent)
|
||||
original_thread_(nullptr),
|
||||
next_mount_request_id_(0) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
original_thread_ = thread();
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ DeviceManager::DeviceManager(const SharedPtr<TaskManager> task_manager,
|
||||
albumcover_loader_(albumcover_loader),
|
||||
not_connected_overlay_(IconLoader::Load(u"edit-delete"_s)) {
|
||||
|
||||
setObjectName(QLatin1String(metaObject()->className()));
|
||||
setObjectName(QLatin1String(QObject::metaObject()->className()));
|
||||
|
||||
thread_pool_.setMaxThreadCount(1);
|
||||
QObject::connect(&*task_manager, &TaskManager::TasksChanged, this, &DeviceManager::TasksChanged);
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
#include "core/iconloader.h"
|
||||
#include "core/deletefiles.h"
|
||||
#include "core/mergedproxymodel.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "mimedata/mimedata.h"
|
||||
#include "core/musicstorage.h"
|
||||
#include "utilities/colorutils.h"
|
||||
#include "organize/organizedialog.h"
|
||||
|
||||
@@ -484,16 +484,22 @@ void GioLister::DeviceInfo::ReadMountInfo(GMount *mount) {
|
||||
}
|
||||
|
||||
#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);
|
||||
#endif
|
||||
if (unix_mount) {
|
||||
// the GIO's definition of system internal mounts include filesystems like
|
||||
// autofs, tmpfs, sysfs, etc, and various system directories, including the root,
|
||||
// /boot, /var, /home, etc.
|
||||
// The GIO's definition of system internal mounts include filesystems like autofs, tmpfs, sysfs, etc,
|
||||
// and various system directories, including the root, /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);
|
||||
g_unix_mount_free(unix_mount);
|
||||
// Although checking most of the internal mounts is safe,
|
||||
// we really don't want to touch autofs filesystems, as that would
|
||||
// trigger automounting.
|
||||
#endif
|
||||
// Although checking most of the internal mounts is safe, we really don't want to touch autofs filesystems, as that would trigger automounting.
|
||||
if (is_system_internal) return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/standardpaths.h"
|
||||
#include "core/temporaryfile.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/database.h"
|
||||
@@ -211,9 +211,9 @@ bool GPodDevice::CopyToStorage(const CopyJob &job, QString &error_text) {
|
||||
bool result = false;
|
||||
if (!job.cover_image_.isNull()) {
|
||||
#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
|
||||
QString temp_path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
||||
QString temp_path = StandardPaths::WritableLocation(StandardPaths::StandardLocation::TempLocation);
|
||||
#endif
|
||||
if (!QDir(temp_path).exists()) QDir().mkpath(temp_path);
|
||||
SharedPtr<TemporaryFile> cover_file = make_shared<TemporaryFile>(temp_path + u"/track-albumcover-XXXXXX.jpg"_s);
|
||||
|
||||
59
src/dialogs/CMakeLists.txt
Normal file
59
src/dialogs/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
set(DIALOGS_SOURCES
|
||||
about.cpp
|
||||
console.cpp
|
||||
errordialog.cpp
|
||||
addstreamdialog.cpp
|
||||
userpassdialog.cpp
|
||||
deleteconfirmationdialog.cpp
|
||||
lastfmimportdialog.cpp
|
||||
messagedialog.cpp
|
||||
snapdialog.cpp
|
||||
saveplaylistsdialog.cpp
|
||||
)
|
||||
|
||||
set(DIALOGS_HEADERS
|
||||
about.h
|
||||
errordialog.h
|
||||
console.h
|
||||
addstreamdialog.h
|
||||
userpassdialog.h
|
||||
deleteconfirmationdialog.h
|
||||
lastfmimportdialog.h
|
||||
messagedialog.h
|
||||
snapdialog.h
|
||||
saveplaylistsdialog.h
|
||||
)
|
||||
|
||||
set(DIALOGS_UI
|
||||
about.ui
|
||||
errordialog.ui
|
||||
console.ui
|
||||
addstreamdialog.ui
|
||||
userpassdialog.ui
|
||||
lastfmimportdialog.ui
|
||||
messagedialog.ui
|
||||
saveplaylistsdialog.ui
|
||||
)
|
||||
|
||||
qt_wrap_cpp(DIALOGS_SOURCES ${DIALOGS_HEADERS})
|
||||
qt_wrap_ui(DIALOGS_SOURCES ${DIALOGS_UI})
|
||||
|
||||
add_library(strawberry_dialogs STATIC ${DIALOGS_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_dialogs PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_dialogs PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
)
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "ui_about.h"
|
||||
#include "dialogs/ui_about.h"
|
||||
|
||||
class QWidget;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QUrl>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "ui_addstreamdialog.h"
|
||||
#include "dialogs/ui_addstreamdialog.h"
|
||||
|
||||
class AddStreamDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
#include "ui_console.h"
|
||||
#include "dialogs/ui_console.h"
|
||||
|
||||
#include "includes/shared_ptr.h"
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
ErrorDialog::ErrorDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
parent_(parent),
|
||||
ui_(new Ui_ErrorDialog) {
|
||||
|
||||
ui_->setupUi(this);
|
||||
@@ -66,8 +67,11 @@ void ErrorDialog::ShowMessage(const QString &message) {
|
||||
UpdateContent();
|
||||
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
|
||||
if (parent_ && parent_->isMaximized()) {
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class ErrorDialog : public QDialog {
|
||||
private:
|
||||
void UpdateContent();
|
||||
|
||||
QWidget *parent_;
|
||||
Ui_ErrorDialog *ui_;
|
||||
|
||||
QStringList current_messages_;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "includes/shared_ptr.h"
|
||||
|
||||
#include "ui_lastfmimportdialog.h"
|
||||
#include "dialogs/ui_lastfmimportdialog.h"
|
||||
|
||||
class QCloseEvent;
|
||||
class LastFMImport;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "ui_saveplaylistsdialog.h"
|
||||
#include "dialogs/ui_saveplaylistsdialog.h"
|
||||
|
||||
class SavePlaylistsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
44
src/edittagdialog/CMakeLists.txt
Normal file
44
src/edittagdialog/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
set(EDITTAGDIALOG_SOURCES
|
||||
edittagdialog.cpp
|
||||
trackselectiondialog.cpp
|
||||
)
|
||||
|
||||
set(EDITTAGDIALOG_HEADERS
|
||||
edittagdialog.h
|
||||
trackselectiondialog.h
|
||||
)
|
||||
|
||||
set(EDITTAGDIALOG_UI
|
||||
edittagdialog.ui
|
||||
trackselectiondialog.ui
|
||||
)
|
||||
|
||||
qt_wrap_cpp(EDITTAGDIALOG_SOURCES ${EDITTAGDIALOG_HEADERS})
|
||||
qt_wrap_ui(EDITTAGDIALOG_SOURCES ${EDITTAGDIALOG_UI})
|
||||
|
||||
add_library(strawberry_edittagdialog STATIC ${EDITTAGDIALOG_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_edittagdialog PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_edittagdialog PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_core
|
||||
strawberry_utilities
|
||||
strawberry_tagreader
|
||||
strawberry_covermanager
|
||||
strawberry_widgets
|
||||
strawberry_collection
|
||||
strawberry_lyrics
|
||||
)
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QtConcurrentRun>
|
||||
@@ -99,6 +100,7 @@
|
||||
#include "edittagdialog.h"
|
||||
#include "ui_edittagdialog.h"
|
||||
|
||||
using std::make_shared;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
namespace {
|
||||
@@ -1311,7 +1313,11 @@ void EditTagDialog::SaveData() {
|
||||
save_tags_options |= TagReaderClient::SaveOption::Cover;
|
||||
}
|
||||
TagReaderReplyPtr reply = tagreader_client_->WriteFileAsync(ref.current_.url().toLocalFile(), ref.current_, save_tags_options, save_tag_cover_data);
|
||||
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, ref]() { SongSaveTagsComplete(reply, ref.current_.url().toLocalFile(), ref.current_, ref.cover_action_); }, Qt::QueuedConnection);
|
||||
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, ref, connection]() {
|
||||
SongSaveTagsComplete(reply, ref.current_.url().toLocalFile(), ref.current_, ref.cover_action_);
|
||||
QObject::disconnect(*connection);
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
// If the cover was changed, but no tags written, make sure to update the collection.
|
||||
else if (ref.cover_action_ != UpdateCoverAction::None && !ref.current_.effective_albumartist().isEmpty() && !ref.current_.album().isEmpty()) {
|
||||
89
src/engine/CMakeLists.txt
Normal file
89
src/engine/CMakeLists.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
set(ENGINE_SOURCES
|
||||
enginebase.cpp
|
||||
enginedevice.cpp
|
||||
devicefinders.cpp
|
||||
devicefinder.cpp
|
||||
gststartup.cpp
|
||||
gstengine.cpp
|
||||
gstenginepipeline.cpp
|
||||
)
|
||||
|
||||
set(ENGINE_HEADERS
|
||||
enginebase.h
|
||||
devicefinders.h
|
||||
gststartup.h
|
||||
gstengine.h
|
||||
gstenginepipeline.h
|
||||
)
|
||||
|
||||
if(HAVE_ALSA)
|
||||
list(APPEND ENGINE_SOURCES alsadevicefinder.cpp alsapcmdevicefinder.cpp)
|
||||
endif()
|
||||
|
||||
if(HAVE_PULSE)
|
||||
list(APPEND ENGINE_SOURCES pulsedevicefinder.cpp)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
list(APPEND ENGINE_SOURCES uwpdevicefinder.cpp asiodevicefinder.cpp)
|
||||
endif()
|
||||
|
||||
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||
list(APPEND ENGINE_SOURCES chromaprinter.cpp)
|
||||
endif()
|
||||
|
||||
if(HAVE_EBUR128)
|
||||
list(APPEND ENGINE_SOURCES ebur128analysis.cpp)
|
||||
endif()
|
||||
|
||||
if(HAVE_MOODBAR)
|
||||
list(APPEND ENGINE_SOURCES gstfastspectrumplugin.cpp gstfastspectrum.cpp)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND ENGINE_SOURCES macosdevicefinder.cpp)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND ENGINE_SOURCES directsounddevicefinder.cpp mmdevicefinder.cpp)
|
||||
endif()
|
||||
|
||||
qt_wrap_cpp(ENGINE_SOURCES ${ENGINE_HEADERS})
|
||||
|
||||
add_library(strawberry_engine STATIC ${ENGINE_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_engine PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_engine PRIVATE
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GOBJECT
|
||||
$<$<BOOL:${HAVE_ALSA}>:ALSA::ALSA>
|
||||
$<$<BOOL:${HAVE_PULSE}>:PkgConfig::LIBPULSE>
|
||||
$<$<BOOL:${WIN32}>:dsound>
|
||||
$<$<BOOL:${MSVC}>:WindowsApp>
|
||||
PkgConfig::GSTREAMER
|
||||
PkgConfig::GSTREAMER_BASE
|
||||
PkgConfig::GSTREAMER_AUDIO
|
||||
PkgConfig::GSTREAMER_APP
|
||||
PkgConfig::GSTREAMER_TAG
|
||||
PkgConfig::GSTREAMER_PBUTILS
|
||||
$<$<BOOL:${HAVE_SONGFINGERPRINTING} OR ${HAVE_MUSICBRAINZ}>:PkgConfig::CHROMAPRINT>
|
||||
$<$<BOOL:${HAVE_EBUR128}>:PkgConfig::LIBEBUR128>
|
||||
$<$<BOOL:${HAVE_MOODBAR}>:PkgConfig::FFTW3>
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
strawberry_core
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(strawberry_engine PRIVATE
|
||||
"-framework CoreAudio"
|
||||
)
|
||||
endif()
|
||||
@@ -53,7 +53,7 @@ using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
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),
|
||||
volume_control_(true),
|
||||
volume_(100),
|
||||
beginning_nanosec_(0),
|
||||
end_nanosec_(0),
|
||||
beginning_offset_nanosec_(0),
|
||||
end_offset_nanosec_(0),
|
||||
ebur128_loudness_normalizing_gain_db_(0.0),
|
||||
scope_(kScopeSize),
|
||||
buffering_(false),
|
||||
@@ -84,15 +84,15 @@ EngineBase::EngineBase(QObject *parent)
|
||||
|
||||
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(force_stop_at_end);
|
||||
|
||||
media_url_ = media_url;
|
||||
stream_url_ = stream_url;
|
||||
beginning_nanosec_ = beginning_nanosec;
|
||||
end_nanosec_ = end_nanosec;
|
||||
beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||
end_offset_nanosec_ = end_offset_nanosec;
|
||||
|
||||
ebur128_loudness_normalizing_gain_db_ = 0.0;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class EngineBase : public QObject {
|
||||
virtual bool Init() = 0;
|
||||
virtual State state() const = 0;
|
||||
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 void Stop(const bool stop_after = false) = 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.
|
||||
// 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) {
|
||||
beginning_nanosec_ = beginning_nanosec;
|
||||
end_nanosec_ = end_nanosec;
|
||||
virtual void RefreshMarkers(const quint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) {
|
||||
beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||
end_offset_nanosec_ = end_offset_nanosec;
|
||||
}
|
||||
|
||||
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).
|
||||
// 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);
|
||||
|
||||
public Q_SLOTS:
|
||||
@@ -179,8 +179,8 @@ class EngineBase : public QObject {
|
||||
bool exclusive_mode_;
|
||||
bool volume_control_;
|
||||
uint volume_;
|
||||
quint64 beginning_nanosec_;
|
||||
qint64 end_nanosec_;
|
||||
quint64 beginning_offset_nanosec_;
|
||||
qint64 end_offset_nanosec_;
|
||||
QUrl media_url_;
|
||||
QUrl stream_url_;
|
||||
double ebur128_loudness_normalizing_gain_db_;
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
#include "gstbufferconsumer.h"
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
using std::make_shared;
|
||||
|
||||
#ifdef __clang__
|
||||
# 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);
|
||||
|
||||
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
|
||||
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
|
||||
if (discoverer_ && media_url.scheme() != u"spotify"_s) {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
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) {
|
||||
|
||||
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()) {
|
||||
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();
|
||||
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);
|
||||
|
||||
return true;
|
||||
@@ -306,7 +315,7 @@ void GstEngine::Stop(const bool stop_after) {
|
||||
|
||||
media_url_.clear();
|
||||
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.
|
||||
if (fadeout_pause_pipeline_) {
|
||||
@@ -384,7 +393,7 @@ void GstEngine::Seek(const quint64 offset_nanosec) {
|
||||
|
||||
if (!current_pipeline_) return;
|
||||
|
||||
seek_pos_ = beginning_nanosec_ + offset_nanosec;
|
||||
seek_pos_ = beginning_offset_nanosec_ + offset_nanosec;
|
||||
waiting_to_seek_ = true;
|
||||
|
||||
if (!seek_timer_->isActive()) {
|
||||
@@ -402,7 +411,7 @@ qint64 GstEngine::position_nanosec() const {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -411,7 +420,7 @@ qint64 GstEngine::length_nanosec() const {
|
||||
|
||||
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) {
|
||||
return result;
|
||||
@@ -742,7 +751,7 @@ void GstEngine::PlayDone(const GstStateChangeReturn ret, const bool pause, const
|
||||
stream_url = old_pipeline->stream_url();
|
||||
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);
|
||||
Play(pause, offset_nanosec);
|
||||
return;
|
||||
@@ -761,7 +770,7 @@ void GstEngine::PlayDone(const GstStateChangeReturn ret, const bool pause, const
|
||||
|
||||
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_);
|
||||
|
||||
}
|
||||
@@ -887,7 +896,7 @@ void GstEngine::StopTimers() {
|
||||
|
||||
GstEnginePipelinePtr GstEngine::CreatePipeline() {
|
||||
|
||||
GstEnginePipelinePtr pipeline = make_shared<GstEnginePipeline>();
|
||||
GstEnginePipelinePtr pipeline = GstEnginePipelinePtr(new GstEnginePipeline);
|
||||
pipeline->set_output_device(output_, device_);
|
||||
pipeline->set_exclusive_mode(exclusive_mode_);
|
||||
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();
|
||||
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();
|
||||
Q_EMIT Error(error);
|
||||
Q_EMIT StateChanged(State::Error);
|
||||
|
||||
@@ -60,8 +60,8 @@ class GstEngine : public EngineBase, public GstBufferConsumer {
|
||||
|
||||
bool Init() 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;
|
||||
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;
|
||||
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_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;
|
||||
void Stop(const bool stop_after = false) override;
|
||||
void Pause() override;
|
||||
@@ -133,7 +133,7 @@ class GstEngine : public EngineBase, public GstBufferConsumer {
|
||||
void StopTimers();
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
||||
ebur128_loudness_normalizing_gain_db_(0.0),
|
||||
segment_start_(0),
|
||||
segment_start_received_(false),
|
||||
beginning_offset_nanosec_(-1),
|
||||
end_offset_nanosec_(-1),
|
||||
next_beginning_offset_nanosec_(-1),
|
||||
next_end_offset_nanosec_(-1),
|
||||
@@ -173,7 +174,11 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
||||
logged_unsupported_analyzer_format_(false),
|
||||
about_to_finish_(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);
|
||||
for (int i = 0; i < kEqBandCount; ++i) eq_band_gains_ << 0;
|
||||
@@ -418,18 +423,23 @@ bool GstEnginePipeline::Finish() {
|
||||
|
||||
Disconnect();
|
||||
|
||||
if (IsStateNull()) {
|
||||
if (IsStateNull() && set_state_async_in_progress_ == 0 && set_state_in_progress_ == 0) {
|
||||
finished_ = true;
|
||||
}
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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_);
|
||||
@@ -438,7 +448,8 @@ bool GstEnginePipeline::InitFromUrl(const QUrl &media_url, const QUrl &stream_ur
|
||||
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;
|
||||
|
||||
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->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.
|
||||
instance->beginning_offset_nanosec_ = instance->next_beginning_offset_nanosec_;
|
||||
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
|
||||
instance->next_media_url_.clear();
|
||||
instance->next_stream_url_.clear();
|
||||
@@ -1477,6 +1489,7 @@ void GstEnginePipeline::StreamStartMessageReceived() {
|
||||
next_media_url_.clear();
|
||||
next_gst_url_.clear();
|
||||
}
|
||||
beginning_offset_nanosec_ = next_beginning_offset_nanosec_;
|
||||
end_offset_nanosec_ = next_end_offset_nanosec_;
|
||||
next_beginning_offset_nanosec_ = 0;
|
||||
next_end_offset_nanosec_ = 0;
|
||||
@@ -1788,7 +1801,19 @@ bool GstEnginePipeline::IsStateNull() const {
|
||||
|
||||
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);
|
||||
|
||||
last_set_state_in_progress_ = state;
|
||||
++set_state_in_progress_;
|
||||
|
||||
QFutureWatcher<GstStateChangeReturn> *watcher = new QFutureWatcher<GstStateChangeReturn>();
|
||||
QObject::connect(watcher, &QFutureWatcher<GstStateChangeReturn>::finished, this, [this, watcher, state]() {
|
||||
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) {
|
||||
|
||||
last_set_state_in_progress_ = GST_STATE_VOID_PENDING;
|
||||
--set_state_in_progress_;
|
||||
|
||||
switch (state_change_return) {
|
||||
case GST_STATE_CHANGE_SUCCESS:
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
case GST_STATE_CHANGE_NO_PREROLL:
|
||||
qLog(Debug) << "Pipeline" << id() << "state successfully set to" << GstStateText(state);
|
||||
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;
|
||||
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_);
|
||||
@@ -2146,8 +2177,8 @@ void GstEnginePipeline::PrepareNextUrl(const QUrl &media_url, const QUrl &stream
|
||||
next_gst_url_ = gst_url;
|
||||
}
|
||||
|
||||
next_beginning_offset_nanosec_ = beginning_nanosec;
|
||||
next_end_offset_nanosec_ = end_nanosec;
|
||||
next_beginning_offset_nanosec_ = beginning_offset_nanosec;
|
||||
next_end_offset_nanosec_ = end_offset_nanosec;
|
||||
|
||||
if (about_to_finish_.value()) {
|
||||
SetNextUrl();
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "includes/mutex_protected.h"
|
||||
@@ -83,7 +84,7 @@ class GstEnginePipeline : public QObject {
|
||||
bool Finish();
|
||||
|
||||
// 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.
|
||||
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
|
||||
bool HasNextUrl() 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 SetSourceDevice(const QString &device);
|
||||
@@ -197,6 +198,7 @@ class GstEnginePipeline : public QObject {
|
||||
void ProcessPendingSeek(const GstState state);
|
||||
|
||||
private Q_SLOTS:
|
||||
void SetStateAsyncSlot(const GstState state);
|
||||
void SetStateFinishedSlot(const GstState state, const GstStateChangeReturn state_change_return);
|
||||
void SetFaderVolume(const qreal volume);
|
||||
void FaderTimelineStateChanged(const QTimeLine::State state);
|
||||
@@ -287,6 +289,7 @@ class GstEnginePipeline : public QObject {
|
||||
mutex_protected<bool> segment_start_received_;
|
||||
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.
|
||||
mutex_protected<qint64> end_offset_nanosec_;
|
||||
|
||||
@@ -367,8 +370,14 @@ class GstEnginePipeline : public QObject {
|
||||
mutex_protected<bool> about_to_finish_;
|
||||
mutex_protected<bool> finish_requested_;
|
||||
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
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/standardpaths.h"
|
||||
#include "utilities/envutils.h"
|
||||
|
||||
#ifdef HAVE_MOODBAR
|
||||
@@ -145,7 +145,7 @@ void SetEnvironment() {
|
||||
#endif // USE_BUNDLE
|
||||
|
||||
#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;
|
||||
Utilities::SetEnv("GST_REGISTRY", gst_registry_filename);
|
||||
#endif
|
||||
|
||||
36
src/equalizer/CMakeLists.txt
Normal file
36
src/equalizer/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
set(EQUALIZER_SOURCES
|
||||
equalizer.cpp
|
||||
equalizerslider.cpp
|
||||
)
|
||||
|
||||
set(EQUALIZER_HEADERS
|
||||
equalizer.h
|
||||
equalizerslider.h
|
||||
)
|
||||
|
||||
set(EQUALIZER_UI
|
||||
equalizer.ui
|
||||
equalizerslider.ui
|
||||
)
|
||||
|
||||
qt_wrap_cpp(EQUALIZER_SOURCES ${EQUALIZER_HEADERS})
|
||||
qt_wrap_ui(EQUALIZER_SOURCES ${EQUALIZER_UI})
|
||||
|
||||
add_library(strawberry_equalizer STATIC ${EQUALIZER_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_equalizer PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_equalizer PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_core
|
||||
strawberry_widgets
|
||||
)
|
||||
35
src/fileview/CMakeLists.txt
Normal file
35
src/fileview/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
set(FILEVIEW_SOURCES
|
||||
fileview.cpp
|
||||
fileviewlist.cpp
|
||||
)
|
||||
|
||||
set(FILEVIEW_HEADERS
|
||||
fileview.h
|
||||
fileviewlist.h
|
||||
)
|
||||
|
||||
set(FILEVIEW_UI
|
||||
fileview.ui
|
||||
)
|
||||
|
||||
qt_wrap_cpp(FILEVIEW_SOURCES ${FILEVIEW_HEADERS})
|
||||
qt_wrap_ui(FILEVIEW_SOURCES ${FILEVIEW_UI})
|
||||
|
||||
add_library(strawberry_fileview STATIC ${FILEVIEW_SOURCES})
|
||||
|
||||
target_include_directories(strawberry_fileview PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_fileview PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
strawberry_utilities
|
||||
strawberry_core
|
||||
strawberry_mimedata
|
||||
strawberry_dialogs
|
||||
strawberry_organize
|
||||
)
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "core/filesystemmusicstorage.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "mimedata/mimedata.h"
|
||||
#include "dialogs/deleteconfirmationdialog.h"
|
||||
#include "fileview.h"
|
||||
#include "fileviewlist.h"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QtEvents>
|
||||
|
||||
#include "core/iconloader.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "mimedata/mimedata.h"
|
||||
#include "utilities/filemanagerutils.h"
|
||||
#include "fileviewlist.h"
|
||||
|
||||
|
||||
52
src/filterparser/CMakeLists.txt
Normal file
52
src/filterparser/CMakeLists.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
set(SOURCES
|
||||
filterparser.cpp
|
||||
filtertree.cpp
|
||||
filtertreeand.cpp
|
||||
filtertreecolumnterm.cpp
|
||||
filtertreenop.cpp
|
||||
filtertreenot.cpp
|
||||
filtertreeor.cpp
|
||||
filtertreeterm.cpp
|
||||
filterparserfloateqcomparator.cpp
|
||||
filterparserfloatgecomparator.cpp
|
||||
filterparserfloatgtcomparator.cpp
|
||||
filterparserfloatlecomparator.cpp
|
||||
filterparserfloatltcomparator.cpp
|
||||
filterparserfloatnecomparator.cpp
|
||||
filterparserint64eqcomparator.cpp
|
||||
filterparserint64gecomparator.cpp
|
||||
filterparserint64gtcomparator.cpp
|
||||
filterparserint64lecomparator.cpp
|
||||
filterparserint64ltcomparator.cpp
|
||||
filterparserint64necomparator.cpp
|
||||
filterparserinteqcomparator.cpp
|
||||
filterparserintgecomparator.cpp
|
||||
filterparserintgtcomparator.cpp
|
||||
filterparserintlecomparator.cpp
|
||||
filterparserintltcomparator.cpp
|
||||
filterparserintnecomparator.cpp
|
||||
filterparsersearchtermcomparator.cpp
|
||||
filterparsertextcontainscomparator.cpp
|
||||
filterparsertexteqcomparator.cpp
|
||||
filterparsertextnecomparator.cpp
|
||||
filterparseruinteqcomparator.cpp
|
||||
filterparseruintgecomparator.cpp
|
||||
filterparseruintgtcomparator.cpp
|
||||
filterparseruintlecomparator.cpp
|
||||
filterparseruintltcomparator.cpp
|
||||
filterparseruintnecomparator.cpp
|
||||
)
|
||||
|
||||
add_library(strawberry_filterparser STATIC ${SOURCES})
|
||||
|
||||
target_include_directories(strawberry_filterparser PRIVATE
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(strawberry_filterparser PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
@@ -188,8 +188,15 @@ FilterTree *FilterParser::parseSearchTerm() {
|
||||
QString value;
|
||||
|
||||
bool in_quotes = false;
|
||||
bool previous_char_operator = false;
|
||||
|
||||
for (; iter_ != end_; ++iter_) {
|
||||
if (previous_char_operator) {
|
||||
if (iter_->isSpace()) {
|
||||
continue;
|
||||
}
|
||||
previous_char_operator = false;
|
||||
}
|
||||
if (in_quotes) {
|
||||
if (*iter_ == u'"') {
|
||||
in_quotes = false;
|
||||
@@ -206,6 +213,7 @@ FilterTree *FilterParser::parseSearchTerm() {
|
||||
column = buf_.toLower();
|
||||
buf_.clear();
|
||||
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'-') {
|
||||
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
|
||||
if (prefix.isEmpty() && (*iter_ == u'>' || *iter_ == u'<' || *iter_ == u'=' || *iter_ == u'!')) {
|
||||
prefix += *iter_;
|
||||
previous_char_operator = true;
|
||||
}
|
||||
else if (prefix != u'=' && *iter_ == u'=') {
|
||||
prefix += *iter_;
|
||||
previous_char_operator = true;
|
||||
}
|
||||
else {
|
||||
buf_ += *iter_;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user