Compare commits
172 Commits
1.2.12
...
visualisat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c435464972 | ||
|
|
da2f28811a | ||
|
|
0bfa736081 | ||
|
|
1392bcbbe1 | ||
|
|
11705889f1 | ||
|
|
604dd2dbde | ||
|
|
25065ba98f | ||
|
|
7b16ec62bb | ||
|
|
d8f31592b9 | ||
|
|
80bb0f476d | ||
|
|
b7222ac85c | ||
|
|
241bca0828 | ||
|
|
90d86b10a3 | ||
|
|
4130c6670f | ||
|
|
8d262959c1 | ||
|
|
b9b70399d8 | ||
|
|
527ccd212a | ||
|
|
4a5afbeb1e | ||
|
|
63c14e014b | ||
|
|
801658c6b9 | ||
|
|
16fe665295 | ||
|
|
2bb0dbada2 | ||
|
|
2cd9498469 | ||
|
|
d1ee27fff9 | ||
|
|
91adf5ba32 | ||
|
|
d68f464269 | ||
|
|
c684a95f89 | ||
|
|
1d03bb2178 | ||
|
|
39f9128ecf | ||
|
|
ca2e802239 | ||
|
|
9a513a9a56 | ||
|
|
1c2e87b741 | ||
|
|
fe4d9979ce | ||
|
|
d8ae790ebf | ||
|
|
ac31d79294 | ||
|
|
4ffebd77b1 | ||
|
|
6682efae2f | ||
|
|
480161c6b7 | ||
|
|
a8ba420d72 | ||
|
|
fc0ec91652 | ||
|
|
0701b97324 | ||
|
|
3867932e1e | ||
|
|
e2907f6051 | ||
|
|
0827ec7f16 | ||
|
|
24d2adf363 | ||
|
|
592729d00b | ||
|
|
c4a564bb56 | ||
|
|
812a02a3a1 | ||
|
|
944936914b | ||
|
|
e7c901d4f3 | ||
|
|
8e996119af | ||
|
|
4348a654ca | ||
|
|
f0be1c782a | ||
|
|
e9898d08bc | ||
|
|
1ad13cd3b0 | ||
|
|
5c640e0e36 | ||
|
|
059def8d0c | ||
|
|
cf15a1f423 | ||
|
|
5d35b0eedd | ||
|
|
5fcb71d08f | ||
|
|
15c2237d4a | ||
|
|
93af866185 | ||
|
|
109ff90401 | ||
|
|
d4b06289c3 | ||
|
|
4351c555a0 | ||
|
|
ce4db40983 | ||
|
|
d37fb7410c | ||
|
|
f1cdd71494 | ||
|
|
000ba997fb | ||
|
|
579efffd14 | ||
|
|
3a098c8a0c | ||
|
|
5bce0ae87f | ||
|
|
afe6967c46 | ||
|
|
e91bab6d42 | ||
|
|
5a64247761 | ||
|
|
9ed89afdb2 | ||
|
|
0ac338026c | ||
|
|
4e5f84a7b7 | ||
|
|
320a3c6815 | ||
|
|
72dd1d783a | ||
|
|
d2205cfe81 | ||
|
|
5830f247f6 | ||
|
|
8b4c57d933 | ||
|
|
67cec09176 | ||
|
|
2df658e1f3 | ||
|
|
f3bc9b151c | ||
|
|
b06b59d0c5 | ||
|
|
99d75ade06 | ||
|
|
3f63246068 | ||
|
|
b205a5f964 | ||
|
|
aeaef12dd4 | ||
|
|
02d76f17f7 | ||
|
|
e4e12c6fa6 | ||
|
|
270ae6085b | ||
|
|
7065a405a5 | ||
|
|
d8c72c3dd9 | ||
|
|
b65502e167 | ||
|
|
132f8df853 | ||
|
|
12e3cffe63 | ||
|
|
56a637682d | ||
|
|
d9b105f89e | ||
|
|
bd6b45e43f | ||
|
|
539172fb70 | ||
|
|
ebd92b3a7f | ||
|
|
b00ae5b210 | ||
|
|
c8e3cf981b | ||
|
|
038f69779f | ||
|
|
a4de7559ac | ||
|
|
0537b072fe | ||
|
|
2657c9f96a | ||
|
|
7e178b1f1a | ||
|
|
dd8513d02c | ||
|
|
5f0175094b | ||
|
|
b4c5b9e1e1 | ||
|
|
2ce0ed2ef8 | ||
|
|
176768f7f8 | ||
|
|
50b954034c | ||
|
|
cab7b6c335 | ||
|
|
fce1dacafc | ||
|
|
94aa6fb940 | ||
|
|
0cfd4aaad1 | ||
|
|
9e72b4fe80 | ||
|
|
1151443372 | ||
|
|
8f6993e7c8 | ||
|
|
a6ab1a7689 | ||
|
|
098b21d818 | ||
|
|
d61adeb595 | ||
|
|
8bfc3bc41c | ||
|
|
0dda2feec3 | ||
|
|
1d0d03ed83 | ||
|
|
330284f03e | ||
|
|
fc3ed3a2ce | ||
|
|
6a656036fe | ||
|
|
5c76c633a5 | ||
|
|
d487c3ea07 | ||
|
|
83dca405af | ||
|
|
acd0b6d3ea | ||
|
|
159242aff4 | ||
|
|
4b014253cf | ||
|
|
1ec6b5582e | ||
|
|
08b8d04500 | ||
|
|
54679b1d57 | ||
|
|
8d648e668e | ||
|
|
5897e786dc | ||
|
|
7f549aa991 | ||
|
|
792e7b6274 | ||
|
|
92c58b0b60 | ||
|
|
5fac9a1c8d | ||
|
|
7f4f715003 | ||
|
|
75d1d4098e | ||
|
|
30e80068a3 | ||
|
|
5fe9a1528f | ||
|
|
7777eda115 | ||
|
|
ce4f2ece93 | ||
|
|
52399d73fe | ||
|
|
6e98166148 | ||
|
|
c658a77b05 | ||
|
|
1880dc8153 | ||
|
|
b5fd3d5717 | ||
|
|
3c3480fb84 | ||
|
|
f628914173 | ||
|
|
c100fb1bb8 | ||
|
|
8c804c4fba | ||
|
|
912a7c7da9 | ||
|
|
0a5815c82e | ||
|
|
6513b3032b | ||
|
|
8c51401bdc | ||
|
|
45fc9c83d4 | ||
|
|
be57d8147a | ||
|
|
a97908fb6b | ||
|
|
c0417d4bb3 | ||
|
|
062e2cfb84 |
@@ -130,7 +130,10 @@ InsertBraces: false
|
|||||||
InsertTrailingCommas: None
|
InsertTrailingCommas: None
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
KeepEmptyLines:
|
||||||
|
AtEndOfFile: true
|
||||||
|
AtStartOfBlock: true
|
||||||
|
AtStartOfFile: true
|
||||||
LambdaBodyIndentation: Signature
|
LambdaBodyIndentation: Signature
|
||||||
MacroBlockBegin: ''
|
MacroBlockBegin: ''
|
||||||
MacroBlockEnd: ''
|
MacroBlockEnd: ''
|
||||||
|
|||||||
142
.github/workflows/build.yml
vendored
142
.github/workflows/build.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
opensuse_version: [ 'tumbleweed', 'leap:15.6' ]
|
opensuse_version: [ 'tumbleweed', 'leap:15.6', 'leap:16.0' ]
|
||||||
container:
|
container:
|
||||||
image: opensuse/${{matrix.opensuse_version}}
|
image: opensuse/${{matrix.opensuse_version}}
|
||||||
steps:
|
steps:
|
||||||
@@ -27,11 +27,11 @@ jobs:
|
|||||||
- name: Upgrade packages (Leap)
|
- name: Upgrade packages (Leap)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version != 'tumbleweed'
|
||||||
run: zypper -n --gpg-auto-import-keys up
|
run: zypper -n --gpg-auto-import-keys up
|
||||||
- name: Install gcc (Tumbleweed)
|
- name: Install gcc
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version != 'leap:15.6'
|
||||||
run: zypper -n --gpg-auto-import-keys in gcc gcc-c++
|
run: zypper -n --gpg-auto-import-keys in gcc gcc-c++
|
||||||
- name: Install gcc (Leap)
|
- name: Install gcc (leap:15.6)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version == 'leap:15.6'
|
||||||
run: zypper -n --gpg-auto-import-keys in gcc14 gcc14-c++
|
run: zypper -n --gpg-auto-import-keys in gcc14 gcc14-c++
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: >
|
run: >
|
||||||
@@ -62,6 +62,7 @@ jobs:
|
|||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
fftw3-devel
|
fftw3-devel
|
||||||
libebur128-devel
|
libebur128-devel
|
||||||
|
projectM-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
appstream-glib
|
appstream-glib
|
||||||
@@ -78,12 +79,13 @@ jobs:
|
|||||||
qt6-base-common-devel
|
qt6-base-common-devel
|
||||||
qt6-sql-sqlite
|
qt6-sql-sqlite
|
||||||
qt6-linguist-devel
|
qt6-linguist-devel
|
||||||
|
qt6-openglwidgets-devel
|
||||||
gtest
|
gtest
|
||||||
gmock
|
gmock
|
||||||
sparsehash-devel
|
sparsehash-devel
|
||||||
rapidjson-devel
|
rapidjson-devel
|
||||||
- name: Install kdsingleapplication-qt6-devel
|
- name: Install kdsingleapplication-qt6-devel
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version != 'leap:15.6'
|
||||||
run: zypper -n --gpg-auto-import-keys in kdsingleapplication-qt6-devel
|
run: zypper -n --gpg-auto-import-keys in kdsingleapplication-qt6-devel
|
||||||
- name: Build and install KDSingleApplication
|
- name: Build and install KDSingleApplication
|
||||||
if: matrix.opensuse_version == 'leap:15.6'
|
if: matrix.opensuse_version == 'leap:15.6'
|
||||||
@@ -97,7 +99,7 @@ jobs:
|
|||||||
cmake --build build --config Release --parallel 4
|
cmake --build build --config Release --parallel 4
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -115,14 +117,14 @@ jobs:
|
|||||||
- name: Copy source tarball
|
- name: Copy source tarball
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||||
- name: Build RPM (Tumbleweed)
|
- name: Build RPM
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version != 'leap:15.6'
|
||||||
env:
|
env:
|
||||||
RPM_BUILD_NCPUS: 4
|
RPM_BUILD_NCPUS: 4
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Build RPM (Leap)
|
- name: Build RPM (leap:15.6)
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version == 'leap:15.6'
|
||||||
env:
|
env:
|
||||||
RPM_BUILD_NCPUS: 4
|
RPM_BUILD_NCPUS: 4
|
||||||
CC: gcc-14
|
CC: gcc-14
|
||||||
@@ -134,14 +136,14 @@ jobs:
|
|||||||
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
|
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
|
||||||
- name: Upload source
|
- name: Upload source
|
||||||
if: matrix.opensuse_version == 'tumbleweed'
|
if: matrix.opensuse_version == 'tumbleweed'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: source
|
name: source
|
||||||
path: |
|
path: |
|
||||||
/usr/src/packages/SOURCES/*.xz
|
/usr/src/packages/SOURCES/*.xz
|
||||||
- name: Upload rpm
|
- name: Upload rpm
|
||||||
if: matrix.opensuse_version != 'tumbleweed'
|
if: matrix.opensuse_version != 'tumbleweed'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: opensuse-${{steps.set-subdir.outputs.subdir}}
|
name: opensuse-${{steps.set-subdir.outputs.subdir}}
|
||||||
path: |
|
path: |
|
||||||
@@ -200,6 +202,7 @@ jobs:
|
|||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
libebur128-devel
|
libebur128-devel
|
||||||
fftw-devel
|
fftw-devel
|
||||||
|
libprojectM-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
libappstream-glib
|
libappstream-glib
|
||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
@@ -209,7 +212,7 @@ jobs:
|
|||||||
sparsehash-devel
|
sparsehash-devel
|
||||||
rapidjson-devel
|
rapidjson-devel
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -234,7 +237,7 @@ jobs:
|
|||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: fedora-${{matrix.fedora_version}}
|
name: fedora-${{matrix.fedora_version}}
|
||||||
path: |
|
path: |
|
||||||
@@ -290,6 +293,7 @@ jobs:
|
|||||||
lib64Qt6DBus-devel
|
lib64Qt6DBus-devel
|
||||||
lib64Qt6Gui-devel
|
lib64Qt6Gui-devel
|
||||||
lib64Qt6Widgets-devel
|
lib64Qt6Widgets-devel
|
||||||
|
lib64Qt6OpenGLWidgets-devel
|
||||||
lib64Qt6Test-devel
|
lib64Qt6Test-devel
|
||||||
lib64kdsingleapplication-devel
|
lib64kdsingleapplication-devel
|
||||||
lib64xkbcommon-devel
|
lib64xkbcommon-devel
|
||||||
@@ -307,7 +311,7 @@ jobs:
|
|||||||
- name: Remove files
|
- name: Remove files
|
||||||
run: rm -rf /usr/lib64/qt6/lib/cmake/Qt6Sql/{Qt6QMYSQL*,Qt6QODBCD*,Qt6QPSQL*,Qt6QIBase*}
|
run: rm -rf /usr/lib64/qt6/lib/cmake/Qt6Sql/{Qt6QMYSQL*,Qt6QODBCD*,Qt6QPSQL*,Qt6QIBase*}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -333,7 +337,7 @@ jobs:
|
|||||||
run: rpmbuild -ba strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: matrix.openmandriva_version != 'cooker'
|
if: matrix.openmandriva_version != 'cooker'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: openmandriva-${{matrix.openmandriva_version}}
|
name: openmandriva-${{matrix.openmandriva_version}}
|
||||||
path: |
|
path: |
|
||||||
@@ -384,6 +388,7 @@ jobs:
|
|||||||
lib64fftw-devel
|
lib64fftw-devel
|
||||||
lib64dbus-devel
|
lib64dbus-devel
|
||||||
lib64appstream-devel
|
lib64appstream-devel
|
||||||
|
lib64projectm-devel
|
||||||
lib64qt6core-devel
|
lib64qt6core-devel
|
||||||
lib64qt6gui-devel
|
lib64qt6gui-devel
|
||||||
lib64qt6widgets-devel
|
lib64qt6widgets-devel
|
||||||
@@ -393,6 +398,7 @@ jobs:
|
|||||||
lib64qt6dbus-devel
|
lib64qt6dbus-devel
|
||||||
lib64qt6help-devel
|
lib64qt6help-devel
|
||||||
lib64qt6test-devel
|
lib64qt6test-devel
|
||||||
|
lib64qt6openglwidgets-devel
|
||||||
lib64sparsehash-devel
|
lib64sparsehash-devel
|
||||||
lib64kdsingleapplication-devel
|
lib64kdsingleapplication-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
@@ -409,7 +415,7 @@ jobs:
|
|||||||
cmake --build build --config Release --parallel 4
|
cmake --build build --config Release --parallel 4
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -434,7 +440,7 @@ jobs:
|
|||||||
working-directory: build
|
working-directory: build
|
||||||
run: rpmbuild -ba strawberry.spec
|
run: rpmbuild -ba strawberry.spec
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: mageia-${{matrix.mageia_version}}
|
name: mageia-${{matrix.mageia_version}}
|
||||||
path: |
|
path: |
|
||||||
@@ -449,7 +455,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
debian_version: [ 'bookworm', 'trixie' ]
|
debian_version: [ 'bookworm', 'trixie', 'forky' ]
|
||||||
container:
|
container:
|
||||||
image: debian:${{matrix.debian_version}}
|
image: debian:${{matrix.debian_version}}
|
||||||
steps:
|
steps:
|
||||||
@@ -499,7 +505,12 @@ jobs:
|
|||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
qt6-l10n-tools
|
qt6-l10n-tools
|
||||||
rapidjson-dev
|
rapidjson-dev
|
||||||
|
libprojectm-dev
|
||||||
|
- name: Install KDSingleApplication
|
||||||
|
if: matrix.debian_version != 'bookworm'
|
||||||
|
run: apt install -y libkdsingleapplication-qt6-dev
|
||||||
- name: Build and install KDSingleApplication
|
- name: Build and install KDSingleApplication
|
||||||
|
if: matrix.debian_version == 'bookworm'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --recurse-submodules https://github.com/KDAB/KDSingleApplication
|
git clone --depth 1 --recurse-submodules https://github.com/KDAB/KDSingleApplication
|
||||||
cd KDSingleApplication
|
cd KDSingleApplication
|
||||||
@@ -507,7 +518,7 @@ jobs:
|
|||||||
cmake --build build --config Release --parallel 4
|
cmake --build build --config Release --parallel 4
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -524,7 +535,7 @@ jobs:
|
|||||||
- name: Copy deb
|
- name: Copy deb
|
||||||
run: cp ../*.deb .
|
run: cp ../*.deb .
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: debian-${{matrix.debian_version}}
|
name: debian-${{matrix.debian_version}}
|
||||||
path: |
|
path: |
|
||||||
@@ -538,7 +549,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ubuntu_version: [ 'noble', 'plucky' ]
|
ubuntu_version: [ 'noble', 'plucky', 'questing' ]
|
||||||
container:
|
container:
|
||||||
image: ubuntu:${{matrix.ubuntu_version}}
|
image: ubuntu:${{matrix.ubuntu_version}}
|
||||||
steps:
|
steps:
|
||||||
@@ -591,7 +602,12 @@ jobs:
|
|||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
qt6-l10n-tools
|
qt6-l10n-tools
|
||||||
rapidjson-dev
|
rapidjson-dev
|
||||||
|
libprojectm-dev
|
||||||
|
- name: Install KDSingleApplication
|
||||||
|
if: matrix.ubuntu_version != 'noble' && matrix.ubuntu_version != 'plucky'
|
||||||
|
run: apt install -y libkdsingleapplication-qt6-dev
|
||||||
- name: Build and install KDSingleApplication
|
- name: Build and install KDSingleApplication
|
||||||
|
if: matrix.ubuntu_version == 'noble' || matrix.ubuntu_version == 'plucky'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --recurse-submodules https://github.com/KDAB/KDSingleApplication
|
git clone --depth 1 --recurse-submodules https://github.com/KDAB/KDSingleApplication
|
||||||
cd KDSingleApplication
|
cd KDSingleApplication
|
||||||
@@ -599,7 +615,7 @@ jobs:
|
|||||||
cmake --build build --config Release --parallel 4
|
cmake --build build --config Release --parallel 4
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -616,7 +632,7 @@ jobs:
|
|||||||
- name: Copy deb
|
- name: Copy deb
|
||||||
run: cp ../*.deb ../*.ddeb .
|
run: cp ../*.deb ../*.ddeb .
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ubuntu-${{matrix.ubuntu_version}}
|
name: ubuntu-${{matrix.ubuntu_version}}
|
||||||
path: |
|
path: |
|
||||||
@@ -631,7 +647,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ubuntu_version: [ 'noble', 'plucky' ]
|
ubuntu_version: [ 'noble', 'plucky', 'questing' ]
|
||||||
container:
|
container:
|
||||||
image: ubuntu:${{matrix.ubuntu_version}}
|
image: ubuntu:${{matrix.ubuntu_version}}
|
||||||
steps:
|
steps:
|
||||||
@@ -685,13 +701,14 @@ jobs:
|
|||||||
gstreamer1.0-pulseaudio
|
gstreamer1.0-pulseaudio
|
||||||
libkdsingleapplication-qt6-dev
|
libkdsingleapplication-qt6-dev
|
||||||
rapidjson-dev
|
rapidjson-dev
|
||||||
|
libprojectm-dev
|
||||||
- name: Install keyboxd
|
- name: Install keyboxd
|
||||||
if: matrix.ubuntu_version == 'noble'
|
if: matrix.ubuntu_version == 'noble'
|
||||||
env:
|
env:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
run: apt install -y keyboxd
|
run: apt install -y keyboxd
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -727,16 +744,22 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
df -h
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
|
||||||
|
sudo apt-get clean
|
||||||
|
df -h
|
||||||
- name: Build FreeBSD
|
- name: Build FreeBSD
|
||||||
id: build-freebsd
|
id: build-freebsd
|
||||||
uses: vmactions/freebsd-vm@v1.2.1
|
uses: vmactions/freebsd-vm@v1.3.2
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
mem: 4096
|
mem: 8192
|
||||||
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 sparsehash rapidjson
|
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 sparsehash rapidjson
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
@@ -752,13 +775,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Build OpenBSD
|
- name: Build OpenBSD
|
||||||
id: build-openbsd
|
id: build-openbsd
|
||||||
uses: vmactions/openbsd-vm@v1.1.8
|
uses: vmactions/openbsd-vm@v1.2.9
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
mem: 4096
|
mem: 4096
|
||||||
@@ -779,7 +802,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
runner: [ 'macos-13', 'macos-15' ]
|
runner: [ 'macos-15-intel', 'macos-15' ]
|
||||||
buildtype: [ 'release' ]
|
buildtype: [ 'release' ]
|
||||||
|
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
@@ -818,20 +841,20 @@ jobs:
|
|||||||
rm -f uninstall.sh
|
rm -f uninstall.sh
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Import certificate file
|
- name: Import certificate file
|
||||||
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false
|
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false
|
||||||
uses: apple-actions/import-codesign-certs@v5
|
uses: apple-actions/import-codesign-certs@v6
|
||||||
with:
|
with:
|
||||||
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE }}
|
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE }}
|
||||||
p12-password: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
|
p12-password: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
|
||||||
|
|
||||||
- name: Download macOS dependencies
|
- name: Download macOS dependencies
|
||||||
run: curl -f -O -L https://github.com/strawberrymusicplayer/strawberry-macos-dependencies/releases/latest/download/strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
|
run: curl -f -O -L https://github.com/strawberrymusicplayer/strawberry-macos-dependencies$(test "${{env.arch}}" = "x86_64" && echo "-intel" || echo "")/releases/latest/download/strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
|
||||||
|
|
||||||
- name: Extract macOS dependencies
|
- name: Extract macOS dependencies
|
||||||
run: sudo tar -C / -xf strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
|
run: sudo tar -C / -xf strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
|
||||||
@@ -882,9 +905,9 @@ jobs:
|
|||||||
run: make deploy
|
run: make deploy
|
||||||
|
|
||||||
- name: Manually Codesign
|
- name: Manually Codesign
|
||||||
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-13'
|
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15-intel'
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: codesign -s 383J84DVB6 -f strawberry.app/Contents/Frameworks/{libpcre2-8.0.dylib,libpcre2-16.0.dylib,libpng16.16.dylib,libfreetype.6.dylib,libzstd.1.dylib} strawberry.app/Contents/Frameworks/png.framework/png strawberry.app
|
run: codesign -s 383J84DVB6 -f strawberry.app/Contents/Frameworks/{libpcre2-8.0.dylib,libpcre2-16.0.dylib,libpng16.16.dylib,libfreetype.6.dylib,libzstd.1.dylib,libbrotlicommon.1.dylib,libbrotlienc.1.dylib} strawberry.app/Contents/Frameworks/png.framework/png strawberry.app
|
||||||
|
|
||||||
- name: Manually Codesign
|
- name: Manually Codesign
|
||||||
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15'
|
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15'
|
||||||
@@ -969,7 +992,7 @@ jobs:
|
|||||||
run: echo "cmake_buildtype=$(echo ${{env.buildtype}} | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')" >> $GITHUB_ENV
|
run: echo "cmake_buildtype=$(echo ${{env.buildtype}} | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -1072,7 +1095,7 @@ jobs:
|
|||||||
run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV
|
run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -1295,6 +1318,20 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV
|
run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Show SDK versions
|
||||||
|
shell: bash
|
||||||
|
run: ls -la "c:/Program Files (x86)/Windows Kits/10/include"
|
||||||
|
|
||||||
|
- name: Set SDK version
|
||||||
|
if: matrix.arch != 'arm64'
|
||||||
|
shell: bash
|
||||||
|
run: echo "sdk_version=10.0.22621.0" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set SDK version
|
||||||
|
if: matrix.arch == 'arm64'
|
||||||
|
shell: bash
|
||||||
|
run: echo "sdk_version=10.0.26100.0" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install rsync
|
- name: Install rsync
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: choco install --no-progress rsync
|
run: choco install --no-progress rsync
|
||||||
@@ -1323,7 +1360,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Copy bin files
|
- name: Copy bin files
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cp /c/strawberry/c/bin/{patch.exe,strip.exe,strings.exe,objdump.exe} ${{env.prefix_path_unix}}/bin
|
run: |
|
||||||
|
cp /c/mingw64/bin/{strip.exe,strings.exe,objdump.exe} ${{env.prefix_path_unix}}/bin
|
||||||
|
cp /c/strawberry/c/bin/patch.exe ${{env.prefix_path_unix}}/bin
|
||||||
|
|
||||||
- name: Delete conflicting files
|
- name: Delete conflicting files
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -1377,11 +1416,11 @@ jobs:
|
|||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
with:
|
with:
|
||||||
arch: ${{matrix.arch}}
|
arch: ${{matrix.arch}}
|
||||||
sdk: 10.0.20348.0
|
sdk: ${{env.sdk_version}}
|
||||||
vsversion: 2022
|
vsversion: 2022
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -1493,14 +1532,11 @@ jobs:
|
|||||||
run: copy ${{env.prefix_path_backslash}}\lib\gstreamer-1.0\*.dll .\gstreamer-plugins\
|
run: copy ${{env.prefix_path_backslash}}\lib\gstreamer-1.0\*.dll .\gstreamer-plugins\
|
||||||
|
|
||||||
- name: Download copydlldeps.sh
|
- name: Download copydlldeps.sh
|
||||||
if: matrix.arch != 'arm64'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: curl -f -O -L https://raw.githubusercontent.com/strawberrymusicplayer/strawberry-mxe/master/tools/copydlldeps.sh
|
run: curl -f -O -L https://raw.githubusercontent.com/strawberrymusicplayer/strawberry-mxe/master/tools/copydlldeps.sh
|
||||||
|
|
||||||
- name: Copy dependencies
|
- name: Copy dependencies
|
||||||
# copydlldeps.sh doesn't work with arm64 binaries.
|
|
||||||
if: matrix.arch != 'arm64'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: >
|
run: >
|
||||||
@@ -1517,12 +1553,6 @@ jobs:
|
|||||||
-F ./gstreamer-plugins
|
-F ./gstreamer-plugins
|
||||||
-R ${{env.prefix_path_unix}}/bin
|
-R ${{env.prefix_path_unix}}/bin
|
||||||
|
|
||||||
- name: Copy dependencies
|
|
||||||
if: matrix.arch == 'arm64'
|
|
||||||
shell: bash
|
|
||||||
working-directory: build
|
|
||||||
run: cp -v ${{env.prefix_path_unix}}/bin/{avcodec*.dll,avfilter*.dll,avformat*.dll,avutil*.dll,brotlicommon.dll,brotlidec.dll,chromaprint.dll,ebur128.dll,faad-2.dll,fdk-aac.dll,ffi-7.dll,FLAC.dll,freetype*.dll,getopt.dll,gio-2.0-0.dll,glib-2.0-0.dll,gme.dll,gmodule-2.0-0.dll,gobject-2.0-0.dll,gst-discoverer-1.0.exe,gst-launch-1.0.exe,gst-play-1.0.exe,gstadaptivedemux-1.0-0.dll,gstapp-1.0-0.dll,gstaudio-1.0-0.dll,gstbadaudio-1.0-0.dll,gstbase-1.0-0.dll,gstcodecparsers-1.0-0.dll,gstfft-1.0-0.dll,gstisoff-1.0-0.dll,gstmpegts-1.0-0.dll,gstnet-1.0-0.dll,gstpbutils-1.0-0.dll,gstreamer-1.0-0.dll,gstriff-1.0-0.dll,gstrtp-1.0-0.dll,gstrtsp-1.0-0.dll,gstsdp-1.0-0.dll,gsttag-1.0-0.dll,gsturidownloader-1.0-0.dll,gstvideo-1.0-0.dll,gstwinrt-1.0-0.dll,harfbuzz.dll,icudt*.dll,icuin*.dll,icuuc*.dll,intl-8.dll,jpeg62.dll,kdsingleapplication*.dll,libbs2b.dll,libcrypto-3-*.dll,fftw3.dll,libiconv*.dll,liblzma.dll,libmp3lame.dll,libopenmpt.dll,libpng16*.dll,libspeex*.dll,libssl-3-*.dll,libxml2*.dll,mpcdec.dll,mpg123.dll,nghttp2.dll,ogg.dll,opus.dll,orc-0.4-0.dll,pcre2-16*.dll,pcre2-8*.dll,postproc*.dll,psl-5.dll,Qt6Concurrent*.dll,Qt6Core*.dll,Qt6Gui*.dll,Qt6Network*.dll,Qt6Sql*.dll,Qt6Widgets*.dll,qtsparkle-qt6.dll,soup-3.0-0.dll,sqlite3.dll,sqlite3.exe,swresample*.dll,swscale*.dll,tag.dll,vorbis.dll,vorbisfile.dll,wavpackdll.dll,zlib*.dll} .
|
|
||||||
|
|
||||||
- name: Copy nsis files
|
- name: Copy nsis files
|
||||||
shell: cmd
|
shell: cmd
|
||||||
working-directory: build
|
working-directory: build
|
||||||
@@ -1623,11 +1653,11 @@ jobs:
|
|||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
run: sudo apt install -y git rsync
|
run: sudo apt install -y git rsync
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: SSH Setup
|
- name: SSH Setup
|
||||||
@@ -1671,7 +1701,7 @@ jobs:
|
|||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
run: sudo apt install -y git jq gh
|
run: sudo apt install -y git jq gh
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Show release assets
|
- name: Show release assets
|
||||||
@@ -1679,7 +1709,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
run: gh release view "${{github.event.release.tag_name}}" --json assets | jq -r '.assets[].name'
|
run: gh release view "${{github.event.release.tag_name}}" --json assets | jq -r '.assets[].name'
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Add artifacts to release
|
- name: Add artifacts to release
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
/build
|
/build
|
||||||
/bin
|
/bin
|
||||||
/CMakeLists.txt.user
|
/CMakeLists.txt.user
|
||||||
|
/.qtcreator
|
||||||
/.kdev4
|
/.kdev4
|
||||||
/strawberry.kdev4
|
/strawberry.kdev4
|
||||||
/.vscode
|
/.vscode
|
||||||
@@ -12,3 +13,4 @@
|
|||||||
/CMakeSettings.json
|
/CMakeSettings.json
|
||||||
/dist/scripts/maketarball.sh
|
/dist/scripts/maketarball.sh
|
||||||
/debian/changelog
|
/debian/changelog
|
||||||
|
_codeql_detected_source_root
|
||||||
|
|||||||
@@ -163,4 +163,3 @@ extern "C" void Discord_Register(const char *applicationId, const char *command)
|
|||||||
Discord_RegisterW(appId, wcommand);
|
Discord_RegisterW(appId, wcommand);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
3rdparty/discord-rpc/discord_rpc.h
vendored
1
3rdparty/discord-rpc/discord_rpc.h
vendored
@@ -32,6 +32,7 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct DiscordRichPresence {
|
typedef struct DiscordRichPresence {
|
||||||
int type;
|
int type;
|
||||||
|
int status_display_type;
|
||||||
const char *name; /* max 128 bytes */
|
const char *name; /* max 128 bytes */
|
||||||
const char *state; /* max 128 bytes */
|
const char *state; /* max 128 bytes */
|
||||||
const char *details; /* max 128 bytes */
|
const char *details; /* max 128 bytes */
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ size_t JsonWriteRichPresenceObj(char *dest, const size_t maxLen, const int nonce
|
|||||||
if (presence->type >= 0 && presence->type <= 5) {
|
if (presence->type >= 0 && presence->type <= 5) {
|
||||||
WriteKey(writer, "type");
|
WriteKey(writer, "type");
|
||||||
writer.Int(presence->type);
|
writer.Int(presence->type);
|
||||||
|
|
||||||
|
WriteKey(writer, "status_display_type");
|
||||||
|
writer.Int(presence->status_display_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteOptionalString(writer, "name", presence->name);
|
WriteOptionalString(writer, "name", presence->name);
|
||||||
|
|||||||
@@ -208,6 +208,15 @@ else()
|
|||||||
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib>=1.12)
|
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib>=1.12)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(projectM4 COMPONENTS Playlist)
|
||||||
|
if(projectM4_FOUND)
|
||||||
|
set(LIBPROJECTM_FOUND ON)
|
||||||
|
set(HAVE_PROJECTM4 ON)
|
||||||
|
set(LIBPROJECTM_LIBRARIES libprojectM::projectM libprojectM::playlist)
|
||||||
|
else()
|
||||||
|
pkg_check_modules(LIBPROJECTM libprojectM)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(GTest)
|
find_package(GTest)
|
||||||
|
|
||||||
pkg_check_modules(LIBSPARSEHASH IMPORTED_TARGET libsparsehash)
|
pkg_check_modules(LIBSPARSEHASH IMPORTED_TARGET libsparsehash)
|
||||||
@@ -218,7 +227,7 @@ set(QT_VERSION_MAJOR 6)
|
|||||||
set(QT_MIN_VERSION 6.4.0)
|
set(QT_MIN_VERSION 6.4.0)
|
||||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
||||||
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
||||||
set(QT_OPTIONAL_COMPONENTS GuiPrivate LinguistTools Test)
|
set(QT_OPTIONAL_COMPONENTS GuiPrivate OpenGLWidgets LinguistTools Test)
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
list(APPEND QT_OPTIONAL_COMPONENTS DBus)
|
list(APPEND QT_OPTIONAL_COMPONENTS DBus)
|
||||||
endif()
|
endif()
|
||||||
@@ -259,7 +268,18 @@ if(APPLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
find_package(getopt-win REQUIRED)
|
find_package(getopt NAMES getopt getopt-win unofficial-getopt-win32 REQUIRED)
|
||||||
|
if(TARGET getopt::getopt)
|
||||||
|
set(GETOPT_LIBRARIES getopt::getopt)
|
||||||
|
elseif(TARGET getopt-win::getopt)
|
||||||
|
set(GETOPT_LIBRARIES getopt-win::getopt)
|
||||||
|
elseif(TARGET getopt::getopt_shared)
|
||||||
|
set(GETOPT_LIBRARIES getopt::getopt_shared)
|
||||||
|
elseif(TARGET unofficial::getopt-win32::getopt)
|
||||||
|
set(GETOPT_LIBRARIES unofficial::getopt-win32::getopt)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Missing getopt")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE OR WIN32)
|
if(APPLE OR WIN32)
|
||||||
@@ -378,6 +398,11 @@ if(HAVE_X11_GLOBALSHORTCUTS OR HAVE_KGLOBALACCEL_GLOBALSHORTCUTS OR APPLE OR WIN
|
|||||||
set(HAVE_GLOBALSHORTCUTS ON)
|
set(HAVE_GLOBALSHORTCUTS ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
optional_component(VISUALIZATIONS ON "Visualizations"
|
||||||
|
DEPENDS "libprojectm" LIBPROJECTM_FOUND
|
||||||
|
DEPENDS "QtOpenGLWidgets" Qt${QT_VERSION_MAJOR}OpenGLWidgets_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
# Check that we have Qt with sqlite driver
|
# Check that we have Qt with sqlite driver
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
@@ -690,6 +715,7 @@ set(SOURCES
|
|||||||
src/lyrics/elyricsnetlyricsprovider.cpp
|
src/lyrics/elyricsnetlyricsprovider.cpp
|
||||||
src/lyrics/letraslyricsprovider.cpp
|
src/lyrics/letraslyricsprovider.cpp
|
||||||
src/lyrics/lyricfindlyricsprovider.cpp
|
src/lyrics/lyricfindlyricsprovider.cpp
|
||||||
|
src/lyrics/lrcliblyricsprovider.cpp
|
||||||
|
|
||||||
src/settings/settingsdialog.cpp
|
src/settings/settingsdialog.cpp
|
||||||
src/settings/settingspage.cpp
|
src/settings/settingspage.cpp
|
||||||
@@ -783,9 +809,7 @@ set(SOURCES
|
|||||||
src/scrobbler/scrobblercache.cpp
|
src/scrobbler/scrobblercache.cpp
|
||||||
src/scrobbler/scrobblercacheitem.cpp
|
src/scrobbler/scrobblercacheitem.cpp
|
||||||
src/scrobbler/scrobblemetadata.cpp
|
src/scrobbler/scrobblemetadata.cpp
|
||||||
src/scrobbler/scrobblingapi20.cpp
|
|
||||||
src/scrobbler/lastfmscrobbler.cpp
|
src/scrobbler/lastfmscrobbler.cpp
|
||||||
src/scrobbler/librefmscrobbler.cpp
|
|
||||||
src/scrobbler/listenbrainzscrobbler.cpp
|
src/scrobbler/listenbrainzscrobbler.cpp
|
||||||
src/scrobbler/lastfmimport.cpp
|
src/scrobbler/lastfmimport.cpp
|
||||||
|
|
||||||
@@ -988,6 +1012,7 @@ set(HEADERS
|
|||||||
src/lyrics/elyricsnetlyricsprovider.h
|
src/lyrics/elyricsnetlyricsprovider.h
|
||||||
src/lyrics/letraslyricsprovider.h
|
src/lyrics/letraslyricsprovider.h
|
||||||
src/lyrics/lyricfindlyricsprovider.h
|
src/lyrics/lyricfindlyricsprovider.h
|
||||||
|
src/lyrics/lrcliblyricsprovider.h
|
||||||
|
|
||||||
src/settings/settingsdialog.h
|
src/settings/settingsdialog.h
|
||||||
src/settings/settingspage.h
|
src/settings/settingspage.h
|
||||||
@@ -1076,9 +1101,7 @@ set(HEADERS
|
|||||||
src/scrobbler/scrobblersettingsservice.h
|
src/scrobbler/scrobblersettingsservice.h
|
||||||
src/scrobbler/scrobblerservice.h
|
src/scrobbler/scrobblerservice.h
|
||||||
src/scrobbler/scrobblercache.h
|
src/scrobbler/scrobblercache.h
|
||||||
src/scrobbler/scrobblingapi20.h
|
|
||||||
src/scrobbler/lastfmscrobbler.h
|
src/scrobbler/lastfmscrobbler.h
|
||||||
src/scrobbler/librefmscrobbler.h
|
|
||||||
src/scrobbler/listenbrainzscrobbler.h
|
src/scrobbler/listenbrainzscrobbler.h
|
||||||
src/scrobbler/lastfmimport.h
|
src/scrobbler/lastfmimport.h
|
||||||
|
|
||||||
@@ -1454,6 +1477,7 @@ optional_source(HAVE_QOBUZ
|
|||||||
src/qobuz/qobuzrequest.cpp
|
src/qobuz/qobuzrequest.cpp
|
||||||
src/qobuz/qobuzstreamurlrequest.cpp
|
src/qobuz/qobuzstreamurlrequest.cpp
|
||||||
src/qobuz/qobuzfavoriterequest.cpp
|
src/qobuz/qobuzfavoriterequest.cpp
|
||||||
|
src/qobuz/qobuzcredentialfetcher.cpp
|
||||||
src/settings/qobuzsettingspage.cpp
|
src/settings/qobuzsettingspage.cpp
|
||||||
src/covermanager/qobuzcoverprovider.cpp
|
src/covermanager/qobuzcoverprovider.cpp
|
||||||
HEADERS
|
HEADERS
|
||||||
@@ -1463,12 +1487,33 @@ optional_source(HAVE_QOBUZ
|
|||||||
src/qobuz/qobuzrequest.h
|
src/qobuz/qobuzrequest.h
|
||||||
src/qobuz/qobuzstreamurlrequest.h
|
src/qobuz/qobuzstreamurlrequest.h
|
||||||
src/qobuz/qobuzfavoriterequest.h
|
src/qobuz/qobuzfavoriterequest.h
|
||||||
|
src/qobuz/qobuzcredentialfetcher.h
|
||||||
src/settings/qobuzsettingspage.h
|
src/settings/qobuzsettingspage.h
|
||||||
src/covermanager/qobuzcoverprovider.h
|
src/covermanager/qobuzcoverprovider.h
|
||||||
UI
|
UI
|
||||||
src/settings/qobuzsettingspage.ui
|
src/settings/qobuzsettingspage.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
optional_source(HAVE_VISUALIZATIONS
|
||||||
|
SOURCES
|
||||||
|
src/visualizations/projectmpresetmodel.cpp
|
||||||
|
src/visualizations/projectmvisualization.cpp
|
||||||
|
src/visualizations/visualizationcontainer.cpp
|
||||||
|
src/visualizations/visualizationoverlay.cpp
|
||||||
|
src/visualizations/visualizationselector.cpp
|
||||||
|
src/visualizations/visualizationopenglwidget.cpp
|
||||||
|
HEADERS
|
||||||
|
src/visualizations/projectmpresetmodel.h
|
||||||
|
src/visualizations/projectmvisualization.h
|
||||||
|
src/visualizations/visualizationcontainer.h
|
||||||
|
src/visualizations/visualizationoverlay.h
|
||||||
|
src/visualizations/visualizationselector.h
|
||||||
|
src/visualizations/visualizationopenglwidget.h
|
||||||
|
UI
|
||||||
|
src/visualizations/visualizationoverlay.ui
|
||||||
|
src/visualizations/visualizationselector.ui
|
||||||
|
)
|
||||||
|
|
||||||
qt_wrap_cpp(SOURCES ${HEADERS})
|
qt_wrap_cpp(SOURCES ${HEADERS})
|
||||||
qt_wrap_ui(SOURCES ${UI})
|
qt_wrap_ui(SOURCES ${UI})
|
||||||
qt_add_resources(SOURCES data/data.qrc data/icons.qrc)
|
qt_add_resources(SOURCES data/data.qrc data/icons.qrc)
|
||||||
@@ -1539,6 +1584,7 @@ target_link_libraries(strawberry_lib PUBLIC
|
|||||||
Qt${QT_VERSION_MAJOR}::Sql
|
Qt${QT_VERSION_MAJOR}::Sql
|
||||||
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
|
||||||
$<$<BOOL:${HAVE_QPA_QPLATFORMNATIVEINTERFACE}>:Qt${QT_VERSION_MAJOR}::GuiPrivate>
|
$<$<BOOL:${HAVE_QPA_QPLATFORMNATIVEINTERFACE}>:Qt${QT_VERSION_MAJOR}::GuiPrivate>
|
||||||
|
$<$<BOOL:${HAVE_VISUALIZATIONS}>:Qt${QT_VERSION_MAJOR}::OpenGLWidgets>
|
||||||
ICU::uc
|
ICU::uc
|
||||||
ICU::i18n
|
ICU::i18n
|
||||||
$<$<BOOL:${HAVE_STREAMTAGREADER}>:PkgConfig::LIBSPARSEHASH>
|
$<$<BOOL:${HAVE_STREAMTAGREADER}>:PkgConfig::LIBSPARSEHASH>
|
||||||
@@ -1554,7 +1600,8 @@ target_link_libraries(strawberry_lib PUBLIC
|
|||||||
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
|
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
|
||||||
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
|
||||||
$<$<BOOL:${HAVE_QTSPARKLE}>:qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle>
|
$<$<BOOL:${HAVE_QTSPARKLE}>:qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle>
|
||||||
$<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt>
|
$<$<BOOL:${HAVE_VISUALIZATIONS}>:${LIBPROJECTM_LIBRARIES}>
|
||||||
|
$<$<BOOL:${WIN32}>:dsound dwmapi ${GETOPT_LIBRARIES}>
|
||||||
$<$<BOOL:${MSVC}>:WindowsApp>
|
$<$<BOOL:${MSVC}>:WindowsApp>
|
||||||
KDAB::kdsingleapplication
|
KDAB::kdsingleapplication
|
||||||
$<$<BOOL:${HAVE_DISCORD_RPC}>:discord-rpc>
|
$<$<BOOL:${HAVE_DISCORD_RPC}>:discord-rpc>
|
||||||
|
|||||||
56
Changelog
56
Changelog
@@ -2,6 +2,62 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
|
Version 1.2.16 (2025.12.16):
|
||||||
|
|
||||||
|
* Make Discord Rich presence use filename if song title is missing
|
||||||
|
* Added better error message when a GStreamer plugin is missing
|
||||||
|
* Preserve track order in album shuffle mode when restarting playback (#1623)
|
||||||
|
* Possible fixes for context word wrap
|
||||||
|
* Added lyrics from lrclib.net
|
||||||
|
* Added option to turn off the use of sort tags for the collection
|
||||||
|
* Fixed Spotify login
|
||||||
|
* Fixed error dialog shown minimized if another Strawberry window than the mainwindow was active
|
||||||
|
* Fixed seeking to the end of the track and back causing seeking to stop working (#1675)
|
||||||
|
* Set current index when automatically selecting track (#1825)
|
||||||
|
* Make icon size for shuffle and repeat buttons adjust to screen resolution (#1838)
|
||||||
|
* Fixed song being removed from playlist when dragging to another application (#1815)
|
||||||
|
* Don't automatically scroll on dynamic playlists (#1427)
|
||||||
|
|
||||||
|
Version 1.2.15 (2025.11.25):
|
||||||
|
|
||||||
|
* Fixed system default language not respected
|
||||||
|
* Fixed length filter search
|
||||||
|
* Fixed playlist parser converting Spotify URL's
|
||||||
|
* Removed use of deprecated QStyle::State_Editing
|
||||||
|
* Ignore connection closed errors for ListenBrainz
|
||||||
|
* (Windows) Support building with vcpkg unofficial::getopt-win32::getopt
|
||||||
|
|
||||||
|
Version 1.2.14 (2025.10.25):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed showing error dialog minimized when main window is not current active window (#1739)
|
||||||
|
* Fixed Discord timestamp update when seeking (#1813)
|
||||||
|
* Fixed CD metadata lookup to respect MusicBrainz rate limiting
|
||||||
|
* Fixed Tidal Open API cover provider
|
||||||
|
* (Windows) Fixed device selection with WASAPI2
|
||||||
|
|
||||||
|
Enhancements/Other:
|
||||||
|
* Removed libre.fm support
|
||||||
|
* Rewrote MusicBrainzClient to use Json instead of XML
|
||||||
|
* Subsonic will now use cover art from album when available
|
||||||
|
* Added option to remove "Remastered", etc from song titles for Tidal, Qobuz and Spotify
|
||||||
|
* Added webm to supported file extensions
|
||||||
|
* (Windows|MinGW) Added WASAPI2 support
|
||||||
|
* (Windows) Added experimental exclusive mode for WASAPI2
|
||||||
|
|
||||||
|
Version 1.2.13 (2025.08.31):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed playlist alternating row colors no longer working with some styles (#1806)
|
||||||
|
* Fixed "Open Audio CD" no longer working (#1803)
|
||||||
|
* Fixed systemtray icon playback status not working with scaling (#1782)
|
||||||
|
* Fixed build without MusicBrainz (#1799)
|
||||||
|
* Fixed build without MTP (#1804)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Added Discord status text option (#1796)
|
||||||
|
* Read Vorbis/FLAC "Other" embedded covers if front cover is not available (#1793)
|
||||||
|
|
||||||
Version 1.2.12 (2025.08.12):
|
Version 1.2.12 (2025.08.12):
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|||||||
171
README.md
171
README.md
@@ -1,118 +1,137 @@
|
|||||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
# :strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||||
=======================
|
|
||||||
[](https://github.com/sponsors/jonaski)
|
[](https://github.com/sponsors/jonaski)
|
||||||
[](https://patreon.com/jonaskvinge)
|
[](https://patreon.com/jonaskvinge)
|
||||||
[](https://paypal.me/jonaskvinge)
|
[](https://paypal.me/jonaskvinge)
|
||||||
|
|
||||||
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt framework.
|
Strawberry is a **music player and music collection organizer**, originally forked from *Clementine* in 2018.
|
||||||
|
It’s written in **C++ using the Qt framework**, designed for **audiophiles and music collectors**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Resources:
|
---
|
||||||
|
|
||||||
* Website: https://www.strawberrymusicplayer.org/
|
## :globe_with_meridians: Resources
|
||||||
* Wiki: https://wiki.strawberrymusicplayer.org/
|
|
||||||
* Forum: https://forum.strawberrymusicplayer.org/
|
|
||||||
* Github: https://github.com/strawberrymusicplayer/strawberry
|
|
||||||
* Latest builds: https://builds.strawberrymusicplayer.org/
|
|
||||||
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
|
|
||||||
* Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
|
|
||||||
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
|
|
||||||
* Translations: https://crowdin.com/project/strawberrymusicplayer/
|
|
||||||
|
|
||||||
### :bangbang: Opening an issue
|
- **Website:** https://www.strawberrymusicplayer.org
|
||||||
|
- **Wiki:** https://wiki.strawberrymusicplayer.org
|
||||||
|
- **Forum:** https://forum.strawberrymusicplayer.org
|
||||||
|
- **GitHub:** https://github.com/strawberrymusicplayer/strawberry
|
||||||
|
- **Latest builds:** https://builds.strawberrymusicplayer.org
|
||||||
|
- **openSUSE Build Service:**
|
||||||
|
- Stable: https://build.opensuse.org/package/show/home:jonaski:strawberry/strawberry
|
||||||
|
- Unstable: https://build.opensuse.org/package/show/home:jonaski:strawberry-dev/strawberry
|
||||||
|
- **Ubuntu PPAs:**
|
||||||
|
- Stable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
|
||||||
|
- Unstable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
|
||||||
|
- **Translations:** https://crowdin.com/project/strawberrymusicplayer
|
||||||
|
|
||||||
* Read the FAQ: https://wiki.strawberrymusicplayer.org/wiki/FAQ
|
---
|
||||||
* Search for the issue to see if it is already solved, or if there is an open issue for it already. If there is an open issue already, you can comment on it if you have additional information that could be useful to us.
|
|
||||||
* For technical problems, discussion, questions and feature suggestions use the forum (https://forum.strawberrymusicplayer.org/) instead. The forum is better suited for discussion.
|
|
||||||
* We do not take feature requests from users on GitHub. Any issues related to feature requests will be closed. This does not necessarily mean that we won't add new features, but we don't have time to take feature requests or answer questions about new features from users. It is still possible to suggest or discuss new features on the forum (https://forum.strawberrymusicplayer.org/).
|
|
||||||
* We do not maintain the Flatpak package. Do not report issues related to Flatpak unless the issue can be reproduced with a native package, use Flatpak support instead https://flatpak.org/about/
|
|
||||||
|
|
||||||
### :moneybag: Sponsoring
|
## :warning: Opening an Issue
|
||||||
|
|
||||||
The program is free software, released under GPL. If you like this program and can make use of it, consider sponsoring or donating to help fund the project.
|
Before creating a new GitHub issue:
|
||||||
There are currently 4 options for sponsoring:
|
|
||||||
|
|
||||||
|
1. **Read the [FAQ](https://wiki.strawberrymusicplayer.org/wiki/FAQ)**.
|
||||||
|
2. **Search existing issues** to avoid duplicates. If one already exists, comment there with any additional information.
|
||||||
|
3. **Use the [forum](https://forum.strawberrymusicplayer.org/)** for technical problems, discussions or feature suggestions — it’s better suited for back-and-forth conversation.
|
||||||
|
4. **Feature requests are not accepted on GitHub.** Issues created for feature requests will be closed. You can still discuss ideas on the forum.
|
||||||
|
5. **Flatpak users:** We do **not** maintain the Flatpak package. Report Flatpak-specific issues via [Flatpak support](https://flatpak.org/about/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## :moneybag: Sponsoring
|
||||||
|
|
||||||
|
Strawberry is **free software released under the GPL**.
|
||||||
|
If you enjoy using it, please consider **supporting development** through sponsorship or donation.
|
||||||
|
|
||||||
|
**Sponsorship options:**
|
||||||
1. [Patreon](https://www.patreon.com/jonaskvinge)
|
1. [Patreon](https://www.patreon.com/jonaskvinge)
|
||||||
2. [GitHub](https://github.com/sponsors/jonaski)
|
2. [GitHub](https://github.com/sponsors/jonaski)
|
||||||
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
|
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
|
||||||
4. [PayPal](https://paypal.me/jonaskvinge)
|
4. [PayPal](https://paypal.me/jonaskvinge)
|
||||||
|
|
||||||
Funding developers is a way to contribute to open source projects you appreciate, it helps developers get the resources they need, and recognize contributors working behind the scenes to make open source better for everyone.
|
Supporting open-source developers helps ensure continued maintenance and improvements.
|
||||||
|
|
||||||
### :heavy_check_mark: Features
|
---
|
||||||
|
|
||||||
* Play and organize music
|
## :white_check_mark: Features
|
||||||
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
|
||||||
* Audio CD playback
|
|
||||||
* Native desktop notifications
|
|
||||||
* Playlist management
|
|
||||||
* Smart and dynamic playlists
|
|
||||||
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
|
||||||
* In-player song loudness analysis and song playback loudness normalization, as per EBU R 128
|
|
||||||
* Edit tags on audio files
|
|
||||||
* Fetch tags from MusicBrainz
|
|
||||||
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
|
|
||||||
* Song lyrics from [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics.com](https://www.lololyrics.com/), [songlyrics.com](https://www.songlyrics.com/), [azlyrics.com](https://www.azlyrics.com/), [elyrics.net](https://www.elyrics.net/), [letras.mus.br](https://www.letras.mus.br) and [LyricFind](https://lyrics.lyricfind.com)
|
|
||||||
* Support for multiple backends
|
|
||||||
* Audio analyzer
|
|
||||||
* Audio equalizer
|
|
||||||
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
|
||||||
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
|
||||||
* Streaming from Subsonic compatible servers
|
|
||||||
* Unofficial Tidal, Spotify and Qobuz integration
|
|
||||||
* Discord rich presence
|
|
||||||
|
|
||||||
|
- Play and organize your music collection
|
||||||
|
- Supports formats: WAV, FLAC, WavPack, Ogg Vorbis, Opus, MPC, TrueAudio, AIFF, MP4, MP3, ASF, and Monkey’s Audio
|
||||||
|
- Audio CD playback
|
||||||
|
- Bit-perfect playback on Linux
|
||||||
|
- Native desktop notifications
|
||||||
|
- Advanced playlist management
|
||||||
|
- Smart and dynamic playlists
|
||||||
|
- Loudness analysis and EBU R128 normalization
|
||||||
|
- Editing tags and fetching missing tags via [MusicBrainz](https://musicbrainz.org/)
|
||||||
|
- Album art from: [Last.fm](https://www.last.fm/), [MusicBrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/), [Spotify](https://www.spotify.com/)
|
||||||
|
- Lyrics from: [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics](https://www.lololyrics.com/), [songlyrics](https://www.songlyrics.com/), [azlyrics](https://www.azlyrics.com/), [elyrics](https://www.elyrics.net/), [letras](https://www.letras.mus.br), [LyricFind](https://lyrics.lyricfind.com) and [lrclib.net](https://lrclib.net/)
|
||||||
|
- Audio analyzer and equalizer
|
||||||
|
- Transfer music to USB, MTP and iPod devices
|
||||||
|
- Scrobbling to [Last.fm](https://www.last.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
||||||
|
- Streaming from Subsonic-compatible servers
|
||||||
|
- Unofficial integrations: Tidal, Spotify, and Qobuz
|
||||||
|
- Discord Rich Presence
|
||||||
|
|
||||||
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
---
|
||||||
|
|
||||||
**Access to macOS and Windows releases are currently restricted to sponsors, a 5 USD monthly sponsorship is required. You can sponsor strawberry through <a href="https://www.patreon.com/jonaskvinge">Patreon</a> for direct access to new releases. If you are sponsoring through GitHub, Ko-fi or PayPal, please e-mail support AT strawberrymusicplayer.org for access to downloads.**
|
:white_check_mark: Tested on **Linux**, **OpenBSD**, **FreeBSD**, **macOS**, and **Windows**.
|
||||||
|
|
||||||
### :heavy_exclamation_mark: Requirements
|
> **Note:** macOS and Windows releases are currently **available to sponsors only**.
|
||||||
|
> A monthly sponsorship via [Patreon](https://www.patreon.com/jonaskvinge) grants direct access to new releases.
|
||||||
|
|
||||||
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
|
---
|
||||||
|
|
||||||
* [CMake 3.13 or higher](https://cmake.org/)
|
## :gear: Requirements
|
||||||
* C/C++ compiler ([GCC](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/) or [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/))
|
|
||||||
* [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) or [pkgconf](https://github.com/pkgconf/pkgconf)
|
|
||||||
* [Boost](https://www.boost.org/)
|
|
||||||
* [GLib](https://developer.gnome.org/glib/)
|
|
||||||
* [Qt 6.4.0 or higher with components Core, Concurrent, Gui, Widgets, Network, Sql and D-Bus](https://www.qt.io/)
|
|
||||||
* [SQLite 3.9 or newer](https://www.sqlite.org)
|
|
||||||
* [ALSA (Required on Linux)](https://www.alsa-project.org/)
|
|
||||||
* [GStreamer](https://gstreamer.freedesktop.org/)
|
|
||||||
* [TagLib 1.12 or higher](https://www.taglib.org/)
|
|
||||||
* [ICU](https://unicode-org.github.io/icu/)
|
|
||||||
* [KDSingleApplication 1.1.0 or higher](https://github.com/KDAB/KDSingleApplication)
|
|
||||||
|
|
||||||
Optional dependencies:
|
To build Strawberry from source, you’ll need:
|
||||||
|
|
||||||
* Song fingerprinting and MusicBrainz tagging: [Chromaprint](https://acoustid.org/chromaprint)
|
**Dependencies:**
|
||||||
* Moodbar: [fftw3](http://www.fftw.org/)
|
- [CMake ≥= 3.13](https://cmake.org/)
|
||||||
* PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
- C/C++ compiler ([GCC](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/), or [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/))
|
||||||
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
- [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) or [pkgconf](https://github.com/pkgconf/pkgconf)
|
||||||
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
- [Boost](https://www.boost.org/)
|
||||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
- [GLib](https://developer.gnome.org/glib/)
|
||||||
* EBU R 128 loudness normalization [libebur128](https://github.com/jiixyj/libebur128)
|
- [Qt ≥= 6.4](https://www.qt.io/) (Core, Concurrent, Gui, Widgets, Network, SQL, D-Bus)
|
||||||
* Discord rich presence [RapidJSON](https://rapidjson.org/)
|
- [SQLite ≥= 3.9](https://www.sqlite.org)
|
||||||
|
- [ALSA (Linux only)](https://www.alsa-project.org/)
|
||||||
|
- [GStreamer](https://gstreamer.freedesktop.org/)
|
||||||
|
- [TagLib ≥= 1.12](https://www.taglib.org/)
|
||||||
|
- [ICU](https://unicode-org.github.io/icu/)
|
||||||
|
- [KDSingleApplication ≥= 1.1.0](https://github.com/KDAB/KDSingleApplication)
|
||||||
|
|
||||||
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
|
**Dependencies for optional features:**
|
||||||
|
- Fingerprinting & tagging: [Chromaprint](https://acoustid.org/chromaprint)
|
||||||
|
- Moodbar: [FFTW3](http://www.fftw.org/)
|
||||||
|
- PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/)
|
||||||
|
- Audio CD support: [libcdio](https://www.gnu.org/software/libcdio/)
|
||||||
|
- MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
||||||
|
- iPod Classic: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||||
|
- EBU R128 normalization: [libebur128](https://github.com/jiixyj/libebur128)
|
||||||
|
- Discord presence: [RapidJSON](https://rapidjson.org/)
|
||||||
|
|
||||||
### :wrench: Build from source
|
Also install GStreamer plugins **base**, **good**, and optionally **bad**, **ugly** and **libav** for full codec support.
|
||||||
|
|
||||||
### Get the code:
|
---
|
||||||
|
|
||||||
|
## :wrench: Build from Source
|
||||||
|
|
||||||
|
**Get the code:**
|
||||||
|
|
||||||
git clone --recursive https://github.com/strawberrymusicplayer/strawberry
|
git clone --recursive https://github.com/strawberrymusicplayer/strawberry
|
||||||
|
|
||||||
### Build and install:
|
**Build and install:**
|
||||||
|
|
||||||
cd strawberry
|
cd strawberry
|
||||||
cmake -S . -B build
|
cmake -S . -B build
|
||||||
cmake --build build --parallel $(nproc)
|
cmake --build build --parallel $(nproc)
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
|
||||||
To build on Windows with Visual Studio 2022, see https://github.com/strawberrymusicplayer/strawberry-msvc
|
For building on Windows with Visual Studio 2022, see: :point_right: https://github.com/strawberrymusicplayer/strawberry-msvc
|
||||||
|
|
||||||
### :penguin: Packaging status
|
---
|
||||||
|
|
||||||
|
## :package: Packaging status
|
||||||
|
|
||||||
[](https://repology.org/metapackage/strawberry/versions)
|
[](https://repology.org/metapackage/strawberry/versions)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||||
set(STRAWBERRY_VERSION_MINOR 2)
|
set(STRAWBERRY_VERSION_MINOR 2)
|
||||||
set(STRAWBERRY_VERSION_PATCH 12)
|
set(STRAWBERRY_VERSION_PATCH 16)
|
||||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#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}")
|
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
|
||||||
|
|
||||||
|
|||||||
7
debian/control
vendored
7
debian/control
vendored
@@ -32,7 +32,8 @@ Build-Depends: debhelper-compat (= 12),
|
|||||||
libfftw3-dev,
|
libfftw3-dev,
|
||||||
libebur128-dev,
|
libebur128-dev,
|
||||||
libsparsehash-dev,
|
libsparsehash-dev,
|
||||||
rapidjson-dev
|
rapidjson-dev,
|
||||||
|
libprojectm-dev
|
||||||
Standards-Version: 4.7.0
|
Standards-Version: 4.7.0
|
||||||
|
|
||||||
Package: strawberry
|
Package: strawberry
|
||||||
@@ -60,11 +61,11 @@ Description: music player and music collection organizer
|
|||||||
- Edit tags on audio files
|
- Edit tags on audio files
|
||||||
- Automatically retrieve tags from MusicBrainz
|
- Automatically retrieve tags from MusicBrainz
|
||||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||||
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind
|
- Lyrics from multiple sources
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
- Audio equalizer
|
- Audio equalizer
|
||||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
- Scrobbler with support for Last.fm and ListenBrainz
|
||||||
- Streaming support for Subsonic-compatible servers
|
- Streaming support for Subsonic-compatible servers
|
||||||
- Unofficial streaming support for Tidal and Qobuz
|
- Unofficial streaming support for Tidal and Qobuz
|
||||||
.
|
.
|
||||||
|
|||||||
22
dist/macos/macversion.sh
vendored
22
dist/macos/macversion.sh
vendored
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
macos_version=$(sw_vers -productVersion)
|
|
||||||
macos_version_major=$(echo $macos_version | awk -F '[.]' '{print $1}')
|
|
||||||
macos_version_minor=$(echo $macos_version | awk -F '[.]' '{print $2}')
|
|
||||||
|
|
||||||
if [ "${macos_version_major}" = "10" ]; then
|
|
||||||
macos_codenames=(
|
|
||||||
["13"]="highsierra"
|
|
||||||
["14"]="mojave"
|
|
||||||
["15"]="catalina"
|
|
||||||
)
|
|
||||||
if [[ -n "${macos_codenames[$macos_version_minor]}" ]]; then
|
|
||||||
echo "${macos_codenames[$macos_version_minor]}"
|
|
||||||
else
|
|
||||||
echo "unknown"
|
|
||||||
fi
|
|
||||||
elif [ "${macos_version_major}" = "11" ]; then
|
|
||||||
echo "bigsur"
|
|
||||||
else
|
|
||||||
echo "unknown"
|
|
||||||
fi
|
|
||||||
@@ -31,10 +31,10 @@
|
|||||||
<li>Edit tags on audio files</li>
|
<li>Edit tags on audio files</li>
|
||||||
<li>Automatically retrieve tags from MusicBrainz</li>
|
<li>Automatically retrieve tags from MusicBrainz</li>
|
||||||
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
|
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
|
||||||
<li>Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind</li>
|
<li>Lyrics from multiple sources</li>
|
||||||
<li>Audio analyzer and equalizer</li>
|
<li>Audio analyzer and equalizer</li>
|
||||||
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
|
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
|
||||||
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
|
<li>Scrobbler with support for Last.fm and ListenBrainz</li>
|
||||||
<li>Streaming support for Subsonic-compatible servers</li>
|
<li>Streaming support for Subsonic-compatible servers</li>
|
||||||
<li>Unofficial streaming support for Tidal, Spotify and Qobuz</li>
|
<li>Unofficial streaming support for Tidal, Spotify and Qobuz</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -51,6 +51,10 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.2.16" date="2025-12-16"/>
|
||||||
|
<release version="1.2.15" date="2025-11-25"/>
|
||||||
|
<release version="1.2.14" date="2025-10-25"/>
|
||||||
|
<release version="1.2.13" date="2025-08-31"/>
|
||||||
<release version="1.2.12" date="2025-08-12"/>
|
<release version="1.2.12" date="2025-08-12"/>
|
||||||
<release version="1.2.11" date="2025-05-15"/>
|
<release version="1.2.11" date="2025-05-15"/>
|
||||||
<release version="1.2.10" date="2025-04-18"/>
|
<release version="1.2.10" date="2025-04-18"/>
|
||||||
|
|||||||
6
dist/unix/strawberry.1
vendored
6
dist/unix/strawberry.1
vendored
@@ -29,9 +29,7 @@ Features:
|
|||||||
.br
|
.br
|
||||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||||
.br
|
.br
|
||||||
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind
|
- Lyrics from multiple sources
|
||||||
.br
|
|
||||||
- Support for multiple backends
|
|
||||||
.br
|
.br
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
.br
|
.br
|
||||||
@@ -39,7 +37,7 @@ Features:
|
|||||||
.br
|
.br
|
||||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||||
.br
|
.br
|
||||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
- Scrobbler with support for Last.fm and ListenBrainz
|
||||||
.br
|
.br
|
||||||
- Streaming support for Subsonic-compatible servers
|
- Streaming support for Subsonic-compatible servers
|
||||||
.br
|
.br
|
||||||
|
|||||||
8
dist/unix/strawberry.spec.in
vendored
8
dist/unix/strawberry.spec.in
vendored
@@ -43,6 +43,7 @@ BuildRequires: pkgconfig(taglib)
|
|||||||
BuildRequires: pkgconfig(fftw3)
|
BuildRequires: pkgconfig(fftw3)
|
||||||
BuildRequires: pkgconfig(icu-uc)
|
BuildRequires: pkgconfig(icu-uc)
|
||||||
BuildRequires: pkgconfig(icu-i18n)
|
BuildRequires: pkgconfig(icu-i18n)
|
||||||
|
BuildRequires: pkgconfig(libprojectM)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
|
||||||
@@ -93,16 +94,15 @@ Features:
|
|||||||
- Edit tags on audio files
|
- Edit tags on audio files
|
||||||
- Automatically retrieve tags from MusicBrainz
|
- Automatically retrieve tags from MusicBrainz
|
||||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||||
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind
|
- Lyrics from multiple sources
|
||||||
- Support for multiple backends
|
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
- Audio equalizer
|
- Audio equalizer
|
||||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
- Scrobbler with support for Last.fm and ListenBrainz
|
||||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||||
- Streaming support for Subsonic-compatible servers
|
- Streaming support for Subsonic-compatible servers
|
||||||
- Unofficial streaming support for Tidal and Qobuz
|
- Unofficial streaming support for Tidal and Qobuz
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} <= 1600
|
%if 0%{?suse_version} && 0%{?suse_version} < 1600
|
||||||
%debug_package
|
%debug_package
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|||||||
78
dist/windows/strawberry.nsi.in
vendored
78
dist/windows/strawberry.nsi.in
vendored
@@ -395,9 +395,6 @@ Section "Strawberry" Strawberry
|
|||||||
File "glib-2.0-0.dll"
|
File "glib-2.0-0.dll"
|
||||||
File "gme.dll"
|
File "gme.dll"
|
||||||
File "gmodule-2.0-0.dll"
|
File "gmodule-2.0-0.dll"
|
||||||
!ifndef arch_arm64
|
|
||||||
File "gnutls.dll"
|
|
||||||
!endif
|
|
||||||
File "gobject-2.0-0.dll"
|
File "gobject-2.0-0.dll"
|
||||||
File "gstadaptivedemux-1.0-0.dll"
|
File "gstadaptivedemux-1.0-0.dll"
|
||||||
File "gstapp-1.0-0.dll"
|
File "gstapp-1.0-0.dll"
|
||||||
@@ -418,18 +415,11 @@ Section "Strawberry" Strawberry
|
|||||||
File "gsttag-1.0-0.dll"
|
File "gsttag-1.0-0.dll"
|
||||||
File "gsturidownloader-1.0-0.dll"
|
File "gsturidownloader-1.0-0.dll"
|
||||||
File "gstvideo-1.0-0.dll"
|
File "gstvideo-1.0-0.dll"
|
||||||
!ifdef arch_arm64
|
|
||||||
File "gstwinrt-1.0-0.dll"
|
|
||||||
!endif
|
|
||||||
File "harfbuzz.dll"
|
File "harfbuzz.dll"
|
||||||
File "intl-8.dll"
|
File "intl-8.dll"
|
||||||
File "jpeg62.dll"
|
File "jpeg62.dll"
|
||||||
File "kdsingleapplication-qt6.dll"
|
File "kdsingleapplication-qt6.dll"
|
||||||
File "libbs2b.dll"
|
File "libbs2b.dll"
|
||||||
!ifndef arch_arm64
|
|
||||||
File "libfaac_dll.dll"
|
|
||||||
!endif
|
|
||||||
File "liblzma.dll"
|
|
||||||
File "libmp3lame.dll"
|
File "libmp3lame.dll"
|
||||||
File "libopenmpt.dll"
|
File "libopenmpt.dll"
|
||||||
File "mpcdec.dll"
|
File "mpcdec.dll"
|
||||||
@@ -447,6 +437,11 @@ Section "Strawberry" Strawberry
|
|||||||
File "vorbisfile.dll"
|
File "vorbisfile.dll"
|
||||||
File "wavpackdll.dll"
|
File "wavpackdll.dll"
|
||||||
|
|
||||||
|
!ifndef arch_arm64
|
||||||
|
File "gnutls.dll"
|
||||||
|
File "libfaac_dll.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
!ifdef release
|
!ifdef release
|
||||||
File "freetype.dll"
|
File "freetype.dll"
|
||||||
File "libiconv.dll"
|
File "libiconv.dll"
|
||||||
@@ -454,10 +449,10 @@ Section "Strawberry" Strawberry
|
|||||||
File "libspeex.dll"
|
File "libspeex.dll"
|
||||||
File "pcre2-8.dll"
|
File "pcre2-8.dll"
|
||||||
File "pcre2-16.dll"
|
File "pcre2-16.dll"
|
||||||
|
File "zlib1.dll"
|
||||||
!ifndef arch_arm64
|
!ifndef arch_arm64
|
||||||
File "twolame.dll"
|
File "twolame.dll"
|
||||||
!endif
|
!endif
|
||||||
File "zlib1.dll"
|
|
||||||
!endif
|
!endif
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
File "freetyped.dll"
|
File "freetyped.dll"
|
||||||
@@ -466,10 +461,10 @@ Section "Strawberry" Strawberry
|
|||||||
File "libspeexd.dll"
|
File "libspeexd.dll"
|
||||||
File "pcre2-8d.dll"
|
File "pcre2-8d.dll"
|
||||||
File "pcre2-16d.dll"
|
File "pcre2-16d.dll"
|
||||||
|
File "zlibd1.dll"
|
||||||
!ifndef arch_arm64
|
!ifndef arch_arm64
|
||||||
File "twolamed.dll"
|
File "twolamed.dll"
|
||||||
!endif
|
!endif
|
||||||
File "zlibd1.dll"
|
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
; Used by libfftw3-3.dll because fftw is compiled with MinGW.
|
; Used by libfftw3-3.dll because fftw is compiled with MinGW.
|
||||||
@@ -482,15 +477,15 @@ Section "Strawberry" Strawberry
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
File "icudt77.dll"
|
File "icudt78.dll"
|
||||||
!ifdef msvc && arch_arm64
|
!ifdef msvc && arch_arm64
|
||||||
File "fftw3.dll"
|
File "fftw3.dll"
|
||||||
!else
|
!else
|
||||||
File "libfftw3-3.dll"
|
File "libfftw3-3.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
File "icuin77d.dll"
|
File "icuin78d.dll"
|
||||||
File "icuuc77d.dll"
|
File "icuuc78d.dll"
|
||||||
File "libxml2d.dll"
|
File "libxml2d.dll"
|
||||||
File "Qt6Concurrentd.dll"
|
File "Qt6Concurrentd.dll"
|
||||||
File "Qt6Cored.dll"
|
File "Qt6Cored.dll"
|
||||||
@@ -499,8 +494,8 @@ Section "Strawberry" Strawberry
|
|||||||
File "Qt6Sqld.dll"
|
File "Qt6Sqld.dll"
|
||||||
File "Qt6Widgetsd.dll"
|
File "Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
File "icuin77.dll"
|
File "icuin78.dll"
|
||||||
File "icuuc77.dll"
|
File "icuuc78.dll"
|
||||||
File "libxml2.dll"
|
File "libxml2.dll"
|
||||||
File "Qt6Concurrent.dll"
|
File "Qt6Concurrent.dll"
|
||||||
File "Qt6Core.dll"
|
File "Qt6Core.dll"
|
||||||
@@ -510,6 +505,7 @@ Section "Strawberry" Strawberry
|
|||||||
File "Qt6Widgets.dll"
|
File "Qt6Widgets.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
!ifdef msvc && arch_x86
|
||||||
File "avcodec-61.dll"
|
File "avcodec-61.dll"
|
||||||
File "avfilter-10.dll"
|
File "avfilter-10.dll"
|
||||||
File "avformat-61.dll"
|
File "avformat-61.dll"
|
||||||
@@ -517,6 +513,14 @@ Section "Strawberry" Strawberry
|
|||||||
File "postproc-58.dll"
|
File "postproc-58.dll"
|
||||||
File "swresample-5.dll"
|
File "swresample-5.dll"
|
||||||
File "swscale-8.dll"
|
File "swscale-8.dll"
|
||||||
|
!else
|
||||||
|
File "avcodec-62.dll"
|
||||||
|
File "avfilter-11.dll"
|
||||||
|
File "avformat-62.dll"
|
||||||
|
File "avutil-60.dll"
|
||||||
|
File "swresample-6.dll"
|
||||||
|
File "swscale-9.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
; Register Strawberry with Default Programs
|
; Register Strawberry with Default Programs
|
||||||
Var /GLOBAL AppIcon
|
Var /GLOBAL AppIcon
|
||||||
@@ -677,6 +681,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
|
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
|
||||||
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
|
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
|
||||||
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
||||||
|
File "/oname=libgstwasapi2.dll" "gstreamer-plugins\libgstwasapi2.dll"
|
||||||
File "/oname=libgstwaveform.dll" "gstreamer-plugins\libgstwaveform.dll"
|
File "/oname=libgstwaveform.dll" "gstreamer-plugins\libgstwaveform.dll"
|
||||||
File "/oname=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
|
File "/oname=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
|
||||||
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
|
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
|
||||||
@@ -942,9 +947,6 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\glib-2.0-0.dll"
|
Delete "$INSTDIR\glib-2.0-0.dll"
|
||||||
Delete "$INSTDIR\gme.dll"
|
Delete "$INSTDIR\gme.dll"
|
||||||
Delete "$INSTDIR\gmodule-2.0-0.dll"
|
Delete "$INSTDIR\gmodule-2.0-0.dll"
|
||||||
!ifndef arch_arm64
|
|
||||||
Delete "$INSTDIR\gnutls.dll"
|
|
||||||
!endif
|
|
||||||
Delete "$INSTDIR\gobject-2.0-0.dll"
|
Delete "$INSTDIR\gobject-2.0-0.dll"
|
||||||
Delete "$INSTDIR\gstadaptivedemux-1.0-0.dll"
|
Delete "$INSTDIR\gstadaptivedemux-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstapp-1.0-0.dll"
|
Delete "$INSTDIR\gstapp-1.0-0.dll"
|
||||||
@@ -965,18 +967,11 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\gsttag-1.0-0.dll"
|
Delete "$INSTDIR\gsttag-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gsturidownloader-1.0-0.dll"
|
Delete "$INSTDIR\gsturidownloader-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstvideo-1.0-0.dll"
|
Delete "$INSTDIR\gstvideo-1.0-0.dll"
|
||||||
!ifdef arch_arm64
|
|
||||||
Delete "$INSTDIR\gstwinrt-1.0-0.dll"
|
|
||||||
!endif
|
|
||||||
Delete "$INSTDIR\harfbuzz.dll"
|
Delete "$INSTDIR\harfbuzz.dll"
|
||||||
Delete "$INSTDIR\intl-8.dll"
|
Delete "$INSTDIR\intl-8.dll"
|
||||||
Delete "$INSTDIR\jpeg62.dll"
|
Delete "$INSTDIR\jpeg62.dll"
|
||||||
Delete "$INSTDIR\kdsingleapplication-qt6.dll"
|
Delete "$INSTDIR\kdsingleapplication-qt6.dll"
|
||||||
Delete "$INSTDIR\libbs2b.dll"
|
Delete "$INSTDIR\libbs2b.dll"
|
||||||
!ifndef arch_arm64
|
|
||||||
Delete "$INSTDIR\libfaac_dll.dll"
|
|
||||||
!endif
|
|
||||||
Delete "$INSTDIR\liblzma.dll"
|
|
||||||
Delete "$INSTDIR\libmp3lame.dll"
|
Delete "$INSTDIR\libmp3lame.dll"
|
||||||
Delete "$INSTDIR\libopenmpt.dll"
|
Delete "$INSTDIR\libopenmpt.dll"
|
||||||
Delete "$INSTDIR\mpcdec.dll"
|
Delete "$INSTDIR\mpcdec.dll"
|
||||||
@@ -994,6 +989,11 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\vorbisfile.dll"
|
Delete "$INSTDIR\vorbisfile.dll"
|
||||||
Delete "$INSTDIR\wavpackdll.dll"
|
Delete "$INSTDIR\wavpackdll.dll"
|
||||||
|
|
||||||
|
!ifndef arch_arm64
|
||||||
|
Delete "$INSTDIR\gnutls.dll"
|
||||||
|
Delete "$INSTDIR\libfaac_dll.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
!ifdef release
|
!ifdef release
|
||||||
Delete "$INSTDIR\freetype.dll"
|
Delete "$INSTDIR\freetype.dll"
|
||||||
Delete "$INSTDIR\libiconv.dll"
|
Delete "$INSTDIR\libiconv.dll"
|
||||||
@@ -1001,10 +1001,10 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libspeex.dll"
|
Delete "$INSTDIR\libspeex.dll"
|
||||||
Delete "$INSTDIR\pcre2-8.dll"
|
Delete "$INSTDIR\pcre2-8.dll"
|
||||||
Delete "$INSTDIR\pcre2-16.dll"
|
Delete "$INSTDIR\pcre2-16.dll"
|
||||||
|
Delete "$INSTDIR\zlib1.dll"
|
||||||
!ifndef arch_arm64
|
!ifndef arch_arm64
|
||||||
Delete "$INSTDIR\twolame.dll"
|
Delete "$INSTDIR\twolame.dll"
|
||||||
!endif
|
!endif
|
||||||
Delete "$INSTDIR\zlib1.dll"
|
|
||||||
!endif
|
!endif
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
Delete "$INSTDIR\freetyped.dll"
|
Delete "$INSTDIR\freetyped.dll"
|
||||||
@@ -1013,10 +1013,10 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libspeexd.dll"
|
Delete "$INSTDIR\libspeexd.dll"
|
||||||
Delete "$INSTDIR\pcre2-8d.dll"
|
Delete "$INSTDIR\pcre2-8d.dll"
|
||||||
Delete "$INSTDIR\pcre2-16d.dll"
|
Delete "$INSTDIR\pcre2-16d.dll"
|
||||||
|
Delete "$INSTDIR\zlibd1.dll"
|
||||||
!ifndef arch_arm64
|
!ifndef arch_arm64
|
||||||
Delete "$INSTDIR\twolamed.dll"
|
Delete "$INSTDIR\twolamed.dll"
|
||||||
!endif
|
!endif
|
||||||
Delete "$INSTDIR\zlibd1.dll"
|
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
@@ -1028,15 +1028,15 @@ Section "Uninstall"
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
Delete "$INSTDIR\icudt77.dll"
|
Delete "$INSTDIR\icudt78.dll"
|
||||||
!ifdef msvc && arch_arm64
|
!ifdef msvc && arch_arm64
|
||||||
Delete "$INSTDIR\fftw3.dll"
|
Delete "$INSTDIR\fftw3.dll"
|
||||||
!else
|
!else
|
||||||
Delete "$INSTDIR\libfftw3-3.dll"
|
Delete "$INSTDIR\libfftw3-3.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
Delete "$INSTDIR\icuin77d.dll"
|
Delete "$INSTDIR\icuin78d.dll"
|
||||||
Delete "$INSTDIR\icuuc77d.dll"
|
Delete "$INSTDIR\icuuc78d.dll"
|
||||||
Delete "$INSTDIR\libxml2d.dll"
|
Delete "$INSTDIR\libxml2d.dll"
|
||||||
Delete "$INSTDIR\Qt6Concurrentd.dll"
|
Delete "$INSTDIR\Qt6Concurrentd.dll"
|
||||||
Delete "$INSTDIR\Qt6Cored.dll"
|
Delete "$INSTDIR\Qt6Cored.dll"
|
||||||
@@ -1045,8 +1045,8 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\Qt6Sqld.dll"
|
Delete "$INSTDIR\Qt6Sqld.dll"
|
||||||
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
Delete "$INSTDIR\icuin77.dll"
|
Delete "$INSTDIR\icuin78.dll"
|
||||||
Delete "$INSTDIR\icuuc77.dll"
|
Delete "$INSTDIR\icuuc78.dll"
|
||||||
Delete "$INSTDIR\libxml2.dll"
|
Delete "$INSTDIR\libxml2.dll"
|
||||||
Delete "$INSTDIR\Qt6Concurrent.dll"
|
Delete "$INSTDIR\Qt6Concurrent.dll"
|
||||||
Delete "$INSTDIR\Qt6Core.dll"
|
Delete "$INSTDIR\Qt6Core.dll"
|
||||||
@@ -1056,6 +1056,7 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\Qt6Widgets.dll"
|
Delete "$INSTDIR\Qt6Widgets.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
!ifdef msvc && arch_x86
|
||||||
Delete "$INSTDIR\avcodec-61.dll"
|
Delete "$INSTDIR\avcodec-61.dll"
|
||||||
Delete "$INSTDIR\avfilter-10.dll"
|
Delete "$INSTDIR\avfilter-10.dll"
|
||||||
Delete "$INSTDIR\avformat-61.dll"
|
Delete "$INSTDIR\avformat-61.dll"
|
||||||
@@ -1063,6 +1064,14 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\postproc-58.dll"
|
Delete "$INSTDIR\postproc-58.dll"
|
||||||
Delete "$INSTDIR\swresample-5.dll"
|
Delete "$INSTDIR\swresample-5.dll"
|
||||||
Delete "$INSTDIR\swscale-8.dll"
|
Delete "$INSTDIR\swscale-8.dll"
|
||||||
|
!else
|
||||||
|
Delete "$INSTDIR\avcodec-62.dll"
|
||||||
|
Delete "$INSTDIR\avfilter-11.dll"
|
||||||
|
Delete "$INSTDIR\avformat-62.dll"
|
||||||
|
Delete "$INSTDIR\avutil-60.dll"
|
||||||
|
Delete "$INSTDIR\swresample-6.dll"
|
||||||
|
Delete "$INSTDIR\swscale-9.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
!ifdef mingw
|
!ifdef mingw
|
||||||
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
||||||
@@ -1156,6 +1165,7 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi2.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwaveform.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwaveform.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
|
||||||
|
|||||||
@@ -90,4 +90,3 @@ class AnalyzerBase : public QWidget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANALYZERBASE_H
|
#endif // ANALYZERBASE_H
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
|||||||
double_click_timer_(new QTimer(this)),
|
double_click_timer_(new QTimer(this)),
|
||||||
ignore_next_click_(false),
|
ignore_next_click_(false),
|
||||||
current_analyzer_(nullptr),
|
current_analyzer_(nullptr),
|
||||||
engine_(nullptr) {
|
engine_(nullptr),
|
||||||
|
action_visualization_(nullptr) {
|
||||||
|
|
||||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
@@ -118,6 +119,17 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
|
|
||||||
|
Q_UNUSED(e);
|
||||||
|
|
||||||
|
double_click_timer_->stop();
|
||||||
|
ignore_next_click_ = true;
|
||||||
|
|
||||||
|
if (action_visualization_) action_visualization_->trigger();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::ShowPopupMenu() {
|
void AnalyzerContainer::ShowPopupMenu() {
|
||||||
context_menu_->popup(last_click_pos_);
|
context_menu_->popup(last_click_pos_);
|
||||||
}
|
}
|
||||||
@@ -249,3 +261,10 @@ void AnalyzerContainer::AddFramerate(const QString &name, const int framerate) {
|
|||||||
QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } );
|
QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnalyzerContainer::SetVisualizationsAction(QAction *visualization) {
|
||||||
|
|
||||||
|
action_visualization_ = visualization;
|
||||||
|
context_menu_->addAction(action_visualization_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class AnalyzerContainer : public QWidget {
|
|||||||
explicit AnalyzerContainer(QWidget *parent);
|
explicit AnalyzerContainer(QWidget *parent);
|
||||||
|
|
||||||
void SetEngine(SharedPtr<EngineBase> engine);
|
void SetEngine(SharedPtr<EngineBase> engine);
|
||||||
|
void SetVisualizationsAction(QAction *visualization);
|
||||||
|
|
||||||
static const char *kSettingsGroup;
|
static const char *kSettingsGroup;
|
||||||
static const char *kSettingsFramerate;
|
static const char *kSettingsFramerate;
|
||||||
@@ -55,6 +56,7 @@ class AnalyzerContainer : public QWidget {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||||
void wheelEvent(QWheelEvent *e) override;
|
void wheelEvent(QWheelEvent *e) override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
@@ -89,6 +91,8 @@ class AnalyzerContainer : public QWidget {
|
|||||||
|
|
||||||
AnalyzerBase *current_analyzer_;
|
AnalyzerBase *current_analyzer_;
|
||||||
SharedPtr<EngineBase> engine_;
|
SharedPtr<EngineBase> engine_;
|
||||||
|
|
||||||
|
QAction *action_visualization_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -102,7 +106,6 @@ void AnalyzerContainer::AddAnalyzerType() {
|
|||||||
action->setCheckable(true);
|
action->setCheckable(true);
|
||||||
actions_ << action;
|
actions_ << action;
|
||||||
QObject::connect(action, &QAction::triggered, [this, id]() { ChangeAnalyzer(id); });
|
QObject::connect(action, &QAction::triggered, [this, id]() { ChangeAnalyzer(id); });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ANALYZERCONTAINER_H
|
#endif // ANALYZERCONTAINER_H
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
|
|||||||
|
|
||||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||||
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1));
|
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ class BlockAnalyzer : public AnalyzerBase {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit BlockAnalyzer(QWidget*);
|
Q_INVOKABLE explicit BlockAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transform(Scope&) override;
|
void transform(Scope &s) override;
|
||||||
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
void resizeEvent(QResizeEvent*) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
virtual void paletteChange(const QPalette &_palette);
|
virtual void paletteChange(const QPalette &_palette);
|
||||||
void framerateChanged() override;
|
void framerateChanged() override;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class BoomAnalyzer : public AnalyzerBase {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit BoomAnalyzer(QWidget*);
|
Q_INVOKABLE explicit BoomAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
@@ -70,7 +70,6 @@ class BoomAnalyzer : public AnalyzerBase {
|
|||||||
|
|
||||||
QPixmap barPixmap_;
|
QPixmap barPixmap_;
|
||||||
QPixmap canvas_;
|
QPixmap canvas_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BOOMANALYZER_H
|
#endif // BOOMANALYZER_H
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class FHT {
|
|||||||
/**
|
/**
|
||||||
* Recursive in-place Hartley transform. For internal use only!
|
* Recursive in-place Hartley transform. For internal use only!
|
||||||
*/
|
*/
|
||||||
void _transform(float*, int, int);
|
void _transform(float *p, int n, int k);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +68,7 @@ class FHT {
|
|||||||
~FHT();
|
~FHT();
|
||||||
int sizeExp() const;
|
int sizeExp() const;
|
||||||
int size() const;
|
int size() const;
|
||||||
void scale(float*, float) const;
|
void scale(float *p, float d) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exponentially Weighted Moving Average (EWMA) filter.
|
* Exponentially Weighted Moving Average (EWMA) filter.
|
||||||
@@ -90,12 +90,12 @@ class FHT {
|
|||||||
/**
|
/**
|
||||||
* Semi-logarithmic audio spectrum.
|
* Semi-logarithmic audio spectrum.
|
||||||
*/
|
*/
|
||||||
void semiLogSpectrum(float*);
|
void semiLogSpectrum(float *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fourier spectrum.
|
* Fourier spectrum.
|
||||||
*/
|
*/
|
||||||
void spectrum(float*);
|
void spectrum(float *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates a mathematically correct FFT power spectrum.
|
* Calculates a mathematically correct FFT power spectrum.
|
||||||
@@ -103,7 +103,7 @@ class FHT {
|
|||||||
* and factor the 0.5 in the final scaling factor.
|
* and factor the 0.5 in the final scaling factor.
|
||||||
* @see FHT::power2()
|
* @see FHT::power2()
|
||||||
*/
|
*/
|
||||||
void power(float*);
|
void power(float *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates an FFT power spectrum with doubled values as a
|
* Calculates an FFT power spectrum with doubled values as a
|
||||||
@@ -112,14 +112,14 @@ class FHT {
|
|||||||
* of @f$2^n@f$ input values. This is the fastest transform.
|
* of @f$2^n@f$ input values. This is the fastest transform.
|
||||||
* @see FHT::power()
|
* @see FHT::power()
|
||||||
*/
|
*/
|
||||||
void power2(float*);
|
void power2(float *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discrete Hartley transform of data sets with 8 values.
|
* Discrete Hartley transform of data sets with 8 values.
|
||||||
*/
|
*/
|
||||||
static void transform8(float*);
|
static void transform8(float *p);
|
||||||
|
|
||||||
void transform(float*);
|
void transform(float *p);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FHT_H
|
#endif // FHT_H
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
||||||
|
|
||||||
~CollectionBackend();
|
~CollectionBackend();
|
||||||
@@ -331,4 +330,3 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONBACKEND_H
|
#endif // COLLECTIONBACKEND_H
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
class CollectionFilterOptions {
|
class CollectionFilterOptions {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit CollectionFilterOptions();
|
explicit CollectionFilterOptions();
|
||||||
|
|
||||||
// Filter mode:
|
// Filter mode:
|
||||||
|
|||||||
@@ -135,4 +135,3 @@ class CollectionFilterWidget : public QWidget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONFILTERWIDGET_H
|
#endif // COLLECTIONFILTERWIDGET_H
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -189,6 +189,26 @@ void CollectionLibrary::ReloadSettings() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionLibrary::CurrentSongChanged(const Song &song) {
|
||||||
|
|
||||||
|
current_song_url_ = song.url();
|
||||||
|
|
||||||
|
if (!pending_song_saves_.isEmpty()) {
|
||||||
|
SavePendingPlaycountsAndRatings();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionLibrary::Stopped() {
|
||||||
|
|
||||||
|
current_song_url_ = QUrl();
|
||||||
|
|
||||||
|
if (!pending_song_saves_.isEmpty()) {
|
||||||
|
SavePendingPlaycountsAndRatings();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionLibrary::SyncPlaycountAndRatingToFilesAsync() {
|
void CollectionLibrary::SyncPlaycountAndRatingToFilesAsync() {
|
||||||
|
|
||||||
(void)QtConcurrent::run(&CollectionLibrary::SyncPlaycountAndRatingToFiles, this);
|
(void)QtConcurrent::run(&CollectionLibrary::SyncPlaycountAndRatingToFiles, this);
|
||||||
@@ -212,18 +232,85 @@ void CollectionLibrary::SyncPlaycountAndRatingToFiles() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionLibrary::SongsPlaycountChanged(const SongList &songs, const bool save_tags) const {
|
void CollectionLibrary::SongsPlaycountChanged(const SongList &songs, const bool save_tags) {
|
||||||
|
|
||||||
if (save_tags || save_playcounts_to_files_) {
|
if (save_tags || save_playcounts_to_files_) {
|
||||||
tagreader_client_->SaveSongsPlaycountAsync(songs);
|
SongList songs_to_save_now;
|
||||||
|
for (const Song &song : songs) {
|
||||||
|
if (song.url().isLocalFile() && song.url() == current_song_url_ &&
|
||||||
|
(song.filetype() == Song::FileType::OggFlac || song.filetype() == Song::FileType::OggVorbis || song.filetype() == Song::FileType::OggOpus)) {
|
||||||
|
qLog(Debug) << "Deferring playcount save for currently playing file" << song.url().toLocalFile();
|
||||||
|
if (pending_song_saves_.contains(song.url())) {
|
||||||
|
SharedPtr<PendingSongSave> pending_song_save = pending_song_saves_[song.url()];
|
||||||
|
pending_song_save->save_playcount = true;
|
||||||
|
pending_song_save->song.set_playcount(song.playcount());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SharedPtr<PendingSongSave> pending_song_save = make_shared<PendingSongSave>();
|
||||||
|
pending_song_save->save_playcount = true;
|
||||||
|
pending_song_save->song = song;
|
||||||
|
pending_song_saves_.insert(song.url(), pending_song_save);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
songs_to_save_now << song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!songs_to_save_now.isEmpty()) {
|
||||||
|
tagreader_client_->SaveSongsPlaycountAsync(songs_to_save_now);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionLibrary::SongsRatingChanged(const SongList &songs, const bool save_tags) const {
|
void CollectionLibrary::SongsRatingChanged(const SongList &songs, const bool save_tags) {
|
||||||
|
|
||||||
if (save_tags || save_ratings_to_files_) {
|
if (save_tags || save_ratings_to_files_) {
|
||||||
tagreader_client_->SaveSongsRatingAsync(songs);
|
SongList songs_to_save_now;
|
||||||
|
for (const Song &song : songs) {
|
||||||
|
if (song.url().isLocalFile() && song.url() == current_song_url_ &&
|
||||||
|
(song.filetype() == Song::FileType::OggFlac || song.filetype() == Song::FileType::OggVorbis || song.filetype() == Song::FileType::OggOpus)) {
|
||||||
|
qLog(Debug) << "Deferring rating save for currently playing file" << song.url().toLocalFile();
|
||||||
|
if (pending_song_saves_.contains(song.url())) {
|
||||||
|
SharedPtr<PendingSongSave> pending_song_save = pending_song_saves_[song.url()];
|
||||||
|
pending_song_save->save_rating = true;
|
||||||
|
pending_song_save->song.set_rating(song.rating());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SharedPtr<PendingSongSave> pending_song_save = make_shared<PendingSongSave>();
|
||||||
|
pending_song_save->save_rating = true;
|
||||||
|
pending_song_save->song = song;
|
||||||
|
pending_song_saves_.insert(song.url(), pending_song_save);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
songs_to_save_now << song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!songs_to_save_now.isEmpty()) {
|
||||||
|
tagreader_client_->SaveSongsRatingAsync(songs_to_save_now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionLibrary::SavePendingPlaycountsAndRatings() {
|
||||||
|
|
||||||
|
for (auto it = pending_song_saves_.constBegin(); it != pending_song_saves_.constEnd();) {
|
||||||
|
const QUrl url = it.key();
|
||||||
|
SharedPtr<PendingSongSave> pending_song_save = it.value();
|
||||||
|
if (url == current_song_url_) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
qLog(Debug) << "Saving deferred playcount/rating for" << url.toLocalFile();
|
||||||
|
if (pending_song_save->save_playcount) {
|
||||||
|
tagreader_client_->SaveSongsPlaycountAsync(SongList() << pending_song_save->song);
|
||||||
|
}
|
||||||
|
if (pending_song_save->save_rating) {
|
||||||
|
tagreader_client_->SaveSongsRatingAsync(SongList() << pending_song_save->song);
|
||||||
|
}
|
||||||
|
it = pending_song_saves_.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
@@ -71,6 +72,7 @@ class CollectionLibrary : public QObject {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void SyncPlaycountAndRatingToFiles();
|
void SyncPlaycountAndRatingToFiles();
|
||||||
|
void SavePendingPlaycountsAndRatings();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
@@ -84,16 +86,26 @@ class CollectionLibrary : public QObject {
|
|||||||
|
|
||||||
void IncrementalScan();
|
void IncrementalScan();
|
||||||
|
|
||||||
|
void CurrentSongChanged(const Song &song);
|
||||||
|
void Stopped();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void ExitReceived();
|
void ExitReceived();
|
||||||
void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false) const;
|
void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false);
|
||||||
void SongsRatingChanged(const SongList &songs, const bool save_tags = false) const;
|
void SongsRatingChanged(const SongList &songs, const bool save_tags = false);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void Error(const QString &error);
|
void Error(const QString &error);
|
||||||
void ExitFinished();
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class PendingSongSave {
|
||||||
|
public:
|
||||||
|
Song song;
|
||||||
|
bool save_playcount = false;
|
||||||
|
bool save_rating = false;
|
||||||
|
};
|
||||||
|
|
||||||
const SharedPtr<TaskManager> task_manager_;
|
const SharedPtr<TaskManager> task_manager_;
|
||||||
const SharedPtr<TagReaderClient> tagreader_client_;
|
const SharedPtr<TagReaderClient> tagreader_client_;
|
||||||
|
|
||||||
@@ -111,6 +123,10 @@ class CollectionLibrary : public QObject {
|
|||||||
|
|
||||||
bool save_playcounts_to_files_;
|
bool save_playcounts_to_files_;
|
||||||
bool save_ratings_to_files_;
|
bool save_ratings_to_files_;
|
||||||
|
|
||||||
|
QUrl current_song_url_;
|
||||||
|
|
||||||
|
QMap<QUrl, SharedPtr<PendingSongSave>> pending_song_saves_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ void CollectionModel::ReloadSettings() {
|
|||||||
const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool();
|
const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool();
|
||||||
const bool sort_skip_articles_for_artists = settings.value(CollectionSettings::kSkipArticlesForArtists, true).toBool();
|
const bool sort_skip_articles_for_artists = settings.value(CollectionSettings::kSkipArticlesForArtists, true).toBool();
|
||||||
const bool sort_skip_articles_for_albums = settings.value(CollectionSettings::kSkipArticlesForAlbums, false).toBool();
|
const bool sort_skip_articles_for_albums = settings.value(CollectionSettings::kSkipArticlesForAlbums, false).toBool();
|
||||||
|
const bool use_sort_tags = settings.value(CollectionSettings::kUseSortTags, true).toBool();
|
||||||
|
|
||||||
use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool();
|
use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool();
|
||||||
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024));
|
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024));
|
||||||
@@ -227,12 +228,14 @@ void CollectionModel::ReloadSettings() {
|
|||||||
show_dividers != options_current_.show_dividers ||
|
show_dividers != options_current_.show_dividers ||
|
||||||
show_various_artists != options_current_.show_various_artists ||
|
show_various_artists != options_current_.show_various_artists ||
|
||||||
sort_skip_articles_for_artists != options_current_.sort_skip_articles_for_artists ||
|
sort_skip_articles_for_artists != options_current_.sort_skip_articles_for_artists ||
|
||||||
sort_skip_articles_for_albums != options_current_.sort_skip_articles_for_albums) {
|
sort_skip_articles_for_albums != options_current_.sort_skip_articles_for_albums ||
|
||||||
|
use_sort_tags != options_current_.use_sort_tags) {
|
||||||
options_current_.show_pretty_covers = show_pretty_covers;
|
options_current_.show_pretty_covers = show_pretty_covers;
|
||||||
options_current_.show_dividers = show_dividers;
|
options_current_.show_dividers = show_dividers;
|
||||||
options_current_.show_various_artists = show_various_artists;
|
options_current_.show_various_artists = show_various_artists;
|
||||||
options_current_.sort_skip_articles_for_artists = sort_skip_articles_for_artists;
|
options_current_.sort_skip_articles_for_artists = sort_skip_articles_for_artists;
|
||||||
options_current_.sort_skip_articles_for_albums = sort_skip_articles_for_albums;
|
options_current_.sort_skip_articles_for_albums = sort_skip_articles_for_albums;
|
||||||
|
options_current_.use_sort_tags = use_sort_tags;
|
||||||
ScheduleReset();
|
ScheduleReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +430,8 @@ void CollectionModel::ScheduleUpdate(const CollectionModelUpdate::Type type, con
|
|||||||
|
|
||||||
void CollectionModel::ScheduleReset() {
|
void CollectionModel::ScheduleReset() {
|
||||||
|
|
||||||
|
if (!updates_.isEmpty() && updates_.first().type == CollectionModelUpdate::Type::Reset) return;
|
||||||
|
|
||||||
ScheduleUpdate(CollectionModelUpdate::Type::Reset);
|
ScheduleUpdate(CollectionModelUpdate::Type::Reset);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -540,7 +545,10 @@ void CollectionModel::AddSongsInternal(const SongList &songs) {
|
|||||||
// Sanity check to make sure we don't add songs that are outside the user's filter
|
// Sanity check to make sure we don't add songs that are outside the user's filter
|
||||||
if (!options_active_.filter_options.Matches(song)) continue;
|
if (!options_active_.filter_options.Matches(song)) continue;
|
||||||
|
|
||||||
if (song_nodes_.contains(song.id())) continue;
|
if (song_nodes_.contains(song.id())) {
|
||||||
|
qLog(Debug) << song.id() << song.title() << "already exists, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Before we can add each song we need to make sure the required container items already exist in the tree.
|
// Before we can add each song we need to make sure the required container items already exist in the tree.
|
||||||
// These depend on which "group by" settings the user has on the collection.
|
// These depend on which "group by" settings the user has on the collection.
|
||||||
@@ -700,7 +708,7 @@ CollectionItem *CollectionModel::CreateContainerItem(const GroupBy group_by, con
|
|||||||
|
|
||||||
QString divider_key;
|
QString divider_key;
|
||||||
if (options_active_.show_dividers && container_level == 0) {
|
if (options_active_.show_dividers && container_level == 0) {
|
||||||
divider_key = DividerKey(group_by, song, SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums));
|
divider_key = DividerKey(group_by, song, SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums, options_active_.use_sort_tags));
|
||||||
if (!divider_key.isEmpty()) {
|
if (!divider_key.isEmpty()) {
|
||||||
if (!divider_nodes_.contains(divider_key)) {
|
if (!divider_nodes_.contains(divider_key)) {
|
||||||
CreateDividerItem(divider_key, DividerDisplayText(group_by, divider_key), parent);
|
CreateDividerItem(divider_key, DividerDisplayText(group_by, divider_key), parent);
|
||||||
@@ -714,7 +722,7 @@ CollectionItem *CollectionModel::CreateContainerItem(const GroupBy group_by, con
|
|||||||
item->container_level = container_level;
|
item->container_level = container_level;
|
||||||
item->container_key = container_key;
|
item->container_key = container_key;
|
||||||
item->display_text = DisplayText(group_by, song);
|
item->display_text = DisplayText(group_by, song);
|
||||||
item->sort_text = SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums);
|
item->sort_text = SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums, options_active_.use_sort_tags);
|
||||||
if (!divider_key.isEmpty()) {
|
if (!divider_key.isEmpty()) {
|
||||||
item->sort_text.prepend(divider_key + QLatin1Char(' '));
|
item->sort_text.prepend(divider_key + QLatin1Char(' '));
|
||||||
}
|
}
|
||||||
@@ -1069,37 +1077,37 @@ QString CollectionModel::PrettyFormat(const Song &song) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionModel::SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums) {
|
QString CollectionModel::SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums, const bool use_sort_tags) {
|
||||||
|
|
||||||
switch (group_by) {
|
switch (group_by) {
|
||||||
case GroupBy::AlbumArtist:
|
case GroupBy::AlbumArtist:
|
||||||
return SortTextForName(song.effective_albumartistsort(), sort_skip_articles_for_artists);
|
return SortTextForName(use_sort_tags ? song.effective_albumartistsort() : song.effective_albumartist(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Artist:
|
case GroupBy::Artist:
|
||||||
return SortTextForName(song.effective_artistsort(), sort_skip_articles_for_artists);
|
return SortTextForName(use_sort_tags ? song.effective_artistsort() : song.artist(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Album:
|
case GroupBy::Album:
|
||||||
return SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums);
|
return SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
|
||||||
case GroupBy::AlbumDisc:
|
case GroupBy::AlbumDisc:
|
||||||
return SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
return SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
||||||
case GroupBy::YearAlbum:
|
case GroupBy::YearAlbum:
|
||||||
return SortTextForNumber(std::max(0, song.year())) + song.grouping() + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums);
|
return SortTextForYear(song.year()) + song.grouping() + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
|
||||||
case GroupBy::YearAlbumDisc:
|
case GroupBy::YearAlbumDisc:
|
||||||
return SortTextForNumber(std::max(0, song.year())) + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
return SortTextForYear(song.year()) + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
||||||
case GroupBy::OriginalYearAlbum:
|
case GroupBy::OriginalYearAlbum:
|
||||||
return SortTextForNumber(std::max(0, song.effective_originalyear())) + song.grouping() + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums);
|
return SortTextForYear(song.effective_originalyear()) + song.grouping() + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
|
||||||
case GroupBy::OriginalYearAlbumDisc:
|
case GroupBy::OriginalYearAlbumDisc:
|
||||||
return SortTextForNumber(std::max(0, song.effective_originalyear())) + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
return SortTextForYear(song.effective_originalyear()) + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
|
||||||
case GroupBy::Disc:
|
case GroupBy::Disc:
|
||||||
return SortTextForNumber(std::max(0, song.disc()));
|
return SortTextForNumber(std::max(0, song.disc()));
|
||||||
case GroupBy::Year:
|
case GroupBy::Year:
|
||||||
return SortTextForNumber(std::max(0, song.year())) + QLatin1Char(' ');
|
return SortTextForYear(song.year()) + QLatin1Char(' ');
|
||||||
case GroupBy::OriginalYear:
|
case GroupBy::OriginalYear:
|
||||||
return SortTextForNumber(std::max(0, song.effective_originalyear())) + QLatin1Char(' ');
|
return SortTextForYear(song.effective_originalyear()) + QLatin1Char(' ');
|
||||||
case GroupBy::Genre:
|
case GroupBy::Genre:
|
||||||
return SortText(song.genre());
|
return SortText(song.genre());
|
||||||
case GroupBy::Composer:
|
case GroupBy::Composer:
|
||||||
return SortTextForName(song.effective_composersort(), sort_skip_articles_for_artists);
|
return SortTextForName(use_sort_tags ? song.effective_composersort() : song.composer(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Performer:
|
case GroupBy::Performer:
|
||||||
return SortTextForName(song.effective_performersort(), sort_skip_articles_for_artists);
|
return SortTextForName(use_sort_tags ? song.effective_performersort() : song.performer(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Grouping:
|
case GroupBy::Grouping:
|
||||||
return SortText(song.grouping());
|
return SortText(song.grouping());
|
||||||
case GroupBy::FileType:
|
case GroupBy::FileType:
|
||||||
@@ -1157,14 +1165,14 @@ QString CollectionModel::SortTextForSong(const Song &song) {
|
|||||||
|
|
||||||
QString CollectionModel::SortTextForYear(const int year) {
|
QString CollectionModel::SortTextForYear(const int year) {
|
||||||
|
|
||||||
QString str = QString::number(year);
|
const QString str = QString::number(std::max(year, 0));
|
||||||
return QStringLiteral("0").repeated(qMax(0, 4 - str.length())) + str;
|
return QStringLiteral("0").repeated(qMax(0, 4 - str.length())) + str;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionModel::SortTextForBitrate(const int bitrate) {
|
QString CollectionModel::SortTextForBitrate(const int bitrate) {
|
||||||
|
|
||||||
QString str = QString::number(bitrate);
|
const QString str = QString::number(bitrate);
|
||||||
return QStringLiteral("0").repeated(qMax(0, 3 - str.length())) + str;
|
return QStringLiteral("0").repeated(qMax(0, 3 - str.length())) + str;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1213,27 +1221,30 @@ QString CollectionModel::ContainerKey(const GroupBy group_by, const Song &song,
|
|||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy::AlbumDisc:
|
case GroupBy::AlbumDisc:
|
||||||
key = PrettyAlbumDisc(song.album(), song.disc());
|
key = TextOrUnknown(song.album());
|
||||||
|
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
|
||||||
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy::YearAlbum:
|
case GroupBy::YearAlbum:
|
||||||
key = PrettyYearAlbum(song.year(), song.album());
|
key = SortTextForYear(song.year()) + QLatin1Char('-') + TextOrUnknown(song.album());
|
||||||
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy::YearAlbumDisc:
|
case GroupBy::YearAlbumDisc:
|
||||||
key = PrettyYearAlbumDisc(song.year(), song.album(), song.disc());
|
key = SortTextForYear(song.year()) + QLatin1Char('-') + TextOrUnknown(song.album());
|
||||||
|
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
|
||||||
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy::OriginalYearAlbum:
|
case GroupBy::OriginalYearAlbum:
|
||||||
key = PrettyYearAlbum(song.effective_originalyear(), song.album());
|
key = SortTextForYear(song.effective_originalyear()) + QLatin1Char('-') + TextOrUnknown(song.album());
|
||||||
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
case GroupBy::OriginalYearAlbumDisc:
|
case GroupBy::OriginalYearAlbumDisc:
|
||||||
key = PrettyYearAlbumDisc(song.effective_originalyear(), song.album(), song.disc());
|
key = SortTextForYear(song.effective_originalyear()) + QLatin1Char('-') + TextOrUnknown(song.album());
|
||||||
|
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
|
||||||
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
|
||||||
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
|
||||||
break;
|
break;
|
||||||
@@ -1241,10 +1252,10 @@ QString CollectionModel::ContainerKey(const GroupBy group_by, const Song &song,
|
|||||||
key = PrettyDisc(song.disc());
|
key = PrettyDisc(song.disc());
|
||||||
break;
|
break;
|
||||||
case GroupBy::Year:
|
case GroupBy::Year:
|
||||||
key = QString::number(std::max(0, song.year()));
|
key = SortTextForYear(song.year());
|
||||||
break;
|
break;
|
||||||
case GroupBy::OriginalYear:
|
case GroupBy::OriginalYear:
|
||||||
key = QString::number(std::max(0, song.effective_originalyear()));
|
key = SortTextForYear(song.effective_originalyear());
|
||||||
break;
|
break;
|
||||||
case GroupBy::Genre:
|
case GroupBy::Genre:
|
||||||
key = TextOrUnknown(song.genre());
|
key = TextOrUnknown(song.genre());
|
||||||
@@ -1336,7 +1347,7 @@ QString CollectionModel::DividerKey(const GroupBy group_by, const Song &song, co
|
|||||||
case GroupBy::Bitdepth:
|
case GroupBy::Bitdepth:
|
||||||
return SortTextForNumber(song.bitdepth());
|
return SortTextForNumber(song.bitdepth());
|
||||||
case GroupBy::Bitrate:
|
case GroupBy::Bitrate:
|
||||||
return SortTextForNumber(song.bitrate());
|
return SortTextForBitrate(song.bitrate());
|
||||||
case GroupBy::None:
|
case GroupBy::None:
|
||||||
case GroupBy::GroupByCount:
|
case GroupBy::GroupByCount:
|
||||||
return QString();
|
return QString();
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
show_various_artists(true),
|
show_various_artists(true),
|
||||||
sort_skip_articles_for_artists(false),
|
sort_skip_articles_for_artists(false),
|
||||||
sort_skip_articles_for_albums(false),
|
sort_skip_articles_for_albums(false),
|
||||||
|
use_sort_tags(true),
|
||||||
separate_albums_by_grouping(false) {}
|
separate_albums_by_grouping(false) {}
|
||||||
|
|
||||||
Grouping group_by;
|
Grouping group_by;
|
||||||
@@ -139,6 +140,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
bool show_various_artists;
|
bool show_various_artists;
|
||||||
bool sort_skip_articles_for_artists;
|
bool sort_skip_articles_for_artists;
|
||||||
bool sort_skip_articles_for_albums;
|
bool sort_skip_articles_for_albums;
|
||||||
|
bool use_sort_tags;
|
||||||
bool separate_albums_by_grouping;
|
bool separate_albums_by_grouping;
|
||||||
CollectionFilterOptions filter_options;
|
CollectionFilterOptions filter_options;
|
||||||
};
|
};
|
||||||
@@ -178,14 +180,14 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
// Utility functions for manipulating text
|
// Utility functions for manipulating text
|
||||||
QString DisplayText(const GroupBy group_by, const Song &song);
|
static QString DisplayText(const GroupBy group_by, const Song &song);
|
||||||
static QString TextOrUnknown(const QString &text);
|
static QString TextOrUnknown(const QString &text);
|
||||||
static QString PrettyYearAlbum(const int year, const QString &album);
|
static QString PrettyYearAlbum(const int year, const QString &album);
|
||||||
static QString PrettyAlbumDisc(const QString &album, const int disc);
|
static QString PrettyAlbumDisc(const QString &album, const int disc);
|
||||||
static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc);
|
static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc);
|
||||||
static QString PrettyDisc(const int disc);
|
static QString PrettyDisc(const int disc);
|
||||||
static QString PrettyFormat(const Song &song);
|
static QString PrettyFormat(const Song &song);
|
||||||
QString SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums);
|
static QString SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums, const bool use_sort_tags);
|
||||||
static QString SortText(QString text);
|
static QString SortText(QString text);
|
||||||
static QString SortTextForName(const QString &name, const bool sort_skip_articles);
|
static QString SortTextForName(const QString &name, const bool sort_skip_articles);
|
||||||
static QString SortTextForNumber(const int number);
|
static QString SortTextForNumber(const int number);
|
||||||
|
|||||||
@@ -58,4 +58,3 @@ class CollectionPlaylistItem : public PlaylistItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONPLAYLISTITEM_H
|
#endif // COLLECTIONPLAYLISTITEM_H
|
||||||
|
|
||||||
|
|||||||
@@ -706,8 +706,14 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
qLog(Debug) << file << "is missing EBU R 128 loudness characteristics.";
|
qLog(Debug) << file << "is missing EBU R 128 loudness characteristics.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the song is unavailable and nothing has changed, just mark it as available without re-scanning
|
||||||
|
// For CUE files with multiple sections, all sections share the same file and would have the same availability status
|
||||||
|
if (matching_song.unavailable() && !changed && !missing_fingerprint && !missing_loudness_characteristics) {
|
||||||
|
qLog(Debug) << "Unavailable song" << file << "restored without re-scanning.";
|
||||||
|
t->readded_songs << matching_songs;
|
||||||
|
}
|
||||||
// The song's changed or missing fingerprint - create fingerprint and reread the metadata from file.
|
// The song's changed or missing fingerprint - create fingerprint and reread the metadata from file.
|
||||||
if (t->ignores_mtime() || changed || missing_fingerprint || missing_loudness_characteristics) {
|
else if (t->ignores_mtime() || changed || missing_fingerprint || missing_loudness_characteristics) {
|
||||||
|
|
||||||
QString fingerprint;
|
QString fingerprint;
|
||||||
#ifdef HAVE_SONGFINGERPRINTING
|
#ifdef HAVE_SONGFINGERPRINTING
|
||||||
@@ -728,12 +734,6 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing has changed - mark the song available without re-scanning
|
|
||||||
else if (matching_song.unavailable()) {
|
|
||||||
qLog(Debug) << "Unavailable song" << file << "restored.";
|
|
||||||
t->readded_songs << matching_songs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else { // Search the DB by fingerprint.
|
else { // Search the DB by fingerprint.
|
||||||
QString fingerprint;
|
QString fingerprint;
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class CollectionWatcher : public QObject {
|
|||||||
QStringList files_changed_path_;
|
QStringList files_changed_path_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScanTransaction &operator=(const ScanTransaction&) { return *this; }
|
ScanTransaction &operator=(const ScanTransaction &transaction) { Q_UNUSED(transaction); return *this; }
|
||||||
|
|
||||||
int task_id_;
|
int task_id_;
|
||||||
quint64 progress_;
|
quint64 progress_;
|
||||||
@@ -261,7 +261,6 @@ class CollectionWatcher : public QObject {
|
|||||||
static QStringList sValidImages;
|
static QStringList sValidImages;
|
||||||
|
|
||||||
qint64 last_scan_time_;
|
qint64 last_scan_time_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) {
|
inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) {
|
||||||
|
|||||||
@@ -48,4 +48,7 @@
|
|||||||
|
|
||||||
#cmakedefine ENABLE_WIN32_CONSOLE
|
#cmakedefine ENABLE_WIN32_CONSOLE
|
||||||
|
|
||||||
|
#cmakedefine HAVE_VISUALIZATIONS
|
||||||
|
#cmakedefine HAVE_PROJECTM4
|
||||||
|
|
||||||
#endif // CONFIG_H_IN
|
#endif // CONFIG_H_IN
|
||||||
|
|||||||
@@ -70,6 +70,6 @@ enum class BackgroundImagePosition {
|
|||||||
BottomRight = 5
|
BottomRight = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace AppearanceSettings
|
||||||
|
|
||||||
#endif // APPEARANCESETTINGS_H
|
#endif // APPEARANCESETTINGS_H
|
||||||
|
|||||||
@@ -63,6 +63,6 @@ constexpr qint64 kDefaultBufferDuration = 4000;
|
|||||||
constexpr double kDefaultBufferLowWatermark = 0.33;
|
constexpr double kDefaultBufferLowWatermark = 0.33;
|
||||||
constexpr double kDefaultBufferHighWatermark = 0.99;
|
constexpr double kDefaultBufferHighWatermark = 0.99;
|
||||||
|
|
||||||
} // namespace
|
} // namespace BackendSettings
|
||||||
|
|
||||||
#endif // BACKENDSETTINGS_H
|
#endif // BACKENDSETTINGS_H
|
||||||
|
|||||||
@@ -71,6 +71,6 @@ constexpr char kDoubleClickPlaylistAddMode[] = "doubleclick_playlist_addmode";
|
|||||||
constexpr char kSeekStepSec[] = "seek_step_sec";
|
constexpr char kSeekStepSec[] = "seek_step_sec";
|
||||||
constexpr char kVolumeIncrement[] = "volume_increment";
|
constexpr char kVolumeIncrement[] = "volume_increment";
|
||||||
|
|
||||||
} // namespace
|
} // namespace BehaviourSettings
|
||||||
|
|
||||||
#endif // BEHAVIOURSETTINGS_H
|
#endif // BEHAVIOURSETTINGS_H
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ constexpr char kPrettyCovers[] = "pretty_covers";
|
|||||||
constexpr char kVariousArtists[] = "various_artists";
|
constexpr char kVariousArtists[] = "various_artists";
|
||||||
constexpr char kSkipArticlesForArtists[] = "skip_articles_for_artists";
|
constexpr char kSkipArticlesForArtists[] = "skip_articles_for_artists";
|
||||||
constexpr char kSkipArticlesForAlbums[] = "skip_articles_for_albums";
|
constexpr char kSkipArticlesForAlbums[] = "skip_articles_for_albums";
|
||||||
constexpr char kShowSortText[] = "show_sort_text";
|
constexpr char kUseSortTags[] = "use_short_tags";
|
||||||
constexpr char kSettingsCacheSize[] = "cache_size";
|
constexpr char kSettingsCacheSize[] = "cache_size";
|
||||||
constexpr char kSettingsCacheSizeUnit[] = "cache_size_unit";
|
constexpr char kSettingsCacheSizeUnit[] = "cache_size_unit";
|
||||||
constexpr char kSettingsDiskCacheEnable[] = "disk_cache_enable";
|
constexpr char kSettingsDiskCacheEnable[] = "disk_cache_enable";
|
||||||
@@ -59,6 +59,6 @@ enum class CacheSizeUnit {
|
|||||||
TB
|
TB
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace CollectionSettings
|
||||||
|
|
||||||
#endif // COLLECTIONSETTINGS_H
|
#endif // COLLECTIONSETTINGS_H
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ constexpr char kSettingsSummaryFmt[] = "SummaryFmt";
|
|||||||
constexpr char kDefaultFontFamily[] = "Noto Sans";
|
constexpr char kDefaultFontFamily[] = "Noto Sans";
|
||||||
constexpr qreal kDefaultFontSizeHeadline = 11;
|
constexpr qreal kDefaultFontSizeHeadline = 11;
|
||||||
|
|
||||||
} // namespace
|
} // namespace ContextSettings
|
||||||
|
|
||||||
#endif // CONTEXTSETTINGS_H
|
#endif // CONTEXTSETTINGS_H
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ constexpr char kSaveOverwrite[] = "save_overwrite";
|
|||||||
constexpr char kSaveLowercase[] = "save_lowercase";
|
constexpr char kSaveLowercase[] = "save_lowercase";
|
||||||
constexpr char kSaveReplaceSpaces[] = "save_replace_spaces";
|
constexpr char kSaveReplaceSpaces[] = "save_replace_spaces";
|
||||||
|
|
||||||
} // namespace
|
} // namespace CoversSettings
|
||||||
|
|
||||||
#endif // COVERSSETTINGS_H
|
#endif // COVERSSETTINGS_H
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ constexpr char kAllFilesFilterSpec[] = QT_TRANSLATE_NOOP("FileFilter", "All File
|
|||||||
constexpr char kFileFilter[] =
|
constexpr char kFileFilter[] =
|
||||||
"*.wav *.flac *.wv *.ogg *.oga *.opus *.spx *.ape *.mpc "
|
"*.wav *.flac *.wv *.ogg *.oga *.opus *.spx *.ape *.mpc "
|
||||||
"*.mp2 *.mp3 *.m4a *.mp4 *.aac *.asf *.asx *.wma "
|
"*.mp2 *.mp3 *.m4a *.mp4 *.aac *.asf *.asx *.wma "
|
||||||
"*.aif *.aiff *.mka *.tta *.dsf *.dsd "
|
"*.aif *.aiff *.mka *.tta *.dsf *.dsd *.webm "
|
||||||
"*.cue *.m3u *.m3u8 *.pls *.xspf *.asxini "
|
"*.cue *.m3u *.m3u8 *.pls *.xspf *.asxini "
|
||||||
"*.ac3 *.dts "
|
"*.ac3 *.dts "
|
||||||
"*.mod *.s3m *.xm *.it "
|
"*.mod *.s3m *.xm *.it "
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ constexpr char kSettingsGroup[] = "GlobalShortcuts";
|
|||||||
constexpr char kUseKGlobalAccel[] = "use_kglobalaccel";
|
constexpr char kUseKGlobalAccel[] = "use_kglobalaccel";
|
||||||
constexpr char kUseX11[] = "use_x11";
|
constexpr char kUseX11[] = "use_x11";
|
||||||
|
|
||||||
} // namespace
|
} // namespace GlobalShortcutsSettings
|
||||||
|
|
||||||
#endif // GLOBALSHORTCUTSSETTINGS_H
|
#endif // GLOBALSHORTCUTSSETTINGS_H
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ namespace LyricsSettings {
|
|||||||
constexpr char kSettingsGroup[] = "Lyrics";
|
constexpr char kSettingsGroup[] = "Lyrics";
|
||||||
constexpr char kProviders[] = "providers";
|
constexpr char kProviders[] = "providers";
|
||||||
|
|
||||||
} // namespace
|
} // namespace LyricsSettings
|
||||||
|
|
||||||
#endif // LYRICSSETTINGS_H
|
#endif // LYRICSSETTINGS_H
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ constexpr char kGeometry[] = "geometry";
|
|||||||
constexpr char kSplitterState[] = "splitter_state";
|
constexpr char kSplitterState[] = "splitter_state";
|
||||||
constexpr char kDoNotShowSponsorMessage[] = "do_not_show_sponsor_message";
|
constexpr char kDoNotShowSponsorMessage[] = "do_not_show_sponsor_message";
|
||||||
|
|
||||||
} // namespace
|
} // namespace MainWindowSettings
|
||||||
|
|
||||||
#endif // MAINWINDOWSETTINGS_H
|
#endif // MAINWINDOWSETTINGS_H
|
||||||
|
|||||||
@@ -38,6 +38,6 @@ constexpr char kShow[] = "show";
|
|||||||
constexpr char kStyle[] = "style";
|
constexpr char kStyle[] = "style";
|
||||||
constexpr char kSave[] = "save";
|
constexpr char kSave[] = "save";
|
||||||
|
|
||||||
} // namespace
|
} // namespace MoodbarSettings
|
||||||
|
|
||||||
#endif // MOODBARSETTINGS_H
|
#endif // MOODBARSETTINGS_H
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ constexpr char kUsername[] = "username";
|
|||||||
constexpr char kPassword[] = "password";
|
constexpr char kPassword[] = "password";
|
||||||
constexpr char kEngine[] = "engine";
|
constexpr char kEngine[] = "engine";
|
||||||
|
|
||||||
} // namespace
|
} // namespace NetworkProxySettings
|
||||||
|
|
||||||
#endif // NETWORKPROXYSETTINGS_H
|
#endif // NETWORKPROXYSETTINGS_H
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ constexpr char kCustomTextEnabled[] = "CustomTextEnabled";
|
|||||||
constexpr char kCustomText1[] = "CustomText1";
|
constexpr char kCustomText1[] = "CustomText1";
|
||||||
constexpr char kCustomText2[] = "CustomText2";
|
constexpr char kCustomText2[] = "CustomText2";
|
||||||
|
|
||||||
} // namespace
|
} // namespace OSDSettings
|
||||||
|
|
||||||
namespace OSDPrettySettings {
|
namespace OSDPrettySettings {
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ constexpr char kFading[] = "fading";
|
|||||||
constexpr QRgb kPresetBlue = qRgb(102, 150, 227);
|
constexpr QRgb kPresetBlue = qRgb(102, 150, 227);
|
||||||
constexpr QRgb kPresetRed = qRgb(202, 22, 16);
|
constexpr QRgb kPresetRed = qRgb(202, 22, 16);
|
||||||
|
|
||||||
} // namespace
|
} // namespace OSDPrettySettings
|
||||||
|
|
||||||
namespace DiscordRPCSettings {
|
namespace DiscordRPCSettings {
|
||||||
|
|
||||||
@@ -71,6 +71,14 @@ constexpr char kSettingsGroup[] = "DiscordRPC";
|
|||||||
|
|
||||||
constexpr char kEnabled[] = "enabled";
|
constexpr char kEnabled[] = "enabled";
|
||||||
|
|
||||||
} // namespace
|
constexpr char kStatusDisplayType[] = "StatusDisplayType";
|
||||||
|
|
||||||
|
enum class StatusDisplayType {
|
||||||
|
App = 0,
|
||||||
|
Artist,
|
||||||
|
Song
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace DiscordRPCSettings
|
||||||
|
|
||||||
#endif // NOTIFICATIONSSETTINGS_H
|
#endif // NOTIFICATIONSSETTINGS_H
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ constexpr char kLastSaveExtension[] = "last_save_extension";
|
|||||||
constexpr char kLastSaveAllPath[] = "last_save_all_path";
|
constexpr char kLastSaveAllPath[] = "last_save_all_path";
|
||||||
constexpr char kLastSaveAllExtension[] = "last_save_all_extension";
|
constexpr char kLastSaveAllExtension[] = "last_save_all_extension";
|
||||||
|
|
||||||
} // namespace
|
} // namespace PlaylistSettings
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(PlaylistSettings::PathType)
|
Q_DECLARE_METATYPE(PlaylistSettings::PathType)
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ constexpr char kAlbumsSearchLimit[] = "albumssearchlimit";
|
|||||||
constexpr char kSongsSearchLimit[] = "songssearchlimit";
|
constexpr char kSongsSearchLimit[] = "songssearchlimit";
|
||||||
constexpr char kBase64Secret[] = "base64secret";
|
constexpr char kBase64Secret[] = "base64secret";
|
||||||
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
||||||
|
constexpr char kRemoveRemastered[] = "remove_remastered";
|
||||||
|
|
||||||
constexpr char kUserId[] = "user_id";
|
constexpr char kUserId[] = "user_id";
|
||||||
constexpr char kCredentialsId[] = "credentials_id";
|
constexpr char kCredentialsId[] = "credentials_id";
|
||||||
constexpr char kDeviceId[] = "device_id";
|
constexpr char kDeviceId[] = "device_id";
|
||||||
constexpr char kUserAuthToken[] = "user_auth_token";
|
constexpr char kUserAuthToken[] = "user_auth_token";
|
||||||
|
|
||||||
} // namespace
|
} // namespace QobuzSettings
|
||||||
|
|
||||||
#endif // QOBUZSETTINGS_H
|
#endif // QOBUZSETTINGS_H
|
||||||
|
|||||||
@@ -35,6 +35,6 @@ constexpr char kStripRemastered[] = "strip_remastered";
|
|||||||
constexpr char kSources[] = "sources";
|
constexpr char kSources[] = "sources";
|
||||||
constexpr char kUserToken[] = "user_token";
|
constexpr char kUserToken[] = "user_token";
|
||||||
|
|
||||||
} // namespace
|
} // namespace ScrobblerSettings
|
||||||
|
|
||||||
#endif // SCROBBLERSETTINGS_H
|
#endif // SCROBBLERSETTINGS_H
|
||||||
|
|||||||
@@ -31,12 +31,13 @@ constexpr char kAlbumsSearchLimit[] = "albumssearchlimit";
|
|||||||
constexpr char kSongsSearchLimit[] = "songssearchlimit";
|
constexpr char kSongsSearchLimit[] = "songssearchlimit";
|
||||||
constexpr char kFetchAlbums[] = "fetchalbums";
|
constexpr char kFetchAlbums[] = "fetchalbums";
|
||||||
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
||||||
|
constexpr char kRemoveRemastered[] = "remove_remastered";
|
||||||
|
|
||||||
constexpr char kAccessToken[] = "access_token";
|
constexpr char kAccessToken[] = "access_token";
|
||||||
constexpr char kRefreshToken[] = "refresh_token";
|
constexpr char kRefreshToken[] = "refresh_token";
|
||||||
constexpr char kExpiresIn[] = "expires_in";
|
constexpr char kExpiresIn[] = "expires_in";
|
||||||
constexpr char kLoginTime[] = "login_time";
|
constexpr char kLoginTime[] = "login_time";
|
||||||
|
|
||||||
} // namespace
|
} // namespace SpotifySettings
|
||||||
|
|
||||||
#endif // SPOTIFYSETTINGS_H
|
#endif // SPOTIFYSETTINGS_H
|
||||||
|
|||||||
@@ -41,6 +41,6 @@ constexpr char kUseAlbumIdForAlbumCovers[] = "usealbumidforalbumcovers";
|
|||||||
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
|
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
|
||||||
constexpr char kAuthMethod[] = "authmethod";
|
constexpr char kAuthMethod[] = "authmethod";
|
||||||
|
|
||||||
} // namespace
|
} // namespace SubsonicSettings
|
||||||
|
|
||||||
#endif // SUBSONICETTINGS_H
|
#endif // SUBSONICETTINGS_H
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
|
|||||||
constexpr char kCoverSize[] = "coversize";
|
constexpr char kCoverSize[] = "coversize";
|
||||||
constexpr char kStreamUrl[] = "streamurl";
|
constexpr char kStreamUrl[] = "streamurl";
|
||||||
constexpr char kAlbumExplicit[] = "album_explicit";
|
constexpr char kAlbumExplicit[] = "album_explicit";
|
||||||
|
constexpr char kRemoveRemastered[] = "remove_remastered";
|
||||||
|
|
||||||
enum class StreamUrlMethod {
|
enum class StreamUrlMethod {
|
||||||
StreamUrl,
|
StreamUrl,
|
||||||
@@ -47,6 +48,6 @@ enum class StreamUrlMethod {
|
|||||||
PlaybackInfoPostPaywall
|
PlaybackInfoPostPaywall
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace TidalSettings
|
||||||
|
|
||||||
#endif // TIDALSETTINGS_H
|
#endif // TIDALSETTINGS_H
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ constexpr char kSettingsGroup[] = "Transcoder";
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // TRANSCODERSETTINGS_H
|
#endif // TRANSCODERSETTINGS_H
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class ContextAlbum : public QWidget {
|
|||||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct PreviousCover {
|
struct PreviousCover {
|
||||||
explicit PreviousCover() : opacity(0.0) {}
|
explicit PreviousCover() : opacity(0.0) {}
|
||||||
QImage image;
|
QImage image;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2013-2022, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2013-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
#include "covermanager/albumcoverchoicecontroller.h"
|
#include "covermanager/albumcoverchoicecontroller.h"
|
||||||
#include "lyrics/lyricsfetcher.h"
|
#include "lyrics/lyricsfetcher.h"
|
||||||
#include "constants/contextsettings.h"
|
#include "constants/contextsettings.h"
|
||||||
|
#include "constants/timeconstants.h"
|
||||||
|
|
||||||
#include "contextview.h"
|
#include "contextview.h"
|
||||||
#include "contextalbum.h"
|
#include "contextalbum.h"
|
||||||
@@ -100,15 +101,11 @@ ContextView::ContextView(QWidget *parent)
|
|||||||
label_samplerate_title_(new QLabel(this)),
|
label_samplerate_title_(new QLabel(this)),
|
||||||
label_bitdepth_title_(new QLabel(this)),
|
label_bitdepth_title_(new QLabel(this)),
|
||||||
label_bitrate_title_(new QLabel(this)),
|
label_bitrate_title_(new QLabel(this)),
|
||||||
label_ebur128_integrated_loudness_title_(new QLabel(this)),
|
|
||||||
label_ebur128_loudness_range_title_(new QLabel(this)),
|
|
||||||
label_filetype_(new QLabel(this)),
|
label_filetype_(new QLabel(this)),
|
||||||
label_length_(new QLabel(this)),
|
label_length_(new QLabel(this)),
|
||||||
label_samplerate_(new QLabel(this)),
|
label_samplerate_(new QLabel(this)),
|
||||||
label_bitdepth_(new QLabel(this)),
|
label_bitdepth_(new QLabel(this)),
|
||||||
label_bitrate_(new QLabel(this)),
|
label_bitrate_(new QLabel(this)),
|
||||||
label_ebur128_integrated_loudness_(new QLabel(this)),
|
|
||||||
label_ebur128_loudness_range_(new QLabel(this)),
|
|
||||||
lyrics_tried_(false),
|
lyrics_tried_(false),
|
||||||
lyrics_id_(-1) {
|
lyrics_id_(-1) {
|
||||||
|
|
||||||
@@ -166,24 +163,18 @@ ContextView::ContextView(QWidget *parent)
|
|||||||
label_samplerate_title_->setText(tr("Samplerate"));
|
label_samplerate_title_->setText(tr("Samplerate"));
|
||||||
label_bitdepth_title_->setText(tr("Bit depth"));
|
label_bitdepth_title_->setText(tr("Bit depth"));
|
||||||
label_bitrate_title_->setText(tr("Bitrate"));
|
label_bitrate_title_->setText(tr("Bitrate"));
|
||||||
label_ebur128_integrated_loudness_title_->setText(tr("EBU R 128 Integrated Loudness"));
|
|
||||||
label_ebur128_loudness_range_title_->setText(tr("EBU R 128 Loudness Range"));
|
|
||||||
|
|
||||||
label_filetype_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
label_filetype_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
label_length_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
label_length_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
label_samplerate_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
label_samplerate_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
label_bitdepth_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
label_bitdepth_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
label_bitrate_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
label_bitrate_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
label_ebur128_integrated_loudness_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
||||||
label_ebur128_loudness_range_title_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
||||||
|
|
||||||
label_filetype_->setWordWrap(true);
|
label_filetype_->setWordWrap(true);
|
||||||
label_length_->setWordWrap(true);
|
label_length_->setWordWrap(true);
|
||||||
label_samplerate_->setWordWrap(true);
|
label_samplerate_->setWordWrap(true);
|
||||||
label_bitdepth_->setWordWrap(true);
|
label_bitdepth_->setWordWrap(true);
|
||||||
label_bitrate_->setWordWrap(true);
|
label_bitrate_->setWordWrap(true);
|
||||||
label_ebur128_integrated_loudness_->setWordWrap(true);
|
|
||||||
label_ebur128_loudness_range_->setWordWrap(true);
|
|
||||||
|
|
||||||
layout_play_data_->setContentsMargins(0, 0, 0, 0);
|
layout_play_data_->setContentsMargins(0, 0, 0, 0);
|
||||||
layout_play_data_->addWidget(label_filetype_title_, 0, 0);
|
layout_play_data_->addWidget(label_filetype_title_, 0, 0);
|
||||||
@@ -197,11 +188,6 @@ ContextView::ContextView(QWidget *parent)
|
|||||||
layout_play_data_->addWidget(label_bitrate_title_, 4, 0);
|
layout_play_data_->addWidget(label_bitrate_title_, 4, 0);
|
||||||
layout_play_data_->addWidget(label_bitrate_, 4, 1);
|
layout_play_data_->addWidget(label_bitrate_, 4, 1);
|
||||||
|
|
||||||
layout_play_data_->addWidget(label_ebur128_integrated_loudness_title_, 5, 0);
|
|
||||||
layout_play_data_->addWidget(label_ebur128_integrated_loudness_, 5, 1);
|
|
||||||
layout_play_data_->addWidget(label_ebur128_loudness_range_title_, 6, 0);
|
|
||||||
layout_play_data_->addWidget(label_ebur128_loudness_range_, 6, 1);
|
|
||||||
|
|
||||||
widget_play_data_->setLayout(layout_play_data_);
|
widget_play_data_->setLayout(layout_play_data_);
|
||||||
|
|
||||||
textedit_play_lyrics_->setReadOnly(true);
|
textedit_play_lyrics_->setReadOnly(true);
|
||||||
@@ -218,17 +204,13 @@ ContextView::ContextView(QWidget *parent)
|
|||||||
<< label_length_title_
|
<< label_length_title_
|
||||||
<< label_samplerate_title_
|
<< label_samplerate_title_
|
||||||
<< label_bitdepth_title_
|
<< label_bitdepth_title_
|
||||||
<< label_bitrate_title_
|
<< label_bitrate_title_;
|
||||||
<< label_ebur128_integrated_loudness_title_
|
|
||||||
<< label_ebur128_loudness_range_title_;
|
|
||||||
|
|
||||||
labels_play_data_ << label_filetype_
|
labels_play_data_ << label_filetype_
|
||||||
<< label_length_
|
<< label_length_
|
||||||
<< label_samplerate_
|
<< label_samplerate_
|
||||||
<< label_bitdepth_
|
<< label_bitdepth_
|
||||||
<< label_bitrate_
|
<< label_bitrate_;
|
||||||
<< label_ebur128_integrated_loudness_
|
|
||||||
<< label_ebur128_loudness_range_;
|
|
||||||
|
|
||||||
labels_play_all_ = labels_play_ << labels_play_data_;
|
labels_play_all_ = labels_play_ << labels_play_data_;
|
||||||
|
|
||||||
@@ -372,7 +354,7 @@ void ContextView::SearchLyrics() {
|
|||||||
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked() && action_search_lyrics_->isChecked() && !song_playing_.artist().isEmpty() && !song_playing_.title().isEmpty() && !lyrics_tried_ && lyrics_id_ == -1) {
|
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked() && action_search_lyrics_->isChecked() && !song_playing_.artist().isEmpty() && !song_playing_.title().isEmpty() && !lyrics_tried_ && lyrics_id_ == -1) {
|
||||||
lyrics_fetcher_->Clear();
|
lyrics_fetcher_->Clear();
|
||||||
lyrics_tried_ = true;
|
lyrics_tried_ = true;
|
||||||
lyrics_id_ = static_cast<qint64>(lyrics_fetcher_->Search(song_playing_.effective_albumartist(), song_playing_.artist(), song_playing_.album(), song_playing_.title()));
|
lyrics_id_ = static_cast<qint64>(lyrics_fetcher_->Search(song_playing_.effective_albumartist(), song_playing_.artist(), song_playing_.album(), song_playing_.title(), song_playing_.length_nanosec() / kNsecPerSec));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -493,26 +475,6 @@ void ContextView::SetSong() {
|
|||||||
label_bitrate_->show();
|
label_bitrate_->show();
|
||||||
SetLabelText(label_bitrate_, song_playing_.bitrate(), tr("kbps"));
|
SetLabelText(label_bitrate_, song_playing_.bitrate(), tr("kbps"));
|
||||||
}
|
}
|
||||||
if (!song_playing_.ebur128_integrated_loudness_lufs()) {
|
|
||||||
label_ebur128_integrated_loudness_title_->hide();
|
|
||||||
label_ebur128_integrated_loudness_->hide();
|
|
||||||
label_ebur128_integrated_loudness_->clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
label_ebur128_integrated_loudness_title_->show();
|
|
||||||
label_ebur128_integrated_loudness_->show();
|
|
||||||
label_ebur128_integrated_loudness_->setText(song_playing_.Ebur128LoudnessLUFSToText());
|
|
||||||
}
|
|
||||||
if (!song_playing_.ebur128_loudness_range_lu()) {
|
|
||||||
label_ebur128_loudness_range_title_->hide();
|
|
||||||
label_ebur128_loudness_range_->hide();
|
|
||||||
label_ebur128_loudness_range_->clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
label_ebur128_loudness_range_title_->show();
|
|
||||||
label_ebur128_loudness_range_->show();
|
|
||||||
label_ebur128_loudness_range_->setText(song_playing_.Ebur128LoudnessRangeLUToText());
|
|
||||||
}
|
|
||||||
spacer_play_data_->changeSize(20, 20, QSizePolicy::Fixed);
|
spacer_play_data_->changeSize(20, 20, QSizePolicy::Fixed);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -522,8 +484,6 @@ void ContextView::SetSong() {
|
|||||||
label_samplerate_->clear();
|
label_samplerate_->clear();
|
||||||
label_bitdepth_->clear();
|
label_bitdepth_->clear();
|
||||||
label_bitrate_->clear();
|
label_bitrate_->clear();
|
||||||
label_ebur128_integrated_loudness_->clear();
|
|
||||||
label_ebur128_loudness_range_->clear();
|
|
||||||
spacer_play_data_->changeSize(0, 0, QSizePolicy::Fixed);
|
spacer_play_data_->changeSize(0, 0, QSizePolicy::Fixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,12 +558,6 @@ void ContextView::UpdateSong(const Song &song) {
|
|||||||
SetLabelText(label_bitrate_, song.bitrate(), tr("kbps"));
|
SetLabelText(label_bitrate_, song.bitrate(), tr("kbps"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (song.ebur128_integrated_loudness_lufs() != song_playing_.ebur128_integrated_loudness_lufs()) {
|
|
||||||
label_ebur128_integrated_loudness_->setText(song_playing_.Ebur128LoudnessLUFSToText());
|
|
||||||
}
|
|
||||||
if (song.ebur128_loudness_range_lu() != song_playing_.ebur128_loudness_range_lu()) {
|
|
||||||
label_ebur128_loudness_range_->setText(song_playing_.Ebur128LoudnessRangeLUToText());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
song_playing_ = song;
|
song_playing_ = song;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2013-2022, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2013-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -65,9 +65,9 @@ class ContextView : public QWidget {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void contextMenuEvent(QContextMenuEvent*) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
void dragEnterEvent(QDragEnterEvent*) override;
|
void dragEnterEvent(QDragEnterEvent *e) override;
|
||||||
void dropEvent(QDropEvent*) override;
|
void dropEvent(QDropEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddActions();
|
void AddActions();
|
||||||
@@ -135,18 +135,12 @@ class ContextView : public QWidget {
|
|||||||
QLabel *label_bitdepth_title_;
|
QLabel *label_bitdepth_title_;
|
||||||
QLabel *label_bitrate_title_;
|
QLabel *label_bitrate_title_;
|
||||||
|
|
||||||
QLabel *label_ebur128_integrated_loudness_title_;
|
|
||||||
QLabel *label_ebur128_loudness_range_title_;
|
|
||||||
|
|
||||||
QLabel *label_filetype_;
|
QLabel *label_filetype_;
|
||||||
QLabel *label_length_;
|
QLabel *label_length_;
|
||||||
QLabel *label_samplerate_;
|
QLabel *label_samplerate_;
|
||||||
QLabel *label_bitdepth_;
|
QLabel *label_bitdepth_;
|
||||||
QLabel *label_bitrate_;
|
QLabel *label_bitrate_;
|
||||||
|
|
||||||
QLabel *label_ebur128_integrated_loudness_;
|
|
||||||
QLabel *label_ebur128_loudness_range_;
|
|
||||||
|
|
||||||
Song song_playing_;
|
Song song_playing_;
|
||||||
Song song_prev_;
|
Song song_prev_;
|
||||||
QImage image_original_;
|
QImage image_original_;
|
||||||
|
|||||||
@@ -74,10 +74,10 @@
|
|||||||
#include "lyrics/elyricsnetlyricsprovider.h"
|
#include "lyrics/elyricsnetlyricsprovider.h"
|
||||||
#include "lyrics/letraslyricsprovider.h"
|
#include "lyrics/letraslyricsprovider.h"
|
||||||
#include "lyrics/lyricfindlyricsprovider.h"
|
#include "lyrics/lyricfindlyricsprovider.h"
|
||||||
|
#include "lyrics/lrcliblyricsprovider.h"
|
||||||
|
|
||||||
#include "scrobbler/audioscrobbler.h"
|
#include "scrobbler/audioscrobbler.h"
|
||||||
#include "scrobbler/lastfmscrobbler.h"
|
#include "scrobbler/lastfmscrobbler.h"
|
||||||
#include "scrobbler/librefmscrobbler.h"
|
|
||||||
#include "scrobbler/listenbrainzscrobbler.h"
|
#include "scrobbler/listenbrainzscrobbler.h"
|
||||||
#include "scrobbler/lastfmimport.h"
|
#include "scrobbler/lastfmimport.h"
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
@@ -118,8 +118,8 @@ using namespace std::chrono_literals;
|
|||||||
|
|
||||||
class ApplicationImpl {
|
class ApplicationImpl {
|
||||||
public:
|
public:
|
||||||
explicit ApplicationImpl(Application *app) :
|
explicit ApplicationImpl(Application *app)
|
||||||
tagreader_client_([app](){
|
: tagreader_client_([app]() {
|
||||||
TagReaderClient *client = new TagReaderClient();
|
TagReaderClient *client = new TagReaderClient();
|
||||||
app->MoveToNewThread(client);
|
app->MoveToNewThread(client);
|
||||||
return client;
|
return client;
|
||||||
@@ -183,6 +183,7 @@ class ApplicationImpl {
|
|||||||
lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->AddProvider(new LetrasLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new LetrasLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->AddProvider(new LyricFindLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new LyricFindLyricsProvider(lyrics_providers->network()));
|
||||||
|
lyrics_providers->AddProvider(new LrcLibLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->ReloadSettings();
|
lyrics_providers->ReloadSettings();
|
||||||
return lyrics_providers;
|
return lyrics_providers;
|
||||||
}),
|
}),
|
||||||
@@ -206,7 +207,6 @@ class ApplicationImpl {
|
|||||||
scrobbler_([app]() {
|
scrobbler_([app]() {
|
||||||
AudioScrobbler *scrobbler = new AudioScrobbler(app);
|
AudioScrobbler *scrobbler = new AudioScrobbler(app);
|
||||||
scrobbler->AddService(make_shared<LastFMScrobbler>(scrobbler->settings(), app->network()));
|
scrobbler->AddService(make_shared<LastFMScrobbler>(scrobbler->settings(), app->network()));
|
||||||
scrobbler->AddService(make_shared<LibreFMScrobbler>(scrobbler->settings(), app->network()));
|
|
||||||
scrobbler->AddService(make_shared<ListenBrainzScrobbler>(scrobbler->settings(), app->network()));
|
scrobbler->AddService(make_shared<ListenBrainzScrobbler>(scrobbler->settings(), app->network()));
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app->network(), app->streaming_services()->Service<SubsonicService>(), app));
|
scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app->network(), app->streaming_services()->Service<SubsonicService>(), app));
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ constexpr char kMagicAllSongsTables[] = "%allsongstables";
|
|||||||
int Database::sNextConnectionId = 1;
|
int Database::sNextConnectionId = 1;
|
||||||
QMutex Database::sNextConnectionIdMutex;
|
QMutex Database::sNextConnectionIdMutex;
|
||||||
|
|
||||||
Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name) :
|
Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name)
|
||||||
QObject(parent),
|
: QObject(parent),
|
||||||
task_manager_(task_manager),
|
task_manager_(task_manager),
|
||||||
injected_database_name_(database_name),
|
injected_database_name_(database_name),
|
||||||
query_hash_(0),
|
query_hash_(0),
|
||||||
@@ -503,7 +503,9 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!error_reported) { Q_EMIT Error(tr("Database corruption detected.")); }
|
if (!error_reported) {
|
||||||
|
Q_EMIT Error(tr("Database corruption detected."));
|
||||||
|
}
|
||||||
Q_EMIT Error(u"Database: "_s + message);
|
Q_EMIT Error(u"Database: "_s + message);
|
||||||
error_reported = true;
|
error_reported = true;
|
||||||
}
|
}
|
||||||
@@ -594,8 +596,7 @@ void Database::BackupFile(const QString &filename) {
|
|||||||
ret = sqlite3_backup_step(backup, 16);
|
ret = sqlite3_backup_step(backup, 16);
|
||||||
const int page_count = sqlite3_backup_pagecount(backup);
|
const int page_count = sqlite3_backup_pagecount(backup);
|
||||||
task_manager_->SetTaskProgress(task_id, static_cast<quint64>(page_count - sqlite3_backup_remaining(backup)), static_cast<quint64>(page_count));
|
task_manager_->SetTaskProgress(task_id, static_cast<quint64>(page_count - sqlite3_backup_remaining(backup)), static_cast<quint64>(page_count));
|
||||||
}
|
} while (ret == SQLITE_OK);
|
||||||
while (ret == SQLITE_OK);
|
|
||||||
|
|
||||||
if (ret != SQLITE_DONE) {
|
if (ret != SQLITE_DONE) {
|
||||||
qLog(Error) << "Database backup failed";
|
qLog(Error) << "Database backup failed";
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ class Database : public QObject {
|
|||||||
int startup_schema_version_;
|
int startup_schema_version_;
|
||||||
|
|
||||||
QThread *original_thread_;
|
QThread *original_thread_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DATABASE_H
|
#endif // DATABASE_H
|
||||||
|
|||||||
@@ -110,21 +110,32 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob &job, QString &error_te
|
|||||||
|
|
||||||
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) {
|
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) {
|
||||||
|
|
||||||
QString path = job.metadata_.url().toLocalFile();
|
const QString path = job.metadata_.url().toLocalFile();
|
||||||
QFileInfo fileInfo(path);
|
const QFileInfo fileInfo(path);
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||||
if (job.use_trash_ && QFile::supportsMoveToTrash()) {
|
if (job.use_trash_ && QFile::supportsMoveToTrash()) {
|
||||||
#else
|
#else
|
||||||
if (job.use_trash_) {
|
if (job.use_trash_) {
|
||||||
#endif
|
#endif
|
||||||
return QFile::moveToTrash(path);
|
if (QFile::moveToTrash(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
qLog(Warning) << "Moving file to trash failed for" << path << ", falling back to direct deletion";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
if (fileInfo.isDir()) {
|
if (fileInfo.isDir()) {
|
||||||
return Utilities::RemoveRecursive(path);
|
success = Utilities::RemoveRecursive(path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
success = QFile::remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QFile::remove(path);
|
if (!success) {
|
||||||
|
qLog(Error) << "Failed to delete file" << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class IconLoader {
|
|||||||
public:
|
public:
|
||||||
static void Init();
|
static void Init();
|
||||||
static QIcon Load(const QString &name, const bool system_icon = true, const int fixed_size = 0, const int min_size = 0, const int max_size = 0);
|
static QIcon Load(const QString &name, const bool system_icon = true, const int fixed_size = 0, const int min_size = 0, const int max_size = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit IconLoader() {}
|
explicit IconLoader() {}
|
||||||
static bool system_icons_;
|
static bool system_icons_;
|
||||||
|
|||||||
@@ -155,11 +155,7 @@ void LocalRedirectServer::WriteTemplate() const {
|
|||||||
|
|
||||||
QBuffer image_buffer;
|
QBuffer image_buffer;
|
||||||
if (image_buffer.open(QIODevice::ReadWrite)) {
|
if (image_buffer.open(QIODevice::ReadWrite)) {
|
||||||
QApplication::style()
|
QApplication::style()->standardIcon(QStyle::SP_DialogOkButton).pixmap(16).toImage().save(&image_buffer, "PNG");
|
||||||
->standardIcon(QStyle::SP_DialogOkButton)
|
|
||||||
.pixmap(16)
|
|
||||||
.toImage()
|
|
||||||
.save(&image_buffer, "PNG");
|
|
||||||
page_data.replace("@IMAGE_DATA@"_L1, QString::fromUtf8(image_buffer.data().toBase64()));
|
page_data.replace("@IMAGE_DATA@"_L1, QString::fromUtf8(image_buffer.data().toBase64()));
|
||||||
image_buffer.close();
|
image_buffer.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2013-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -58,7 +58,6 @@
|
|||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QErrorMessage>
|
#include <QErrorMessage>
|
||||||
#include <QSettings>
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QItemSelectionModel>
|
#include <QItemSelectionModel>
|
||||||
@@ -207,6 +206,11 @@
|
|||||||
|
|
||||||
#include "organize/organizeerrordialog.h"
|
#include "organize/organizeerrordialog.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_VISUALIZATIONS
|
||||||
|
# include "visualizations/visualizationcontainer.h"
|
||||||
|
# include "engine/gstengine.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
# include "core/windows7thumbbar.h"
|
# include "core/windows7thumbbar.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -279,7 +283,7 @@ constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-
|
|||||||
#endif // HAVE_QTSPARKLE
|
#endif // HAVE_QTSPARKLE
|
||||||
|
|
||||||
MainWindow::MainWindow(Application *app,
|
MainWindow::MainWindow(Application *app,
|
||||||
SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd,
|
SharedPtr<SystemTrayIcon> systemtrayicon, OSDBase *osd,
|
||||||
#ifdef HAVE_DISCORD_RPC
|
#ifdef HAVE_DISCORD_RPC
|
||||||
discord::RichPresence *discord_rich_presence,
|
discord::RichPresence *discord_rich_presence,
|
||||||
#endif
|
#endif
|
||||||
@@ -291,7 +295,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
thumbbar_(new Windows7ThumbBar(this)),
|
thumbbar_(new Windows7ThumbBar(this)),
|
||||||
#endif
|
#endif
|
||||||
app_(app),
|
app_(app),
|
||||||
tray_icon_(tray_icon),
|
systemtrayicon_(systemtrayicon),
|
||||||
osd_(osd),
|
osd_(osd),
|
||||||
#ifdef HAVE_DISCORD_RPC
|
#ifdef HAVE_DISCORD_RPC
|
||||||
discord_rich_presence_(discord_rich_presence),
|
discord_rich_presence_(discord_rich_presence),
|
||||||
@@ -408,6 +412,8 @@ MainWindow::MainWindow(Application *app,
|
|||||||
setWindowIcon(IconLoader::Load(u"strawberry"_s));
|
setWindowIcon(IconLoader::Load(u"strawberry"_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
systemtrayicon_->SetDevicePixelRatioF(devicePixelRatioF());
|
||||||
|
|
||||||
QObject::connect(&*app->database(), &Database::Error, this, &MainWindow::ShowErrorDialog);
|
QObject::connect(&*app->database(), &Database::Error, this, &MainWindow::ShowErrorDialog);
|
||||||
|
|
||||||
album_cover_choice_controller_->Init(app->network(), app->tagreader_client(), app->collection()->backend(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services());
|
album_cover_choice_controller_->Init(app->network(), app->tagreader_client(), app->collection()->backend(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services());
|
||||||
@@ -622,6 +628,12 @@ MainWindow::MainWindow(Application *app,
|
|||||||
stop_menu->addAction(ui_->action_stop_after_this_track);
|
stop_menu->addAction(ui_->action_stop_after_this_track);
|
||||||
ui_->stop_button->setMenu(stop_menu);
|
ui_->stop_button->setMenu(stop_menu);
|
||||||
|
|
||||||
|
#ifdef HAVE_VISUALIZATIONS
|
||||||
|
QObject::connect(ui_->action_visualizations, &QAction::triggered, this, &MainWindow::ShowVisualizations);
|
||||||
|
#else
|
||||||
|
ui_->action_visualizations->setEnabled(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Player connections
|
// Player connections
|
||||||
QObject::connect(ui_->volume, &VolumeSlider::valueChanged, &*app_->player(), &Player::SetVolumeFromSlider);
|
QObject::connect(ui_->volume, &VolumeSlider::valueChanged, &*app_->player(), &Player::SetVolumeFromSlider);
|
||||||
|
|
||||||
@@ -695,6 +707,9 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
|
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
|
||||||
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher);
|
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher);
|
||||||
|
|
||||||
|
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->collection(), &CollectionLibrary::CurrentSongChanged);
|
||||||
|
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->collection(), &CollectionLibrary::Stopped);
|
||||||
|
|
||||||
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover);
|
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover);
|
||||||
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded);
|
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded);
|
||||||
QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
|
QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
|
||||||
@@ -846,14 +861,14 @@ MainWindow::MainWindow(Application *app,
|
|||||||
mac::SetApplicationHandler(this);
|
mac::SetApplicationHandler(this);
|
||||||
#endif
|
#endif
|
||||||
// Tray icon
|
// Tray icon
|
||||||
tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause, ui_->action_stop, ui_->action_stop_after_this_track, ui_->action_next_track, ui_->action_mute, ui_->action_love, ui_->action_quit);
|
systemtrayicon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause, ui_->action_stop, ui_->action_stop_after_this_track, ui_->action_next_track, ui_->action_mute, ui_->action_love, ui_->action_quit);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::PlayPause, &*app_->player(), &Player::PlayPauseHelper);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::PlayPause, &*app_->player(), &Player::PlayPauseHelper);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::SeekForward, &*app_->player(), &Player::SeekForward);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::SeekForward, &*app_->player(), &Player::SeekForward);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::SeekBackward, &*app_->player(), &Player::SeekBackward);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::SeekBackward, &*app_->player(), &Player::SeekBackward);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::NextTrack, &*app_->player(), &Player::Next);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::NextTrack, &*app_->player(), &Player::Next);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::PreviousTrack, &*app_->player(), &Player::Previous);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::PreviousTrack, &*app_->player(), &Player::Previous);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::ShowHide, this, &MainWindow::ToggleShowHide);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::ShowHide, this, &MainWindow::ToggleShowHide);
|
||||||
QObject::connect(&*tray_icon_, &SystemTrayIcon::ChangeVolume, this, &MainWindow::VolumeWheelEvent);
|
QObject::connect(&*systemtrayicon_, &SystemTrayIcon::ChangeVolume, this, &MainWindow::VolumeWheelEvent);
|
||||||
|
|
||||||
// Windows 7 thumbbar buttons
|
// Windows 7 thumbbar buttons
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
@@ -903,6 +918,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
|
|
||||||
// Analyzer
|
// Analyzer
|
||||||
QObject::connect(ui_->analyzer, &AnalyzerContainer::WheelEvent, this, &MainWindow::VolumeWheelEvent);
|
QObject::connect(ui_->analyzer, &AnalyzerContainer::WheelEvent, this, &MainWindow::VolumeWheelEvent);
|
||||||
|
ui_->analyzer->SetVisualizationsAction(ui_->action_visualizations);
|
||||||
|
|
||||||
// Statusbar widgets
|
// Statusbar widgets
|
||||||
ui_->playlist_summary->setMinimumWidth(QFontMetrics(font()).horizontalAdvance(u"WW selected of WW tracks - [ WW:WW ]"_s));
|
ui_->playlist_summary->setMinimumWidth(QFontMetrics(font()).horizontalAdvance(u"WW selected of WW tracks - [ WW:WW ]"_s));
|
||||||
@@ -975,27 +991,28 @@ MainWindow::MainWindow(Application *app,
|
|||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
qLog(Debug) << "Loading settings";
|
qLog(Debug) << "Loading settings";
|
||||||
settings_.beginGroup(MainWindowSettings::kSettingsGroup);
|
Settings settings;
|
||||||
|
settings.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
|
||||||
// Set last used geometry to position window on the correct monitor
|
// Set last used geometry to position window on the correct monitor
|
||||||
// Set window state only if the window was last maximized
|
// Set window state only if the window was last maximized
|
||||||
if (settings_.contains("geometry")) {
|
if (settings.contains("geometry")) {
|
||||||
restoreGeometry(settings_.value("geometry").toByteArray());
|
restoreGeometry(settings.value("geometry").toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings_.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings_.value(MainWindowSettings::kSplitterState).toByteArray())) {
|
if (!settings.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings.value(MainWindowSettings::kSplitterState).toByteArray())) {
|
||||||
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
|
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
|
||||||
}
|
}
|
||||||
|
|
||||||
ui_->tabs->setCurrentIndex(settings_.value("current_tab", 1).toInt());
|
ui_->tabs->setCurrentIndex(settings.value("current_tab", 1).toInt());
|
||||||
FancyTabWidget::Mode default_mode = FancyTabWidget::Mode::LargeSidebar;
|
FancyTabWidget::Mode default_mode = FancyTabWidget::Mode::LargeSidebar;
|
||||||
FancyTabWidget::Mode tab_mode = static_cast<FancyTabWidget::Mode>(settings_.value("tab_mode", static_cast<int>(default_mode)).toInt());
|
FancyTabWidget::Mode tab_mode = static_cast<FancyTabWidget::Mode>(settings.value("tab_mode", static_cast<int>(default_mode)).toInt());
|
||||||
if (tab_mode == FancyTabWidget::Mode::None) tab_mode = default_mode;
|
if (tab_mode == FancyTabWidget::Mode::None) tab_mode = default_mode;
|
||||||
ui_->tabs->SetMode(tab_mode);
|
ui_->tabs->SetMode(tab_mode);
|
||||||
|
|
||||||
TabSwitched();
|
TabSwitched();
|
||||||
|
|
||||||
file_view_->SetPath(settings_.value("file_path", QDir::homePath()).toString());
|
file_view_->SetPath(settings.value("file_path", QDir::homePath()).toString());
|
||||||
|
|
||||||
// Users often collapse one side of the splitter by mistake and don't know how to restore it. This must be set after the state is restored above.
|
// Users often collapse one side of the splitter by mistake and don't know how to restore it. This must be set after the state is restored above.
|
||||||
ui_->splitter->setChildrenCollapsible(false);
|
ui_->splitter->setChildrenCollapsible(false);
|
||||||
@@ -1031,20 +1048,20 @@ MainWindow::MainWindow(Application *app,
|
|||||||
show();
|
show();
|
||||||
break;
|
break;
|
||||||
case BehaviourSettings::StartupBehaviour::Hide:
|
case BehaviourSettings::StartupBehaviour::Hide:
|
||||||
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible()) {
|
if (systemtrayicon_->IsSystemTrayAvailable() && systemtrayicon_->isVisible()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case BehaviourSettings::StartupBehaviour::Remember:
|
case BehaviourSettings::StartupBehaviour::Remember:
|
||||||
default:{
|
default:{
|
||||||
|
|
||||||
was_maximized_ = settings_.value(MainWindowSettings::kMaximized, true).toBool();
|
was_maximized_ = settings.value(MainWindowSettings::kMaximized, true).toBool();
|
||||||
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
|
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
|
||||||
|
|
||||||
was_minimized_ = settings_.value(MainWindowSettings::kMinimized, false).toBool();
|
was_minimized_ = settings.value(MainWindowSettings::kMinimized, false).toBool();
|
||||||
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
|
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
|
||||||
|
|
||||||
if (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !settings_.value(MainWindowSettings::kHidden, false).toBool()) {
|
if (!systemtrayicon_->IsSystemTrayAvailable() || !systemtrayicon_->isVisible() || !settings.value(MainWindowSettings::kHidden, false).toBool()) {
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1052,7 +1069,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool show_sidebar = settings_.value(MainWindowSettings::kShowSidebar, true).toBool();
|
bool show_sidebar = settings.value(MainWindowSettings::kShowSidebar, true).toBool();
|
||||||
ui_->sidebar_layout->setVisible(show_sidebar);
|
ui_->sidebar_layout->setVisible(show_sidebar);
|
||||||
ui_->action_toggle_show_sidebar->setChecked(show_sidebar);
|
ui_->action_toggle_show_sidebar->setChecked(show_sidebar);
|
||||||
|
|
||||||
@@ -1156,13 +1173,13 @@ void MainWindow::ReloadSettings() {
|
|||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
constexpr bool keeprunning_available = true;
|
constexpr bool keeprunning_available = true;
|
||||||
#else
|
#else
|
||||||
const bool systemtray_available = tray_icon_->IsSystemTrayAvailable();
|
const bool systemtray_available = systemtrayicon_->IsSystemTrayAvailable();
|
||||||
s.beginGroup(BehaviourSettings::kSettingsGroup);
|
s.beginGroup(BehaviourSettings::kSettingsGroup);
|
||||||
const bool showtrayicon = s.value(BehaviourSettings::kShowTrayIcon, systemtray_available).toBool();
|
const bool showtrayicon = s.value(BehaviourSettings::kShowTrayIcon, systemtray_available).toBool();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
const bool keeprunning_available = systemtray_available && showtrayicon;
|
const bool keeprunning_available = systemtray_available && showtrayicon;
|
||||||
if (systemtray_available) {
|
if (systemtray_available) {
|
||||||
tray_icon_->setVisible(showtrayicon);
|
systemtrayicon_->setVisible(showtrayicon);
|
||||||
}
|
}
|
||||||
if ((!showtrayicon || !systemtray_available) && !isVisible()) {
|
if ((!showtrayicon || !systemtray_available) && !isVisible()) {
|
||||||
show();
|
show();
|
||||||
@@ -1187,7 +1204,7 @@ void MainWindow::ReloadSettings() {
|
|||||||
int iconsize = s.value(AppearanceSettings::kIconSizePlayControlButtons, 32).toInt();
|
int iconsize = s.value(AppearanceSettings::kIconSizePlayControlButtons, 32).toInt();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
tray_icon_->SetTrayiconProgress(trayicon_progress);
|
systemtrayicon_->SetTrayiconProgress(trayicon_progress);
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (taskbar_progress_ && !taskbar_progress) {
|
if (taskbar_progress_ && !taskbar_progress) {
|
||||||
@@ -1209,11 +1226,11 @@ void MainWindow::ReloadSettings() {
|
|||||||
ui_->volume->SetEnabled(volume_control);
|
ui_->volume->SetEnabled(volume_control);
|
||||||
if (volume_control) {
|
if (volume_control) {
|
||||||
if (!ui_->action_mute->isVisible()) ui_->action_mute->setVisible(true);
|
if (!ui_->action_mute->isVisible()) ui_->action_mute->setVisible(true);
|
||||||
if (!tray_icon_->MuteEnabled()) tray_icon_->SetMuteEnabled(true);
|
if (!systemtrayicon_->MuteEnabled()) systemtrayicon_->SetMuteEnabled(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (ui_->action_mute->isVisible()) ui_->action_mute->setVisible(false);
|
if (ui_->action_mute->isVisible()) ui_->action_mute->setVisible(false);
|
||||||
if (tray_icon_->MuteEnabled()) tray_icon_->SetMuteEnabled(false);
|
if (systemtrayicon_->MuteEnabled()) systemtrayicon_->SetMuteEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1223,7 +1240,9 @@ void MainWindow::ReloadSettings() {
|
|||||||
|
|
||||||
osd_->ReloadSettings();
|
osd_->ReloadSettings();
|
||||||
|
|
||||||
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value(MainWindowSettings::kSearchForCoverAuto, true).toBool());
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
album_cover_choice_controller_->search_cover_auto_action()->setChecked(s.value(MainWindowSettings::kSearchForCoverAuto, true).toBool());
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
s.beginGroup(SubsonicSettings::kSettingsGroup);
|
s.beginGroup(SubsonicSettings::kSettingsGroup);
|
||||||
@@ -1340,8 +1359,11 @@ void MainWindow::SaveSettings() {
|
|||||||
ui_->playlist->view()->SaveSettings();
|
ui_->playlist->view()->SaveSettings();
|
||||||
app_->scrobbler()->WriteCache();
|
app_->scrobbler()->WriteCache();
|
||||||
|
|
||||||
settings_.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked());
|
Settings s;
|
||||||
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked());
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
s.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked());
|
||||||
|
s.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked());
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1365,8 +1387,8 @@ void MainWindow::Exit() {
|
|||||||
if (app_->player()->GetState() == EngineBase::State::Playing) {
|
if (app_->player()->GetState() == EngineBase::State::Playing) {
|
||||||
app_->player()->Stop();
|
app_->player()->Stop();
|
||||||
hide();
|
hide();
|
||||||
if (tray_icon_->IsSystemTrayAvailable()) {
|
if (systemtrayicon_->IsSystemTrayAvailable()) {
|
||||||
tray_icon_->setVisible(false);
|
systemtrayicon_->setVisible(false);
|
||||||
}
|
}
|
||||||
return; // Don't quit the application now: wait for the fadeout finished signal
|
return; // Don't quit the application now: wait for the fadeout finished signal
|
||||||
}
|
}
|
||||||
@@ -1423,7 +1445,7 @@ void MainWindow::MediaStopped() {
|
|||||||
|
|
||||||
ui_->action_love->setEnabled(false);
|
ui_->action_love->setEnabled(false);
|
||||||
ui_->button_love->setEnabled(false);
|
ui_->button_love->setEnabled(false);
|
||||||
tray_icon_->LoveStateChanged(false);
|
systemtrayicon_->LoveStateChanged(false);
|
||||||
|
|
||||||
if (track_position_timer_->isActive()) {
|
if (track_position_timer_->isActive()) {
|
||||||
track_position_timer_->stop();
|
track_position_timer_->stop();
|
||||||
@@ -1432,8 +1454,8 @@ void MainWindow::MediaStopped() {
|
|||||||
track_slider_timer_->stop();
|
track_slider_timer_->stop();
|
||||||
}
|
}
|
||||||
ui_->track_slider->SetStopped();
|
ui_->track_slider->SetStopped();
|
||||||
tray_icon_->SetProgress(0);
|
systemtrayicon_->SetProgress(0);
|
||||||
tray_icon_->SetStopped();
|
systemtrayicon_->SetStopped();
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (taskbar_progress_) {
|
if (taskbar_progress_) {
|
||||||
@@ -1465,7 +1487,7 @@ void MainWindow::MediaPaused() {
|
|||||||
track_slider_timer_->start();
|
track_slider_timer_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_icon_->SetPaused();
|
systemtrayicon_->SetPaused();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,7 +1508,7 @@ void MainWindow::MediaPlaying() {
|
|||||||
}
|
}
|
||||||
ui_->action_play_pause->setEnabled(enable_play_pause);
|
ui_->action_play_pause->setEnabled(enable_play_pause);
|
||||||
ui_->track_slider->SetCanSeek(can_seek);
|
ui_->track_slider->SetCanSeek(can_seek);
|
||||||
tray_icon_->SetPlaying(enable_play_pause);
|
systemtrayicon_->SetPlaying(enable_play_pause);
|
||||||
|
|
||||||
if (!track_position_timer_->isActive()) {
|
if (!track_position_timer_->isActive()) {
|
||||||
track_position_timer_->start();
|
track_position_timer_->start();
|
||||||
@@ -1507,14 +1529,14 @@ void MainWindow::SendNowPlaying() {
|
|||||||
app_->scrobbler()->UpdateNowPlaying(playlist->current_item()->EffectiveMetadata());
|
app_->scrobbler()->UpdateNowPlaying(playlist->current_item()->EffectiveMetadata());
|
||||||
ui_->action_love->setEnabled(true);
|
ui_->action_love->setEnabled(true);
|
||||||
ui_->button_love->setEnabled(true);
|
ui_->button_love->setEnabled(true);
|
||||||
tray_icon_->LoveStateChanged(true);
|
systemtrayicon_->LoveStateChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::VolumeChanged(const uint volume) {
|
void MainWindow::VolumeChanged(const uint volume) {
|
||||||
ui_->action_mute->setChecked(volume == 0);
|
ui_->action_mute->setChecked(volume == 0);
|
||||||
tray_icon_->MuteButtonStateChanged(volume == 0);
|
systemtrayicon_->MuteButtonStateChanged(volume == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::SongChanged(const Song &song) {
|
void MainWindow::SongChanged(const Song &song) {
|
||||||
@@ -1524,7 +1546,7 @@ void MainWindow::SongChanged(const Song &song) {
|
|||||||
song_playing_ = song;
|
song_playing_ = song;
|
||||||
song_ = song;
|
song_ = song;
|
||||||
setWindowTitle(song.PrettyTitleWithArtist());
|
setWindowTitle(song.PrettyTitleWithArtist());
|
||||||
tray_icon_->SetProgress(0);
|
systemtrayicon_->SetProgress(0);
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (taskbar_progress_) {
|
if (taskbar_progress_) {
|
||||||
@@ -1582,23 +1604,35 @@ void MainWindow::ToggleSidebar(const bool checked) {
|
|||||||
|
|
||||||
ui_->sidebar_layout->setVisible(checked);
|
ui_->sidebar_layout->setVisible(checked);
|
||||||
TabSwitched();
|
TabSwitched();
|
||||||
settings_.setValue(MainWindowSettings::kShowSidebar, checked);
|
|
||||||
|
Settings s;
|
||||||
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
s.setValue(MainWindowSettings::kShowSidebar, checked);
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ToggleSearchCoverAuto(const bool checked) {
|
void MainWindow::ToggleSearchCoverAuto(const bool checked) {
|
||||||
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
|
|
||||||
|
Settings s;
|
||||||
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
s.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::SaveGeometry() {
|
void MainWindow::SaveGeometry() {
|
||||||
|
|
||||||
if (!initialized_) return;
|
if (!initialized_) return;
|
||||||
|
|
||||||
settings_.setValue(MainWindowSettings::kMaximized, isMaximized());
|
Settings s;
|
||||||
settings_.setValue(MainWindowSettings::kMinimized, isMinimized());
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
settings_.setValue(MainWindowSettings::kHidden, isHidden());
|
s.setValue(MainWindowSettings::kMaximized, isMaximized());
|
||||||
settings_.setValue(MainWindowSettings::kGeometry, saveGeometry());
|
s.setValue(MainWindowSettings::kMinimized, isMinimized());
|
||||||
settings_.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
|
s.setValue(MainWindowSettings::kHidden, isHidden());
|
||||||
|
s.setValue(MainWindowSettings::kGeometry, saveGeometry());
|
||||||
|
s.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1682,17 +1716,6 @@ void MainWindow::StopAfterCurrent() {
|
|||||||
Q_EMIT StopAfterToggled(app_->playlist_manager()->active()->stop_after_current());
|
Q_EMIT StopAfterToggled(app_->playlist_manager()->active()->stop_after_current());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::showEvent(QShowEvent *e) {
|
|
||||||
|
|
||||||
if (error_dialog_ && error_dialog_->isVisible() && error_dialog_->isMinimized()) {
|
|
||||||
error_dialog_->raise();
|
|
||||||
error_dialog_->activateWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
QMainWindow::showEvent(e);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::hideEvent(QHideEvent *e) {
|
void MainWindow::hideEvent(QHideEvent *e) {
|
||||||
|
|
||||||
// Some window managers don't remember maximized state between
|
// Some window managers don't remember maximized state between
|
||||||
@@ -1707,7 +1730,7 @@ void MainWindow::hideEvent(QHideEvent *e) {
|
|||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *e) {
|
void MainWindow::closeEvent(QCloseEvent *e) {
|
||||||
|
|
||||||
if (!exit_ && (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !keep_running_)) {
|
if (!exit_ && (!systemtrayicon_->IsSystemTrayAvailable() || !systemtrayicon_->isVisible() || !keep_running_)) {
|
||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1715,10 +1738,20 @@ void MainWindow::closeEvent(QCloseEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::changeEvent(QEvent *e) {
|
||||||
|
|
||||||
|
if (e->type() == QEvent::Show || e->type() == QEvent::WindowStateChange || e->type() == QEvent::WindowActivate) {
|
||||||
|
CheckShowErrorDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMainWindow::changeEvent(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::SetHiddenInTray(const bool hidden) {
|
void MainWindow::SetHiddenInTray(const bool hidden) {
|
||||||
|
|
||||||
if (hidden && isVisible()) {
|
if (hidden && isVisible()) {
|
||||||
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible() && keep_running_) {
|
if (systemtrayicon_->IsSystemTrayAvailable() && systemtrayicon_->isVisible() && keep_running_) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1735,19 +1768,25 @@ void MainWindow::SetHiddenInTray(const bool hidden) {
|
|||||||
else {
|
else {
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
CheckShowErrorDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::FilePathChanged(const QString &path) {
|
void MainWindow::FilePathChanged(const QString &path) {
|
||||||
settings_.setValue("file_path", path);
|
|
||||||
|
Settings s;
|
||||||
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
s.setValue("file_path", path);
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::Seeked(const qint64 microseconds) {
|
void MainWindow::Seeked(const qint64 microseconds) {
|
||||||
|
|
||||||
const qint64 position = microseconds / kUsecPerSec;
|
const qint64 position = microseconds / kUsecPerSec;
|
||||||
const qint64 length = app_->player()->GetCurrentItem()->EffectiveMetadata().length_nanosec() / kNsecPerSec;
|
const qint64 length = app_->player()->GetCurrentItem()->EffectiveMetadata().length_nanosec() / kNsecPerSec;
|
||||||
tray_icon_->SetProgress(static_cast<int>(static_cast<double>(position) / static_cast<double>(length) * 100.0));
|
systemtrayicon_->SetProgress(static_cast<int>(static_cast<double>(position) / static_cast<double>(length) * 100.0));
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (taskbar_progress_) {
|
if (taskbar_progress_) {
|
||||||
@@ -1767,7 +1806,7 @@ void MainWindow::UpdateTrackPosition() {
|
|||||||
const int position = std::floor(static_cast<float>(app_->player()->engine()->position_nanosec()) / static_cast<float>(kNsecPerSec) + 0.5);
|
const int position = std::floor(static_cast<float>(app_->player()->engine()->position_nanosec()) / static_cast<float>(kNsecPerSec) + 0.5);
|
||||||
|
|
||||||
// Update the tray icon every 10 seconds
|
// Update the tray icon every 10 seconds
|
||||||
if (position % 10 == 0) tray_icon_->SetProgress(static_cast<int>(static_cast<double>(position) / static_cast<double>(length) * 100.0));
|
if (position % 10 == 0) systemtrayicon_->SetProgress(static_cast<int>(static_cast<double>(position) / static_cast<double>(length) * 100.0));
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (taskbar_progress_) {
|
if (taskbar_progress_) {
|
||||||
@@ -2322,7 +2361,9 @@ void MainWindow::EditValue() {
|
|||||||
void MainWindow::AddFile() {
|
void MainWindow::AddFile() {
|
||||||
|
|
||||||
// Last used directory
|
// Last used directory
|
||||||
QString directory = settings_.value("add_media_path", QDir::currentPath()).toString();
|
Settings s;
|
||||||
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
QString directory = s.value("add_media_path", QDir::currentPath()).toString();
|
||||||
|
|
||||||
PlaylistParser parser(app_->tagreader_client(), app_->collection_backend());
|
PlaylistParser parser(app_->tagreader_client(), app_->collection_backend());
|
||||||
|
|
||||||
@@ -2332,7 +2373,7 @@ void MainWindow::AddFile() {
|
|||||||
if (filenames.isEmpty()) return;
|
if (filenames.isEmpty()) return;
|
||||||
|
|
||||||
// Save last used directory
|
// Save last used directory
|
||||||
settings_.setValue("add_media_path", filenames[0]);
|
s.setValue("add_media_path", filenames[0]);
|
||||||
|
|
||||||
// Convert to URLs
|
// Convert to URLs
|
||||||
QList<QUrl> urls;
|
QList<QUrl> urls;
|
||||||
@@ -2350,14 +2391,16 @@ void MainWindow::AddFile() {
|
|||||||
void MainWindow::AddFolder() {
|
void MainWindow::AddFolder() {
|
||||||
|
|
||||||
// Last used directory
|
// Last used directory
|
||||||
QString directory = settings_.value("add_folder_path", QDir::currentPath()).toString();
|
Settings s;
|
||||||
|
s.beginGroup(MainWindowSettings::kSettingsGroup);
|
||||||
|
QString directory = s.value("add_folder_path", QDir::currentPath()).toString();
|
||||||
|
|
||||||
// Show dialog
|
// Show dialog
|
||||||
directory = QFileDialog::getExistingDirectory(this, tr("Add folder"), directory);
|
directory = QFileDialog::getExistingDirectory(this, tr("Add folder"), directory);
|
||||||
if (directory.isEmpty()) return;
|
if (directory.isEmpty()) return;
|
||||||
|
|
||||||
// Save last used directory
|
// Save last used directory
|
||||||
settings_.setValue("add_folder_path", directory);
|
s.setValue("add_folder_path", directory);
|
||||||
|
|
||||||
// Add media
|
// Add media
|
||||||
MimeData *mimedata = new MimeData;
|
MimeData *mimedata = new MimeData;
|
||||||
@@ -2988,6 +3031,14 @@ void MainWindow::ShowErrorDialog(const QString &message) {
|
|||||||
error_dialog_->ShowMessage(message);
|
error_dialog_->ShowMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::CheckShowErrorDialog() {
|
||||||
|
|
||||||
|
if (isVisible() && !isMinimized() && error_dialog_ && error_dialog_->isVisible() && !error_dialog_->isActiveWindow()) {
|
||||||
|
error_dialog_->ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::CheckFullRescanRevisions() {
|
void MainWindow::CheckFullRescanRevisions() {
|
||||||
|
|
||||||
int from = app_->database()->startup_schema_version();
|
int from = app_->database()->startup_schema_version();
|
||||||
@@ -3259,7 +3310,7 @@ void MainWindow::LoveButtonVisibilityChanged(const bool value) {
|
|||||||
else
|
else
|
||||||
ui_->widget_love->hide();
|
ui_->widget_love->hide();
|
||||||
|
|
||||||
tray_icon_->LoveVisibilityChanged(value);
|
systemtrayicon_->LoveVisibilityChanged(value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3282,7 +3333,7 @@ void MainWindow::Love() {
|
|||||||
app_->scrobbler()->Love();
|
app_->scrobbler()->Love();
|
||||||
ui_->button_love->setEnabled(false);
|
ui_->button_love->setEnabled(false);
|
||||||
ui_->action_love->setEnabled(false);
|
ui_->action_love->setEnabled(false);
|
||||||
tray_icon_->LoveStateChanged(false);
|
systemtrayicon_->LoveStateChanged(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3308,7 +3359,7 @@ void MainWindow::PlaylistDelete() {
|
|||||||
|
|
||||||
if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return;
|
if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return;
|
||||||
|
|
||||||
if (app_->player()->GetState() == EngineBase::State::Playing && app_->playlist_manager()->current()->rowCount() == selected_songs.count()) {
|
if (app_->player()->GetState() == EngineBase::State::Playing && app_->playlist_manager()->current() == app_->playlist_manager()->active() && app_->playlist_manager()->current()->rowCount() == selected_songs.count()) {
|
||||||
app_->player()->Stop();
|
app_->player()->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3365,3 +3416,24 @@ void MainWindow::FocusSearchField() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::ShowVisualizations() {
|
||||||
|
|
||||||
|
#ifdef HAVE_VISUALIZATIONS
|
||||||
|
|
||||||
|
if (!visualization_) {
|
||||||
|
visualization_.reset(new VisualizationContainer);
|
||||||
|
|
||||||
|
visualization_->SetActions(ui_->action_previous_track, ui_->action_play_pause, ui_->action_stop, ui_->action_next_track);
|
||||||
|
connect(&*app_->player(), &Player::Stopped, &*visualization_, &VisualizationContainer::Stopped);
|
||||||
|
connect(&*app_->player(), &Player::ForceShowOSD, &*visualization_, &VisualizationContainer::SongMetadataChanged);
|
||||||
|
connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*visualization_, &VisualizationContainer::SongMetadataChanged);
|
||||||
|
|
||||||
|
visualization_->SetEngine(qobject_cast<GstEngine*>(&*app_->player()->engine()));
|
||||||
|
}
|
||||||
|
|
||||||
|
visualization_->show();
|
||||||
|
|
||||||
|
#endif // HAVE_VISUALIZATIONS
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2013-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -45,7 +45,6 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QSettings>
|
|
||||||
#include <QtEvents>
|
#include <QtEvents>
|
||||||
|
|
||||||
#include "includes/scoped_ptr.h"
|
#include "includes/scoped_ptr.h"
|
||||||
@@ -53,7 +52,6 @@
|
|||||||
#include "includes/lazy.h"
|
#include "includes/lazy.h"
|
||||||
#include "core/platforminterface.h"
|
#include "core/platforminterface.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/settings.h"
|
|
||||||
#include "core/commandlineoptions.h"
|
#include "core/commandlineoptions.h"
|
||||||
#include "tagreader/tagreaderclient.h"
|
#include "tagreader/tagreaderclient.h"
|
||||||
#include "osd/osdbase.h"
|
#include "osd/osdbase.h"
|
||||||
@@ -99,6 +97,7 @@ class Windows7ThumbBar;
|
|||||||
class AddStreamDialog;
|
class AddStreamDialog;
|
||||||
class LastFMImportDialog;
|
class LastFMImportDialog;
|
||||||
class RadioViewContainer;
|
class RadioViewContainer;
|
||||||
|
class VisualizationContainer;
|
||||||
|
|
||||||
#ifdef HAVE_DISCORD_RPC
|
#ifdef HAVE_DISCORD_RPC
|
||||||
namespace discord {
|
namespace discord {
|
||||||
@@ -111,7 +110,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(Application *app,
|
explicit MainWindow(Application *app,
|
||||||
SharedPtr<SystemTrayIcon> tray_icon,
|
SharedPtr<SystemTrayIcon> systemtrayicon,
|
||||||
OSDBase *osd,
|
OSDBase *osd,
|
||||||
#ifdef HAVE_DISCORD_RPC
|
#ifdef HAVE_DISCORD_RPC
|
||||||
discord::RichPresence *discord_rich_presence,
|
discord::RichPresence *discord_rich_presence,
|
||||||
@@ -124,9 +123,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
void CommandlineOptionsReceived(const CommandlineOptions &options);
|
void CommandlineOptionsReceived(const CommandlineOptions &options);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *e) override;
|
|
||||||
void hideEvent(QHideEvent *e) override;
|
void hideEvent(QHideEvent *e) override;
|
||||||
void closeEvent(QCloseEvent *e) override;
|
void closeEvent(QCloseEvent *e) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||||
@@ -236,6 +235,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
void ShowAboutDialog();
|
void ShowAboutDialog();
|
||||||
void ShowErrorDialog(const QString &message);
|
void ShowErrorDialog(const QString &message);
|
||||||
|
void CheckShowErrorDialog();
|
||||||
void ShowTranscodeDialog();
|
void ShowTranscodeDialog();
|
||||||
SettingsDialog *CreateSettingsDialog();
|
SettingsDialog *CreateSettingsDialog();
|
||||||
EditTagDialog *CreateEditTagDialog();
|
EditTagDialog *CreateEditTagDialog();
|
||||||
@@ -282,9 +282,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void CommandlineOptionsReceived(const QByteArray &string_options);
|
void CommandlineOptionsReceived(const QByteArray &string_options);
|
||||||
void Raise();
|
void Raise();
|
||||||
|
void ShowVisualizations();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
|
||||||
static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
|
static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
|
||||||
@@ -310,11 +310,12 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Application *app_;
|
Application *app_;
|
||||||
SharedPtr<SystemTrayIcon> tray_icon_;
|
SharedPtr<SystemTrayIcon> systemtrayicon_;
|
||||||
OSDBase *osd_;
|
OSDBase *osd_;
|
||||||
#ifdef HAVE_DISCORD_RPC
|
#ifdef HAVE_DISCORD_RPC
|
||||||
discord::RichPresence *discord_rich_presence_;
|
discord::RichPresence *discord_rich_presence_;
|
||||||
#endif
|
#endif
|
||||||
|
Lazy<ErrorDialog> error_dialog_;
|
||||||
Lazy<About> about_dialog_;
|
Lazy<About> about_dialog_;
|
||||||
Lazy<Console> console_;
|
Lazy<Console> console_;
|
||||||
Lazy<EditTagDialog> edit_tag_dialog_;
|
Lazy<EditTagDialog> edit_tag_dialog_;
|
||||||
@@ -329,7 +330,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
PlaylistListContainer *playlist_list_;
|
PlaylistListContainer *playlist_list_;
|
||||||
QueueView *queue_view_;
|
QueueView *queue_view_;
|
||||||
|
|
||||||
Lazy<ErrorDialog> error_dialog_;
|
|
||||||
Lazy<SettingsDialog> settings_dialog_;
|
Lazy<SettingsDialog> settings_dialog_;
|
||||||
Lazy<AlbumCoverManager> cover_manager_;
|
Lazy<AlbumCoverManager> cover_manager_;
|
||||||
SharedPtr<Equalizer> equalizer_;
|
SharedPtr<Equalizer> equalizer_;
|
||||||
@@ -362,6 +362,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
LastFMImportDialog *lastfm_import_dialog_;
|
LastFMImportDialog *lastfm_import_dialog_;
|
||||||
|
|
||||||
|
#ifdef HAVE_VISUALIZATIONS
|
||||||
|
ScopedPtr<VisualizationContainer> visualization_;
|
||||||
|
#endif
|
||||||
|
|
||||||
QAction *collection_show_all_;
|
QAction *collection_show_all_;
|
||||||
QAction *collection_show_duplicates_;
|
QAction *collection_show_duplicates_;
|
||||||
QAction *collection_show_untagged_;
|
QAction *collection_show_untagged_;
|
||||||
@@ -390,7 +394,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
QTimer *track_position_timer_;
|
QTimer *track_position_timer_;
|
||||||
QTimer *track_slider_timer_;
|
QTimer *track_slider_timer_;
|
||||||
Settings settings_;
|
|
||||||
|
|
||||||
bool keep_running_;
|
bool keep_running_;
|
||||||
bool playing_widget_;
|
bool playing_widget_;
|
||||||
@@ -414,7 +417,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
bool playlists_loaded_;
|
bool playlists_loaded_;
|
||||||
bool delete_files_;
|
bool delete_files_;
|
||||||
std::optional<CommandlineOptions> options_;
|
std::optional<CommandlineOptions> options_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@@ -517,6 +517,7 @@
|
|||||||
<addaction name="action_cover_manager"/>
|
<addaction name="action_cover_manager"/>
|
||||||
<addaction name="action_equalizer"/>
|
<addaction name="action_equalizer"/>
|
||||||
<addaction name="action_transcoder"/>
|
<addaction name="action_transcoder"/>
|
||||||
|
<addaction name="action_visualizations"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="action_update_collection"/>
|
<addaction name="action_update_collection"/>
|
||||||
<addaction name="action_full_collection_scan"/>
|
<addaction name="action_full_collection_scan"/>
|
||||||
@@ -863,6 +864,11 @@
|
|||||||
<string>Import data from last.fm...</string>
|
<string>Import data from last.fm...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_visualizations">
|
||||||
|
<property name="text">
|
||||||
|
<string>Visualizations</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkInformation>
|
||||||
|
|
||||||
#include "networkaccessmanager.h"
|
#include "networkaccessmanager.h"
|
||||||
#include "threadsafenetworkdiskcache.h"
|
#include "threadsafenetworkdiskcache.h"
|
||||||
@@ -41,6 +42,22 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent)
|
|||||||
setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
setCache(new ThreadSafeNetworkDiskCache(this));
|
setCache(new ThreadSafeNetworkDiskCache(this));
|
||||||
|
|
||||||
|
// Handle network state changes after system suspend/resume
|
||||||
|
// QNetworkInformation provides cross-platform network reachability monitoring in Qt 6
|
||||||
|
if (QNetworkInformation::loadDefaultBackend()) {
|
||||||
|
QNetworkInformation *network_info = QNetworkInformation::instance();
|
||||||
|
if (network_info) {
|
||||||
|
QObject::connect(network_info, &QNetworkInformation::reachabilityChanged, this, [this](QNetworkInformation::Reachability reachability) {
|
||||||
|
if (reachability == QNetworkInformation::Reachability::Online) {
|
||||||
|
// Clear connection cache to force reconnection after network becomes available
|
||||||
|
// This fixes issues after system suspend/resume
|
||||||
|
clearConnectionCache();
|
||||||
|
clearAccessCache();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) {
|
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) {
|
||||||
@@ -50,7 +67,7 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
|
|||||||
user_agent = network_request.header(QNetworkRequest::UserAgentHeader).toByteArray();
|
user_agent = network_request.header(QNetworkRequest::UserAgentHeader).toByteArray();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
user_agent = QStringLiteral("%1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()).toUtf8();
|
user_agent = "Strawberry Music Player";
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkRequest new_network_request(network_request);
|
QNetworkRequest new_network_request(network_request);
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ class Player : public PlayerInterface {
|
|||||||
void PlayPlaylistInternal(const EngineBase::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
void PlayPlaylistInternal(const EngineBase::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
||||||
|
|
||||||
void FatalError();
|
void FatalError();
|
||||||
void ValidSongRequested(const QUrl&);
|
void ValidSongRequested(const QUrl &url);
|
||||||
void InvalidSongRequested(const QUrl&);
|
void InvalidSongRequested(const QUrl &url);
|
||||||
|
|
||||||
void HandleLoadResult(const UrlHandler::LoadResult &result);
|
void HandleLoadResult(const UrlHandler::LoadResult &result);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class QtFSListener : public FileSystemWatcherInterface {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QFileSystemWatcher watcher_;
|
QFileSystemWatcher watcher_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QTFSLISTENER_H
|
#endif // QTFSLISTENER_H
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class ScopedNSAutoreleasePool {
|
|||||||
// Only use then when you're certain the items currently in the pool are
|
// Only use then when you're certain the items currently in the pool are
|
||||||
// no longer needed.
|
// no longer needed.
|
||||||
void Recycle();
|
void Recycle();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NSAutoreleasePool *autorelease_pool_;
|
NSAutoreleasePool *autorelease_pool_;
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ const QStringList Song::kAcceptedExtensions = QStringList() << u"wav"_s
|
|||||||
<< u"tta"_s
|
<< u"tta"_s
|
||||||
<< u"dsf"_s
|
<< u"dsf"_s
|
||||||
<< u"dsd"_s
|
<< u"dsd"_s
|
||||||
|
<< u"webm"_s
|
||||||
<< u"ac3"_s
|
<< u"ac3"_s
|
||||||
<< u"dts"_s
|
<< u"dts"_s
|
||||||
<< u"spc"_s
|
<< u"spc"_s
|
||||||
@@ -352,6 +353,8 @@ struct Song::Private : public QSharedData {
|
|||||||
std::optional<double> ebur128_integrated_loudness_lufs_;
|
std::optional<double> ebur128_integrated_loudness_lufs_;
|
||||||
std::optional<double> ebur128_loudness_range_lu_;
|
std::optional<double> ebur128_loudness_range_lu_;
|
||||||
|
|
||||||
|
int id3v2_version_; // ID3v2 tag version (3 or 4), 0 if not applicable or unknown
|
||||||
|
|
||||||
bool init_from_file_; // Whether this song was loaded from a file using taglib.
|
bool init_from_file_; // Whether this song was loaded from a file using taglib.
|
||||||
bool suspicious_tags_; // Whether our encoding guesser thinks these tags might be incorrectly encoded.
|
bool suspicious_tags_; // Whether our encoding guesser thinks these tags might be incorrectly encoded.
|
||||||
|
|
||||||
@@ -399,6 +402,8 @@ Song::Private::Private(const Source source)
|
|||||||
rating_(-1),
|
rating_(-1),
|
||||||
bpm_(-1),
|
bpm_(-1),
|
||||||
|
|
||||||
|
id3v2_version_(0),
|
||||||
|
|
||||||
init_from_file_(false),
|
init_from_file_(false),
|
||||||
suspicious_tags_(false)
|
suspicious_tags_(false)
|
||||||
|
|
||||||
@@ -509,6 +514,8 @@ const QString &Song::musicbrainz_work_id() const { return d->musicbrainz_work_id
|
|||||||
std::optional<double> Song::ebur128_integrated_loudness_lufs() const { return d->ebur128_integrated_loudness_lufs_; }
|
std::optional<double> Song::ebur128_integrated_loudness_lufs() const { return d->ebur128_integrated_loudness_lufs_; }
|
||||||
std::optional<double> Song::ebur128_loudness_range_lu() const { return d->ebur128_loudness_range_lu_; }
|
std::optional<double> Song::ebur128_loudness_range_lu() const { return d->ebur128_loudness_range_lu_; }
|
||||||
|
|
||||||
|
int Song::id3v2_version() const { return d->id3v2_version_; }
|
||||||
|
|
||||||
QString *Song::mutable_title() { return &d->title_; }
|
QString *Song::mutable_title() { return &d->title_; }
|
||||||
QString *Song::mutable_album() { return &d->album_; }
|
QString *Song::mutable_album() { return &d->album_; }
|
||||||
QString *Song::mutable_artist() { return &d->artist_; }
|
QString *Song::mutable_artist() { return &d->artist_; }
|
||||||
@@ -623,6 +630,8 @@ void Song::set_musicbrainz_work_id(const QString &v) { d->musicbrainz_work_id_ =
|
|||||||
void Song::set_ebur128_integrated_loudness_lufs(const std::optional<double> v) { d->ebur128_integrated_loudness_lufs_ = v; }
|
void Song::set_ebur128_integrated_loudness_lufs(const std::optional<double> v) { d->ebur128_integrated_loudness_lufs_ = v; }
|
||||||
void Song::set_ebur128_loudness_range_lu(const std::optional<double> v) { d->ebur128_loudness_range_lu_ = v; }
|
void Song::set_ebur128_loudness_range_lu(const std::optional<double> v) { d->ebur128_loudness_range_lu_ = v; }
|
||||||
|
|
||||||
|
void Song::set_id3v2_version(const int v) { d->id3v2_version_ = v; }
|
||||||
|
|
||||||
void Song::set_init_from_file(const bool v) { d->init_from_file_ = v; }
|
void Song::set_init_from_file(const bool v) { d->init_from_file_ = v; }
|
||||||
|
|
||||||
void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; }
|
void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; }
|
||||||
@@ -796,19 +805,19 @@ bool Song::lyrics_supported() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Song::albumartistsort_supported() const {
|
bool Song::albumartistsort_supported() const {
|
||||||
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG;
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::albumsort_supported() const {
|
bool Song::albumsort_supported() const {
|
||||||
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG;
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::artistsort_supported() const {
|
bool Song::artistsort_supported() const {
|
||||||
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG;
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::composersort_supported() const {
|
bool Song::composersort_supported() const {
|
||||||
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG;
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::performersort_supported() const {
|
bool Song::performersort_supported() const {
|
||||||
@@ -817,7 +826,7 @@ bool Song::performersort_supported() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Song::titlesort_supported() const {
|
bool Song::titlesort_supported() const {
|
||||||
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG;
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::save_embedded_cover_supported(const FileType filetype) {
|
bool Song::save_embedded_cover_supported(const FileType filetype) {
|
||||||
@@ -832,6 +841,10 @@ bool Song::save_embedded_cover_supported(const FileType filetype) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::id3v2_tags_supported() const {
|
||||||
|
return d->filetype_ == FileType::MPEG || d->filetype_ == FileType::WAV || d->filetype_ == FileType::AIFF;
|
||||||
|
}
|
||||||
|
|
||||||
int Song::ColumnIndex(const QString &field) {
|
int Song::ColumnIndex(const QString &field) {
|
||||||
|
|
||||||
return static_cast<int>(kRowIdColumns.indexOf(field));
|
return static_cast<int>(kRowIdColumns.indexOf(field));
|
||||||
@@ -2149,4 +2162,3 @@ QString Song::GetNameForNewPlaylist(const SongList &songs) {
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,6 +234,8 @@ class Song {
|
|||||||
std::optional<double> ebur128_integrated_loudness_lufs() const;
|
std::optional<double> ebur128_integrated_loudness_lufs() const;
|
||||||
std::optional<double> ebur128_loudness_range_lu() const;
|
std::optional<double> ebur128_loudness_range_lu() const;
|
||||||
|
|
||||||
|
int id3v2_version() const;
|
||||||
|
|
||||||
QString *mutable_title();
|
QString *mutable_title();
|
||||||
QString *mutable_album();
|
QString *mutable_album();
|
||||||
QString *mutable_artist();
|
QString *mutable_artist();
|
||||||
@@ -349,6 +351,8 @@ class Song {
|
|||||||
void set_ebur128_integrated_loudness_lufs(const std::optional<double> v);
|
void set_ebur128_integrated_loudness_lufs(const std::optional<double> v);
|
||||||
void set_ebur128_loudness_range_lu(const std::optional<double> v);
|
void set_ebur128_loudness_range_lu(const std::optional<double> v);
|
||||||
|
|
||||||
|
void set_id3v2_version(const int v);
|
||||||
|
|
||||||
void set_init_from_file(const bool v);
|
void set_init_from_file(const bool v);
|
||||||
|
|
||||||
void set_stream_url(const QUrl &v);
|
void set_stream_url(const QUrl &v);
|
||||||
@@ -439,6 +443,8 @@ class Song {
|
|||||||
static bool save_embedded_cover_supported(const FileType filetype);
|
static bool save_embedded_cover_supported(const FileType filetype);
|
||||||
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
|
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
|
||||||
|
|
||||||
|
bool id3v2_tags_supported() const;
|
||||||
|
|
||||||
static int ColumnIndex(const QString &field);
|
static int ColumnIndex(const QString &field);
|
||||||
static QString JoinSpec(const QString &table);
|
static QString JoinSpec(const QString &table);
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ SongLoader::SongLoader(const SharedPtr<UrlHandlers> url_handlers,
|
|||||||
|
|
||||||
QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout);
|
QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout);
|
||||||
|
|
||||||
|
QObject::connect(playlist_parser_, &PlaylistParser::Error, this, &SongLoader::ParserError);
|
||||||
|
QObject::connect(cue_parser_, &CueParser::Error, this, &SongLoader::ParserError);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SongLoader::~SongLoader() {
|
SongLoader::~SongLoader() {
|
||||||
@@ -106,6 +109,10 @@ SongLoader::~SongLoader() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SongLoader::ParserError(const QString &error) {
|
||||||
|
errors_ << error;
|
||||||
|
}
|
||||||
|
|
||||||
SongLoader::Result SongLoader::Load(const QUrl &url) {
|
SongLoader::Result SongLoader::Load(const QUrl &url) {
|
||||||
|
|
||||||
if (url.isEmpty()) return Result::Error;
|
if (url.isEmpty()) return Result::Error;
|
||||||
@@ -179,8 +186,10 @@ SongLoader::Result SongLoader::LoadAudioCD() {
|
|||||||
|
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
CDDASongLoader *cdda_song_loader = new CDDASongLoader(QUrl(), this);
|
CDDASongLoader *cdda_song_loader = new CDDASongLoader(QUrl(), this);
|
||||||
QObject::connect(cdda_song_loader, &CDDASongLoader::SongsDurationLoaded, this, &SongLoader::AudioCDTracksLoadFinishedSlot);
|
QObject::connect(cdda_song_loader, &CDDASongLoader::LoadError, this, &SongLoader::AudioCDTracksLoadErrorSlot);
|
||||||
QObject::connect(cdda_song_loader, &CDDASongLoader::SongsMetadataLoaded, this, &SongLoader::AudioCDTracksTagsLoaded);
|
QObject::connect(cdda_song_loader, &CDDASongLoader::SongsLoaded, this, &SongLoader::AudioCDTracksLoadedSlot);
|
||||||
|
QObject::connect(cdda_song_loader, &CDDASongLoader::SongsUpdated, this, &SongLoader::AudioCDTracksUpdatedSlot);
|
||||||
|
QObject::connect(cdda_song_loader, &CDDASongLoader::LoadingFinished, this, &SongLoader::AudioCDLoadingFinishedSlot);
|
||||||
cdda_song_loader->LoadSongs();
|
cdda_song_loader->LoadSongs();
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
#else
|
#else
|
||||||
@@ -192,23 +201,38 @@ SongLoader::Result SongLoader::LoadAudioCD() {
|
|||||||
|
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
|
|
||||||
void SongLoader::AudioCDTracksLoadFinishedSlot(const SongList &songs, const QString &error) {
|
void SongLoader::AudioCDTracksLoadErrorSlot(const QString &error) {
|
||||||
|
|
||||||
songs_ = songs;
|
|
||||||
errors_ << error;
|
errors_ << error;
|
||||||
Q_EMIT AudioCDTracksLoadFinished();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongLoader::AudioCDTracksTagsLoaded(const SongList &songs) {
|
void SongLoader::AudioCDTracksLoadedSlot(const SongList &songs) {
|
||||||
|
|
||||||
|
songs_ = songs;
|
||||||
|
|
||||||
|
Q_EMIT AudioCDTracksLoaded();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SongLoader::AudioCDTracksUpdatedSlot(const SongList &songs) {
|
||||||
|
|
||||||
|
songs_ = songs;
|
||||||
|
|
||||||
|
Q_EMIT AudioCDTracksUpdated();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SongLoader::AudioCDLoadingFinishedSlot() {
|
||||||
|
|
||||||
CDDASongLoader *cdda_song_loader = qobject_cast<CDDASongLoader*>(sender());
|
CDDASongLoader *cdda_song_loader = qobject_cast<CDDASongLoader*>(sender());
|
||||||
cdda_song_loader->deleteLater();
|
cdda_song_loader->deleteLater();
|
||||||
songs_ = songs;
|
|
||||||
Q_EMIT LoadAudioCDFinished(true);
|
Q_EMIT AudioCDLoadingFinished(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#endif // HAVE_AUDIOCD
|
||||||
|
|
||||||
SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
|
SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
|
||||||
|
|
||||||
@@ -270,6 +294,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parser) { // It's a playlist!
|
if (parser) { // It's a playlist!
|
||||||
|
QObject::connect(parser, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
|
||||||
qLog(Debug) << "Parsing using" << parser->name();
|
qLog(Debug) << "Parsing using" << parser->name();
|
||||||
LoadPlaylist(parser, filename);
|
LoadPlaylist(parser, filename);
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
@@ -689,6 +714,10 @@ void SongLoader::MagicReady() {
|
|||||||
StopTypefindAsync(true);
|
StopTypefindAsync(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser_) {
|
||||||
|
QObject::connect(parser_, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
|
||||||
|
}
|
||||||
|
|
||||||
state_ = State::WaitingForData;
|
state_ = State::WaitingForData;
|
||||||
|
|
||||||
if (!IsPipelinePlaying()) {
|
if (!IsPipelinePlaying()) {
|
||||||
@@ -767,4 +796,3 @@ void SongLoader::CleanupPipeline() {
|
|||||||
state_ = State::Finished;
|
state_ = State::Finished;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,17 +90,22 @@ class SongLoader : public QObject {
|
|||||||
QStringList errors() { return errors_; }
|
QStringList errors() { return errors_; }
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void AudioCDTracksLoadFinished();
|
void AudioCDTracksLoaded();
|
||||||
void LoadAudioCDFinished(const bool success);
|
void AudioCDTracksUpdated();
|
||||||
|
void AudioCDLoadingFinished(const bool success);
|
||||||
void LoadRemoteFinished();
|
void LoadRemoteFinished();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void ScheduleTimeout();
|
void ScheduleTimeout();
|
||||||
void Timeout();
|
void Timeout();
|
||||||
void StopTypefind();
|
void StopTypefind();
|
||||||
|
void ParserError(const QString &error);
|
||||||
|
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
void AudioCDTracksLoadFinishedSlot(const SongList &songs, const QString &error);
|
void AudioCDTracksLoadErrorSlot(const QString &error);
|
||||||
void AudioCDTracksTagsLoaded(const SongList &songs);
|
void AudioCDTracksLoadedSlot(const SongList &songs);
|
||||||
|
void AudioCDTracksUpdatedSlot(const SongList &songs);
|
||||||
|
void AudioCDLoadingFinishedSlot();
|
||||||
#endif // HAVE_AUDIOCD
|
#endif // HAVE_AUDIOCD
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -167,7 +172,6 @@ class SongLoader : public QObject {
|
|||||||
QStringList errors_;
|
QStringList errors_;
|
||||||
|
|
||||||
bool success_;
|
bool success_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SONGLOADER_H
|
#endif // SONGLOADER_H
|
||||||
|
|||||||
@@ -41,4 +41,3 @@ class SongMimeData : public MimeData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // SONGMIMEDATA_H
|
#endif // SONGMIMEDATA_H
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#include <QColor>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
@@ -79,13 +80,13 @@ void StyleSheetLoader::UpdateStyleSheet(QWidget *widget, SharedPtr<StyleSheetDat
|
|||||||
// Replace %palette-role with actual colours
|
// Replace %palette-role with actual colours
|
||||||
QPalette p(widget->palette());
|
QPalette p(widget->palette());
|
||||||
|
|
||||||
{
|
|
||||||
QColor color_altbase = p.color(QPalette::AlternateBase);
|
QColor color_altbase = p.color(QPalette::AlternateBase);
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
color_altbase.setAlpha(color_altbase.lightness() > 180 ? 130 : 16);
|
color_altbase.setAlpha(color_altbase.alpha() >= 180 ? (color_altbase.lightness() > 180 ? 130 : 16) : color_altbase.alpha());
|
||||||
|
#else
|
||||||
|
color_altbase.setAlpha(color_altbase.alpha() >= 180 ? 116 : color_altbase.alpha());
|
||||||
#endif
|
#endif
|
||||||
stylesheet.replace("%palette-alternate-base"_L1, QStringLiteral("rgba(%1,%2,%3,%4)").arg(color_altbase.red()).arg(color_altbase.green()).arg(color_altbase.blue()).arg(color_altbase.alpha()));
|
stylesheet.replace("%palette-alternate-base"_L1, QStringLiteral("rgba(%1,%2,%3,%4)").arg(color_altbase.red()).arg(color_altbase.green()).arg(color_altbase.blue()).arg(color_altbase.alpha()));
|
||||||
}
|
|
||||||
|
|
||||||
ReplaceColor(&stylesheet, u"Window"_s, p, QPalette::Window);
|
ReplaceColor(&stylesheet, u"Window"_s, p, QPalette::Window);
|
||||||
ReplaceColor(&stylesheet, u"Background"_s, p, QPalette::Window);
|
ReplaceColor(&stylesheet, u"Background"_s, p, QPalette::Window);
|
||||||
|
|||||||
@@ -41,17 +41,19 @@ Translations::~Translations() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translations::LoadTranslation(const QString &prefix, const QString &path, const QString &language) {
|
bool Translations::LoadTranslation(const QString &prefix, const QString &path, const QString &language) {
|
||||||
|
|
||||||
const QString basefilename = prefix + u'_' + language;
|
const QString basefilename = prefix + u'_' + language;
|
||||||
QTranslator *t = new QTranslator;
|
QTranslator *t = new QTranslator;
|
||||||
if (t->load(basefilename, path)) {
|
if (!t->load(basefilename, path)) {
|
||||||
|
delete t;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Tranlations loaded from" << basefilename;
|
qLog(Debug) << "Tranlations loaded from" << basefilename;
|
||||||
QCoreApplication::installTranslator(t);
|
QCoreApplication::installTranslator(t);
|
||||||
translations_ << t;
|
translations_ << t;
|
||||||
}
|
|
||||||
else {
|
return true;
|
||||||
delete t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Translations {
|
|||||||
public:
|
public:
|
||||||
explicit Translations();
|
explicit Translations();
|
||||||
~Translations();
|
~Translations();
|
||||||
void LoadTranslation(const QString &prefix, const QString &path, const QString &language);
|
bool LoadTranslation(const QString &prefix, const QString &path, const QString &language);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QTranslator*> translations_;
|
QList<QTranslator*> translations_;
|
||||||
|
|||||||
@@ -53,4 +53,3 @@ UrlHandler::LoadResult::LoadResult(const QUrl &media_url, const Type type, const
|
|||||||
bit_depth_(-1),
|
bit_depth_(-1),
|
||||||
length_nanosec_(-1),
|
length_nanosec_(-1),
|
||||||
error_(error) {}
|
error_(error) {}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ class UrlHandler : public QObject {
|
|||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void AsyncLoadComplete(const UrlHandler::LoadResult &result);
|
void AsyncLoadComplete(const UrlHandler::LoadResult &result);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // URLHANDLER_H
|
#endif // URLHANDLER_H
|
||||||
|
|||||||
@@ -812,8 +812,8 @@ QUrl AlbumCoverChoiceController::SaveCoverAutomatic(Song *song, const AlbumCover
|
|||||||
SaveCoverEmbeddedToCollectionSongs(*song, result);
|
SaveCoverEmbeddedToCollectionSongs(*song, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
}
|
||||||
case CoverOptions::CoverType::Cache:
|
case CoverOptions::CoverType::Cache:
|
||||||
case CoverOptions::CoverType::Album:{
|
case CoverOptions::CoverType::Album:{
|
||||||
cover_url = SaveCoverToFileAutomatic(song, result);
|
cover_url = SaveCoverToFileAutomatic(song, result);
|
||||||
|
|||||||
@@ -75,4 +75,3 @@ class AlbumCoverExporter : public QObject {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // ALBUMCOVEREXPORTER_H
|
#endif // ALBUMCOVEREXPORTER_H
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ class AlbumCoverFetcherSearch : public QObject {
|
|||||||
SharedPtr<NetworkAccessManager> network_;
|
SharedPtr<NetworkAccessManager> network_;
|
||||||
|
|
||||||
bool cancel_requested_;
|
bool cancel_requested_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ALBUMCOVERFETCHERSEARCH_H
|
#endif // ALBUMCOVERFETCHERSEARCH_H
|
||||||
|
|||||||
@@ -58,5 +58,4 @@ AlbumCoverLoaderOptions::Types AlbumCoverLoaderOptions::LoadTypes() {
|
|||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
return cover_types;
|
return cover_types;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ class AlbumCoverLoaderResult {
|
|||||||
const AlbumCoverImageResult &_album_cover = AlbumCoverImageResult(),
|
const AlbumCoverImageResult &_album_cover = AlbumCoverImageResult(),
|
||||||
const QImage &_image_scaled = QImage(),
|
const QImage &_image_scaled = QImage(),
|
||||||
const QUrl &_art_manual_updated = QUrl(),
|
const QUrl &_art_manual_updated = QUrl(),
|
||||||
const QUrl &_art_automatic_updated = QUrl()) :
|
const QUrl &_art_automatic_updated = QUrl())
|
||||||
success(_success),
|
: success(_success),
|
||||||
type(_type),
|
type(_type),
|
||||||
album_cover(_album_cover),
|
album_cover(_album_cover),
|
||||||
image_scaled(_image_scaled),
|
image_scaled(_image_scaled),
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ using std::make_shared;
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr char kSettingsGroup[] = "CoverManager";
|
constexpr char kSettingsGroup[] = "CoverManager";
|
||||||
constexpr int kThumbnailSize = 120;
|
constexpr int kThumbnailSize = 120;
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
AlbumCoverManager::AlbumCoverManager(const SharedPtr<NetworkAccessManager> network,
|
AlbumCoverManager::AlbumCoverManager(const SharedPtr<NetworkAccessManager> network,
|
||||||
const SharedPtr<CollectionBackend> collection_backend,
|
const SharedPtr<CollectionBackend> collection_backend,
|
||||||
@@ -604,10 +604,7 @@ void AlbumCoverManager::AlbumCoverFetched(const quint64 id, const AlbumCoverImag
|
|||||||
|
|
||||||
void AlbumCoverManager::UpdateStatusText() {
|
void AlbumCoverManager::UpdateStatusText() {
|
||||||
|
|
||||||
QString message = tr("Got %1 covers out of %2 (%3 failed)")
|
QString message = tr("Got %1 covers out of %2 (%3 failed)").arg(fetch_statistics_.chosen_images_).arg(jobs_).arg(fetch_statistics_.missing_images_);
|
||||||
.arg(fetch_statistics_.chosen_images_)
|
|
||||||
.arg(jobs_)
|
|
||||||
.arg(fetch_statistics_.missing_images_);
|
|
||||||
|
|
||||||
if (fetch_statistics_.bytes_transferred_ > 0) {
|
if (fetch_statistics_.bytes_transferred_ > 0) {
|
||||||
message += ", "_L1 + tr("%1 transferred").arg(Utilities::PrettySize(fetch_statistics_.bytes_transferred_));
|
message += ", "_L1 + tr("%1 transferred").arg(Utilities::PrettySize(fetch_statistics_.bytes_transferred_));
|
||||||
@@ -1083,10 +1080,7 @@ void AlbumCoverManager::UpdateExportStatus(const int exported, const int skipped
|
|||||||
|
|
||||||
progress_bar_->setValue(exported);
|
progress_bar_->setValue(exported);
|
||||||
|
|
||||||
QString message = tr("Exported %1 covers out of %2 (%3 skipped)")
|
QString message = tr("Exported %1 covers out of %2 (%3 skipped)").arg(exported).arg(max).arg(skipped);
|
||||||
.arg(exported)
|
|
||||||
.arg(max)
|
|
||||||
.arg(skipped);
|
|
||||||
statusBar()->showMessage(message);
|
statusBar()->showMessage(message);
|
||||||
|
|
||||||
// End of the current process
|
// End of the current process
|
||||||
@@ -1131,4 +1125,3 @@ void AlbumCoverManager::SaveEmbeddedCoverFinished(TagReaderReplyPtr reply, Album
|
|||||||
LoadAlbumCoverAsync(album_item);
|
LoadAlbumCoverAsync(album_item);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class AlbumCoverSearcher : public QDialog {
|
|||||||
AlbumCoverImageResult Exec(const QString &artist, const QString &album);
|
AlbumCoverImageResult Exec(const QString &artist, const QString &album);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent*) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void Search();
|
void Search();
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ CoverSearchStatisticsDialog::CoverSearchStatisticsDialog(QWidget *parent)
|
|||||||
details_layout_ = new QVBoxLayout(ui_->details);
|
details_layout_ = new QVBoxLayout(ui_->details);
|
||||||
details_layout_->setSpacing(0);
|
details_layout_->setSpacing(0);
|
||||||
|
|
||||||
setStyleSheet(
|
setStyleSheet(u"#details {"
|
||||||
u"#details {"
|
|
||||||
" background-color: palette(base);"
|
" background-color: palette(base);"
|
||||||
"}"
|
"}"
|
||||||
"#details QLabel[type=\"label\"] {"
|
"#details QLabel[type=\"label\"] {"
|
||||||
@@ -67,10 +66,7 @@ void CoverSearchStatisticsDialog::Show(const CoverSearchStatistics &statistics)
|
|||||||
QStringList providers(statistics.total_images_by_provider_.keys());
|
QStringList providers(statistics.total_images_by_provider_.keys());
|
||||||
std::sort(providers.begin(), providers.end());
|
std::sort(providers.begin(), providers.end());
|
||||||
|
|
||||||
ui_->summary->setText(tr("Got %1 covers out of %2 (%3 failed)")
|
ui_->summary->setText(tr("Got %1 covers out of %2 (%3 failed)").arg(statistics.chosen_images_).arg(statistics.chosen_images_ + statistics.missing_images_).arg(statistics.missing_images_));
|
||||||
.arg(statistics.chosen_images_)
|
|
||||||
.arg(statistics.chosen_images_ + statistics.missing_images_)
|
|
||||||
.arg(statistics.missing_images_));
|
|
||||||
|
|
||||||
for (const QString &provider : std::as_const(providers)) {
|
for (const QString &provider : std::as_const(providers)) {
|
||||||
AddLine(tr("Covers from %1").arg(provider), QString::number(statistics.chosen_images_by_provider_[provider]));
|
AddLine(tr("Covers from %1").arg(provider), QString::number(statistics.chosen_images_by_provider_[provider]));
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/networkaccessmanager.h"
|
#include "core/networkaccessmanager.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/song.h"
|
|
||||||
#include "core/oauthenticator.h"
|
#include "core/oauthenticator.h"
|
||||||
#include "albumcoverfetcher.h"
|
#include "albumcoverfetcher.h"
|
||||||
#include "jsoncoverprovider.h"
|
#include "jsoncoverprovider.h"
|
||||||
@@ -50,8 +49,9 @@ constexpr char kOAuthAccessTokenUrl[] = "https://auth.tidal.com/v1/oauth2/token"
|
|||||||
constexpr char kApiUrl[] = "https://openapi.tidal.com/v2";
|
constexpr char kApiUrl[] = "https://openapi.tidal.com/v2";
|
||||||
constexpr char kApiClientIdB64[] = "RHBwV3FpTEM4ZFJSV1RJaQ==";
|
constexpr char kApiClientIdB64[] = "RHBwV3FpTEM4ZFJSV1RJaQ==";
|
||||||
constexpr char kApiClientSecretB64[] = "cGk0QmxpclZXQWlteWpBc0RnWmZ5RmVlRzA2b3E1blVBVTljUW1IdFhDST0=";
|
constexpr char kApiClientSecretB64[] = "cGk0QmxpclZXQWlteWpBc0RnWmZ5RmVlRzA2b3E1blVBVTljUW1IdFhDST0=";
|
||||||
constexpr int kLimit = 10;
|
constexpr char kContentTypeHeader[] = "application/vnd.api+json";
|
||||||
constexpr const int kRequestsDelay = 1000;
|
constexpr int kSearchLimit = 6;
|
||||||
|
constexpr const int kRequestsDelay = 300;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
@@ -87,8 +87,7 @@ bool OpenTidalCoverProvider::StartSearch(const QString &artist, const QString &a
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchRequestPtr search_request = make_shared<SearchRequest>(id, artist, album, title);
|
search_requests_queue_.enqueue(make_shared<QueuedSearchRequest>(make_shared<SearchRequest>(id, artist, album, title)));
|
||||||
search_requests_queue_ << search_request;
|
|
||||||
|
|
||||||
if (!timer_flush_requests_->isActive()) {
|
if (!timer_flush_requests_->isActive()) {
|
||||||
timer_flush_requests_->start();
|
timer_flush_requests_->start();
|
||||||
@@ -109,8 +108,21 @@ void OpenTidalCoverProvider::FlushRequests() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!artwork_requests_queue_.isEmpty()) {
|
||||||
|
QueuedArtworkRequestPtr queued_artwork_request = artwork_requests_queue_.dequeue();
|
||||||
|
SendArtworkRequest(queued_artwork_request->search, queued_artwork_request->albumcover, queued_artwork_request->artwork);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!albumcover_requests_queue_.isEmpty()) {
|
||||||
|
QueuedAlbumCoverRequestPtr queued_albumcover_request = albumcover_requests_queue_.dequeue();
|
||||||
|
SendAlbumCoverRequest(queued_albumcover_request->search, queued_albumcover_request->albumcover);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!search_requests_queue_.isEmpty()) {
|
if (!search_requests_queue_.isEmpty()) {
|
||||||
SendSearchRequest(search_requests_queue_.dequeue());
|
QueuedSearchRequestPtr queued_search_request = search_requests_queue_.dequeue();
|
||||||
|
SendSearchRequest(queued_search_request->search);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,14 +245,14 @@ void OpenTidalCoverProvider::SendSearchRequest(SearchRequestPtr search_request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QUrlQuery url_query;
|
QUrlQuery url_query;
|
||||||
url_query.addQueryItem(u"include"_s, u"albums"_s);
|
|
||||||
url_query.addQueryItem(u"limit"_s, QString::number(kLimit));
|
|
||||||
url_query.addQueryItem(u"countryCode"_s, u"US"_s);
|
url_query.addQueryItem(u"countryCode"_s, u"US"_s);
|
||||||
QUrl url(QLatin1String(kApiUrl) + "/searchresults/"_L1 + QString::fromUtf8(QUrl::toPercentEncoding(query)));
|
url_query.addQueryItem(u"limit"_s, QString::number(kSearchLimit));
|
||||||
|
url_query.addQueryItem(u"include"_s, u"albums"_s);
|
||||||
|
QUrl url(QLatin1String(kApiUrl) + "/searchResults/"_L1 + QString::fromUtf8(QUrl::toPercentEncoding(query)));
|
||||||
url.setQuery(url_query);
|
url.setQuery(url_query);
|
||||||
QNetworkRequest network_request(url);
|
QNetworkRequest network_request(url);
|
||||||
network_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
network_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
network_request.setHeader(QNetworkRequest::ContentTypeHeader, u"application/vnd.tidal.v1+json"_s);
|
network_request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(kContentTypeHeader));
|
||||||
if (oauth_->authenticated()) {
|
if (oauth_->authenticated()) {
|
||||||
network_request.setRawHeader("Authorization", oauth_->authorization_header());
|
network_request.setRawHeader("Authorization", oauth_->authorization_header());
|
||||||
}
|
}
|
||||||
@@ -258,94 +270,246 @@ void OpenTidalCoverProvider::HandleSearchReply(QNetworkReply *reply, SearchReque
|
|||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
CoverProviderSearchResults results;
|
const QScopeGuard search_finished = qScopeGuard([this, search_request]() {
|
||||||
const QScopeGuard search_finished = qScopeGuard([this, search_request, &results]() { Q_EMIT SearchFinished(search_request->id, results); });
|
if (!search_request->finished && search_request->albumcover_requests.isEmpty()) {
|
||||||
|
Q_EMIT SearchFinished(search_request->id, search_request->results);
|
||||||
|
search_request->finished = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||||
if (!json_object_result.success()) {
|
if (!json_object_result.success()) {
|
||||||
Error(json_object_result.error_message);
|
Error(json_object_result.error_message);
|
||||||
if (login_in_progress_) {
|
if (login_in_progress_) {
|
||||||
search_requests_queue_.prepend(search_request);
|
search_requests_queue_.prepend(make_shared<QueuedSearchRequest>(search_request));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const QJsonObject &json_object = json_object_result.json_object;
|
const QJsonObject &json_object = json_object_result.json_object;
|
||||||
|
|
||||||
if (!json_object.contains("included"_L1) || !json_object["included"_L1].isArray()) {
|
if (!json_object.contains("included"_L1) || !json_object["included"_L1].isArray()) {
|
||||||
qLog(Error) << "OpenTidal: Json object is missing included.";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QJsonArray array_included = json_object["included"_L1].toArray();
|
const QJsonArray array_included = json_object["included"_L1].toArray();
|
||||||
if (array_included.isEmpty()) {
|
if (array_included.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for (const auto &value : array_included) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QJsonObject object = value.toObject();
|
||||||
|
const QString id = object["id"_L1].toString();
|
||||||
|
const QString type = object["type"_L1].toString();
|
||||||
|
if (type == "albums"_L1) {
|
||||||
|
QString title;
|
||||||
|
if (object.contains("attributes"_L1)) {
|
||||||
|
const QJsonObject attributes = object["attributes"_L1].toObject();
|
||||||
|
if (attributes.contains("title"_L1)) {
|
||||||
|
title = attributes["title"_L1].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddAlbumCoverRequest(search_request, id, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::AddAlbumCoverRequest(SearchRequestPtr search_request, const QString &album_id, const QString &album_title) {
|
||||||
|
|
||||||
|
AlbumCoverRequestPtr albumcover_request = make_shared<AlbumCoverRequest>(album_id, album_title);
|
||||||
|
search_request->albumcover_requests << albumcover_request;
|
||||||
|
albumcover_requests_queue_.enqueue(make_shared<QueuedAlbumCoverRequest>(search_request, albumcover_request));
|
||||||
|
|
||||||
|
if (!timer_flush_requests_->isActive()) {
|
||||||
|
timer_flush_requests_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::SendAlbumCoverRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request) {
|
||||||
|
|
||||||
|
QUrlQuery url_query;
|
||||||
|
url_query.addQueryItem(u"countryCode"_s, u"US"_s);
|
||||||
|
QUrl url(QLatin1String(kApiUrl) + QLatin1String("/albums/%1/relationships/coverArt"_L1).arg(albumcover_request->album_id));
|
||||||
|
url.setQuery(url_query);
|
||||||
|
QNetworkRequest network_request(url);
|
||||||
|
network_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
network_request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(kContentTypeHeader));
|
||||||
|
if (oauth_->authenticated()) {
|
||||||
|
network_request.setRawHeader("Authorization", oauth_->authorization_header());
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply *reply = network_->get(network_request);
|
||||||
|
replies_ << reply;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, search_request, albumcover_request]() { HandleAlbumCoverReply(reply, search_request, albumcover_request); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::HandleAlbumCoverReply(QNetworkReply *reply, SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request) {
|
||||||
|
|
||||||
|
if (!replies_.contains(reply)) return;
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
const QScopeGuard search_finished = qScopeGuard([this, search_request, albumcover_request]() {
|
||||||
|
if (albumcover_request->artwork_requests.isEmpty()) {
|
||||||
|
search_request->albumcover_requests.removeAll(albumcover_request);
|
||||||
|
}
|
||||||
|
if (!search_request->finished && search_request->albumcover_requests.isEmpty()) {
|
||||||
|
Q_EMIT SearchFinished(search_request->id, search_request->results);
|
||||||
|
search_request->finished = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||||
|
if (!json_object_result.success()) {
|
||||||
|
Error(json_object_result.error_message);
|
||||||
|
if (login_in_progress_) {
|
||||||
|
albumcover_requests_queue_.prepend(make_shared<QueuedAlbumCoverRequest>(search_request, albumcover_request));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonObject &json_object = json_object_result.json_object;
|
||||||
|
if (!json_object.contains("data"_L1) || !json_object["data"_L1].isArray()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonArray array_data = json_object["data"_L1].toArray();
|
||||||
|
if (array_data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &value : array_data) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QJsonObject object = value.toObject();
|
||||||
|
if (!object.contains("id"_L1) || !object.contains("type"_L1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QString id = object["id"_L1].toString();
|
||||||
|
const QString type = object["type"_L1].toString();
|
||||||
|
if (type == "artworks"_L1) {
|
||||||
|
AddArtworkRequest(search_request, albumcover_request, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::AddArtworkRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, const QString &artwork_id) {
|
||||||
|
|
||||||
|
ArtworkRequestPtr artwork_request = make_shared<ArtworkRequest>(artwork_id);
|
||||||
|
albumcover_request->artwork_requests << artwork_request;
|
||||||
|
artwork_requests_queue_.enqueue(make_shared<QueuedArtworkRequest>(search_request, albumcover_request, artwork_request));
|
||||||
|
|
||||||
|
if (!timer_flush_requests_->isActive()) {
|
||||||
|
timer_flush_requests_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::SendArtworkRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, ArtworkRequestPtr artwork_request) {
|
||||||
|
|
||||||
|
QUrlQuery url_query;
|
||||||
|
url_query.addQueryItem(u"countryCode"_s, u"US"_s);
|
||||||
|
QUrl url(QLatin1String(kApiUrl) + QLatin1String("/artworks/%1").arg(artwork_request->artwork_id));
|
||||||
|
url.setQuery(url_query);
|
||||||
|
QNetworkRequest network_request(url);
|
||||||
|
network_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
network_request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(kContentTypeHeader));
|
||||||
|
if (oauth_->authenticated()) {
|
||||||
|
network_request.setRawHeader("Authorization", oauth_->authorization_header());
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply *reply = network_->get(network_request);
|
||||||
|
replies_ << reply;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, search_request, albumcover_request, artwork_request]() { HandleArtworkReply(reply, search_request, albumcover_request, artwork_request); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTidalCoverProvider::HandleArtworkReply(QNetworkReply *reply, SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, ArtworkRequestPtr artwork_request) {
|
||||||
|
|
||||||
|
if (!replies_.contains(reply)) return;
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
const QScopeGuard search_finished = qScopeGuard([this, search_request, albumcover_request, artwork_request]() {
|
||||||
|
albumcover_request->artwork_requests.removeAll(artwork_request);
|
||||||
|
if (albumcover_request->artwork_requests.isEmpty()) {
|
||||||
|
search_request->albumcover_requests.removeAll(albumcover_request);
|
||||||
|
}
|
||||||
|
if (!search_request->finished && search_request->albumcover_requests.isEmpty()) {
|
||||||
|
Q_EMIT SearchFinished(search_request->id, search_request->results);
|
||||||
|
search_request->finished = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||||
|
if (!json_object_result.success()) {
|
||||||
|
Error(json_object_result.error_message);
|
||||||
|
if (login_in_progress_) {
|
||||||
|
artwork_requests_queue_.prepend(make_shared<QueuedArtworkRequest>(search_request, albumcover_request, artwork_request));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonObject &json_object = json_object_result.json_object;
|
||||||
|
if (!json_object.contains("data"_L1) || !json_object["data"_L1].isObject()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonObject object_data = json_object["data"_L1].toObject();
|
||||||
|
if (!object_data.contains("attributes"_L1) || !object_data["attributes"_L1].isObject()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonObject object_attributes = object_data["attributes"_L1].toObject();
|
||||||
|
if (!object_attributes.contains("files"_L1) || !object_attributes["files"_L1].isArray()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonArray array_files = object_attributes["files"_L1].toArray();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto &value_included : array_included) {
|
for (const auto &value_file : array_files) {
|
||||||
|
if (!value_file.isObject()) {
|
||||||
if (!value_included.isObject()) {
|
|
||||||
qLog(Error) << "OpenTidal: Invalid Json reply: Albums array value is not a object.";
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QJsonObject object_included = value_included.toObject();
|
const QJsonObject object_file = value_file.toObject();
|
||||||
|
if (!object_file.contains("href"_L1) || !object_file["href"_L1].isString()) {
|
||||||
if (!object_included.contains("attributes"_L1) || !object_included["attributes"_L1].isObject()) {
|
|
||||||
qLog(Error) << "OpenTidal: Invalid Json reply: Included array item is missing attributes object." << object_included;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QJsonObject object_attributes = object_included["attributes"_L1].toObject();
|
if (!object_file.contains("meta"_L1) || !object_file["meta"_L1].isObject()) {
|
||||||
|
|
||||||
if (!object_attributes.contains("title"_L1) || !object_attributes["title"_L1].isString()) {
|
|
||||||
qLog(Error) << "OpenTidal: Invalid Json reply: Attributes is missing title string." << object_attributes;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const QString href = object_file["href"_L1].toString();
|
||||||
if (!object_attributes.contains("imageLinks"_L1) || !object_attributes["imageLinks"_L1].isArray()) {
|
const QJsonObject object_meta = object_file["meta"_L1].toObject();
|
||||||
qLog(Error) << "OpenTidal: Invalid Json reply: Attributes is missing imageLinks object." << object_attributes;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString album = object_attributes["title"_L1].toString();
|
|
||||||
const QJsonArray array_imagelinks = object_attributes["imageLinks"_L1].toArray();
|
|
||||||
|
|
||||||
for (const auto &value_imagelink : array_imagelinks) {
|
|
||||||
if (!value_imagelink.isObject()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const QJsonObject object_imagelink = value_imagelink.toObject();
|
|
||||||
if (!object_imagelink.contains("href"_L1) || !object_imagelink.contains("meta"_L1) || !object_imagelink["meta"_L1].isObject()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QJsonObject object_meta = object_imagelink["meta"_L1].toObject();
|
|
||||||
if (!object_meta.contains("width"_L1) || !object_meta.contains("height"_L1)) {
|
if (!object_meta.contains("width"_L1) || !object_meta.contains("height"_L1)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QUrl url(object_imagelink["href"_L1].toString());
|
|
||||||
const int width = object_meta["width"_L1].toInt();
|
const int width = object_meta["width"_L1].toInt();
|
||||||
const int height = object_meta["height"_L1].toInt();
|
const int height = object_meta["height"_L1].toInt();
|
||||||
if (!url.isValid()) continue;
|
const QUrl url(href);
|
||||||
if (width < 640 || height < 640) continue;
|
if (!url.isValid() || width < 640 || height < 640) continue;
|
||||||
CoverProviderSearchResult cover_result;
|
CoverProviderSearchResult cover_result;
|
||||||
cover_result.artist = search_request->artist;
|
cover_result.artist = search_request->artist;
|
||||||
cover_result.album = Song::AlbumRemoveDiscMisc(album);
|
cover_result.album = albumcover_request->album_title;
|
||||||
cover_result.image_url = url;
|
cover_result.image_url = url;
|
||||||
cover_result.image_size = QSize(width, height);
|
cover_result.image_size = QSize(width, height);
|
||||||
cover_result.number = ++i;
|
cover_result.number = ++i;
|
||||||
results << cover_result;
|
search_request->results << cover_result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenTidalCoverProvider::FinishAllSearches() {
|
void OpenTidalCoverProvider::FinishAllSearches() {
|
||||||
|
|
||||||
timer_flush_requests_->stop();
|
|
||||||
|
|
||||||
while (!search_requests_queue_.isEmpty()) {
|
while (!search_requests_queue_.isEmpty()) {
|
||||||
SearchRequestPtr search_request = search_requests_queue_.dequeue();
|
QueuedSearchRequestPtr queued_search_request = search_requests_queue_.dequeue();
|
||||||
|
SearchRequestPtr search_request = queued_search_request->search;
|
||||||
|
search_request->albumcover_requests.clear();
|
||||||
|
if (!search_request->finished) {
|
||||||
Q_EMIT SearchFinished(search_request->id, CoverProviderSearchResults());
|
Q_EMIT SearchFinished(search_request->id, CoverProviderSearchResults());
|
||||||
|
search_request->finished = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_flush_requests_->stop();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "jsoncoverprovider.h"
|
#include "jsoncoverprovider.h"
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class NetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class QTimer;
|
|
||||||
class OAuthenticator;
|
class OAuthenticator;
|
||||||
|
|
||||||
class OpenTidalCoverProvider : public JsonCoverProvider {
|
class OpenTidalCoverProvider : public JsonCoverProvider {
|
||||||
@@ -45,20 +45,68 @@ class OpenTidalCoverProvider : public JsonCoverProvider {
|
|||||||
void CancelSearch(const int id) override;
|
void CancelSearch(const int id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SearchRequest {
|
class ArtworkRequest {
|
||||||
explicit SearchRequest(const int _id, const QString &_artist, const QString &_album, const QString &_title) : id(_id), artist(_artist), album(_album), title(_title) {}
|
public:
|
||||||
|
explicit ArtworkRequest(const QString &_artwork_id) : artwork_id(_artwork_id) {}
|
||||||
|
QString artwork_id;
|
||||||
|
};
|
||||||
|
using ArtworkRequestPtr = SharedPtr<ArtworkRequest>;
|
||||||
|
|
||||||
|
class AlbumCoverRequest {
|
||||||
|
public:
|
||||||
|
explicit AlbumCoverRequest(const QString &_album_id, const QString &_album_title) : album_id(_album_id), album_title(_album_title) {}
|
||||||
|
QString album_id;
|
||||||
|
QString album_title;
|
||||||
|
QList<ArtworkRequestPtr> artwork_requests;
|
||||||
|
};
|
||||||
|
using AlbumCoverRequestPtr = SharedPtr<AlbumCoverRequest>;
|
||||||
|
|
||||||
|
class SearchRequest {
|
||||||
|
public:
|
||||||
|
explicit SearchRequest(const int _id, const QString &_artist, const QString &_album, const QString &_title) : id(_id), artist(_artist), album(_album), title(_title), finished(false) {}
|
||||||
int id;
|
int id;
|
||||||
QString artist;
|
QString artist;
|
||||||
QString album;
|
QString album;
|
||||||
QString title;
|
QString title;
|
||||||
|
QList<AlbumCoverRequestPtr> albumcover_requests;
|
||||||
|
CoverProviderSearchResults results;
|
||||||
|
bool finished;
|
||||||
};
|
};
|
||||||
using SearchRequestPtr = SharedPtr<SearchRequest>;
|
using SearchRequestPtr = SharedPtr<SearchRequest>;
|
||||||
|
|
||||||
|
class QueuedSearchRequest {
|
||||||
|
public:
|
||||||
|
explicit QueuedSearchRequest(SearchRequestPtr _search) : search(_search) {}
|
||||||
|
SearchRequestPtr search;
|
||||||
|
};
|
||||||
|
using QueuedSearchRequestPtr = SharedPtr<QueuedSearchRequest>;
|
||||||
|
|
||||||
|
class QueuedAlbumCoverRequest {
|
||||||
|
public:
|
||||||
|
explicit QueuedAlbumCoverRequest(SearchRequestPtr _search, AlbumCoverRequestPtr _albumcover) : search(_search), albumcover(_albumcover) {}
|
||||||
|
SearchRequestPtr search;
|
||||||
|
AlbumCoverRequestPtr albumcover;
|
||||||
|
};
|
||||||
|
using QueuedAlbumCoverRequestPtr = SharedPtr<QueuedAlbumCoverRequest>;
|
||||||
|
|
||||||
|
class QueuedArtworkRequest {
|
||||||
|
public:
|
||||||
|
explicit QueuedArtworkRequest(SearchRequestPtr _search, AlbumCoverRequestPtr _albumcover, ArtworkRequestPtr _artwork) : search(_search), albumcover(_albumcover), artwork(_artwork) {}
|
||||||
|
SearchRequestPtr search;
|
||||||
|
AlbumCoverRequestPtr albumcover;
|
||||||
|
ArtworkRequestPtr artwork;
|
||||||
|
};
|
||||||
|
using QueuedArtworkRequestPtr = SharedPtr<QueuedArtworkRequest>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoginCheck();
|
void LoginCheck();
|
||||||
void Login();
|
void Login();
|
||||||
void SendSearchRequest(SearchRequestPtr request);
|
|
||||||
JsonObjectResult ParseJsonObject(QNetworkReply *reply);
|
JsonObjectResult ParseJsonObject(QNetworkReply *reply);
|
||||||
|
void SendSearchRequest(SearchRequestPtr request);
|
||||||
|
void AddAlbumCoverRequest(SearchRequestPtr search_request, const QString &album_id, const QString &album_title);
|
||||||
|
void SendAlbumCoverRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request);
|
||||||
|
void AddArtworkRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, const QString &artwork_id);
|
||||||
|
void SendArtworkRequest(SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, ArtworkRequestPtr artwork_request);
|
||||||
void FinishAllSearches();
|
void FinishAllSearches();
|
||||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
|
|
||||||
@@ -66,13 +114,17 @@ class OpenTidalCoverProvider : public JsonCoverProvider {
|
|||||||
void OAuthFinished(const bool success, const QString &error = QString());
|
void OAuthFinished(const bool success, const QString &error = QString());
|
||||||
void FlushRequests();
|
void FlushRequests();
|
||||||
void HandleSearchReply(QNetworkReply *reply, OpenTidalCoverProvider::SearchRequestPtr search_request);
|
void HandleSearchReply(QNetworkReply *reply, OpenTidalCoverProvider::SearchRequestPtr search_request);
|
||||||
|
void HandleAlbumCoverReply(QNetworkReply *reply, SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request);
|
||||||
|
void HandleArtworkReply(QNetworkReply *reply, SearchRequestPtr search_request, AlbumCoverRequestPtr albumcover_request, ArtworkRequestPtr artwork_request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OAuthenticator *oauth_;
|
OAuthenticator *oauth_;
|
||||||
QTimer *timer_flush_requests_;
|
QTimer *timer_flush_requests_;
|
||||||
bool login_in_progress_;
|
bool login_in_progress_;
|
||||||
QDateTime last_login_attempt_;
|
QDateTime last_login_attempt_;
|
||||||
QQueue<SearchRequestPtr> search_requests_queue_;
|
QQueue<QueuedSearchRequestPtr> search_requests_queue_;
|
||||||
|
QQueue<QueuedAlbumCoverRequestPtr> albumcover_requests_queue_;
|
||||||
|
QQueue<QueuedArtworkRequestPtr> artwork_requests_queue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OPENTIDALCOVERPROVIDER_H
|
#endif // OPENTIDALCOVERPROVIDER_H
|
||||||
|
|||||||
@@ -63,9 +63,8 @@ CDDADevice::CDDADevice(const QUrl &url,
|
|||||||
timer_disc_changed_->setInterval(1s);
|
timer_disc_changed_->setInterval(1s);
|
||||||
|
|
||||||
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongsLoaded, this, &CDDADevice::SongsLoaded);
|
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongsLoaded, this, &CDDADevice::SongsLoaded);
|
||||||
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongsDurationLoaded, this, &CDDADevice::SongsLoaded);
|
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongsUpdated, this, &CDDADevice::SongsLoaded);
|
||||||
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongsMetadataLoaded, this, &CDDADevice::SongsLoaded);
|
QObject::connect(&cdda_song_loader_, &CDDASongLoader::LoadingFinished, this, &CDDADevice::SongLoadingFinished);
|
||||||
QObject::connect(&cdda_song_loader_, &CDDASongLoader::SongLoadingFinished, this, &CDDADevice::SongLoadingFinished);
|
|
||||||
QObject::connect(this, &CDDADevice::SongsDiscovered, collection_model_, &CollectionModel::AddReAddOrUpdate);
|
QObject::connect(this, &CDDADevice::SongsDiscovered, collection_model_, &CollectionModel::AddReAddOrUpdate);
|
||||||
QObject::connect(timer_disc_changed_, &QTimer::timeout, this, &CDDADevice::CheckDiscChanged);
|
QObject::connect(timer_disc_changed_, &QTimer::timeout, this, &CDDADevice::CheckDiscChanged);
|
||||||
|
|
||||||
@@ -139,4 +138,3 @@ void CDDADevice::SongLoadingFinished() {
|
|||||||
WatchForDiscChanges(true);
|
WatchForDiscChanges(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,3 +159,11 @@ bool CDDALister::Init() {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDDALister::AskForScan(const QString &id) const {
|
||||||
|
|
||||||
|
Q_UNUSED(id)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,11 +47,11 @@ class CDDALister : public DeviceLister {
|
|||||||
quint64 DeviceCapacity(const QString &id) override;
|
quint64 DeviceCapacity(const QString &id) override;
|
||||||
quint64 DeviceFreeSpace(const QString &id) override;
|
quint64 DeviceFreeSpace(const QString &id) override;
|
||||||
QVariantMap DeviceHardwareInfo(const QString &id) override;
|
QVariantMap DeviceHardwareInfo(const QString &id) override;
|
||||||
bool AskForScan(const QString&) const override { return false; }
|
bool AskForScan(const QString &id) const override;
|
||||||
QString MakeFriendlyName(const QString&) override;
|
QString MakeFriendlyName(const QString &id) override;
|
||||||
QList<QUrl> MakeDeviceUrls(const QString&) override;
|
QList<QUrl> MakeDeviceUrls(const QString &id) override;
|
||||||
void UnmountDevice(const QString&) override;
|
void UnmountDevice(const QString &id) override;
|
||||||
void UpdateDeviceFreeSpace(const QString&) override;
|
void UpdateDeviceFreeSpace(const QString &id) override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool CopyMusic() override { return false; }
|
bool CopyMusic() override { return false; }
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,16 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
CDDASongLoader::CDDASongLoader(const QUrl &url, QObject *parent)
|
CDDASongLoader::CDDASongLoader(const QUrl &url, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
url_(url),
|
url_(url),
|
||||||
network_(make_shared<NetworkAccessManager>()) {
|
network_(make_shared<NetworkAccessManager>()),
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
musicbrainz_client_(new MusicBrainzClient(network_, this)),
|
||||||
|
#endif
|
||||||
|
whatever_(false) {
|
||||||
|
|
||||||
QObject::connect(this, &CDDASongLoader::MusicBrainzDiscIdLoaded, this, &CDDASongLoader::LoadMusicBrainzCDTags);
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
QObject::connect(this, &CDDASongLoader::LoadTagsFromMusicBrainz, this, &CDDASongLoader::LoadTagsFromMusicBrainzSlot);
|
||||||
|
QObject::connect(musicbrainz_client_, &MusicBrainzClient::DiscIdFinished, this, &CDDASongLoader::LoadTagsFromMusicBrainzFinished);
|
||||||
|
#endif // HAVE_MUSICBRAINZ
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +100,7 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
Error(QStringLiteral("%1: %2").arg(error->code).arg(QString::fromUtf8(error->message)));
|
Error(QStringLiteral("%1: %2").arg(error->code).arg(QString::fromUtf8(error->message)));
|
||||||
}
|
}
|
||||||
if (!cdda) {
|
if (!cdda) {
|
||||||
Q_EMIT SongLoadingFinished();
|
Error(tr("Could not create cdiocddasrc"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +117,6 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
gst_object_unref(GST_OBJECT(cdda));
|
gst_object_unref(GST_OBJECT(cdda));
|
||||||
cdda = nullptr;
|
cdda = nullptr;
|
||||||
Error(tr("Error while setting CDDA device to ready state."));
|
Error(tr("Error while setting CDDA device to ready state."));
|
||||||
Q_EMIT SongLoadingFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +125,6 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
gst_object_unref(GST_OBJECT(cdda));
|
gst_object_unref(GST_OBJECT(cdda));
|
||||||
cdda = nullptr;
|
cdda = nullptr;
|
||||||
Error(tr("Error while setting CDDA device to pause state."));
|
Error(tr("Error while setting CDDA device to pause state."));
|
||||||
Q_EMIT SongLoadingFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +137,6 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
gst_object_unref(GST_OBJECT(cdda));
|
gst_object_unref(GST_OBJECT(cdda));
|
||||||
cdda = nullptr;
|
cdda = nullptr;
|
||||||
Error(tr("Error while querying CDDA tracks."));
|
Error(tr("Error while querying CDDA tracks."));
|
||||||
Q_EMIT SongLoadingFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +146,6 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
gst_object_unref(GST_OBJECT(cdda));
|
gst_object_unref(GST_OBJECT(cdda));
|
||||||
cdda = nullptr;
|
cdda = nullptr;
|
||||||
Error(tr("Error while querying CDDA tracks."));
|
Error(tr("Error while querying CDDA tracks."));
|
||||||
Q_EMIT SongLoadingFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,9 +160,12 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
song.set_track(track_number);
|
song.set_track(track_number);
|
||||||
songs.insert(track_number, song);
|
songs.insert(track_number, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_EMIT SongsLoaded(songs.values());
|
Q_EMIT SongsLoaded(songs.values());
|
||||||
|
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
gst_tag_register_musicbrainz_tags();
|
gst_tag_register_musicbrainz_tags();
|
||||||
|
#endif // HAVE_MUSICBRAINZ
|
||||||
|
|
||||||
GstElement *pipeline = gst_pipeline_new("pipeline");
|
GstElement *pipeline = gst_pipeline_new("pipeline");
|
||||||
GstElement *sink = gst_element_factory_make("fakesink", nullptr);
|
GstElement *sink = gst_element_factory_make("fakesink", nullptr);
|
||||||
@@ -172,7 +178,9 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
int track_artist_tags = 0;
|
int track_artist_tags = 0;
|
||||||
int track_album_tags = 0;
|
int track_album_tags = 0;
|
||||||
int track_title_tags = 0;
|
int track_title_tags = 0;
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
QString musicbrainz_discid;
|
QString musicbrainz_discid;
|
||||||
|
#endif // HAVE_MUSICBRAINZ
|
||||||
GstMessageType msg_filter = static_cast<GstMessageType>(GST_MESSAGE_TOC|GST_MESSAGE_TAG);
|
GstMessageType msg_filter = static_cast<GstMessageType>(GST_MESSAGE_TOC|GST_MESSAGE_TAG);
|
||||||
while (msg_filter != 0 && (msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(pipeline), GST_SECOND * 5, msg_filter))) {
|
while (msg_filter != 0 && (msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(pipeline), GST_SECOND * 5, msg_filter))) {
|
||||||
|
|
||||||
@@ -334,66 +342,85 @@ void CDDASongLoader::LoadSongsFromCDDA() {
|
|||||||
// This will also cause cdda to be unref'd.
|
// This will also cause cdda to be unref'd.
|
||||||
gst_object_unref(pipeline);
|
gst_object_unref(pipeline);
|
||||||
|
|
||||||
Q_EMIT SongsMetadataLoaded(songs.values());
|
|
||||||
|
|
||||||
if ((track_artist_tags >= total_tracks && track_album_tags >= total_tracks && track_title_tags >= total_tracks)) {
|
if ((track_artist_tags >= total_tracks && track_album_tags >= total_tracks && track_title_tags >= total_tracks)) {
|
||||||
qLog(Info) << "Songs loaded from CD-Text";
|
qLog(Info) << "Songs loaded from CD-Text";
|
||||||
Q_EMIT SongLoadingFinished();
|
Q_EMIT SongsUpdated(songs.values());
|
||||||
|
Q_EMIT LoadingFinished();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
if (musicbrainz_discid.isEmpty()) {
|
if (musicbrainz_discid.isEmpty()) {
|
||||||
qLog(Info) << "CD is missing tags";
|
qLog(Info) << "CD is missing tags";
|
||||||
|
Q_EMIT LoadingFinished();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Info) << "MusicBrainz Disc ID:" << musicbrainz_discid;
|
qLog(Info) << "MusicBrainz Disc ID:" << musicbrainz_discid;
|
||||||
Q_EMIT MusicBrainzDiscIdLoaded(musicbrainz_discid);
|
Q_EMIT LoadTagsFromMusicBrainz(musicbrainz_discid, songs);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
Q_EMIT LoadingFinished();
|
||||||
|
#endif // HAVE_MUSICBRAINZ
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_MUSICBRAINZ
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
|
||||||
void CDDASongLoader::LoadMusicBrainzCDTags(const QString &musicbrainz_discid) const {
|
void CDDASongLoader::LoadTagsFromMusicBrainzSlot(const QString &musicbrainz_discid, const QMap<int, Song> &songs) {
|
||||||
|
|
||||||
MusicBrainzClient *musicbrainz_client = new MusicBrainzClient(network_);
|
musicbrainz_discid_ = musicbrainz_discid;
|
||||||
QObject::connect(musicbrainz_client, &MusicBrainzClient::DiscIdFinished, this, &CDDASongLoader::MusicBrainzCDTagsLoaded);
|
musicbrainz_songs_ = songs;
|
||||||
musicbrainz_client->StartDiscIdRequest(musicbrainz_discid);
|
musicbrainz_client_->StartDiscIdRequest(musicbrainz_discid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDDASongLoader::MusicBrainzCDTagsLoaded(const QString &artist, const QString &album, const MusicBrainzClient::ResultList &results) {
|
void CDDASongLoader::LoadTagsFromMusicBrainzFinished(const QString &musicbrainz_discid, const MusicBrainzClient::ResultList &results, const QString &error) {
|
||||||
|
|
||||||
MusicBrainzClient *musicbrainz_client = qobject_cast<MusicBrainzClient*>(sender());
|
if (musicbrainz_discid != musicbrainz_discid_) {
|
||||||
musicbrainz_client->deleteLater();
|
|
||||||
|
|
||||||
if (results.empty()) {
|
|
||||||
Q_EMIT SongLoadingFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList songs;
|
QMap<int, Song> songs = musicbrainz_songs_;
|
||||||
songs.reserve(results.count());
|
musicbrainz_discid_.clear();
|
||||||
int track_number = 0;
|
musicbrainz_songs_.clear();
|
||||||
for (const MusicBrainzClient::Result &result : results) {
|
|
||||||
++track_number;
|
if (!error.isEmpty()) {
|
||||||
Song song(Song::Source::CDDA);
|
Error(error);
|
||||||
song.set_artist(artist);
|
return;
|
||||||
song.set_album(album);
|
|
||||||
song.set_title(result.title_);
|
|
||||||
song.set_length_nanosec(result.duration_msec_ * kNsecPerMsec);
|
|
||||||
song.set_track(track_number);
|
|
||||||
song.set_year(result.year_);
|
|
||||||
song.set_id(track_number);
|
|
||||||
song.set_filetype(Song::FileType::CDDA);
|
|
||||||
song.set_valid(true);
|
|
||||||
// We need to set URL, that's how playlist will find the correct item to update
|
|
||||||
song.set_url(GetUrlFromTrack(track_number));
|
|
||||||
songs << song;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_EMIT SongsMetadataLoaded(songs);
|
if (results.empty()) {
|
||||||
Q_EMIT SongLoadingFinished();
|
Q_EMIT LoadingFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MusicBrainzClient::Result &result : results) {
|
||||||
|
if (songs.contains(result.track_)) {
|
||||||
|
Song &song = songs[result.track_];
|
||||||
|
song.set_valid(true);
|
||||||
|
song.set_id(result.track_);
|
||||||
|
song.set_track(result.track_);
|
||||||
|
song.set_artist(result.artist_);
|
||||||
|
song.set_artistsort(result.sort_artist_);
|
||||||
|
song.set_album(result.album_);
|
||||||
|
song.set_title(result.title_);
|
||||||
|
song.set_track(result.track_);
|
||||||
|
song.set_year(result.year_);
|
||||||
|
song.set_url(GetUrlFromTrack(song.track()));
|
||||||
|
if (song.length_nanosec() <= 0) {
|
||||||
|
song.set_length_nanosec(result.duration_msec_ * kNsecPerMsec);
|
||||||
|
}
|
||||||
|
if (!result.album_artist_.isEmpty() && result.album_artist_ != result.artist_) {
|
||||||
|
song.set_albumartist(result.album_artist_);
|
||||||
|
}
|
||||||
|
if (!result.sort_album_artist_.isEmpty() && result.sort_album_artist_ != result.sort_artist_) {
|
||||||
|
song.set_albumartistsort(result.sort_album_artist_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT SongsUpdated(songs.values());
|
||||||
|
Q_EMIT LoadingFinished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +429,8 @@ void CDDASongLoader::MusicBrainzCDTagsLoaded(const QString &artist, const QStrin
|
|||||||
void CDDASongLoader::Error(const QString &error) {
|
void CDDASongLoader::Error(const QString &error) {
|
||||||
|
|
||||||
qLog(Error) << error;
|
qLog(Error) << error;
|
||||||
Q_EMIT SongsDurationLoaded(SongList(), error);
|
|
||||||
|
Q_EMIT LoadError(error);
|
||||||
|
Q_EMIT LoadingFinished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,10 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QFuture>
|
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
@@ -58,24 +59,31 @@ class CDDASongLoader : public QObject {
|
|||||||
QUrl GetUrlFromTrack(const int track_number) const;
|
QUrl GetUrlFromTrack(const int track_number) const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void SongsLoadError(const QString &error);
|
|
||||||
void SongsLoaded(const SongList &songs);
|
void SongsLoaded(const SongList &songs);
|
||||||
void SongLoadingFinished();
|
void SongsUpdated(const SongList &songs);
|
||||||
void SongsDurationLoaded(const SongList &songs, const QString &error = QString());
|
void LoadError(const QString &error);
|
||||||
void SongsMetadataLoaded(const SongList &songs);
|
void LoadingFinished();
|
||||||
void MusicBrainzDiscIdLoaded(const QString &musicbrainz_discid);
|
void LoadTagsFromMusicBrainz(const QString &musicbrainz_discid, const QMap<int, Song> &songs);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
#ifdef HAVE_MUSICBRAINZ
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
void LoadMusicBrainzCDTags(const QString &musicbrainz_discid) const;
|
void LoadTagsFromMusicBrainzSlot(const QString &musicbrainz_discid, const QMap<int, Song> &songs);
|
||||||
void MusicBrainzCDTagsLoaded(const QString &artist, const QString &album, const MusicBrainzClient::ResultList &results);
|
void LoadTagsFromMusicBrainzFinished(const QString &musicbrainz_discid, const MusicBrainzClient::ResultList &results, const QString &error);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QUrl url_;
|
const QUrl url_;
|
||||||
SharedPtr<NetworkAccessManager> network_;
|
SharedPtr<NetworkAccessManager> network_;
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
MusicBrainzClient *musicbrainz_client_;
|
||||||
|
#endif
|
||||||
QMutex mutex_load_;
|
QMutex mutex_load_;
|
||||||
QFuture<void> loading_future_;
|
QFuture<void> loading_future_;
|
||||||
|
#ifdef HAVE_MUSICBRAINZ
|
||||||
|
QString musicbrainz_discid_;
|
||||||
|
QMap<int, Song> musicbrainz_songs_;
|
||||||
|
#endif
|
||||||
|
bool whatever_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CDDASONGLOADER_H
|
#endif // CDDASONGLOADER_H
|
||||||
|
|||||||
@@ -227,8 +227,7 @@ void DeviceDatabaseBackend::SetDeviceOptions(const int id, const QString &friend
|
|||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
SqlQuery q(db);
|
SqlQuery q(db);
|
||||||
q.prepare(QStringLiteral(
|
q.prepare(QStringLiteral("UPDATE devices"
|
||||||
"UPDATE devices"
|
|
||||||
" SET friendly_name=:friendly_name,"
|
" SET friendly_name=:friendly_name,"
|
||||||
" icon=:icon_name,"
|
" icon=:icon_name,"
|
||||||
" transcode_mode=:transcode_mode,"
|
" transcode_mode=:transcode_mode,"
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ class DeviceDatabaseBackend : public QObject {
|
|||||||
private:
|
private:
|
||||||
SharedPtr<Database> db_;
|
SharedPtr<Database> db_;
|
||||||
QThread *original_thread_;
|
QThread *original_thread_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEVICEDATABASEBACKEND_H
|
#endif // DEVICEDATABASEBACKEND_H
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class DeviceItemDelegate : public CollectionItemDelegate {
|
|||||||
static const int kIconPadding;
|
static const int kIconPadding;
|
||||||
|
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const override;
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeviceView : public AutoExpandingTreeView {
|
class DeviceView : public AutoExpandingTreeView {
|
||||||
@@ -80,7 +79,7 @@ class DeviceView : public AutoExpandingTreeView {
|
|||||||
bool CanRecursivelyExpand(const QModelIndex &idx) const override;
|
bool CanRecursivelyExpand(const QModelIndex &idx) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void contextMenuEvent(QContextMenuEvent*) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
|||||||
@@ -160,7 +160,10 @@ QVariantList GioLister::DeviceIcons(const QString &id) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GioLister::DeviceManufacturer(const QString &id) { Q_UNUSED(id); return QString(); }
|
QString GioLister::DeviceManufacturer(const QString &id) {
|
||||||
|
Q_UNUSED(id);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
QString GioLister::DeviceModel(const QString &id) {
|
QString GioLister::DeviceModel(const QString &id) {
|
||||||
|
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ class GioLister : public DeviceLister {
|
|||||||
static void VolumeAddedCallback(GVolumeMonitor *volume_monitor, GVolume *volume, gpointer instance);
|
static void VolumeAddedCallback(GVolumeMonitor *volume_monitor, GVolume *volume, gpointer instance);
|
||||||
static void VolumeRemovedCallback(GVolumeMonitor *volume_monitor, GVolume *volume, gpointer instance);
|
static void VolumeRemovedCallback(GVolumeMonitor *volume_monitor, GVolume *volume, gpointer instance);
|
||||||
|
|
||||||
static void MountAddedCallback(GVolumeMonitor *volume_monitor, GMount*, gpointer instance);
|
static void MountAddedCallback(GVolumeMonitor *volume_monitor, GMount *mount, gpointer instance);
|
||||||
static void MountChangedCallback(GVolumeMonitor *volume_monitor, GMount*, gpointer instance);
|
static void MountChangedCallback(GVolumeMonitor *volume_monitor, GMount *mount, gpointer instance);
|
||||||
static void MountRemovedCallback(GVolumeMonitor *volume_monitor, GMount *mount, gpointer instance);
|
static void MountRemovedCallback(GVolumeMonitor *volume_monitor, GMount *mount, gpointer instance);
|
||||||
|
|
||||||
static void VolumeMountFinished(GObject *object, GAsyncResult *result, gpointer instance);
|
static void VolumeMountFinished(GObject *object, GAsyncResult *result, gpointer instance);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user