Compare commits
277 Commits
1.2.10
...
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 | ||
|
|
700f7dbe36 | ||
|
|
0487118dad | ||
|
|
f3d088e48b | ||
|
|
f8afd49fcf | ||
|
|
363fcb5aba | ||
|
|
183aba4181 | ||
|
|
742be01aa6 | ||
|
|
38c8054873 | ||
|
|
da9e9840b8 | ||
|
|
c4646531b0 | ||
|
|
65d9b6a9e9 | ||
|
|
046f40fbca | ||
|
|
a0ca50ac30 | ||
|
|
d939733675 | ||
|
|
61a8a3a84a | ||
|
|
d4858a338c | ||
|
|
31380a5bd4 | ||
|
|
e45b9aabeb | ||
|
|
27e782d8cf | ||
|
|
0bfc2ee198 | ||
|
|
e7fc4b1706 | ||
|
|
6dea1a2149 | ||
|
|
7844a2b932 | ||
|
|
96a53bfbe5 | ||
|
|
fe5fbae4b4 | ||
|
|
a9140232e5 | ||
|
|
835090dd96 | ||
|
|
af5590dcb1 | ||
|
|
26b5588d7d | ||
|
|
390bf049f2 | ||
|
|
321272b695 | ||
|
|
342805e0f3 | ||
|
|
e614626913 | ||
|
|
2ddacf2f98 | ||
|
|
a47531d4ce | ||
|
|
84b758e395 | ||
|
|
51b69a85c4 | ||
|
|
52774a3222 | ||
|
|
9030b2567b | ||
|
|
ee7bb449a5 | ||
|
|
d901258f11 | ||
|
|
6372c5ee7d | ||
|
|
75f0402793 | ||
|
|
20e5c014ef | ||
|
|
1ebc32c3aa | ||
|
|
a5f94b608b | ||
|
|
e0d61223a4 | ||
|
|
459eea5bc4 | ||
|
|
09d02c53a3 | ||
|
|
61a701554e | ||
|
|
d280e6426f | ||
|
|
5b9bb3efa7 | ||
|
|
b8cbe49f8c | ||
|
|
633e5707ef | ||
|
|
d54290c3a7 | ||
|
|
3ef2b53e46 | ||
|
|
d3a4dd6da6 | ||
|
|
0158f7f08a | ||
|
|
8cea020fac | ||
|
|
f6b38fecb0 | ||
|
|
5e2729fafe | ||
|
|
19dce1c25d | ||
|
|
00bb722e25 | ||
|
|
cbaf4d3121 | ||
|
|
4b5370044b | ||
|
|
ffbe1ec9fd | ||
|
|
53e43db91b | ||
|
|
2858cdabc2 | ||
|
|
cf74eeb120 | ||
|
|
790a1b4dbf | ||
|
|
ee6332af1e | ||
|
|
bf0704f6b2 | ||
|
|
ae13fe7f52 | ||
|
|
90678e72ac | ||
|
|
a0ec244008 | ||
|
|
fba4f84fb6 | ||
|
|
950774f1c8 | ||
|
|
340bc21537 | ||
|
|
a86ba4dffc | ||
|
|
d6bc6e33c0 | ||
|
|
7e128a9af5 | ||
|
|
0f0746be9d | ||
|
|
bec3fe9fd5 | ||
|
|
83c666baf9 | ||
|
|
b9b54e6e96 | ||
|
|
b2ff6240eb | ||
|
|
26a7c74a24 | ||
|
|
a34954ec4a | ||
|
|
349ab62e75 | ||
|
|
65e960f2c5 | ||
|
|
e22fef8ca4 | ||
|
|
3e99045e2c | ||
|
|
4fcade273e | ||
|
|
5eaff0d26e | ||
|
|
5b22f12b4a | ||
|
|
5f85c2e7a5 | ||
|
|
4fb5a7b6bc | ||
|
|
04c6c862c4 | ||
|
|
baec45f742 | ||
|
|
9efdbd2c10 | ||
|
|
d8800b80d5 | ||
|
|
ec715abb0d | ||
|
|
1485801efb | ||
|
|
4f9ac3d33a | ||
|
|
1577ce4d67 |
@@ -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: ''
|
||||||
|
|||||||
194
.github/workflows/build.yml
vendored
194
.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@v4
|
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: |
|
||||||
@@ -156,7 +158,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
fedora_version: [ '39', '40', '41', '42' ]
|
fedora_version: [ '41', '42', '43' ]
|
||||||
container:
|
container:
|
||||||
image: fedora:${{matrix.fedora_version}}
|
image: fedora:${{matrix.fedora_version}}
|
||||||
steps:
|
steps:
|
||||||
@@ -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@v4
|
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@v4
|
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@v4
|
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@v4
|
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', 'oracular', '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@v4
|
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', 'oracular', '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@v4
|
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@v4
|
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.0
|
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@v4
|
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.7
|
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 }}
|
||||||
@@ -788,7 +811,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set MACOSX_DEPLOYMENT_TARGET
|
- name: Set MACOSX_DEPLOYMENT_TARGET
|
||||||
run: |
|
run: |
|
||||||
for i in 13 14 15; do
|
for i in 12 13 14 15; do
|
||||||
if [ -d "/Library/Developer/CommandLineTools/SDKs/MacOSX${i}.sdk" ]; then
|
if [ -d "/Library/Developer/CommandLineTools/SDKs/MacOSX${i}.sdk" ]; then
|
||||||
echo "Using macOS SDK ${i}"
|
echo "Using macOS SDK ${i}"
|
||||||
echo "MACOSX_DEPLOYMENT_TARGET=${i}.0" >> $GITHUB_ENV
|
echo "MACOSX_DEPLOYMENT_TARGET=${i}.0" >> $GITHUB_ENV
|
||||||
@@ -818,20 +841,20 @@ jobs:
|
|||||||
rm -f uninstall.sh
|
rm -f uninstall.sh
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
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'
|
||||||
@@ -946,7 +969,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set MACOSX_DEPLOYMENT_TARGET
|
- name: Set MACOSX_DEPLOYMENT_TARGET
|
||||||
run: |
|
run: |
|
||||||
for i in 13 14 15; do
|
for i in 12 13 14 15; do
|
||||||
if [ -d "/Library/Developer/CommandLineTools/SDKs/MacOSX${i}.sdk" ]; then
|
if [ -d "/Library/Developer/CommandLineTools/SDKs/MacOSX${i}.sdk" ]; then
|
||||||
echo "Using macOS SDK ${i}"
|
echo "Using macOS SDK ${i}"
|
||||||
echo "MACOSX_DEPLOYMENT_TARGET=${i}.0" >> $GITHUB_ENV
|
echo "MACOSX_DEPLOYMENT_TARGET=${i}.0" >> $GITHUB_ENV
|
||||||
@@ -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@v4
|
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@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -1246,12 +1269,42 @@ jobs:
|
|||||||
build-windows-msvc:
|
build-windows-msvc:
|
||||||
name: Build Windows MSVC
|
name: Build Windows MSVC
|
||||||
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
if: github.repository != 'strawberrymusicplayer/strawberry-private' && github.ref != 'refs/heads/l10n_master'
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [ 'x86', 'x86_64' ]
|
include:
|
||||||
buildtype: [ 'release' ]
|
- name: "x86_64 debug"
|
||||||
|
runner: windows-2022
|
||||||
|
arch: x86_64
|
||||||
|
buildtype: debug
|
||||||
|
|
||||||
|
- name: "x86_64 release"
|
||||||
|
runner: windows-2022
|
||||||
|
arch: x86_64
|
||||||
|
buildtype: release
|
||||||
|
|
||||||
|
- name: "x86 debug"
|
||||||
|
runner: windows-2022
|
||||||
|
arch: x86
|
||||||
|
buildtype: debug
|
||||||
|
|
||||||
|
- name: "x86 release"
|
||||||
|
runner: windows-2022
|
||||||
|
arch: x86
|
||||||
|
buildtype: release
|
||||||
|
|
||||||
|
- name: "arm64 debug"
|
||||||
|
runner: windows-11-arm
|
||||||
|
arch: arm64
|
||||||
|
buildtype: debug
|
||||||
|
|
||||||
|
- name: "arm64 release"
|
||||||
|
runner: windows-11-arm
|
||||||
|
arch: arm64
|
||||||
|
buildtype: release
|
||||||
|
|
||||||
|
runs-on: ${{matrix.runner}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Set prefix path
|
- name: Set prefix path
|
||||||
@@ -1265,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
|
||||||
@@ -1293,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
|
||||||
@@ -1347,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@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -1364,15 +1433,18 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
|
|
||||||
- name: Set ENABLE_WIN32_CONSOLE (debug)
|
- name: Set ENABLE_WIN32_CONSOLE
|
||||||
if: matrix.buildtype == 'debug'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: echo "win32_console=ON" >> $GITHUB_ENV
|
run: echo "enable_win32_console=$(test "${{matrix.buildtype}}" = "debug" && echo "ON" || echo "OFF")" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set ENABLE_WIN32_CONSOLE (release)
|
- name: Set ENABLE_SPOTIFY
|
||||||
if: matrix.buildtype == 'release'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: echo "win32_console=OFF" >> $GITHUB_ENV
|
run: echo "enable_spotify=$(test -f "${{env.prefix_path_unix}}/lib/gstreamer-1.0/gstspotify.dll" && echo "ON" || echo "OFF")" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Remove -lm from .pc files
|
||||||
|
if: matrix.arch == 'arm64'
|
||||||
|
shell: bash
|
||||||
|
run: sed -i 's/\-lm$//g' ${{env.prefix_path_unix}}/lib/pkgconfig/*.pc
|
||||||
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
shell: cmd
|
shell: cmd
|
||||||
@@ -1384,14 +1456,14 @@ jobs:
|
|||||||
-DCMAKE_BUILD_TYPE="${{env.cmake_buildtype}}"
|
-DCMAKE_BUILD_TYPE="${{env.cmake_buildtype}}"
|
||||||
-DCMAKE_PREFIX_PATH="${{env.prefix_path_forwardslash}}/lib/cmake"
|
-DCMAKE_PREFIX_PATH="${{env.prefix_path_forwardslash}}/lib/cmake"
|
||||||
-DARCH="${{matrix.arch}}"
|
-DARCH="${{matrix.arch}}"
|
||||||
-DENABLE_WIN32_CONSOLE=${{env.win32_console}}
|
-DENABLE_WIN32_CONSOLE=${{env.enable_win32_console}}
|
||||||
-DPKG_CONFIG_EXECUTABLE="${{env.prefix_path_forwardslash}}/bin/pkg-config.exe"
|
-DPKG_CONFIG_EXECUTABLE="${{env.prefix_path_forwardslash}}/bin/pkg-config.exe"
|
||||||
-DICU_ROOT="${{env.prefix_path_forwardslash}}"
|
-DICU_ROOT="${{env.prefix_path_forwardslash}}"
|
||||||
-DENABLE_GIO=OFF
|
-DENABLE_GIO=OFF
|
||||||
-DENABLE_AUDIOCD=OFF
|
-DENABLE_AUDIOCD=OFF
|
||||||
-DENABLE_MTP=OFF
|
-DENABLE_MTP=OFF
|
||||||
-DENABLE_GPOD=OFF
|
-DENABLE_GPOD=OFF
|
||||||
-DENABLE_SPOTIFY=ON
|
-DENABLE_SPOTIFY=${{env.enable_spotify}}
|
||||||
|
|
||||||
- name: Run Make
|
- name: Run Make
|
||||||
shell: cmd
|
shell: cmd
|
||||||
@@ -1581,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@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: SSH Setup
|
- name: SSH Setup
|
||||||
@@ -1629,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@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Show release assets
|
- name: Show release assets
|
||||||
@@ -1637,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@v4
|
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")
|
||||||
@@ -682,6 +707,7 @@ set(SOURCES
|
|||||||
src/lyrics/htmllyricsprovider.cpp
|
src/lyrics/htmllyricsprovider.cpp
|
||||||
src/lyrics/ovhlyricsprovider.cpp
|
src/lyrics/ovhlyricsprovider.cpp
|
||||||
src/lyrics/lololyricsprovider.cpp
|
src/lyrics/lololyricsprovider.cpp
|
||||||
|
src/lyrics/geniuslyricsprovider.cpp
|
||||||
src/lyrics/musixmatchlyricsprovider.cpp
|
src/lyrics/musixmatchlyricsprovider.cpp
|
||||||
src/lyrics/chartlyricsprovider.cpp
|
src/lyrics/chartlyricsprovider.cpp
|
||||||
src/lyrics/songlyricscomlyricsprovider.cpp
|
src/lyrics/songlyricscomlyricsprovider.cpp
|
||||||
@@ -689,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
|
||||||
@@ -782,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
|
||||||
|
|
||||||
@@ -979,6 +1004,7 @@ set(HEADERS
|
|||||||
src/lyrics/htmllyricsprovider.h
|
src/lyrics/htmllyricsprovider.h
|
||||||
src/lyrics/ovhlyricsprovider.h
|
src/lyrics/ovhlyricsprovider.h
|
||||||
src/lyrics/lololyricsprovider.h
|
src/lyrics/lololyricsprovider.h
|
||||||
|
src/lyrics/geniuslyricsprovider.h
|
||||||
src/lyrics/musixmatchlyricsprovider.h
|
src/lyrics/musixmatchlyricsprovider.h
|
||||||
src/lyrics/chartlyricsprovider.h
|
src/lyrics/chartlyricsprovider.h
|
||||||
src/lyrics/songlyricscomlyricsprovider.h
|
src/lyrics/songlyricscomlyricsprovider.h
|
||||||
@@ -986,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
|
||||||
@@ -1074,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
|
||||||
|
|
||||||
@@ -1452,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
|
||||||
@@ -1461,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)
|
||||||
@@ -1537,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>
|
||||||
@@ -1552,9 +1600,11 @@ 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>
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
@@ -1573,10 +1623,6 @@ if(APPLE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAVE_DISCORD_RPC)
|
|
||||||
target_link_libraries(strawberry_lib PRIVATE discord-rpc)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(strawberry PUBLIC strawberry_lib)
|
target_link_libraries(strawberry PUBLIC strawberry_lib)
|
||||||
|
|
||||||
if(NOT APPLE)
|
if(NOT APPLE)
|
||||||
|
|||||||
94
Changelog
94
Changelog
@@ -2,6 +2,100 @@ 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):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed scrobbling for radio streams.
|
||||||
|
* Fixed CDDA memory leaks.
|
||||||
|
* Fixed device view CDDA loading (#1676).
|
||||||
|
* Fixed collection directory editing (#1767).
|
||||||
|
* Fixed devices sometimes being duplicated in the database.
|
||||||
|
* Fixed alternating playlist row colors with Windows 11 style.
|
||||||
|
* Fixed broken file filter for GME formats.
|
||||||
|
* Fixed collection scanning for GME formats.
|
||||||
|
* Fixed Chartlyrics.
|
||||||
|
* Fixed network cache file descriptor leak on lyrics search with workaround for QTBUG-135641.
|
||||||
|
* Fixed parsing Tidal urls with certain stream URL replies.
|
||||||
|
* Fixed pixelated window icon on Wayland (#1753).
|
||||||
|
* Fixed saving collection grouping with special characters in the name (#1758).
|
||||||
|
* Fixed Spotify token not automatically updated on renewal when playing (#1769).
|
||||||
|
* (macOS/Windows) Fixed network cache file descriptor leak with patch for QTBUG-135641.
|
||||||
|
* (Windows|MSVC) Fixed installer to not restart the computer after installing Visual C++ Redistributable.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Implemented edit tag dialog reset for year, track, disc and rating.
|
||||||
|
* Added ALAC to supported filetypes for iPods.
|
||||||
|
* Added CD-TEXT support.
|
||||||
|
* Added back Genius lyrics.
|
||||||
|
* Added support for reporting more info to ListenBrainz.
|
||||||
|
* Added support for BPM, mood and initial key tags.
|
||||||
|
* Added support for sort tags to collection, playlists and smart playlists.
|
||||||
|
|
||||||
|
Version 1.2.11 (2025.05.15):
|
||||||
|
|
||||||
|
* Fixed playlist songs sometimes not updated with new cover.
|
||||||
|
* Fixed context album cover showing even when it's disabled in the setting (#1744).
|
||||||
|
* Fixed crash when dragging songs to a closed playlist (#1741).
|
||||||
|
* Enable startup notify in desktop file.
|
||||||
|
* (Windows|MSVC) Add experimental support for native ARM64 builds.
|
||||||
|
* (Windows|MinGW) Fixed crash on exit.
|
||||||
|
|
||||||
Version 1.2.10 (2025.04.18):
|
Version 1.2.10 (2025.04.18):
|
||||||
|
|
||||||
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 [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/) and [elyrics.net](https://www.elyrics.net/)
|
|
||||||
* 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 10)
|
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}")
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<file>schema/schema-18.sql</file>
|
<file>schema/schema-18.sql</file>
|
||||||
<file>schema/schema-19.sql</file>
|
<file>schema/schema-19.sql</file>
|
||||||
<file>schema/schema-20.sql</file>
|
<file>schema/schema-20.sql</file>
|
||||||
|
<file>schema/schema-21.sql</file>
|
||||||
<file>schema/device-schema.sql</file>
|
<file>schema/device-schema.sql</file>
|
||||||
<file>style/strawberry.css</file>
|
<file>style/strawberry.css</file>
|
||||||
<file>style/smartplaylistsearchterm.css</file>
|
<file>style/smartplaylistsearchterm.css</file>
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ CREATE TABLE device_%deviceid_subdirectories (
|
|||||||
CREATE TABLE device_%deviceid_songs (
|
CREATE TABLE device_%deviceid_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -22,7 +26,9 @@ CREATE TABLE device_%deviceid_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -86,7 +92,11 @@ CREATE TABLE device_%deviceid_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -94,4 +104,4 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
|
|||||||
|
|
||||||
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
||||||
|
|
||||||
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
UPDATE devices SET schema_version=6 WHERE ROWID=%deviceid;
|
||||||
|
|||||||
43
data/schema/schema-21.sql
Normal file
43
data/schema/schema-21.sql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
DROP INDEX IF EXISTS idx_albumartistsort;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_albumsort;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_artistsort;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_composersort;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_performersort;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_titlesort;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN albumartistsort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN albumsort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN artistsort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN composersort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN performersort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN titlesort TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN bpm REAL;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN mood TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE %allsongstables ADD COLUMN initial_key TEXT;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_albumartistsort ON songs (albumartistsort);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_albumsort ON songs (album);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_artistsort ON songs (artistsort);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_composersort ON songs (title);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_performersort ON songs (title);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_titlesort ON songs (title);
|
||||||
|
|
||||||
|
UPDATE schema_version SET version=21;
|
||||||
@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|||||||
|
|
||||||
DELETE FROM schema_version;
|
DELETE FROM schema_version;
|
||||||
|
|
||||||
INSERT INTO schema_version (version) VALUES (20);
|
INSERT INTO schema_version (version) VALUES (21);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS directories (
|
CREATE TABLE IF NOT EXISTS directories (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
@@ -20,9 +20,13 @@ CREATE TABLE IF NOT EXISTS subdirectories (
|
|||||||
CREATE TABLE IF NOT EXISTS songs (
|
CREATE TABLE IF NOT EXISTS songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -30,7 +34,9 @@ CREATE TABLE IF NOT EXISTS songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -94,16 +100,24 @@ CREATE TABLE IF NOT EXISTS songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS subsonic_songs (
|
CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -111,7 +125,9 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -175,16 +191,24 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -192,7 +216,9 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -256,16 +282,24 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -273,7 +307,9 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -337,16 +373,24 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_songs (
|
CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -354,7 +398,9 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -418,16 +464,24 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS spotify_artists_songs (
|
CREATE TABLE IF NOT EXISTS spotify_artists_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -435,7 +489,9 @@ CREATE TABLE IF NOT EXISTS spotify_artists_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -499,16 +555,24 @@ CREATE TABLE IF NOT EXISTS spotify_artists_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS spotify_albums_songs (
|
CREATE TABLE IF NOT EXISTS spotify_albums_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -516,7 +580,9 @@ CREATE TABLE IF NOT EXISTS spotify_albums_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -580,16 +646,24 @@ CREATE TABLE IF NOT EXISTS spotify_albums_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS spotify_songs (
|
CREATE TABLE IF NOT EXISTS spotify_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -597,7 +671,9 @@ CREATE TABLE IF NOT EXISTS spotify_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -661,16 +737,24 @@ CREATE TABLE IF NOT EXISTS spotify_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -678,7 +762,9 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -742,16 +828,24 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -759,7 +853,9 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -823,16 +919,24 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
@@ -840,7 +944,9 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -904,7 +1010,11 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -931,9 +1041,13 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
|||||||
playlist_url TEXT,
|
playlist_url TEXT,
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
|
titlesort TEXT,
|
||||||
album TEXT,
|
album TEXT,
|
||||||
|
albumsort TEXT,
|
||||||
artist TEXT,
|
artist TEXT,
|
||||||
|
artistsort TEXT,
|
||||||
albumartist TEXT,
|
albumartist TEXT,
|
||||||
|
albumartistsort TEXT,
|
||||||
track INTEGER,
|
track INTEGER,
|
||||||
disc INTEGER,
|
disc INTEGER,
|
||||||
year INTEGER,
|
year INTEGER,
|
||||||
@@ -941,7 +1055,9 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
|||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER DEFAULT 0,
|
compilation INTEGER DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
composersort TEXT,
|
||||||
performer TEXT,
|
performer TEXT,
|
||||||
|
performersort TEXT,
|
||||||
grouping TEXT,
|
grouping TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
lyrics TEXT,
|
lyrics TEXT,
|
||||||
@@ -1005,7 +1121,11 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
|||||||
musicbrainz_work_id TEXT,
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
ebur128_integrated_loudness_lufs REAL,
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
ebur128_loudness_range_lu REAL
|
ebur128_loudness_range_lu REAL,
|
||||||
|
|
||||||
|
bpm REAL,
|
||||||
|
mood TEXT,
|
||||||
|
initial_key TEXT
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1032,10 +1152,22 @@ CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, arti
|
|||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_albumartist ON songs (albumartist);
|
CREATE INDEX IF NOT EXISTS idx_albumartist ON songs (albumartist);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_albumartistsort ON songs (albumartistsort);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_artist ON songs (artist);
|
CREATE INDEX IF NOT EXISTS idx_artist ON songs (artist);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_artistsort ON songs (artistsort);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_album ON songs (album);
|
CREATE INDEX IF NOT EXISTS idx_album ON songs (album);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_albumsort ON songs (album);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
|
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_titlesort ON songs (title);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_composersort ON songs (title);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_performersort ON songs (title);
|
||||||
|
|
||||||
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
|
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
|
||||||
|
|||||||
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 Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
|
- 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 Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net</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,12 @@
|
|||||||
</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.11" date="2025-05-15"/>
|
||||||
<release version="1.2.10" date="2025-04-18"/>
|
<release version="1.2.10" date="2025-04-18"/>
|
||||||
<release version="1.2.9" date="2025-04-08"/>
|
<release version="1.2.9" date="2025-04-08"/>
|
||||||
<release version="1.2.8" date="2025-04-05"/>
|
<release version="1.2.8" date="2025-04-05"/>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ Icon=strawberry
|
|||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=AudioVideo;Player;Qt;Audio;
|
Categories=AudioVideo;Player;Qt;Audio;
|
||||||
Keywords=Audio;Player;Clementine;
|
Keywords=Audio;Player;Clementine;
|
||||||
StartupNotify=false
|
|
||||||
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
|
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
|
||||||
StartupWMClass=strawberry
|
StartupWMClass=strawberry
|
||||||
Actions=Play-Pause;Stop;StopAfterCurrent;Previous;Next;
|
Actions=Play-Pause;Stop;StopAfterCurrent;Previous;Next;
|
||||||
|
|||||||
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 Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
|
- 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 Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
|
- 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
|
||||||
|
|
||||||
|
|||||||
135
dist/windows/strawberry.nsi.in
vendored
135
dist/windows/strawberry.nsi.in
vendored
@@ -21,6 +21,10 @@
|
|||||||
!define arch_x64
|
!define arch_x64
|
||||||
!else if "@ARCH@" == "x86_64-w64-mingw32.shared"
|
!else if "@ARCH@" == "x86_64-w64-mingw32.shared"
|
||||||
!define arch_x64
|
!define arch_x64
|
||||||
|
!else if "@ARCH@" == "arm64"
|
||||||
|
!define arch_arm64
|
||||||
|
!else
|
||||||
|
!error "Missing ARCH"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
@@ -31,6 +35,10 @@
|
|||||||
!define arch "x64"
|
!define arch "x64"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
!ifdef arch_arm64
|
||||||
|
!define arch "arm64"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
|
||||||
!if "@CMAKE_BUILD_TYPE@" == "Release"
|
!if "@CMAKE_BUILD_TYPE@" == "Release"
|
||||||
!define release
|
!define release
|
||||||
@@ -38,6 +46,8 @@
|
|||||||
!define release
|
!define release
|
||||||
!else if "@CMAKE_BUILD_TYPE@" == "Debug"
|
!else if "@CMAKE_BUILD_TYPE@" == "Debug"
|
||||||
!define debug
|
!define debug
|
||||||
|
!else
|
||||||
|
!error "Missing CMAKE_BUILD_TYPE"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef release
|
!ifdef release
|
||||||
@@ -70,7 +80,7 @@
|
|||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player Debug"
|
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player Debug"
|
||||||
!endif
|
!endif
|
||||||
!ifdef arch_x64
|
!ifdef arch_x64 || arch_arm64
|
||||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player Debug"
|
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player Debug"
|
||||||
!endif
|
!endif
|
||||||
!else
|
!else
|
||||||
@@ -80,7 +90,7 @@
|
|||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player"
|
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player"
|
||||||
!endif
|
!endif
|
||||||
!ifdef arch_x64
|
!ifdef arch_x64 || arch_arm64
|
||||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player"
|
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player"
|
||||||
!endif
|
!endif
|
||||||
!endif
|
!endif
|
||||||
@@ -214,7 +224,7 @@ Function InstallMSVCRuntime
|
|||||||
; ${If} $R0 == ""
|
; ${If} $R0 == ""
|
||||||
SetDetailsView hide
|
SetDetailsView hide
|
||||||
; inetc::get /caption "Downloading..." "https://aka.ms/vs/17/release/${vc_redist_file}" "$TEMP\${vc_redist_file}" /end
|
; inetc::get /caption "Downloading..." "https://aka.ms/vs/17/release/${vc_redist_file}" "$TEMP\${vc_redist_file}" /end
|
||||||
ExecWait '"$TEMP\${vc_redist_file}" /install /passive'
|
ExecWait '"$TEMP\${vc_redist_file}" /install /passive /norestart'
|
||||||
Delete "$TEMP\${vc_redist_file}"
|
Delete "$TEMP\${vc_redist_file}"
|
||||||
SetDetailsView show
|
SetDetailsView show
|
||||||
; ${EndIf}
|
; ${EndIf}
|
||||||
@@ -324,7 +334,7 @@ Section "Strawberry" Strawberry
|
|||||||
File "libqtsparkle-qt6.dll"
|
File "libqtsparkle-qt6.dll"
|
||||||
File "libsoup-3.0-0.dll"
|
File "libsoup-3.0-0.dll"
|
||||||
File "libspeex-1.dll"
|
File "libspeex-1.dll"
|
||||||
File "libsqlite3.dll"
|
File "libsqlite3-0.dll"
|
||||||
File "libssp-0.dll"
|
File "libssp-0.dll"
|
||||||
File "libstdc++-6.dll"
|
File "libstdc++-6.dll"
|
||||||
File "libtag.dll"
|
File "libtag.dll"
|
||||||
@@ -367,6 +377,10 @@ Section "Strawberry" Strawberry
|
|||||||
File "libcrypto-3-x64.dll"
|
File "libcrypto-3-x64.dll"
|
||||||
File "libssl-3-x64.dll"
|
File "libssl-3-x64.dll"
|
||||||
!endif
|
!endif
|
||||||
|
!ifdef arch_arm64
|
||||||
|
File "libcrypto-3-arm64.dll"
|
||||||
|
File "libssl-3-arm64.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
File "FLAC.dll"
|
File "FLAC.dll"
|
||||||
File "brotlicommon.dll"
|
File "brotlicommon.dll"
|
||||||
@@ -381,7 +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"
|
||||||
File "gnutls.dll"
|
|
||||||
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"
|
||||||
@@ -402,14 +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"
|
||||||
File "gstwinrt-1.0-0.dll"
|
|
||||||
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"
|
||||||
File "libfaac_dll.dll"
|
|
||||||
File "liblzma.dll"
|
|
||||||
File "libmp3lame.dll"
|
File "libmp3lame.dll"
|
||||||
File "libopenmpt.dll"
|
File "libopenmpt.dll"
|
||||||
File "mpcdec.dll"
|
File "mpcdec.dll"
|
||||||
@@ -427,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"
|
||||||
@@ -434,8 +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
|
||||||
File "twolame.dll"
|
File "twolame.dll"
|
||||||
File "zlib.dll"
|
!endif
|
||||||
!endif
|
!endif
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
File "freetyped.dll"
|
File "freetyped.dll"
|
||||||
@@ -444,8 +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
|
||||||
File "twolamed.dll"
|
File "twolamed.dll"
|
||||||
File "zlibd.dll"
|
!endif
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
; Used by libfftw3-3.dll because fftw is compiled with MinGW.
|
; Used by libfftw3-3.dll because fftw is compiled with MinGW.
|
||||||
@@ -458,11 +477,15 @@ Section "Strawberry" Strawberry
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
File "icudt77.dll"
|
File "icudt78.dll"
|
||||||
|
!ifdef msvc && arch_arm64
|
||||||
|
File "fftw3.dll"
|
||||||
|
!else
|
||||||
File "libfftw3-3.dll"
|
File "libfftw3-3.dll"
|
||||||
|
!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"
|
||||||
@@ -471,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"
|
||||||
@@ -482,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"
|
||||||
@@ -489,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
|
||||||
@@ -526,11 +558,13 @@ Section "GIO modules" gio-modules
|
|||||||
SetOutPath "$INSTDIR\gio-modules"
|
SetOutPath "$INSTDIR\gio-modules"
|
||||||
!ifdef mingw
|
!ifdef mingw
|
||||||
File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll"
|
File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll"
|
||||||
File "/oname=libgioopenssl.dll" "gio-modules\libgioopenssl.dll"
|
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc
|
!ifdef msvc
|
||||||
File "/oname=giognutls.dll" "gio-modules\giognutls.dll"
|
!ifdef arch_arm64
|
||||||
File "/oname=gioopenssl.dll" "gio-modules\gioopenssl.dll"
|
File "/oname=gioopenssl.dll" "gio-modules\gioopenssl.dll"
|
||||||
|
!else
|
||||||
|
File "/oname=giognutls.dll" "gio-modules\giognutls.dll"
|
||||||
|
!endif
|
||||||
!endif
|
!endif
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
@@ -647,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"
|
||||||
@@ -674,7 +709,6 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=gstdirectsound.dll" "gstreamer-plugins\gstdirectsound.dll"
|
File "/oname=gstdirectsound.dll" "gstreamer-plugins\gstdirectsound.dll"
|
||||||
File "/oname=gstdsd.dll" "gstreamer-plugins\gstdsd.dll"
|
File "/oname=gstdsd.dll" "gstreamer-plugins\gstdsd.dll"
|
||||||
File "/oname=gstequalizer.dll" "gstreamer-plugins\gstequalizer.dll"
|
File "/oname=gstequalizer.dll" "gstreamer-plugins\gstequalizer.dll"
|
||||||
File "/oname=gstfaac.dll" "gstreamer-plugins\gstfaac.dll"
|
|
||||||
File "/oname=gstfaad.dll" "gstreamer-plugins\gstfaad.dll"
|
File "/oname=gstfaad.dll" "gstreamer-plugins\gstfaad.dll"
|
||||||
File "/oname=gstfdkaac.dll" "gstreamer-plugins\gstfdkaac.dll"
|
File "/oname=gstfdkaac.dll" "gstreamer-plugins\gstfdkaac.dll"
|
||||||
File "/oname=gstflac.dll" "gstreamer-plugins\gstflac.dll"
|
File "/oname=gstflac.dll" "gstreamer-plugins\gstflac.dll"
|
||||||
@@ -707,7 +741,6 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=gstspeex.dll" "gstreamer-plugins\gstspeex.dll"
|
File "/oname=gstspeex.dll" "gstreamer-plugins\gstspeex.dll"
|
||||||
File "/oname=gsttaglib.dll" "gstreamer-plugins\gsttaglib.dll"
|
File "/oname=gsttaglib.dll" "gstreamer-plugins\gsttaglib.dll"
|
||||||
File "/oname=gsttcp.dll" "gstreamer-plugins\gsttcp.dll"
|
File "/oname=gsttcp.dll" "gstreamer-plugins\gsttcp.dll"
|
||||||
File "/oname=gsttwolame.dll" "gstreamer-plugins\gsttwolame.dll"
|
|
||||||
File "/oname=gsttypefindfunctions.dll" "gstreamer-plugins\gsttypefindfunctions.dll"
|
File "/oname=gsttypefindfunctions.dll" "gstreamer-plugins\gsttypefindfunctions.dll"
|
||||||
File "/oname=gstudp.dll" "gstreamer-plugins\gstudp.dll"
|
File "/oname=gstudp.dll" "gstreamer-plugins\gstudp.dll"
|
||||||
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
|
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
|
||||||
@@ -719,6 +752,10 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=gstwavpack.dll" "gstreamer-plugins\gstwavpack.dll"
|
File "/oname=gstwavpack.dll" "gstreamer-plugins\gstwavpack.dll"
|
||||||
File "/oname=gstwavparse.dll" "gstreamer-plugins\gstwavparse.dll"
|
File "/oname=gstwavparse.dll" "gstreamer-plugins\gstwavparse.dll"
|
||||||
File "/oname=gstxingmux.dll" "gstreamer-plugins\gstxingmux.dll"
|
File "/oname=gstxingmux.dll" "gstreamer-plugins\gstxingmux.dll"
|
||||||
|
!ifndef arch_arm64
|
||||||
|
File "/oname=gstfaac.dll" "gstreamer-plugins\gstfaac.dll"
|
||||||
|
File "/oname=gsttwolame.dll" "gstreamer-plugins\gsttwolame.dll"
|
||||||
|
!endif
|
||||||
!ifdef arch_x64
|
!ifdef arch_x64
|
||||||
File "/oname=gstspotify.dll" "gstreamer-plugins\gstspotify.dll"
|
File "/oname=gstspotify.dll" "gstreamer-plugins\gstspotify.dll"
|
||||||
!endif
|
!endif
|
||||||
@@ -849,7 +886,7 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||||
Delete "$INSTDIR\libsoup-3.0-0.dll"
|
Delete "$INSTDIR\libsoup-3.0-0.dll"
|
||||||
Delete "$INSTDIR\libspeex-1.dll"
|
Delete "$INSTDIR\libspeex-1.dll"
|
||||||
Delete "$INSTDIR\libsqlite3.dll"
|
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||||
Delete "$INSTDIR\libssp-0.dll"
|
Delete "$INSTDIR\libssp-0.dll"
|
||||||
Delete "$INSTDIR\libstdc++-6.dll"
|
Delete "$INSTDIR\libstdc++-6.dll"
|
||||||
Delete "$INSTDIR\libtag.dll"
|
Delete "$INSTDIR\libtag.dll"
|
||||||
@@ -892,6 +929,10 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libcrypto-3-x64.dll"
|
Delete "$INSTDIR\libcrypto-3-x64.dll"
|
||||||
Delete "$INSTDIR\libssl-3-x64.dll"
|
Delete "$INSTDIR\libssl-3-x64.dll"
|
||||||
!endif
|
!endif
|
||||||
|
!ifdef arch_arm64
|
||||||
|
Delete "$INSTDIR\libcrypto-3-arm64.dll"
|
||||||
|
Delete "$INSTDIR\libssl-3-arm64.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
Delete "$INSTDIR\FLAC.dll"
|
Delete "$INSTDIR\FLAC.dll"
|
||||||
Delete "$INSTDIR\brotlicommon.dll"
|
Delete "$INSTDIR\brotlicommon.dll"
|
||||||
@@ -906,7 +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"
|
||||||
Delete "$INSTDIR\gnutls.dll"
|
|
||||||
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"
|
||||||
@@ -927,14 +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"
|
||||||
Delete "$INSTDIR\gstwinrt-1.0-0.dll"
|
|
||||||
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"
|
||||||
Delete "$INSTDIR\libfaac_dll.dll"
|
|
||||||
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"
|
||||||
@@ -952,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"
|
||||||
@@ -959,8 +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
|
||||||
Delete "$INSTDIR\twolame.dll"
|
Delete "$INSTDIR\twolame.dll"
|
||||||
Delete "$INSTDIR\zlib.dll"
|
!endif
|
||||||
!endif
|
!endif
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
Delete "$INSTDIR\freetyped.dll"
|
Delete "$INSTDIR\freetyped.dll"
|
||||||
@@ -969,8 +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
|
||||||
Delete "$INSTDIR\twolamed.dll"
|
Delete "$INSTDIR\twolamed.dll"
|
||||||
Delete "$INSTDIR\zlibd.dll"
|
!endif
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
@@ -982,11 +1028,15 @@ Section "Uninstall"
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
Delete "$INSTDIR\icudt77.dll"
|
Delete "$INSTDIR\icudt78.dll"
|
||||||
|
!ifdef msvc && arch_arm64
|
||||||
|
Delete "$INSTDIR\fftw3.dll"
|
||||||
|
!else
|
||||||
Delete "$INSTDIR\libfftw3-3.dll"
|
Delete "$INSTDIR\libfftw3-3.dll"
|
||||||
|
!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"
|
||||||
@@ -995,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"
|
||||||
@@ -1006,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"
|
||||||
@@ -1013,14 +1064,24 @@ 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"
|
||||||
Delete "$INSTDIR\gio-modules\libgioopenssl.dll"
|
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc
|
!ifdef msvc
|
||||||
Delete "$INSTDIR\gio-modules\giognutls.dll"
|
!ifdef arch_arm64
|
||||||
Delete "$INSTDIR\gio-modules\gioopenssl.dll"
|
Delete "$INSTDIR\gio-modules\gioopenssl.dll"
|
||||||
|
!else
|
||||||
|
Delete "$INSTDIR\gio-modules\giognutls.dll"
|
||||||
|
!endif
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
@@ -1104,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"
|
||||||
@@ -1133,7 +1195,6 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\gstreamer-plugins\gstdirectsound.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstdirectsound.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstdsd.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstdsd.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstequalizer.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstequalizer.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstfaac.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstfaad.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstfaad.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstfdkaac.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstfdkaac.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstflac.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstflac.dll"
|
||||||
@@ -1166,7 +1227,6 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\gstreamer-plugins\gstspeex.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstspeex.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gsttaglib.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gsttaglib.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gsttcp.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gsttcp.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gsttwolame.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gsttypefindfunctions.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gsttypefindfunctions.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstudp.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstudp.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstvolume.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstvolume.dll"
|
||||||
@@ -1178,9 +1238,14 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\gstreamer-plugins\gstwavpack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwavpack.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwavparse.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwavparse.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstxingmux.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstxingmux.dll"
|
||||||
|
!ifndef arch_arm64
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstfaac.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gsttwolame.dll"
|
||||||
|
!endif
|
||||||
!ifdef arch_x64
|
!ifdef arch_x64
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstspotify.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstspotify.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!endif ; msvc
|
!endif ; msvc
|
||||||
|
|
||||||
Delete "$INSTDIR\Uninstall.exe"
|
Delete "$INSTDIR\Uninstall.exe"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -623,6 +623,7 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
|
|||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
QSqlDatabase db(db_->Connect());
|
QSqlDatabase db(db_->Connect());
|
||||||
|
|
||||||
|
CollectionTask task(task_manager_, tr("Updating %1 database.").arg(Song::TextForSource(source_)));
|
||||||
ScopedTransaction transaction(&db);
|
ScopedTransaction transaction(&db);
|
||||||
|
|
||||||
SongList added_songs;
|
SongList added_songs;
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
@@ -295,19 +296,21 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(const QString &saved_
|
|||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version"_L1) continue;
|
const QString &name = saved.at(i);
|
||||||
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
if (name == "version"_L1) continue;
|
||||||
|
QByteArray bytes = s.value(name).toByteArray();
|
||||||
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
||||||
CollectionModel::Grouping g;
|
CollectionModel::Grouping g;
|
||||||
ds >> g;
|
ds >> g;
|
||||||
ret->addAction(CreateGroupByAction(saved.at(i), parent, g));
|
ret->addAction(CreateGroupByAction(QUrl::fromPercentEncoding(name.toUtf8()), parent, g));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version"_L1) continue;
|
const QString &name = saved.at(i);
|
||||||
s.remove(saved.at(i));
|
if (name == "version"_L1) continue;
|
||||||
|
s.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -339,7 +342,7 @@ void CollectionFilterWidget::SaveGroupBy() {
|
|||||||
|
|
||||||
if (!model_) return;
|
if (!model_) return;
|
||||||
|
|
||||||
QString name = QInputDialog::getText(this, tr("Grouping Name"), tr("Grouping name:"));
|
const QString name = QInputDialog::getText(this, tr("Grouping Name"), tr("Grouping name:"));
|
||||||
if (name.isEmpty()) return;
|
if (name.isEmpty()) return;
|
||||||
|
|
||||||
qLog(Debug) << "Saving current grouping to" << name;
|
qLog(Debug) << "Saving current grouping to" << name;
|
||||||
@@ -355,7 +358,7 @@ void CollectionFilterWidget::SaveGroupBy() {
|
|||||||
QDataStream datastream(&buffer, QIODevice::WriteOnly);
|
QDataStream datastream(&buffer, QIODevice::WriteOnly);
|
||||||
datastream << model_->GetGroupBy();
|
datastream << model_->GetGroupBy();
|
||||||
s.setValue("version", u"1"_s);
|
s.setValue("version", u"1"_s);
|
||||||
s.setValue(name, buffer);
|
s.setValue(QUrl::toPercentEncoding(name), buffer);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
UpdateGroupByActions();
|
UpdateGroupByActions();
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -78,6 +78,8 @@ CollectionLibrary::CollectionLibrary(const SharedPtr<Database> database,
|
|||||||
|
|
||||||
model_ = new CollectionModel(backend_, albumcover_loader, this);
|
model_ = new CollectionModel(backend_, albumcover_loader, this);
|
||||||
|
|
||||||
|
full_rescan_revisions_[21] = tr("Support for sort tags artist, album, album artist, title, composer, and performer");
|
||||||
|
|
||||||
ReloadSettings();
|
ReloadSettings();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -187,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);
|
||||||
@@ -210,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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2018-2024, 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
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
#include "includes/scoped_ptr.h"
|
#include "includes/scoped_ptr.h"
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
|
#include "constants/collectionsettings.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/standardpaths.h"
|
#include "core/standardpaths.h"
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
@@ -71,12 +72,12 @@
|
|||||||
#include "covermanager/albumcoverloaderoptions.h"
|
#include "covermanager/albumcoverloaderoptions.h"
|
||||||
#include "covermanager/albumcoverloaderresult.h"
|
#include "covermanager/albumcoverloaderresult.h"
|
||||||
#include "covermanager/albumcoverloader.h"
|
#include "covermanager/albumcoverloader.h"
|
||||||
#include "constants/collectionsettings.h"
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
const int CollectionModel::kPrettyCoverSize = 32;
|
const int CollectionModel::kPrettyCoverSize = 32;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr char kPixmapDiskCacheDir[] = "pixmapcache";
|
constexpr char kPixmapDiskCacheDir[] = "pixmapcache";
|
||||||
constexpr char kVariousArtists[] = QT_TR_NOOP("Various artists");
|
constexpr char kVariousArtists[] = QT_TR_NOOP("Various artists");
|
||||||
@@ -88,7 +89,6 @@ CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, con
|
|||||||
albumcover_loader_(albumcover_loader),
|
albumcover_loader_(albumcover_loader),
|
||||||
dir_model_(new CollectionDirectoryModel(backend, this)),
|
dir_model_(new CollectionDirectoryModel(backend, this)),
|
||||||
filter_(new CollectionFilter(this)),
|
filter_(new CollectionFilter(this)),
|
||||||
timer_reload_(new QTimer(this)),
|
|
||||||
timer_update_(new QTimer(this)),
|
timer_update_(new QTimer(this)),
|
||||||
icon_artist_(IconLoader::Load(u"folder-sound"_s)),
|
icon_artist_(IconLoader::Load(u"folder-sound"_s)),
|
||||||
use_disk_cache_(false),
|
use_disk_cache_(false),
|
||||||
@@ -130,10 +130,6 @@ CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, con
|
|||||||
backend_->UpdateTotalArtistCountAsync();
|
backend_->UpdateTotalArtistCountAsync();
|
||||||
backend_->UpdateTotalAlbumCountAsync();
|
backend_->UpdateTotalAlbumCountAsync();
|
||||||
|
|
||||||
timer_reload_->setSingleShot(true);
|
|
||||||
timer_reload_->setInterval(300ms);
|
|
||||||
QObject::connect(timer_reload_, &QTimer::timeout, this, &CollectionModel::Reload);
|
|
||||||
|
|
||||||
timer_update_->setSingleShot(false);
|
timer_update_->setSingleShot(false);
|
||||||
timer_update_->setInterval(20ms);
|
timer_update_->setInterval(20ms);
|
||||||
QObject::connect(timer_update_, &QTimer::timeout, this, &CollectionModel::ProcessUpdate);
|
QObject::connect(timer_update_, &QTimer::timeout, this, &CollectionModel::ProcessUpdate);
|
||||||
@@ -191,13 +187,9 @@ void CollectionModel::EndReset() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::Reload() {
|
void CollectionModel::ResetInternal() {
|
||||||
|
|
||||||
loading_ = true;
|
loading_ = true;
|
||||||
if (timer_reload_->isActive()) {
|
|
||||||
timer_reload_->stop();
|
|
||||||
}
|
|
||||||
updates_.clear();
|
|
||||||
|
|
||||||
options_active_ = options_current_;
|
options_active_ = options_current_;
|
||||||
|
|
||||||
@@ -211,14 +203,6 @@ void CollectionModel::Reload() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::ScheduleReset() {
|
|
||||||
|
|
||||||
if (!timer_reload_->isActive()) {
|
|
||||||
timer_reload_->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollectionModel::ReloadSettings() {
|
void CollectionModel::ReloadSettings() {
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
@@ -226,7 +210,9 @@ void CollectionModel::ReloadSettings() {
|
|||||||
const bool show_pretty_covers = settings.value(CollectionSettings::kPrettyCovers, true).toBool();
|
const bool show_pretty_covers = settings.value(CollectionSettings::kPrettyCovers, true).toBool();
|
||||||
const bool show_dividers = settings.value(CollectionSettings::kShowDividers, true).toBool();
|
const bool show_dividers = settings.value(CollectionSettings::kShowDividers, true).toBool();
|
||||||
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_skips_articles = settings.value(CollectionSettings::kSortSkipsArticles, 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 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));
|
||||||
@@ -241,11 +227,15 @@ void CollectionModel::ReloadSettings() {
|
|||||||
if (show_pretty_covers != options_current_.show_pretty_covers ||
|
if (show_pretty_covers != options_current_.show_pretty_covers ||
|
||||||
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_skips_articles != options_current_.sort_skips_articles) {
|
sort_skip_articles_for_artists != options_current_.sort_skip_articles_for_artists ||
|
||||||
|
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_skips_articles = sort_skips_articles;
|
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_.use_sort_tags = use_sort_tags;
|
||||||
ScheduleReset();
|
ScheduleReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,11 +411,16 @@ void CollectionModel::RemoveSongs(const SongList &songs) {
|
|||||||
|
|
||||||
void CollectionModel::ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs) {
|
void CollectionModel::ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs) {
|
||||||
|
|
||||||
|
if (type == CollectionModelUpdate::Type::Reset) {
|
||||||
|
updates_.enqueue(CollectionModelUpdate(type));
|
||||||
|
}
|
||||||
|
else {
|
||||||
for (qint64 i = 0; i < songs.count(); i += 400LL) {
|
for (qint64 i = 0; i < songs.count(); i += 400LL) {
|
||||||
const qint64 number = std::min(songs.count() - i, 400LL);
|
const qint64 number = std::min(songs.count() - i, 400LL);
|
||||||
const SongList songs_to_queue = songs.mid(i, number);
|
const SongList songs_to_queue = songs.mid(i, number);
|
||||||
updates_.enqueue(CollectionModelUpdate(type, songs_to_queue));
|
updates_.enqueue(CollectionModelUpdate(type, songs_to_queue));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!timer_update_->isActive()) {
|
if (!timer_update_->isActive()) {
|
||||||
timer_update_->start();
|
timer_update_->start();
|
||||||
@@ -433,6 +428,14 @@ void CollectionModel::ScheduleUpdate(const CollectionModelUpdate::Type type, con
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionModel::ScheduleReset() {
|
||||||
|
|
||||||
|
if (!updates_.isEmpty() && updates_.first().type == CollectionModelUpdate::Type::Reset) return;
|
||||||
|
|
||||||
|
ScheduleUpdate(CollectionModelUpdate::Type::Reset);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionModel::ScheduleAddSongs(const SongList &songs) {
|
void CollectionModel::ScheduleAddSongs(const SongList &songs) {
|
||||||
|
|
||||||
ScheduleUpdate(CollectionModelUpdate::Type::Add, songs);
|
ScheduleUpdate(CollectionModelUpdate::Type::Add, songs);
|
||||||
@@ -465,6 +468,9 @@ void CollectionModel::ProcessUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (update.type) {
|
switch (update.type) {
|
||||||
|
case CollectionModelUpdate::Type::Reset:
|
||||||
|
ResetInternal();
|
||||||
|
break;
|
||||||
case CollectionModelUpdate::Type::AddReAddOrUpdate:
|
case CollectionModelUpdate::Type::AddReAddOrUpdate:
|
||||||
AddReAddOrUpdateSongsInternal(update.songs);
|
AddReAddOrUpdateSongsInternal(update.songs);
|
||||||
break;
|
break;
|
||||||
@@ -539,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.
|
||||||
@@ -699,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_skips_articles));
|
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);
|
||||||
@@ -713,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_skips_articles);
|
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(' '));
|
||||||
}
|
}
|
||||||
@@ -1068,39 +1077,39 @@ QString CollectionModel::PrettyFormat(const Song &song) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionModel::SortText(const GroupBy group_by, const Song &song, const bool sort_skips_articles) {
|
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 SortTextForArtist(song.effective_albumartist(), sort_skips_articles);
|
return SortTextForName(use_sort_tags ? song.effective_albumartistsort() : song.effective_albumartist(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Artist:
|
case GroupBy::Artist:
|
||||||
return SortTextForArtist(song.artist(), sort_skips_articles);
|
return SortTextForName(use_sort_tags ? song.effective_artistsort() : song.artist(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Album:
|
case GroupBy::Album:
|
||||||
return SortText(song.album());
|
return SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
|
||||||
case GroupBy::AlbumDisc:
|
case GroupBy::AlbumDisc:
|
||||||
return song.album() + 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() + song.album();
|
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())) + song.album() + 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() + song.album();
|
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())) + song.album() + 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 SortTextForArtist(song.genre(), sort_skips_articles);
|
return SortText(song.genre());
|
||||||
case GroupBy::Composer:
|
case GroupBy::Composer:
|
||||||
return SortTextForArtist(song.composer(), sort_skips_articles);
|
return SortTextForName(use_sort_tags ? song.effective_composersort() : song.composer(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Performer:
|
case GroupBy::Performer:
|
||||||
return SortTextForArtist(song.performer(), sort_skips_articles);
|
return SortTextForName(use_sort_tags ? song.effective_performersort() : song.performer(), sort_skip_articles_for_artists);
|
||||||
case GroupBy::Grouping:
|
case GroupBy::Grouping:
|
||||||
return SortTextForArtist(song.grouping(), sort_skips_articles);
|
return SortText(song.grouping());
|
||||||
case GroupBy::FileType:
|
case GroupBy::FileType:
|
||||||
return song.TextForFiletype();
|
return song.TextForFiletype();
|
||||||
case GroupBy::Format:
|
case GroupBy::Format:
|
||||||
@@ -1135,21 +1144,9 @@ QString CollectionModel::SortText(QString text) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionModel::SortTextForArtist(QString artist, const bool skip_articles) {
|
QString CollectionModel::SortTextForName(const QString &name, const bool sort_skip_articles) {
|
||||||
|
|
||||||
artist = SortText(artist);
|
return sort_skip_articles ? SkipArticles(SortText(name)) : SortText(name);
|
||||||
|
|
||||||
if (skip_articles) {
|
|
||||||
for (const auto &i : Song::kArticles) {
|
|
||||||
if (artist.startsWith(i)) {
|
|
||||||
qint64 ilen = i.length();
|
|
||||||
artist = artist.right(artist.length() - ilen) + ", "_L1 + i.left(ilen - 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return artist;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,18 +1165,32 @@ 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CollectionModel::SkipArticles(QString name) {
|
||||||
|
|
||||||
|
for (const auto &i : Song::kArticles) {
|
||||||
|
if (name.startsWith(i)) {
|
||||||
|
qint64 ilen = i.length();
|
||||||
|
name = name.right(name.length() - ilen) + ", "_L1 + i.left(ilen - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool CollectionModel::IsSongTitleDataChanged(const Song &song1, const Song &song2) {
|
bool CollectionModel::IsSongTitleDataChanged(const Song &song1, const Song &song2) {
|
||||||
|
|
||||||
return song1.url() != song2.url() ||
|
return song1.url() != song2.url() ||
|
||||||
@@ -1210,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;
|
||||||
@@ -1238,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());
|
||||||
@@ -1333,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();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2018-2024, 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
|
||||||
@@ -129,14 +129,18 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
show_dividers(true),
|
show_dividers(true),
|
||||||
show_pretty_covers(true),
|
show_pretty_covers(true),
|
||||||
show_various_artists(true),
|
show_various_artists(true),
|
||||||
sort_skips_articles(true),
|
sort_skip_articles_for_artists(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;
|
||||||
bool show_dividers;
|
bool show_dividers;
|
||||||
bool show_pretty_covers;
|
bool show_pretty_covers;
|
||||||
bool show_various_artists;
|
bool show_various_artists;
|
||||||
bool sort_skips_articles;
|
bool sort_skip_articles_for_artists;
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
@@ -183,13 +187,14 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
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_skips_articles);
|
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 SortTextForNumber(const int number);
|
static QString SortTextForNumber(const int number);
|
||||||
static QString SortTextForArtist(QString artist, const bool skip_articles);
|
|
||||||
static QString SortTextForSong(const Song &song);
|
static QString SortTextForSong(const Song &song);
|
||||||
static QString SortTextForYear(const int year);
|
static QString SortTextForYear(const int year);
|
||||||
static QString SortTextForBitrate(const int bitrate);
|
static QString SortTextForBitrate(const int bitrate);
|
||||||
|
static QString SkipArticles(QString name);
|
||||||
static bool IsSongTitleDataChanged(const Song &song1, const Song &song2);
|
static bool IsSongTitleDataChanged(const Song &song1, const Song &song2);
|
||||||
QString ContainerKey(const GroupBy group_by, const Song &song, bool &has_unique_album_identifier) const;
|
QString ContainerKey(const GroupBy group_by, const Song &song, bool &has_unique_album_identifier) const;
|
||||||
|
|
||||||
@@ -228,7 +233,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
|
|
||||||
QVariant data(CollectionItem *item, const int role) const;
|
QVariant data(CollectionItem *item, const int role) const;
|
||||||
|
|
||||||
void ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs);
|
void ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs = SongList());
|
||||||
void ScheduleAddSongs(const SongList &songs);
|
void ScheduleAddSongs(const SongList &songs);
|
||||||
void ScheduleUpdateSongs(const SongList &songs);
|
void ScheduleUpdateSongs(const SongList &songs);
|
||||||
void ScheduleRemoveSongs(const SongList &songs);
|
void ScheduleRemoveSongs(const SongList &songs);
|
||||||
@@ -259,7 +264,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void Reload();
|
void ResetInternal();
|
||||||
void ScheduleReset();
|
void ScheduleReset();
|
||||||
void ProcessUpdate();
|
void ProcessUpdate();
|
||||||
void LoadSongsFromSqlAsyncFinished();
|
void LoadSongsFromSqlAsyncFinished();
|
||||||
@@ -278,7 +283,6 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
const SharedPtr<AlbumCoverLoader> albumcover_loader_;
|
const SharedPtr<AlbumCoverLoader> albumcover_loader_;
|
||||||
CollectionDirectoryModel *dir_model_;
|
CollectionDirectoryModel *dir_model_;
|
||||||
CollectionFilter *filter_;
|
CollectionFilter *filter_;
|
||||||
QTimer *timer_reload_;
|
|
||||||
QTimer *timer_update_;
|
QTimer *timer_update_;
|
||||||
|
|
||||||
QPixmap pixmap_no_cover_;
|
QPixmap pixmap_no_cover_;
|
||||||
|
|||||||
@@ -25,12 +25,13 @@
|
|||||||
class CollectionModelUpdate {
|
class CollectionModelUpdate {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
|
Reset,
|
||||||
AddReAddOrUpdate,
|
AddReAddOrUpdate,
|
||||||
Add,
|
Add,
|
||||||
Update,
|
Update,
|
||||||
Remove,
|
Remove,
|
||||||
};
|
};
|
||||||
explicit CollectionModelUpdate(const Type _type, const SongList &_songs);
|
explicit CollectionModelUpdate(const Type _type, const SongList &_songs = SongList());
|
||||||
Type type;
|
Type type;
|
||||||
SongList songs;
|
SongList songs;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ CollectionPlaylistItem::CollectionPlaylistItem(const Song::Source source) : Play
|
|||||||
|
|
||||||
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(song.source()), song_(song) {}
|
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(song.source()), song_(song) {}
|
||||||
|
|
||||||
QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
|
|
||||||
|
|
||||||
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
|
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
|
||||||
|
|
||||||
int col = 0;
|
int col = 0;
|
||||||
@@ -62,7 +60,7 @@ void CollectionPlaylistItem::Reload() {
|
|||||||
qLog(Error) << "Could not reload file" << song_.url() << result.error_string();
|
qLog(Error) << "Could not reload file" << song_.url() << result.error_string();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UpdateTemporaryMetadata(song_);
|
UpdateStreamMetadata(song_);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -78,16 +76,9 @@ QVariant CollectionPlaylistItem::DatabaseValue(const DatabaseColumn database_col
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Song CollectionPlaylistItem::Metadata() const {
|
|
||||||
|
|
||||||
if (HasTemporaryMetadata()) return temp_metadata_;
|
|
||||||
return song_;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollectionPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
void CollectionPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
||||||
|
|
||||||
song_.set_art_manual(cover_url);
|
song_.set_art_manual(cover_url);
|
||||||
if (HasTemporaryMetadata()) temp_metadata_.set_art_manual(cover_url);
|
if (HasStreamMetadata()) stream_song_.set_art_manual(cover_url);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,19 +35,17 @@ class CollectionPlaylistItem : public PlaylistItem {
|
|||||||
explicit CollectionPlaylistItem(const Song::Source source);
|
explicit CollectionPlaylistItem(const Song::Source source);
|
||||||
explicit CollectionPlaylistItem(const Song &song);
|
explicit CollectionPlaylistItem(const Song &song);
|
||||||
|
|
||||||
QUrl Url() const override;
|
Song OriginalMetadata() const override { return song_; }
|
||||||
|
void SetOriginalMetadata(const Song &song) override { song_ = song; }
|
||||||
|
|
||||||
|
QUrl OriginalUrl() const override { return song_.url(); }
|
||||||
|
bool IsLocalCollectionItem() const override { return song_.source() == Song::Source::Collection; }
|
||||||
|
|
||||||
bool InitFromQuery(const SqlRow &query) override;
|
bool InitFromQuery(const SqlRow &query) override;
|
||||||
void Reload() override;
|
void Reload() override;
|
||||||
|
|
||||||
Song Metadata() const override;
|
|
||||||
Song OriginalMetadata() const override { return song_; }
|
|
||||||
void SetMetadata(const Song &song) override { song_ = song; }
|
|
||||||
|
|
||||||
void SetArtManual(const QUrl &cover_url) override;
|
void SetArtManual(const QUrl &cover_url) override;
|
||||||
|
|
||||||
bool IsLocalCollectionItem() const override { return song_.source() == Song::Source::Collection; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant DatabaseValue(const DatabaseColumn database_column) const override;
|
QVariant DatabaseValue(const DatabaseColumn database_column) const override;
|
||||||
Song DatabaseSongMetadata() const override { return Song(source_); }
|
Song DatabaseSongMetadata() const override { return Song(source_); }
|
||||||
@@ -60,4 +58,3 @@ class CollectionPlaylistItem : public PlaylistItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONPLAYLISTITEM_H
|
#endif // COLLECTIONPLAYLISTITEM_H
|
||||||
|
|
||||||
|
|||||||
@@ -65,10 +65,8 @@
|
|||||||
#include "collectionitem.h"
|
#include "collectionitem.h"
|
||||||
#include "collectionitemdelegate.h"
|
#include "collectionitemdelegate.h"
|
||||||
#include "collectionview.h"
|
#include "collectionview.h"
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
#include "device/devicemanager.h"
|
#include "device/devicemanager.h"
|
||||||
#include "device/devicestatefiltermodel.h"
|
#include "device/devicestatefiltermodel.h"
|
||||||
#endif
|
|
||||||
#include "dialogs/edittagdialog.h"
|
#include "dialogs/edittagdialog.h"
|
||||||
#include "dialogs/deleteconfirmationdialog.h"
|
#include "dialogs/deleteconfirmationdialog.h"
|
||||||
#include "organize/organizedialog.h"
|
#include "organize/organizedialog.h"
|
||||||
@@ -95,9 +93,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
action_open_in_new_playlist_(nullptr),
|
action_open_in_new_playlist_(nullptr),
|
||||||
action_organize_(nullptr),
|
action_organize_(nullptr),
|
||||||
action_search_for_this_(nullptr),
|
action_search_for_this_(nullptr),
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
action_copy_to_device_(nullptr),
|
action_copy_to_device_(nullptr),
|
||||||
#endif
|
|
||||||
action_edit_track_(nullptr),
|
action_edit_track_(nullptr),
|
||||||
action_edit_tracks_(nullptr),
|
action_edit_tracks_(nullptr),
|
||||||
action_rescan_songs_(nullptr),
|
action_rescan_songs_(nullptr),
|
||||||
@@ -417,9 +413,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
action_organize_ = context_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &CollectionView::Organize);
|
action_organize_ = context_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &CollectionView::Organize);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
||||||
#endif
|
|
||||||
action_delete_files_ = context_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &CollectionView::Delete);
|
action_delete_files_ = context_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &CollectionView::Delete);
|
||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
@@ -439,10 +433,8 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
context_menu_->addMenu(filter_widget_->menu());
|
context_menu_->addMenu(filter_widget_->menu());
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
|
action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
|
||||||
QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
|
QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,9 +473,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
action_rescan_songs_->setEnabled(regular_editable > 0);
|
action_rescan_songs_->setEnabled(regular_editable > 0);
|
||||||
|
|
||||||
action_organize_->setVisible(regular_elements == regular_editable);
|
action_organize_->setVisible(regular_elements == regular_editable);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
action_copy_to_device_->setVisible(regular_elements == regular_editable);
|
action_copy_to_device_->setVisible(regular_elements == regular_editable);
|
||||||
#endif
|
|
||||||
|
|
||||||
action_delete_files_->setVisible(delete_files_);
|
action_delete_files_->setVisible(delete_files_);
|
||||||
|
|
||||||
@@ -492,9 +482,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
// only when all selected items are editable
|
// only when all selected items are editable
|
||||||
action_organize_->setEnabled(regular_elements == regular_editable);
|
action_organize_->setEnabled(regular_elements == regular_editable);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
action_copy_to_device_->setEnabled(regular_elements == regular_editable);
|
action_copy_to_device_->setEnabled(regular_elements == regular_editable);
|
||||||
#endif
|
|
||||||
|
|
||||||
action_delete_files_->setEnabled(delete_files_);
|
action_delete_files_->setEnabled(delete_files_);
|
||||||
|
|
||||||
@@ -759,7 +747,6 @@ void CollectionView::RescanSongs() {
|
|||||||
|
|
||||||
void CollectionView::CopyToDevice() {
|
void CollectionView::CopyToDevice() {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
if (!organize_dialog_) {
|
if (!organize_dialog_) {
|
||||||
organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, nullptr, this);
|
organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, nullptr, this);
|
||||||
}
|
}
|
||||||
@@ -768,7 +755,6 @@ void CollectionView::CopyToDevice() {
|
|||||||
organize_dialog_->SetCopy(true);
|
organize_dialog_->SetCopy(true);
|
||||||
organize_dialog_->SetSongs(GetSelectedSongs());
|
organize_dialog_->SetSongs(GetSelectedSongs());
|
||||||
organize_dialog_->show();
|
organize_dialog_->show();
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
void DeleteFilesFinished(const SongList &songs_with_errors);
|
void DeleteFilesFinished(const SongList &songs_with_errors);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RecheckIsEmpty();
|
|
||||||
void SetShowInVarious(const bool on);
|
void SetShowInVarious(const bool on);
|
||||||
bool RestoreLevelFocus(const QModelIndex &parent = QModelIndex());
|
bool RestoreLevelFocus(const QModelIndex &parent = QModelIndex());
|
||||||
void SaveContainerPath(const QModelIndex &child);
|
void SaveContainerPath(const QModelIndex &child);
|
||||||
@@ -176,9 +175,7 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
QAction *action_organize_;
|
QAction *action_organize_;
|
||||||
QAction *action_search_for_this_;
|
QAction *action_search_for_this_;
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
QAction *action_copy_to_device_;
|
QAction *action_copy_to_device_;
|
||||||
#endif
|
|
||||||
QAction *action_edit_track_;
|
QAction *action_edit_track_;
|
||||||
QAction *action_edit_tracks_;
|
QAction *action_edit_tracks_;
|
||||||
QAction *action_rescan_songs_;
|
QAction *action_rescan_songs_;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -999,6 +999,18 @@ void CollectionWatcher::AddChangedSong(const QString &file, const Song &matching
|
|||||||
changes << u"file path"_s;
|
changes << u"file path"_s;
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
|
if (matching_song.filetype() != new_song.filetype()) {
|
||||||
|
changes << u"filetype"_s;
|
||||||
|
notify_new = true;
|
||||||
|
}
|
||||||
|
if (matching_song.filesize() != new_song.filesize()) {
|
||||||
|
changes << u"filesize"_s;
|
||||||
|
notify_new = true;
|
||||||
|
}
|
||||||
|
if (matching_song.length_nanosec() != new_song.length_nanosec()) {
|
||||||
|
changes << u"length"_s;
|
||||||
|
notify_new = true;
|
||||||
|
}
|
||||||
if (matching_song.fingerprint() != new_song.fingerprint()) {
|
if (matching_song.fingerprint() != new_song.fingerprint()) {
|
||||||
changes << u"fingerprint"_s;
|
changes << u"fingerprint"_s;
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
@@ -1034,6 +1046,9 @@ void CollectionWatcher::AddChangedSong(const QString &file, const Song &matching
|
|||||||
if (matching_song.mtime() != new_song.mtime()) {
|
if (matching_song.mtime() != new_song.mtime()) {
|
||||||
changes << u"mtime"_s;
|
changes << u"mtime"_s;
|
||||||
}
|
}
|
||||||
|
if (matching_song.ctime() != new_song.ctime()) {
|
||||||
|
changes << u"ctime"_s;
|
||||||
|
}
|
||||||
|
|
||||||
if (changes.isEmpty()) {
|
if (changes.isEmpty()) {
|
||||||
qLog(Debug) << "Song" << file << "unchanged.";
|
qLog(Debug) << "Song" << file << "unchanged.";
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
@@ -167,14 +168,20 @@ void SavedGroupingManager::UpdateModel() {
|
|||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version"_L1) continue;
|
const QString &name = saved.at(i);
|
||||||
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
if (name == "version"_L1) continue;
|
||||||
|
QByteArray bytes = s.value(name).toByteArray();
|
||||||
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
||||||
CollectionModel::Grouping g;
|
CollectionModel::Grouping g;
|
||||||
ds >> g;
|
ds >> g;
|
||||||
|
|
||||||
QList<QStandardItem*> list;
|
QList<QStandardItem*> list;
|
||||||
list << new QStandardItem(saved.at(i))
|
|
||||||
|
QStandardItem *item = new QStandardItem();
|
||||||
|
item->setText(QUrl::fromPercentEncoding(name.toUtf8()));
|
||||||
|
item->setData(name);
|
||||||
|
|
||||||
|
list << item
|
||||||
<< new QStandardItem(GroupByToString(g.first))
|
<< new QStandardItem(GroupByToString(g.first))
|
||||||
<< new QStandardItem(GroupByToString(g.second))
|
<< new QStandardItem(GroupByToString(g.second))
|
||||||
<< new QStandardItem(GroupByToString(g.third));
|
<< new QStandardItem(GroupByToString(g.third));
|
||||||
@@ -185,8 +192,9 @@ void SavedGroupingManager::UpdateModel() {
|
|||||||
else {
|
else {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version"_L1) continue;
|
const QString &name = saved.at(i);
|
||||||
s.remove(saved.at(i));
|
if (name == "version"_L1) continue;
|
||||||
|
s.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -202,7 +210,7 @@ void SavedGroupingManager::Remove() {
|
|||||||
for (const QModelIndex &idx : indexes) {
|
for (const QModelIndex &idx : indexes) {
|
||||||
if (idx.isValid()) {
|
if (idx.isValid()) {
|
||||||
qLog(Debug) << "Remove saved grouping: " << model_->item(idx.row(), 0)->text();
|
qLog(Debug) << "Remove saved grouping: " << model_->item(idx.row(), 0)->text();
|
||||||
s.remove(model_->item(idx.row(), 0)->text());
|
s.remove(model_->item(idx.row(), 0)->data().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2024-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,18 +24,20 @@ namespace CollectionSettings {
|
|||||||
|
|
||||||
constexpr char kSettingsGroup[] = "Collection";
|
constexpr char kSettingsGroup[] = "Collection";
|
||||||
|
|
||||||
|
constexpr char kStartupScan[] = "startup_scan";
|
||||||
|
constexpr char kMonitor[] = "monitor";
|
||||||
|
constexpr char kSongTracking[] = "song_tracking";
|
||||||
|
constexpr char kMarkSongsUnavailable[] = "mark_songs_unavailable";
|
||||||
|
constexpr char kSongENUR128LoudnessAnalysis[] = "song_ebur128_loudness_analysis";
|
||||||
|
constexpr char kExpireUnavailableSongs[] = "expire_unavailable_songs";
|
||||||
|
constexpr char kCoverArtPatterns[] = "cover_art_patterns";
|
||||||
constexpr char kAutoOpen[] = "auto_open";
|
constexpr char kAutoOpen[] = "auto_open";
|
||||||
constexpr char kShowDividers[] = "show_dividers";
|
constexpr char kShowDividers[] = "show_dividers";
|
||||||
constexpr char kPrettyCovers[] = "pretty_covers";
|
constexpr char kPrettyCovers[] = "pretty_covers";
|
||||||
constexpr char kVariousArtists[] = "various_artists";
|
constexpr char kVariousArtists[] = "various_artists";
|
||||||
constexpr char kSortSkipsArticles[] = "sort_skips_articles";
|
constexpr char kSkipArticlesForArtists[] = "skip_articles_for_artists";
|
||||||
constexpr char kStartupScan[] = "startup_scan";
|
constexpr char kSkipArticlesForAlbums[] = "skip_articles_for_albums";
|
||||||
constexpr char kMonitor[] = "monitor";
|
constexpr char kUseSortTags[] = "use_short_tags";
|
||||||
constexpr char kSongTracking[] = "song_tracking";
|
|
||||||
constexpr char kSongENUR128LoudnessAnalysis[] = "song_ebur128_loudness_analysis";
|
|
||||||
constexpr char kMarkSongsUnavailable[] = "mark_songs_unavailable";
|
|
||||||
constexpr char kExpireUnavailableSongs[] = "expire_unavailable_songs";
|
|
||||||
constexpr char kCoverArtPatterns[] = "cover_art_patterns";
|
|
||||||
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";
|
||||||
@@ -57,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -396,7 +378,7 @@ void ContextView::UpdateNoSong() {
|
|||||||
|
|
||||||
void ContextView::NoSong() {
|
void ContextView::NoSong() {
|
||||||
|
|
||||||
if (!widget_album_->isVisible()) {
|
if (!widget_album_->isVisibleTo(this)) {
|
||||||
widget_album_->show();
|
widget_album_->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,11 +422,11 @@ void ContextView::SetSong() {
|
|||||||
label_stop_summary_->clear();
|
label_stop_summary_->clear();
|
||||||
|
|
||||||
bool widget_album_changed = !song_prev_.is_valid();
|
bool widget_album_changed = !song_prev_.is_valid();
|
||||||
if (action_show_album_->isChecked() && !widget_album_->isVisible()) {
|
if (action_show_album_->isChecked() && !widget_album_->isVisibleTo(this)) {
|
||||||
widget_album_->show();
|
widget_album_->show();
|
||||||
widget_album_changed = true;
|
widget_album_changed = true;
|
||||||
}
|
}
|
||||||
else if (!action_show_album_->isChecked() && widget_album_->isVisible()) {
|
else if (!action_show_album_->isChecked() && widget_album_->isVisibleTo(this)) {
|
||||||
widget_album_->hide();
|
widget_album_->hide();
|
||||||
widget_album_changed = true;
|
widget_album_changed = true;
|
||||||
}
|
}
|
||||||
@@ -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_;
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
#include "covermanager/opentidalcoverprovider.h"
|
#include "covermanager/opentidalcoverprovider.h"
|
||||||
|
|
||||||
#include "lyrics/lyricsproviders.h"
|
#include "lyrics/lyricsproviders.h"
|
||||||
|
#include "lyrics/geniuslyricsprovider.h"
|
||||||
#include "lyrics/ovhlyricsprovider.h"
|
#include "lyrics/ovhlyricsprovider.h"
|
||||||
#include "lyrics/lololyricsprovider.h"
|
#include "lyrics/lololyricsprovider.h"
|
||||||
#include "lyrics/musixmatchlyricsprovider.h"
|
#include "lyrics/musixmatchlyricsprovider.h"
|
||||||
@@ -73,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
|
||||||
@@ -117,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;
|
||||||
@@ -172,6 +173,7 @@ class ApplicationImpl {
|
|||||||
lyrics_providers_([app]() {
|
lyrics_providers_([app]() {
|
||||||
LyricsProviders *lyrics_providers = new LyricsProviders(app);
|
LyricsProviders *lyrics_providers = new LyricsProviders(app);
|
||||||
// Initialize the repository of lyrics providers.
|
// Initialize the repository of lyrics providers.
|
||||||
|
lyrics_providers->AddProvider(new GeniusLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->AddProvider(new OVHLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new OVHLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->AddProvider(new LoloLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new LoloLyricsProvider(lyrics_providers->network()));
|
||||||
lyrics_providers->AddProvider(new MusixmatchLyricsProvider(lyrics_providers->network()));
|
lyrics_providers->AddProvider(new MusixmatchLyricsProvider(lyrics_providers->network()));
|
||||||
@@ -181,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;
|
||||||
}),
|
}),
|
||||||
@@ -204,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));
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
const int Database::kSchemaVersion = 20;
|
const int Database::kSchemaVersion = 21;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr char kDatabaseFilename[] = "strawberry.db";
|
constexpr char kDatabaseFilename[] = "strawberry.db";
|
||||||
@@ -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),
|
||||||
@@ -414,11 +414,6 @@ void Database::ExecSongTablesCommands(QSqlDatabase &db, const QStringList &song_
|
|||||||
// We allow a magic value in the schema files to update all songs tables at once.
|
// We allow a magic value in the schema files to update all songs tables at once.
|
||||||
if (command.contains(QLatin1String(kMagicAllSongsTables))) {
|
if (command.contains(QLatin1String(kMagicAllSongsTables))) {
|
||||||
for (const QString &table : song_tables) {
|
for (const QString &table : song_tables) {
|
||||||
// Another horrible hack: device songs tables don't have matching _fts tables, so if this command tries to touch one, ignore it.
|
|
||||||
if (table.startsWith("device_"_L1) && command.contains(QLatin1String(kMagicAllSongsTables) + "_fts"_L1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qLog(Info) << "Updating" << table << "for" << kMagicAllSongsTables;
|
qLog(Info) << "Updating" << table << "for" << kMagicAllSongsTables;
|
||||||
QString new_command(command);
|
QString new_command(command);
|
||||||
new_command.replace(QLatin1String(kMagicAllSongsTables), table);
|
new_command.replace(QLatin1String(kMagicAllSongsTables), table);
|
||||||
@@ -508,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;
|
||||||
}
|
}
|
||||||
@@ -599,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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,12 +157,16 @@ void HttpBaseRequest::HandleSSLErrors(const QList<QSslError> &ssl_errors) {
|
|||||||
HttpBaseRequest::ReplyDataResult HttpBaseRequest::GetReplyData(QNetworkReply *reply) {
|
HttpBaseRequest::ReplyDataResult HttpBaseRequest::GetReplyData(QNetworkReply *reply) {
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
if (reply->error() >= 200) {
|
||||||
|
reply->readAll(); // QTBUG-135641
|
||||||
|
}
|
||||||
return ReplyDataResult(ErrorCode::NetworkError, QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
return ReplyDataResult(ErrorCode::NetworkError, QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid()) {
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid()) {
|
||||||
const int http_status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
const int http_status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
if (http_status_code < 200 || http_status_code > 207) {
|
if (http_status_code < 200 || http_status_code > 207) {
|
||||||
|
reply->readAll(); // QTBUG-135641
|
||||||
return ReplyDataResult(ErrorCode::HttpError, QStringLiteral("Received HTTP code %1").arg(http_status_code));
|
return ReplyDataResult(ErrorCode::HttpError, QStringLiteral("Received HTTP code %1").arg(http_status_code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -156,10 +155,8 @@
|
|||||||
#include "lyrics/lyricsproviders.h"
|
#include "lyrics/lyricsproviders.h"
|
||||||
#include "device/devicemanager.h"
|
#include "device/devicemanager.h"
|
||||||
#include "device/devicestatefiltermodel.h"
|
#include "device/devicestatefiltermodel.h"
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
#include "device/deviceview.h"
|
#include "device/deviceview.h"
|
||||||
#include "device/deviceviewcontainer.h"
|
#include "device/deviceviewcontainer.h"
|
||||||
#endif
|
|
||||||
#include "transcoder/transcodedialog.h"
|
#include "transcoder/transcodedialog.h"
|
||||||
#include "settings/settingsdialog.h"
|
#include "settings/settingsdialog.h"
|
||||||
#include "constants/behavioursettings.h"
|
#include "constants/behavioursettings.h"
|
||||||
@@ -175,6 +172,7 @@
|
|||||||
# include "constants/tidalsettings.h"
|
# include "constants/tidalsettings.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SPOTIFY
|
#ifdef HAVE_SPOTIFY
|
||||||
|
# include "spotify/spotifyservice.h"
|
||||||
# include "constants/spotifysettings.h"
|
# include "constants/spotifysettings.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_QOBUZ
|
#ifdef HAVE_QOBUZ
|
||||||
@@ -208,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
|
||||||
@@ -280,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
|
||||||
@@ -292,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),
|
||||||
@@ -310,9 +313,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
context_view_(new ContextView(this)),
|
context_view_(new ContextView(this)),
|
||||||
collection_view_(new CollectionViewContainer(this)),
|
collection_view_(new CollectionViewContainer(this)),
|
||||||
file_view_(new FileView(this)),
|
file_view_(new FileView(this)),
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
device_view_(new DeviceViewContainer(this)),
|
device_view_(new DeviceViewContainer(this)),
|
||||||
#endif
|
|
||||||
playlist_list_(new PlaylistListContainer(this)),
|
playlist_list_(new PlaylistListContainer(this)),
|
||||||
queue_view_(new QueueView(this)),
|
queue_view_(new QueueView(this)),
|
||||||
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
|
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
|
||||||
@@ -375,9 +376,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
playlist_move_to_collection_(nullptr),
|
playlist_move_to_collection_(nullptr),
|
||||||
playlist_open_in_browser_(nullptr),
|
playlist_open_in_browser_(nullptr),
|
||||||
playlist_organize_(nullptr),
|
playlist_organize_(nullptr),
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
playlist_copy_to_device_(nullptr),
|
playlist_copy_to_device_(nullptr),
|
||||||
#endif
|
|
||||||
playlist_delete_(nullptr),
|
playlist_delete_(nullptr),
|
||||||
playlist_queue_(nullptr),
|
playlist_queue_(nullptr),
|
||||||
playlist_queue_play_next_(nullptr),
|
playlist_queue_play_next_(nullptr),
|
||||||
@@ -409,7 +408,11 @@ MainWindow::MainWindow(Application *app,
|
|||||||
// Initialize the UI
|
// Initialize the UI
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
|
|
||||||
|
if (QGuiApplication::platformName() != "wayland"_L1) {
|
||||||
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);
|
||||||
|
|
||||||
@@ -430,9 +433,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
ui_->tabs->AddTab(smartplaylists_view_, u"smartplaylists"_s, IconLoader::Load(u"view-media-playlist"_s, true, 0, 32), tr("Smart playlists"));
|
ui_->tabs->AddTab(smartplaylists_view_, u"smartplaylists"_s, IconLoader::Load(u"view-media-playlist"_s, true, 0, 32), tr("Smart playlists"));
|
||||||
ui_->tabs->AddTab(file_view_, u"files"_s, IconLoader::Load(u"document-open"_s, true, 0, 32), tr("Files"));
|
ui_->tabs->AddTab(file_view_, u"files"_s, IconLoader::Load(u"document-open"_s, true, 0, 32), tr("Files"));
|
||||||
ui_->tabs->AddTab(radio_view_, u"radios"_s, IconLoader::Load(u"radio"_s, true, 0, 32), tr("Radios"));
|
ui_->tabs->AddTab(radio_view_, u"radios"_s, IconLoader::Load(u"radio"_s, true, 0, 32), tr("Radios"));
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
ui_->tabs->AddTab(device_view_, u"devices"_s, IconLoader::Load(u"device"_s, true, 0, 32), tr("Devices"));
|
ui_->tabs->AddTab(device_view_, u"devices"_s, IconLoader::Load(u"device"_s, true, 0, 32), tr("Devices"));
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
ui_->tabs->AddTab(subsonic_view_, u"subsonic"_s, IconLoader::Load(u"subsonic"_s, true, 0, 32), tr("Subsonic"));
|
ui_->tabs->AddTab(subsonic_view_, u"subsonic"_s, IconLoader::Load(u"subsonic"_s, true, 0, 32), tr("Subsonic"));
|
||||||
#endif
|
#endif
|
||||||
@@ -480,9 +481,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
|
|
||||||
collection_view_->view()->setModel(app_->collection()->model()->filter());
|
collection_view_->view()->setModel(app_->collection()->model()->filter());
|
||||||
collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
|
collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
|
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
|
||||||
#endif
|
|
||||||
playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
|
playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
|
||||||
|
|
||||||
organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
|
organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
|
||||||
@@ -554,9 +553,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(file_view_, &FileView::CopyToCollection, this, &MainWindow::CopyFilesToCollection);
|
QObject::connect(file_view_, &FileView::CopyToCollection, this, &MainWindow::CopyFilesToCollection);
|
||||||
QObject::connect(file_view_, &FileView::MoveToCollection, this, &MainWindow::MoveFilesToCollection);
|
QObject::connect(file_view_, &FileView::MoveToCollection, this, &MainWindow::MoveFilesToCollection);
|
||||||
QObject::connect(file_view_, &FileView::EditTags, this, &MainWindow::EditFileTags);
|
QObject::connect(file_view_, &FileView::EditTags, this, &MainWindow::EditFileTags);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
QObject::connect(file_view_, &FileView::CopyToDevice, this, &MainWindow::CopyFilesToDevice);
|
QObject::connect(file_view_, &FileView::CopyToDevice, this, &MainWindow::CopyFilesToDevice);
|
||||||
#endif
|
|
||||||
file_view_->SetTaskManager(app_->task_manager());
|
file_view_->SetTaskManager(app_->task_manager());
|
||||||
|
|
||||||
// Action connections
|
// Action connections
|
||||||
@@ -631,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);
|
||||||
|
|
||||||
@@ -704,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);
|
||||||
@@ -718,10 +724,8 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::triggered, this, &MainWindow::SearchCoverAutomatically);
|
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::triggered, this, &MainWindow::SearchCoverAutomatically);
|
||||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::toggled, this, &MainWindow::ToggleSearchCoverAuto);
|
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::toggled, this, &MainWindow::ToggleSearchCoverAuto);
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
// Devices connections
|
// Devices connections
|
||||||
QObject::connect(device_view_->view(), &DeviceView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
QObject::connect(device_view_->view(), &DeviceView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
||||||
#endif
|
|
||||||
|
|
||||||
// Collection filter widget
|
// Collection filter widget
|
||||||
QActionGroup *collection_view_group = new QActionGroup(this);
|
QActionGroup *collection_view_group = new QActionGroup(this);
|
||||||
@@ -784,6 +788,9 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(spotify_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
QObject::connect(spotify_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
|
||||||
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
|
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
|
||||||
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
|
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
|
||||||
|
if (SpotifyServicePtr spotifyservice = app_->streaming_services()->Service<SpotifyService>()) {
|
||||||
|
QObject::connect(&*spotifyservice, &SpotifyService::UpdateSpotifyAccessToken, &*app_->player()->engine(), &EngineBase::UpdateSpotifyAccessToken);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QObject::connect(radio_view_, &RadioViewContainer::Refresh, &*app_->radio_services(), &RadioServices::RefreshChannels);
|
QObject::connect(radio_view_, &RadioViewContainer::Refresh, &*app_->radio_services(), &RadioServices::RefreshChannels);
|
||||||
@@ -824,9 +831,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
playlist_organize_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &MainWindow::PlaylistMoveToCollection);
|
playlist_organize_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Organize files..."), this, &MainWindow::PlaylistMoveToCollection);
|
||||||
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Copy to collection..."), this, &MainWindow::PlaylistCopyToCollection);
|
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"edit-copy"_s), tr("Copy to collection..."), this, &MainWindow::PlaylistCopyToCollection);
|
||||||
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"go-jump"_s), tr("Move to collection..."), this, &MainWindow::PlaylistMoveToCollection);
|
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load(u"go-jump"_s), tr("Move to collection..."), this, &MainWindow::PlaylistMoveToCollection);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &MainWindow::PlaylistCopyToDevice);
|
playlist_copy_to_device_ = playlist_menu_->addAction(IconLoader::Load(u"device"_s), tr("Copy to device..."), this, &MainWindow::PlaylistCopyToDevice);
|
||||||
#endif
|
|
||||||
playlist_delete_ = playlist_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &MainWindow::PlaylistDelete);
|
playlist_delete_ = playlist_menu_->addAction(IconLoader::Load(u"edit-delete"_s), tr("Delete from disk..."), this, &MainWindow::PlaylistDelete);
|
||||||
playlist_menu_->addSeparator();
|
playlist_menu_->addSeparator();
|
||||||
playlistitem_actions_separator_ = playlist_menu_->addSeparator();
|
playlistitem_actions_separator_ = playlist_menu_->addSeparator();
|
||||||
@@ -845,10 +850,8 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged);
|
QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged);
|
||||||
|
|
||||||
QObject::connect(&*app_->device_manager(), &DeviceManager::DeviceError, this, &MainWindow::ShowErrorDialog);
|
QObject::connect(&*app_->device_manager(), &DeviceManager::DeviceError, this, &MainWindow::ShowErrorDialog);
|
||||||
#ifndef WIN32
|
|
||||||
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, playlist_copy_to_device_, &QAction::setDisabled);
|
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, playlist_copy_to_device_, &QAction::setDisabled);
|
||||||
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
|
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
|
||||||
#endif
|
|
||||||
|
|
||||||
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
|
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
|
||||||
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
|
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
|
||||||
@@ -858,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
|
||||||
@@ -915,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));
|
||||||
@@ -980,34 +984,35 @@ MainWindow::MainWindow(Application *app,
|
|||||||
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
|
||||||
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
|
||||||
|
|
||||||
#if !defined(HAVE_AUDIOCD) || defined(Q_OS_WIN32)
|
#if !defined(HAVE_AUDIOCD)
|
||||||
ui_->action_open_cd->setEnabled(false);
|
ui_->action_open_cd->setEnabled(false);
|
||||||
ui_->action_open_cd->setVisible(false);
|
ui_->action_open_cd->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -1043,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;
|
||||||
@@ -1064,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);
|
||||||
|
|
||||||
@@ -1168,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();
|
||||||
@@ -1199,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) {
|
||||||
@@ -1221,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1235,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);
|
||||||
@@ -1352,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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1377,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
|
||||||
}
|
}
|
||||||
@@ -1435,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();
|
||||||
@@ -1444,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_) {
|
||||||
@@ -1477,7 +1487,7 @@ void MainWindow::MediaPaused() {
|
|||||||
track_slider_timer_->start();
|
track_slider_timer_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_icon_->SetPaused();
|
systemtrayicon_->SetPaused();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1498,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();
|
||||||
@@ -1515,18 +1525,18 @@ void MainWindow::SendNowPlaying() {
|
|||||||
|
|
||||||
// Send now playing to scrobble services
|
// Send now playing to scrobble services
|
||||||
Playlist *playlist = app_->playlist_manager()->active();
|
Playlist *playlist = app_->playlist_manager()->active();
|
||||||
if (app_->scrobbler()->enabled() && playlist && playlist->current_item() && playlist->current_item()->Metadata().is_metadata_good()) {
|
if (app_->scrobbler()->enabled() && playlist && playlist->current_item() && playlist->current_item()->EffectiveMetadata().is_metadata_good()) {
|
||||||
app_->scrobbler()->UpdateNowPlaying(playlist->current_item()->Metadata());
|
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) {
|
||||||
@@ -1536,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_) {
|
||||||
@@ -1562,9 +1572,9 @@ void MainWindow::TrackSkipped(PlaylistItemPtr item) {
|
|||||||
|
|
||||||
// If it was a collection item then we have to increment its skipped count in the database.
|
// If it was a collection item then we have to increment its skipped count in the database.
|
||||||
|
|
||||||
if (item && item->IsLocalCollectionItem() && item->Metadata().id() != -1) {
|
if (item && item->IsLocalCollectionItem() && item->EffectiveMetadata().id() != -1) {
|
||||||
|
|
||||||
Song song = item->Metadata();
|
Song song = item->EffectiveMetadata();
|
||||||
const qint64 position = app_->player()->engine()->position_nanosec();
|
const qint64 position = app_->player()->engine()->position_nanosec();
|
||||||
const qint64 length = app_->player()->engine()->length_nanosec();
|
const qint64 length = app_->player()->engine()->length_nanosec();
|
||||||
const float percentage = (length == 0 ? 1 : static_cast<float>(position) / static_cast<float>(length));
|
const float percentage = (length == 0 ? 1 : static_cast<float>(position) / static_cast<float>(length));
|
||||||
@@ -1594,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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1694,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
|
||||||
@@ -1719,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1727,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 {
|
||||||
@@ -1747,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()->Metadata().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_) {
|
||||||
@@ -1774,12 +1801,12 @@ void MainWindow::UpdateTrackPosition() {
|
|||||||
PlaylistItemPtr item(app_->player()->GetCurrentItem());
|
PlaylistItemPtr item(app_->player()->GetCurrentItem());
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
const qint64 length = (item->Metadata().length_nanosec() / kNsecPerSec);
|
const qint64 length = (item->EffectiveMetadata().length_nanosec() / kNsecPerSec);
|
||||||
if (length <= 0) return;
|
if (length <= 0) return;
|
||||||
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_) {
|
||||||
@@ -1788,12 +1815,12 @@ void MainWindow::UpdateTrackPosition() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send Scrobble
|
// Send Scrobble
|
||||||
if (app_->scrobbler()->enabled() && item->Metadata().is_metadata_good()) {
|
if (app_->scrobbler()->enabled() && item->EffectiveMetadata().is_metadata_good()) {
|
||||||
Playlist *playlist = app_->playlist_manager()->active();
|
Playlist *playlist = app_->playlist_manager()->active();
|
||||||
if (playlist && !playlist->scrobbled()) {
|
if (playlist && !playlist->scrobbled()) {
|
||||||
const qint64 scrobble_point = (playlist->scrobble_point_nanosec() / kNsecPerSec);
|
const qint64 scrobble_point = (playlist->scrobble_point_nanosec() / kNsecPerSec);
|
||||||
if (position >= scrobble_point) {
|
if (position >= scrobble_point) {
|
||||||
app_->scrobbler()->Scrobble(item->Metadata(), scrobble_point);
|
app_->scrobbler()->Scrobble(item->EffectiveMetadata(), scrobble_point);
|
||||||
playlist->set_scrobbled(true);
|
playlist->set_scrobbled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1910,7 +1937,7 @@ void MainWindow::AddToPlaylistFromAction(QAction *action) {
|
|||||||
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
items << item;
|
items << item;
|
||||||
songs << item->Metadata();
|
songs << item->EffectiveMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're creating a new playlist
|
// We're creating a new playlist
|
||||||
@@ -1989,12 +2016,12 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(src_idx.row());
|
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(src_idx.row());
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
|
|
||||||
if (item->Metadata().url().isLocalFile()) ++local_songs;
|
if (item->EffectiveMetadata().url().isLocalFile()) ++local_songs;
|
||||||
|
|
||||||
if (item->Metadata().has_cue()) {
|
if (item->EffectiveMetadata().has_cue()) {
|
||||||
cue_selected = true;
|
cue_selected = true;
|
||||||
}
|
}
|
||||||
else if (item->Metadata().IsEditable()) {
|
else if (item->EffectiveMetadata().IsEditable()) {
|
||||||
++editable;
|
++editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2032,9 +2059,7 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
playlist_show_in_collection_->setVisible(false);
|
playlist_show_in_collection_->setVisible(false);
|
||||||
playlist_copy_to_collection_->setVisible(false);
|
playlist_copy_to_collection_->setVisible(false);
|
||||||
playlist_move_to_collection_->setVisible(false);
|
playlist_move_to_collection_->setVisible(false);
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
playlist_copy_to_device_->setVisible(false);
|
playlist_copy_to_device_->setVisible(false);
|
||||||
#endif
|
|
||||||
playlist_organize_->setVisible(false);
|
playlist_organize_->setVisible(false);
|
||||||
playlist_delete_->setVisible(false);
|
playlist_delete_->setVisible(false);
|
||||||
|
|
||||||
@@ -2097,7 +2122,7 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
|
|
||||||
// Is it a collection item?
|
// Is it a collection item?
|
||||||
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
||||||
if (item && item->IsLocalCollectionItem() && item->Metadata().id() != -1) {
|
if (item && item->IsLocalCollectionItem() && item->EffectiveMetadata().id() != -1) {
|
||||||
playlist_organize_->setVisible(local_songs > 0 && editable > 0 && !cue_selected);
|
playlist_organize_->setVisible(local_songs > 0 && editable > 0 && !cue_selected);
|
||||||
playlist_show_in_collection_->setVisible(true);
|
playlist_show_in_collection_->setVisible(true);
|
||||||
playlist_open_in_browser_->setVisible(true);
|
playlist_open_in_browser_->setVisible(true);
|
||||||
@@ -2107,9 +2132,7 @@ void MainWindow::PlaylistRightClick(const QPoint global_pos, const QModelIndex &
|
|||||||
playlist_move_to_collection_->setVisible(local_songs > 0);
|
playlist_move_to_collection_->setVisible(local_songs > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
playlist_copy_to_device_->setVisible(local_songs > 0);
|
playlist_copy_to_device_->setVisible(local_songs > 0);
|
||||||
#endif
|
|
||||||
|
|
||||||
playlist_delete_->setVisible(delete_files_ && local_songs > 0);
|
playlist_delete_->setVisible(delete_files_ && local_songs > 0);
|
||||||
|
|
||||||
@@ -2189,9 +2212,9 @@ void MainWindow::RescanSongs() {
|
|||||||
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
|
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
if (item->IsLocalCollectionItem()) {
|
if (item->IsLocalCollectionItem()) {
|
||||||
songs << item->Metadata();
|
songs << item->EffectiveMetadata();
|
||||||
}
|
}
|
||||||
else if (item->Metadata().source() == Song::Source::LocalFile) {
|
else if (item->EffectiveMetadata().source() == Song::Source::LocalFile) {
|
||||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
||||||
app_->playlist_manager()->current()->ItemReload(persistent_index, item->OriginalMetadata(), false);
|
app_->playlist_manager()->current()->ItemReload(persistent_index, item->OriginalMetadata(), false);
|
||||||
}
|
}
|
||||||
@@ -2338,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());
|
||||||
|
|
||||||
@@ -2348,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;
|
||||||
@@ -2366,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;
|
||||||
@@ -2751,7 +2778,6 @@ void MainWindow::MoveFilesToCollection(const QList<QUrl> &urls) {
|
|||||||
|
|
||||||
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
|
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
||||||
organize_dialog_->SetCopy(true);
|
organize_dialog_->SetCopy(true);
|
||||||
if (organize_dialog_->SetUrls(urls)) {
|
if (organize_dialog_->SetUrls(urls)) {
|
||||||
@@ -2761,9 +2787,6 @@ void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
|
|||||||
else {
|
else {
|
||||||
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
Q_UNUSED(urls);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2823,7 +2846,7 @@ void MainWindow::PlaylistOpenInBrowser() {
|
|||||||
for (const QModelIndex &proxy_index : proxy_indexes) {
|
for (const QModelIndex &proxy_index : proxy_indexes) {
|
||||||
const QModelIndex source_index = app_->playlist_manager()->current()->filter()->mapToSource(proxy_index);
|
const QModelIndex source_index = app_->playlist_manager()->current()->filter()->mapToSource(proxy_index);
|
||||||
if (!source_index.isValid()) continue;
|
if (!source_index.isValid()) continue;
|
||||||
urls << QUrl(source_index.sibling(source_index.row(), static_cast<int>(Playlist::Column::Filename)).data().toString());
|
urls << QUrl(source_index.sibling(source_index.row(), static_cast<int>(Playlist::Column::URL)).data().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Utilities::OpenInFileBrowser(urls);
|
Utilities::OpenInFileBrowser(urls);
|
||||||
@@ -2839,7 +2862,7 @@ void MainWindow::PlaylistCopyUrl() {
|
|||||||
if (!source_index.isValid()) continue;
|
if (!source_index.isValid()) continue;
|
||||||
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
urls << item->StreamUrl();
|
urls << item->EffectiveUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urls.count() > 0) {
|
if (urls.count() > 0) {
|
||||||
@@ -2891,8 +2914,6 @@ void MainWindow::PlaylistSkip() {
|
|||||||
|
|
||||||
void MainWindow::PlaylistCopyToDevice() {
|
void MainWindow::PlaylistCopyToDevice() {
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
|
|
||||||
SongList songs;
|
SongList songs;
|
||||||
|
|
||||||
const QModelIndexList proxy_indexes = ui_->playlist->view()->selectionModel()->selectedRows();
|
const QModelIndexList proxy_indexes = ui_->playlist->view()->selectionModel()->selectedRows();
|
||||||
@@ -2917,8 +2938,6 @@ void MainWindow::PlaylistCopyToDevice() {
|
|||||||
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ChangeCollectionFilterMode(QAction *action) {
|
void MainWindow::ChangeCollectionFilterMode(QAction *action) {
|
||||||
@@ -3012,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();
|
||||||
@@ -3283,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3306,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3321,10 +3348,10 @@ void MainWindow::PlaylistDelete() {
|
|||||||
for (const QModelIndex &proxy_idx : proxy_indexes) {
|
for (const QModelIndex &proxy_idx : proxy_indexes) {
|
||||||
QModelIndex source_idx = app_->playlist_manager()->current()->filter()->mapToSource(proxy_idx);
|
QModelIndex source_idx = app_->playlist_manager()->current()->filter()->mapToSource(proxy_idx);
|
||||||
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_idx.row());
|
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_idx.row());
|
||||||
if (!item || !item->Metadata().url().isLocalFile()) continue;
|
if (!item || !item->EffectiveMetadata().url().isLocalFile()) continue;
|
||||||
QString filename = item->Metadata().url().toLocalFile();
|
QString filename = item->EffectiveMetadata().url().toLocalFile();
|
||||||
if (files.contains(filename)) continue;
|
if (files.contains(filename)) continue;
|
||||||
selected_songs << item->Metadata();
|
selected_songs << item->EffectiveMetadata();
|
||||||
files << filename;
|
files << filename;
|
||||||
if (item == app_->player()->GetCurrentItem()) is_current_item = true;
|
if (item == app_->player()->GetCurrentItem()) is_current_item = true;
|
||||||
}
|
}
|
||||||
@@ -3332,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3389,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"
|
||||||
@@ -73,9 +71,7 @@ class CollectionViewContainer;
|
|||||||
class CollectionFilter;
|
class CollectionFilter;
|
||||||
class AlbumCoverChoiceController;
|
class AlbumCoverChoiceController;
|
||||||
class CommandlineOptions;
|
class CommandlineOptions;
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
class DeviceViewContainer;
|
class DeviceViewContainer;
|
||||||
#endif
|
|
||||||
class EditTagDialog;
|
class EditTagDialog;
|
||||||
class Equalizer;
|
class Equalizer;
|
||||||
class ErrorDialog;
|
class ErrorDialog;
|
||||||
@@ -101,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 {
|
||||||
@@ -113,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,
|
||||||
@@ -126,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;
|
||||||
@@ -238,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();
|
||||||
@@ -284,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);
|
||||||
@@ -312,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_;
|
||||||
@@ -327,13 +326,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
ContextView *context_view_;
|
ContextView *context_view_;
|
||||||
CollectionViewContainer *collection_view_;
|
CollectionViewContainer *collection_view_;
|
||||||
FileView *file_view_;
|
FileView *file_view_;
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
DeviceViewContainer *device_view_;
|
DeviceViewContainer *device_view_;
|
||||||
#endif
|
|
||||||
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_;
|
||||||
@@ -366,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_;
|
||||||
@@ -380,9 +380,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
QAction *playlist_move_to_collection_;
|
QAction *playlist_move_to_collection_;
|
||||||
QAction *playlist_open_in_browser_;
|
QAction *playlist_open_in_browser_;
|
||||||
QAction *playlist_organize_;
|
QAction *playlist_organize_;
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
QAction *playlist_copy_to_device_;
|
QAction *playlist_copy_to_device_;
|
||||||
#endif
|
|
||||||
QAction *playlist_delete_;
|
QAction *playlist_delete_;
|
||||||
QAction *playlist_queue_;
|
QAction *playlist_queue_;
|
||||||
QAction *playlist_queue_play_next_;
|
QAction *playlist_queue_play_next_;
|
||||||
@@ -396,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_;
|
||||||
@@ -420,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
|
||||||
|
|||||||
@@ -13,10 +13,6 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Strawberry Music Player</string>
|
<string>Strawberry Music Player</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset resource="../../data/icons.qrc">
|
|
||||||
<normaloff>:/icons/128x128/strawberry.png</normaloff>:/icons/128x128/strawberry.png</iconset>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralWidget">
|
<widget class="QWidget" name="centralWidget">
|
||||||
<layout class="QVBoxLayout" name="layout_centralWidget">
|
<layout class="QVBoxLayout" name="layout_centralWidget">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
@@ -37,7 +33,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QSplitter" name="splitter">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="sidebar_layout">
|
<widget class="QWidget" name="sidebar_layout">
|
||||||
<layout class="QVBoxLayout" name="layout_left">
|
<layout class="QVBoxLayout" name="layout_left">
|
||||||
@@ -77,7 +73,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_6">
|
<widget class="Line" name="line_6">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -102,7 +98,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="player_controls">
|
<widget class="QFrame" name="player_controls">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::Shape::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="layout_player_controls">
|
<layout class="QHBoxLayout" name="layout_player_controls">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
@@ -167,7 +163,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="popupMode">
|
<property name="popupMode">
|
||||||
<enum>QToolButton::ToolButtonPopupMode::MenuButtonPopup</enum>
|
<enum>QToolButton::MenuButtonPopup</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoRaise">
|
<property name="autoRaise">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -211,7 +207,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_love">
|
<widget class="Line" name="line_love">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -237,7 +233,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_buttons">
|
<widget class="Line" name="line_buttons">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -260,10 +256,10 @@
|
|||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeType">
|
<property name="sizeType">
|
||||||
<enum>QSizePolicy::Policy::Expanding</enum>
|
<enum>QSizePolicy::Expanding</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@@ -276,7 +272,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_volume">
|
<widget class="Line" name="line_volume">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -292,7 +288,7 @@
|
|||||||
<number>100</number>
|
<number>100</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -326,7 +322,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="status_bar_line">
|
<widget class="Line" name="status_bar_line">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -380,7 +376,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="playlist_summary">
|
<widget class="QLabel" name="playlist_summary">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -391,7 +387,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_5">
|
<widget class="Line" name="line_5">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -401,7 +397,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_2">
|
<widget class="Line" name="line_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -521,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"/>
|
||||||
@@ -580,7 +577,7 @@
|
|||||||
<string>Ctrl+Q</string>
|
<string>Ctrl+Q</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="menuRole">
|
<property name="menuRole">
|
||||||
<enum>QAction::MenuRole::QuitRole</enum>
|
<enum>QAction::QuitRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_stop_after_this_track">
|
<action name="action_stop_after_this_track">
|
||||||
@@ -644,7 +641,7 @@
|
|||||||
<string>Ctrl+P</string>
|
<string>Ctrl+P</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="menuRole">
|
<property name="menuRole">
|
||||||
<enum>QAction::MenuRole::PreferencesRole</enum>
|
<enum>QAction::PreferencesRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_about_strawberry">
|
<action name="action_about_strawberry">
|
||||||
@@ -659,7 +656,7 @@
|
|||||||
<string>F1</string>
|
<string>F1</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="menuRole">
|
<property name="menuRole">
|
||||||
<enum>QAction::MenuRole::AboutRole</enum>
|
<enum>QAction::AboutRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_shuffle">
|
<action name="action_shuffle">
|
||||||
@@ -785,7 +782,7 @@
|
|||||||
<string>About &Qt</string>
|
<string>About &Qt</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="menuRole">
|
<property name="menuRole">
|
||||||
<enum>QAction::MenuRole::AboutQtRole</enum>
|
<enum>QAction::AboutQtRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_mute">
|
<action name="action_mute">
|
||||||
@@ -867,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,31 +42,47 @@ 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 &request, QIODevice *outgoingData) {
|
}
|
||||||
|
|
||||||
|
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) {
|
||||||
|
|
||||||
QByteArray user_agent;
|
QByteArray user_agent;
|
||||||
if (request.hasRawHeader("User-Agent")) {
|
if (network_request.hasRawHeader("User-Agent")) {
|
||||||
user_agent = 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_request(request);
|
QNetworkRequest new_network_request(network_request);
|
||||||
new_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
new_network_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
new_request.setHeader(QNetworkRequest::UserAgentHeader, user_agent);
|
new_network_request.setHeader(QNetworkRequest::UserAgentHeader, user_agent);
|
||||||
|
|
||||||
if (op == QNetworkAccessManager::PostOperation && !new_request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
|
if (op == QNetworkAccessManager::PostOperation && !new_network_request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
|
||||||
new_request.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
new_network_request.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the cache unless the caller has changed the setting already
|
// Prefer the cache unless the caller has changed the setting already
|
||||||
if (request.attribute(QNetworkRequest::CacheLoadControlAttribute).toInt() == QNetworkRequest::PreferNetwork) {
|
if (!network_request.attribute(QNetworkRequest::CacheLoadControlAttribute).isValid()) {
|
||||||
new_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
new_network_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
return QNetworkAccessManager::createRequest(op, new_network_request, outgoing_data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class NetworkAccessManager : public QNetworkAccessManager {
|
|||||||
explicit NetworkAccessManager(QObject *parent = nullptr);
|
explicit NetworkAccessManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override;
|
QNetworkReply *createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NETWORKACCESSMANAGER_H
|
#endif // NETWORKACCESSMANAGER_H
|
||||||
|
|||||||
@@ -288,10 +288,10 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
|||||||
bool is_current = false;
|
bool is_current = false;
|
||||||
bool is_next = false;
|
bool is_next = false;
|
||||||
|
|
||||||
if (result.media_url_ == current_item->Url()) {
|
if (result.media_url_ == current_item->OriginalUrl()) {
|
||||||
is_current = true;
|
is_current = true;
|
||||||
}
|
}
|
||||||
else if (has_next_row && next_item->Url() == result.media_url_) {
|
else if (has_next_row && next_item->OriginalUrl() == result.media_url_) {
|
||||||
is_next = true;
|
is_next = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -316,8 +316,8 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
|||||||
qLog(Debug) << "URL handler for" << result.media_url_ << "returned" << result.stream_url_;
|
qLog(Debug) << "URL handler for" << result.media_url_ << "returned" << result.stream_url_;
|
||||||
|
|
||||||
Song song;
|
Song song;
|
||||||
if (is_current) song = current_item->Metadata();
|
if (is_current) song = current_item->EffectiveMetadata();
|
||||||
else if (is_next) song = next_item->Metadata();
|
else if (is_next) song = next_item->EffectiveMetadata();
|
||||||
|
|
||||||
bool update = false;
|
bool update = false;
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
|||||||
if (
|
if (
|
||||||
(result.stream_url_.isValid())
|
(result.stream_url_.isValid())
|
||||||
&&
|
&&
|
||||||
(result.stream_url_ != song.url())
|
(result.stream_url_ != song.effective_url())
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
song.set_stream_url(result.stream_url_);
|
song.set_stream_url(result.stream_url_);
|
||||||
@@ -371,14 +371,14 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_current) {
|
if (is_current) {
|
||||||
qLog(Debug) << "Playing song" << current_item->Metadata().title() << result.stream_url_ << "position" << play_offset_nanosec_;
|
qLog(Debug) << "Playing song" << current_item->EffectiveMetadata().title() << result.stream_url_ << "position" << play_offset_nanosec_;
|
||||||
engine_->Play(result.media_url_, result.stream_url_, pause_, stream_change_type_, song.has_cue(), static_cast<quint64>(song.beginning_nanosec()), song.end_nanosec(), play_offset_nanosec_, song.ebur128_integrated_loudness_lufs());
|
engine_->Play(result.media_url_, result.stream_url_, pause_, stream_change_type_, song.has_cue(), static_cast<quint64>(song.beginning_nanosec()), song.end_nanosec(), play_offset_nanosec_, song.ebur128_integrated_loudness_lufs());
|
||||||
current_item_ = current_item;
|
current_item_ = current_item;
|
||||||
play_offset_nanosec_ = 0;
|
play_offset_nanosec_ = 0;
|
||||||
}
|
}
|
||||||
else if (is_next && !current_item->Metadata().is_module_music()) {
|
else if (is_next && !current_item->EffectiveMetadata().is_module_music()) {
|
||||||
qLog(Debug) << "Preloading next song" << next_item->Metadata().title() << result.stream_url_;
|
qLog(Debug) << "Preloading next song" << next_item->EffectiveMetadata().title() << result.stream_url_;
|
||||||
engine_->StartPreloading(next_item->Url(), result.stream_url_, song.has_cue(), song.beginning_nanosec(), song.end_nanosec());
|
engine_->StartPreloading(next_item->OriginalUrl(), result.stream_url_, song.has_cue(), song.beginning_nanosec(), song.end_nanosec());
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -504,8 +504,8 @@ bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
|
|||||||
|
|
||||||
void Player::TrackEnded() {
|
void Player::TrackEnded() {
|
||||||
|
|
||||||
if (current_item_ && current_item_->IsLocalCollectionItem() && current_item_->Metadata().id() != -1) {
|
if (current_item_ && current_item_->IsLocalCollectionItem() && current_item_->EffectiveMetadata().id() != -1) {
|
||||||
playlist_manager_->collection_backend()->IncrementPlayCountAsync(current_item_->Metadata().id());
|
playlist_manager_->collection_backend()->IncrementPlayCountAsync(current_item_->EffectiveMetadata().id());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HandleStopAfter(Playlist::AutoScroll::Maybe)) return;
|
if (HandleStopAfter(Playlist::AutoScroll::Maybe)) return;
|
||||||
@@ -554,7 +554,7 @@ void Player::PlayPause(const quint64 offset_nanosec, const Playlist::AutoScroll
|
|||||||
void Player::UnPause() {
|
void Player::UnPause() {
|
||||||
|
|
||||||
if (current_item_ && pause_time_.isValid()) {
|
if (current_item_ && pause_time_.isValid()) {
|
||||||
const Song &song = current_item_->Metadata();
|
const Song &song = current_item_->EffectiveMetadata();
|
||||||
if (url_handlers_->CanHandle(song.url()) && song.stream_url_can_expire()) {
|
if (url_handlers_->CanHandle(song.url()) && song.stream_url_can_expire()) {
|
||||||
const qint64 time = QDateTime::currentSecsSinceEpoch() - pause_time_.toSecsSinceEpoch();
|
const qint64 time = QDateTime::currentSecsSinceEpoch() - pause_time_.toSecsSinceEpoch();
|
||||||
if (time >= 30) { // Stream URL might be expired.
|
if (time >= 30) { // Stream URL might be expired.
|
||||||
@@ -745,7 +745,7 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
|
|||||||
Q_EMIT TrackSkipped(current_item_);
|
Q_EMIT TrackSkipped(current_item_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_item_ && playlist_manager_->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(playlist_manager_->active()->item_at(index)->Metadata())) {
|
if (current_item_ && playlist_manager_->active()->has_item_at(index) && current_item_->EffectiveMetadata().IsOnSameAlbum(playlist_manager_->active()->item_at(index)->EffectiveMetadata())) {
|
||||||
change |= EngineBase::TrackChangeType::SameAlbum;
|
change |= EngineBase::TrackChangeType::SameAlbum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,7 +758,7 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
|
|||||||
}
|
}
|
||||||
|
|
||||||
current_item_ = playlist_manager_->active()->current_item();
|
current_item_ = playlist_manager_->active()->current_item();
|
||||||
const QUrl url = current_item_->StreamUrl();
|
const QUrl url = current_item_->EffectiveUrl();
|
||||||
|
|
||||||
if (url_handlers_->CanHandle(url)) {
|
if (url_handlers_->CanHandle(url)) {
|
||||||
// It's already loading
|
// It's already loading
|
||||||
@@ -773,8 +773,8 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
|
|||||||
HandleLoadResult(url_handler->StartLoading(url));
|
HandleLoadResult(url_handler->StartLoading(url));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Debug) << "Playing song" << current_item_->Metadata().title() << url << "position" << offset_nanosec;
|
qLog(Debug) << "Playing song" << current_item_->EffectiveMetadata().title() << url << "position" << offset_nanosec;
|
||||||
engine_->Play(current_item_->Url(), url, pause, change, current_item_->Metadata().has_cue(), static_cast<quint64>(current_item_->effective_beginning_nanosec()), current_item_->effective_end_nanosec(), offset_nanosec, current_item_->effective_ebur128_integrated_loudness_lufs());
|
engine_->Play(current_item_->OriginalUrl(), url, pause, change, current_item_->EffectiveMetadata().has_cue(), static_cast<quint64>(current_item_->effective_beginning_nanosec()), current_item_->effective_end_nanosec(), offset_nanosec, current_item_->EffectiveMetadata().ebur128_integrated_loudness_lufs());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -823,8 +823,8 @@ void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
|
|||||||
const int current_row = playlist_manager_->active()->current_row();
|
const int current_row = playlist_manager_->active()->current_row();
|
||||||
if (current_row != -1) {
|
if (current_row != -1) {
|
||||||
PlaylistItemPtr item = playlist_manager_->active()->current_item();
|
PlaylistItemPtr item = playlist_manager_->active()->current_item();
|
||||||
if (item && engine_metadata.media_url == item->Url()) {
|
if (item && engine_metadata.media_url == item->OriginalUrl()) {
|
||||||
Song song = item->Metadata();
|
Song song = item->EffectiveMetadata();
|
||||||
song.MergeFromEngineMetadata(engine_metadata);
|
song.MergeFromEngineMetadata(engine_metadata);
|
||||||
playlist_manager_->active()->UpdateItemMetadata(current_row, item, song, true);
|
playlist_manager_->active()->UpdateItemMetadata(current_row, item, song, true);
|
||||||
return;
|
return;
|
||||||
@@ -836,8 +836,8 @@ void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
|
|||||||
const int next_row = playlist_manager_->active()->next_row();
|
const int next_row = playlist_manager_->active()->next_row();
|
||||||
if (next_row != -1) {
|
if (next_row != -1) {
|
||||||
PlaylistItemPtr next_item = playlist_manager_->active()->item_at(next_row);
|
PlaylistItemPtr next_item = playlist_manager_->active()->item_at(next_row);
|
||||||
if (engine_metadata.media_url == next_item->Url()) {
|
if (engine_metadata.media_url == next_item->OriginalUrl()) {
|
||||||
Song song = next_item->Metadata();
|
Song song = next_item->EffectiveMetadata();
|
||||||
song.MergeFromEngineMetadata(engine_metadata);
|
song.MergeFromEngineMetadata(engine_metadata);
|
||||||
playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
|
playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
|
||||||
}
|
}
|
||||||
@@ -905,11 +905,11 @@ void Player::PlayWithPause(const quint64 offset_nanosec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Player::ShowOSD() {
|
void Player::ShowOSD() {
|
||||||
if (current_item_) Q_EMIT ForceShowOSD(current_item_->Metadata(), false);
|
if (current_item_) Q_EMIT ForceShowOSD(current_item_->EffectiveMetadata(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TogglePrettyOSD() {
|
void Player::TogglePrettyOSD() {
|
||||||
if (current_item_) Q_EMIT ForceShowOSD(current_item_->Metadata(), true);
|
if (current_item_) Q_EMIT ForceShowOSD(current_item_->EffectiveMetadata(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TrackAboutToEnd() {
|
void Player::TrackAboutToEnd() {
|
||||||
@@ -932,7 +932,7 @@ void Player::TrackAboutToEnd() {
|
|||||||
|
|
||||||
// If the next track is on the same album (or same cue file),
|
// If the next track is on the same album (or same cue file),
|
||||||
// and the user doesn't want to crossfade between tracks on the same album, then don't do this automatic crossfading.
|
// and the user doesn't want to crossfade between tracks on the same album, then don't do this automatic crossfading.
|
||||||
if (engine_->crossfade_same_album() || !has_next_row || !next_item || !current_item_->Metadata().IsOnSameAlbum(next_item->Metadata())) {
|
if (engine_->crossfade_same_album() || !has_next_row || !next_item || !current_item_->EffectiveMetadata().IsOnSameAlbum(next_item->EffectiveMetadata())) {
|
||||||
TrackEnded();
|
TrackEnded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -941,7 +941,7 @@ void Player::TrackAboutToEnd() {
|
|||||||
// Crossfade is off, so start preloading the next track, so we don't get a gap between songs.
|
// Crossfade is off, so start preloading the next track, so we don't get a gap between songs.
|
||||||
if (!has_next_row || !next_item) return;
|
if (!has_next_row || !next_item) return;
|
||||||
|
|
||||||
QUrl url = next_item->StreamUrl();
|
QUrl url = next_item->EffectiveUrl();
|
||||||
|
|
||||||
// Get the actual track URL rather than the stream URL.
|
// Get the actual track URL rather than the stream URL.
|
||||||
if (url_handlers_->CanHandle(url)) {
|
if (url_handlers_->CanHandle(url)) {
|
||||||
@@ -961,20 +961,20 @@ void Player::TrackAboutToEnd() {
|
|||||||
case UrlHandler::LoadResult::Type::TrackAvailable:
|
case UrlHandler::LoadResult::Type::TrackAvailable:
|
||||||
qLog(Debug) << "URL handler for" << result.media_url_ << "returned" << result.stream_url_;
|
qLog(Debug) << "URL handler for" << result.media_url_ << "returned" << result.stream_url_;
|
||||||
url = result.stream_url_;
|
url = result.stream_url_;
|
||||||
Song song = next_item->Metadata();
|
Song song = next_item->EffectiveMetadata();
|
||||||
song.set_stream_url(url);
|
song.set_stream_url(url);
|
||||||
next_item->SetTemporaryMetadata(song);
|
next_item->SetStreamMetadata(song);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preloading any format while currently playing module music is broken in GStreamer.
|
// Preloading any format while currently playing module music is broken in GStreamer.
|
||||||
// See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/769
|
// See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/769
|
||||||
if (current_item_ && current_item_->Metadata().is_module_music()) {
|
if (current_item_ && current_item_->EffectiveMetadata().is_module_music()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_->StartPreloading(next_item->Url(), url, next_item->Metadata().has_cue(), next_item->effective_beginning_nanosec(), next_item->effective_end_nanosec());
|
engine_->StartPreloading(next_item->OriginalUrl(), url, next_item->EffectiveMetadata().has_cue(), next_item->effective_beginning_nanosec(), next_item->effective_end_nanosec());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|
||||||
|
|||||||
@@ -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-2024, 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
|
||||||
@@ -68,9 +68,13 @@
|
|||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
const QStringList Song::kColumns = QStringList() << u"title"_s
|
const QStringList Song::kColumns = QStringList() << u"title"_s
|
||||||
|
<< u"titlesort"_s
|
||||||
<< u"album"_s
|
<< u"album"_s
|
||||||
|
<< u"albumsort"_s
|
||||||
<< u"artist"_s
|
<< u"artist"_s
|
||||||
|
<< u"artistsort"_s
|
||||||
<< u"albumartist"_s
|
<< u"albumartist"_s
|
||||||
|
<< u"albumartistsort"_s
|
||||||
<< u"track"_s
|
<< u"track"_s
|
||||||
<< u"disc"_s
|
<< u"disc"_s
|
||||||
<< u"year"_s
|
<< u"year"_s
|
||||||
@@ -78,7 +82,9 @@ const QStringList Song::kColumns = QStringList() << u"title"_s
|
|||||||
<< u"genre"_s
|
<< u"genre"_s
|
||||||
<< u"compilation"_s
|
<< u"compilation"_s
|
||||||
<< u"composer"_s
|
<< u"composer"_s
|
||||||
|
<< u"composersort"_s
|
||||||
<< u"performer"_s
|
<< u"performer"_s
|
||||||
|
<< u"performersort"_s
|
||||||
<< u"grouping"_s
|
<< u"grouping"_s
|
||||||
<< u"comment"_s
|
<< u"comment"_s
|
||||||
<< u"lyrics"_s
|
<< u"lyrics"_s
|
||||||
@@ -126,6 +132,9 @@ const QStringList Song::kColumns = QStringList() << u"title"_s
|
|||||||
<< u"cue_path"_s
|
<< u"cue_path"_s
|
||||||
|
|
||||||
<< u"rating"_s
|
<< u"rating"_s
|
||||||
|
<< u"bpm"_s
|
||||||
|
<< u"mood"_s
|
||||||
|
<< u"initial_key"_s
|
||||||
|
|
||||||
<< u"acoustid_id"_s
|
<< u"acoustid_id"_s
|
||||||
<< u"acoustid_fingerprint"_s
|
<< u"acoustid_fingerprint"_s
|
||||||
@@ -234,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
|
||||||
@@ -261,9 +271,13 @@ struct Song::Private : public QSharedData {
|
|||||||
bool valid_;
|
bool valid_;
|
||||||
|
|
||||||
QString title_;
|
QString title_;
|
||||||
|
QString titlesort_;
|
||||||
QString album_;
|
QString album_;
|
||||||
|
QString albumsort_;
|
||||||
QString artist_;
|
QString artist_;
|
||||||
|
QString artistsort_;
|
||||||
QString albumartist_;
|
QString albumartist_;
|
||||||
|
QString albumartistsort_;
|
||||||
int track_;
|
int track_;
|
||||||
int disc_;
|
int disc_;
|
||||||
int year_;
|
int year_;
|
||||||
@@ -271,7 +285,9 @@ struct Song::Private : public QSharedData {
|
|||||||
QString genre_;
|
QString genre_;
|
||||||
bool compilation_; // From the file tag
|
bool compilation_; // From the file tag
|
||||||
QString composer_;
|
QString composer_;
|
||||||
|
QString composersort_;
|
||||||
QString performer_;
|
QString performer_;
|
||||||
|
QString performersort_;
|
||||||
QString grouping_;
|
QString grouping_;
|
||||||
QString comment_;
|
QString comment_;
|
||||||
QString lyrics_;
|
QString lyrics_;
|
||||||
@@ -316,6 +332,9 @@ struct Song::Private : public QSharedData {
|
|||||||
QString cue_path_; // If the song has a CUE, this contains it's path.
|
QString cue_path_; // If the song has a CUE, this contains it's path.
|
||||||
|
|
||||||
float rating_; // Database rating, initial rating read from tag.
|
float rating_; // Database rating, initial rating read from tag.
|
||||||
|
float bpm_;
|
||||||
|
QString mood_;
|
||||||
|
QString initial_key_;
|
||||||
|
|
||||||
QString acoustid_id_;
|
QString acoustid_id_;
|
||||||
QString acoustid_fingerprint_;
|
QString acoustid_fingerprint_;
|
||||||
@@ -334,15 +353,12 @@ 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.
|
||||||
|
|
||||||
QString title_sortable_;
|
QUrl stream_url_; // Temporary stream URL set by the URL handler.
|
||||||
QString album_sortable_;
|
|
||||||
QString artist_sortable_;
|
|
||||||
QString albumartist_sortable_;
|
|
||||||
|
|
||||||
QUrl stream_url_; // Temporary stream url set by the URL handler.
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,6 +400,9 @@ Song::Private::Private(const Source source)
|
|||||||
art_unset_(false),
|
art_unset_(false),
|
||||||
|
|
||||||
rating_(-1),
|
rating_(-1),
|
||||||
|
bpm_(-1),
|
||||||
|
|
||||||
|
id3v2_version_(0),
|
||||||
|
|
||||||
init_from_file_(false),
|
init_from_file_(false),
|
||||||
suspicious_tags_(false)
|
suspicious_tags_(false)
|
||||||
@@ -411,9 +430,13 @@ int Song::id() const { return d->id_; }
|
|||||||
bool Song::is_valid() const { return d->valid_; }
|
bool Song::is_valid() const { return d->valid_; }
|
||||||
|
|
||||||
const QString &Song::title() const { return d->title_; }
|
const QString &Song::title() const { return d->title_; }
|
||||||
|
const QString &Song::titlesort() const { return d->titlesort_; }
|
||||||
const QString &Song::album() const { return d->album_; }
|
const QString &Song::album() const { return d->album_; }
|
||||||
|
const QString &Song::albumsort() const { return d->albumsort_; }
|
||||||
const QString &Song::artist() const { return d->artist_; }
|
const QString &Song::artist() const { return d->artist_; }
|
||||||
|
const QString &Song::artistsort() const { return d->artistsort_; }
|
||||||
const QString &Song::albumartist() const { return d->albumartist_; }
|
const QString &Song::albumartist() const { return d->albumartist_; }
|
||||||
|
const QString &Song::albumartistsort() const { return d->albumartistsort_; }
|
||||||
int Song::track() const { return d->track_; }
|
int Song::track() const { return d->track_; }
|
||||||
int Song::disc() const { return d->disc_; }
|
int Song::disc() const { return d->disc_; }
|
||||||
int Song::year() const { return d->year_; }
|
int Song::year() const { return d->year_; }
|
||||||
@@ -421,7 +444,9 @@ int Song::originalyear() const { return d->originalyear_; }
|
|||||||
const QString &Song::genre() const { return d->genre_; }
|
const QString &Song::genre() const { return d->genre_; }
|
||||||
bool Song::compilation() const { return d->compilation_; }
|
bool Song::compilation() const { return d->compilation_; }
|
||||||
const QString &Song::composer() const { return d->composer_; }
|
const QString &Song::composer() const { return d->composer_; }
|
||||||
|
const QString &Song::composersort() const { return d->composersort_; }
|
||||||
const QString &Song::performer() const { return d->performer_; }
|
const QString &Song::performer() const { return d->performer_; }
|
||||||
|
const QString &Song::performersort() const { return d->performersort_; }
|
||||||
const QString &Song::grouping() const { return d->grouping_; }
|
const QString &Song::grouping() const { return d->grouping_; }
|
||||||
const QString &Song::comment() const { return d->comment_; }
|
const QString &Song::comment() const { return d->comment_; }
|
||||||
const QString &Song::lyrics() const { return d->lyrics_; }
|
const QString &Song::lyrics() const { return d->lyrics_; }
|
||||||
@@ -468,6 +493,9 @@ bool Song::art_unset() const { return d->art_unset_; }
|
|||||||
const QString &Song::cue_path() const { return d->cue_path_; }
|
const QString &Song::cue_path() const { return d->cue_path_; }
|
||||||
|
|
||||||
float Song::rating() const { return d->rating_; }
|
float Song::rating() const { return d->rating_; }
|
||||||
|
float Song::bpm() const { return d->bpm_; }
|
||||||
|
const QString &Song::mood() const { return d->mood_; }
|
||||||
|
const QString &Song::initial_key() const { return d->initial_key_; }
|
||||||
|
|
||||||
const QString &Song::acoustid_id() const { return d->acoustid_id_; }
|
const QString &Song::acoustid_id() const { return d->acoustid_id_; }
|
||||||
const QString &Song::acoustid_fingerprint() const { return d->acoustid_fingerprint_; }
|
const QString &Song::acoustid_fingerprint() const { return d->acoustid_fingerprint_; }
|
||||||
@@ -486,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_; }
|
||||||
@@ -511,20 +541,19 @@ QString *Song::mutable_musicbrainz_work_id() { return &d->musicbrainz_work_id_;
|
|||||||
|
|
||||||
bool Song::init_from_file() const { return d->init_from_file_; }
|
bool Song::init_from_file() const { return d->init_from_file_; }
|
||||||
|
|
||||||
const QString &Song::title_sortable() const { return d->title_sortable_; }
|
|
||||||
const QString &Song::album_sortable() const { return d->album_sortable_; }
|
|
||||||
const QString &Song::artist_sortable() const { return d->artist_sortable_; }
|
|
||||||
const QString &Song::albumartist_sortable() const { return d->albumartist_sortable_; }
|
|
||||||
|
|
||||||
const QUrl &Song::stream_url() const { return d->stream_url_; }
|
const QUrl &Song::stream_url() const { return d->stream_url_; }
|
||||||
|
|
||||||
void Song::set_id(const int id) { d->id_ = id; }
|
void Song::set_id(const int id) { d->id_ = id; }
|
||||||
void Song::set_valid(const bool v) { d->valid_ = v; }
|
void Song::set_valid(const bool v) { d->valid_ = v; }
|
||||||
|
|
||||||
void Song::set_title(const QString &v) { d->title_sortable_ = sortable(v); d->title_ = v; }
|
void Song::set_title(const QString &v) { d->title_ = v; }
|
||||||
void Song::set_album(const QString &v) { d->album_sortable_ = sortable(v); d->album_ = v; }
|
void Song::set_titlesort(const QString &v) { d->titlesort_ = v; }
|
||||||
void Song::set_artist(const QString &v) { d->artist_sortable_ = sortable(v); d->artist_ = v; }
|
void Song::set_album(const QString &v) { d->album_ = v; }
|
||||||
void Song::set_albumartist(const QString &v) { d->albumartist_sortable_ = sortable(v); d->albumartist_ = v; }
|
void Song::set_albumsort(const QString &v) { d->albumsort_ = v; }
|
||||||
|
void Song::set_artist(const QString &v) { d->artist_ = v; }
|
||||||
|
void Song::set_artistsort(const QString &v) { d->artistsort_ = v; }
|
||||||
|
void Song::set_albumartist(const QString &v) { d->albumartist_ = v; }
|
||||||
|
void Song::set_albumartistsort(const QString &v) { d->albumartistsort_ = v; }
|
||||||
void Song::set_track(const int v) { d->track_ = v; }
|
void Song::set_track(const int v) { d->track_ = v; }
|
||||||
void Song::set_disc(const int v) { d->disc_ = v; }
|
void Song::set_disc(const int v) { d->disc_ = v; }
|
||||||
void Song::set_year(const int v) { d->year_ = v; }
|
void Song::set_year(const int v) { d->year_ = v; }
|
||||||
@@ -532,7 +561,9 @@ void Song::set_originalyear(const int v) { d->originalyear_ = v; }
|
|||||||
void Song::set_genre(const QString &v) { d->genre_ = v; }
|
void Song::set_genre(const QString &v) { d->genre_ = v; }
|
||||||
void Song::set_compilation(const bool v) { d->compilation_ = v; }
|
void Song::set_compilation(const bool v) { d->compilation_ = v; }
|
||||||
void Song::set_composer(const QString &v) { d->composer_ = v; }
|
void Song::set_composer(const QString &v) { d->composer_ = v; }
|
||||||
|
void Song::set_composersort(const QString &v) { d->composersort_ = v; }
|
||||||
void Song::set_performer(const QString &v) { d->performer_ = v; }
|
void Song::set_performer(const QString &v) { d->performer_ = v; }
|
||||||
|
void Song::set_performersort(const QString &v) { d->performersort_ = v; }
|
||||||
void Song::set_grouping(const QString &v) { d->grouping_ = v; }
|
void Song::set_grouping(const QString &v) { d->grouping_ = v; }
|
||||||
void Song::set_comment(const QString &v) { d->comment_ = v; }
|
void Song::set_comment(const QString &v) { d->comment_ = v; }
|
||||||
void Song::set_lyrics(const QString &v) { d->lyrics_ = v; }
|
void Song::set_lyrics(const QString &v) { d->lyrics_ = v; }
|
||||||
@@ -578,6 +609,9 @@ void Song::set_art_unset(const bool v) { d->art_unset_ = v; }
|
|||||||
void Song::set_cue_path(const QString &v) { d->cue_path_ = v; }
|
void Song::set_cue_path(const QString &v) { d->cue_path_ = v; }
|
||||||
|
|
||||||
void Song::set_rating(const float v) { d->rating_ = v; }
|
void Song::set_rating(const float v) { d->rating_ = v; }
|
||||||
|
void Song::set_bpm(const float v) { d->bpm_ = v; }
|
||||||
|
void Song::set_mood(const QString &v) { d->mood_ = v; }
|
||||||
|
void Song::set_initial_key(const QString &v) { d->initial_key_ = v; }
|
||||||
|
|
||||||
void Song::set_acoustid_id(const QString &v) { d->acoustid_id_ = v; }
|
void Song::set_acoustid_id(const QString &v) { d->acoustid_id_ = v; }
|
||||||
void Song::set_acoustid_fingerprint(const QString &v) { d->acoustid_fingerprint_ = v; }
|
void Song::set_acoustid_fingerprint(const QString &v) { d->acoustid_fingerprint_ = v; }
|
||||||
@@ -596,44 +630,25 @@ 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; }
|
||||||
|
|
||||||
void Song::set_title(const TagLib::String &v) {
|
void Song::set_title(const TagLib::String &v) { d->title_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_titlesort(const TagLib::String &v) { d->titlesort_ = TagLibStringToQString(v); }
|
||||||
const QString title = TagLibStringToQString(v);
|
void Song::set_album(const TagLib::String &v) { d->album_ = TagLibStringToQString(v); }
|
||||||
d->title_sortable_ = sortable(title);
|
void Song::set_albumsort(const TagLib::String &v) { d->albumsort_ = TagLibStringToQString(v); }
|
||||||
d->title_ = title;
|
void Song::set_artist(const TagLib::String &v) { d->artist_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_artistsort(const TagLib::String &v) { d->artistsort_ = TagLibStringToQString(v); }
|
||||||
}
|
void Song::set_albumartist(const TagLib::String &v) { d->albumartist_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_albumartistsort(const TagLib::String &v) { d->albumartistsort_ = TagLibStringToQString(v); }
|
||||||
void Song::set_album(const TagLib::String &v) {
|
|
||||||
|
|
||||||
const QString album = TagLibStringToQString(v);
|
|
||||||
d->album_sortable_ = sortable(album);
|
|
||||||
d->album_ = album;
|
|
||||||
|
|
||||||
}
|
|
||||||
void Song::set_artist(const TagLib::String &v) {
|
|
||||||
|
|
||||||
const QString artist = TagLibStringToQString(v);
|
|
||||||
d->artist_sortable_ = sortable(artist);
|
|
||||||
d->artist_ = artist;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Song::set_albumartist(const TagLib::String &v) {
|
|
||||||
|
|
||||||
const QString albumartist = TagLibStringToQString(v);
|
|
||||||
d->albumartist_sortable_ = sortable(albumartist);
|
|
||||||
d->albumartist_ = albumartist;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Song::set_genre(const TagLib::String &v) { d->genre_ = TagLibStringToQString(v); }
|
void Song::set_genre(const TagLib::String &v) { d->genre_ = TagLibStringToQString(v); }
|
||||||
void Song::set_composer(const TagLib::String &v) { d->composer_ = TagLibStringToQString(v); }
|
void Song::set_composer(const TagLib::String &v) { d->composer_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_composersort(const TagLib::String &v) { d->composersort_ = TagLibStringToQString(v); }
|
||||||
void Song::set_performer(const TagLib::String &v) { d->performer_ = TagLibStringToQString(v); }
|
void Song::set_performer(const TagLib::String &v) { d->performer_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_performersort(const TagLib::String &v) { d->performersort_ = TagLibStringToQString(v); }
|
||||||
void Song::set_grouping(const TagLib::String &v) { d->grouping_ = TagLibStringToQString(v); }
|
void Song::set_grouping(const TagLib::String &v) { d->grouping_ = TagLibStringToQString(v); }
|
||||||
void Song::set_comment(const TagLib::String &v) { d->comment_ = TagLibStringToQString(v); }
|
void Song::set_comment(const TagLib::String &v) { d->comment_ = TagLibStringToQString(v); }
|
||||||
void Song::set_lyrics(const TagLib::String &v) { d->lyrics_ = TagLibStringToQString(v); }
|
void Song::set_lyrics(const TagLib::String &v) { d->lyrics_ = TagLibStringToQString(v); }
|
||||||
@@ -652,14 +667,21 @@ void Song::set_musicbrainz_track_id(const TagLib::String &v) { d->musicbrainz_tr
|
|||||||
void Song::set_musicbrainz_disc_id(const TagLib::String &v) { d->musicbrainz_disc_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
void Song::set_musicbrainz_disc_id(const TagLib::String &v) { d->musicbrainz_disc_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
||||||
void Song::set_musicbrainz_release_group_id(const TagLib::String &v) { d->musicbrainz_release_group_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
void Song::set_musicbrainz_release_group_id(const TagLib::String &v) { d->musicbrainz_release_group_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
||||||
void Song::set_musicbrainz_work_id(const TagLib::String &v) { d->musicbrainz_work_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
void Song::set_musicbrainz_work_id(const TagLib::String &v) { d->musicbrainz_work_id_ = TagLibStringToQString(v).remove(u' ').replace(u';', u'/'); }
|
||||||
|
void Song::set_mood(const TagLib::String &v) { d->mood_ = TagLibStringToQString(v); }
|
||||||
|
void Song::set_initial_key(const TagLib::String &v) { d->initial_key_ = TagLibStringToQString(v); }
|
||||||
|
|
||||||
const QUrl &Song::effective_stream_url() const { return !d->stream_url_.isEmpty() && d->stream_url_.isValid() ? d->stream_url_ : d->url_; }
|
const QUrl &Song::effective_url() const { return !d->stream_url_.isEmpty() && d->stream_url_.isValid() ? d->stream_url_ : d->url_; }
|
||||||
|
const QString &Song::effective_titlesort() const { return d->titlesort_.isEmpty() ? d->title_ : d->titlesort_; }
|
||||||
const QString &Song::effective_albumartist() const { return d->albumartist_.isEmpty() ? d->artist_ : d->albumartist_; }
|
const QString &Song::effective_albumartist() const { return d->albumartist_.isEmpty() ? d->artist_ : d->albumartist_; }
|
||||||
const QString &Song::effective_albumartist_sortable() const { return d->albumartist_.isEmpty() ? d->artist_sortable_ : d->albumartist_sortable_; }
|
const QString &Song::effective_albumartistsort() const { return !d->albumartistsort_.isEmpty() ? d->albumartistsort_ : !d->albumartist_.isEmpty() ? d->albumartist_ : effective_artistsort(); }
|
||||||
|
const QString &Song::effective_artistsort() const { return d->artistsort_.isEmpty() ? d->artist_ : d->artistsort_; }
|
||||||
const QString &Song::effective_album() const { return d->album_.isEmpty() ? d->title_ : d->album_; }
|
const QString &Song::effective_album() const { return d->album_.isEmpty() ? d->title_ : d->album_; }
|
||||||
|
const QString &Song::effective_albumsort() const { return d->albumsort_.isEmpty() ? d->album_ : d->albumsort_; }
|
||||||
|
const QString &Song::effective_composersort() const { return d->composersort_.isEmpty() ? d->composer_ : d->composersort_; }
|
||||||
|
const QString &Song::effective_performersort() const { return d->performersort_.isEmpty() ? d->performer_ : d->performersort_; }
|
||||||
int Song::effective_originalyear() const { return d->originalyear_ < 0 ? d->year_ : d->originalyear_; }
|
int Song::effective_originalyear() const { return d->originalyear_ < 0 ? d->year_ : d->originalyear_; }
|
||||||
const QString &Song::playlist_albumartist() const { return is_compilation() ? d->albumartist_ : effective_albumartist(); }
|
const QString &Song::playlist_effective_albumartist() const { return is_compilation() ? d->albumartist_ : effective_albumartist(); }
|
||||||
const QString &Song::playlist_albumartist_sortable() const { return is_compilation() ? d->albumartist_sortable_ : effective_albumartist_sortable(); }
|
const QString &Song::playlist_effective_albumartistsort() const { return is_compilation() ? (!d->albumartistsort_.isEmpty() ? d->albumartistsort_ : d->albumartist_) : effective_albumartistsort(); }
|
||||||
|
|
||||||
bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.isEmpty() && !d->title_.isEmpty(); }
|
bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.isEmpty() && !d->title_.isEmpty(); }
|
||||||
bool Song::is_local_collection_song() const { return d->source_ == Source::Collection; }
|
bool Song::is_local_collection_song() const { return d->source_ == Source::Collection; }
|
||||||
@@ -782,6 +804,31 @@ bool Song::lyrics_supported() const {
|
|||||||
return additional_tags_supported() || d->filetype_ == FileType::ASF;
|
return additional_tags_supported() || d->filetype_ == FileType::ASF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::albumartistsort_supported() const {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
// Performer sort is a rare custom field even in vorbis comments, no write support in MPEG formats
|
||||||
|
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Song::titlesort_supported() const {
|
||||||
|
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) {
|
||||||
|
|
||||||
return filetype == FileType::FLAC ||
|
return filetype == FileType::FLAC ||
|
||||||
@@ -794,19 +841,8 @@ bool Song::save_embedded_cover_supported(const FileType filetype) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Song::sortable(const QString &v) {
|
bool Song::id3v2_tags_supported() const {
|
||||||
|
return d->filetype_ == FileType::MPEG || d->filetype_ == FileType::WAV || d->filetype_ == FileType::AIFF;
|
||||||
QString copy = v.toLower();
|
|
||||||
|
|
||||||
for (const auto &i : kArticles) {
|
|
||||||
if (copy.startsWith(i)) {
|
|
||||||
qint64 ilen = i.length();
|
|
||||||
return copy.right(copy.length() - ilen) + u", "_s + copy.left(ilen - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Song::ColumnIndex(const QString &field) {
|
int Song::ColumnIndex(const QString &field) {
|
||||||
@@ -923,12 +959,31 @@ bool Song::IsEditable() const {
|
|||||||
return d->valid_ && d->url_.isValid() && ((d->url_.isLocalFile() && write_tags_supported() && !has_cue()) || d->source_ == Source::Stream);
|
return d->valid_ && d->url_.isValid() && ((d->url_.isLocalFile() && write_tags_supported() && !has_cue()) || d->source_ == Source::Stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::IsFileInfoEqual(const Song &other) const {
|
||||||
|
|
||||||
|
return d->beginning_ == other.d->beginning_ &&
|
||||||
|
d->end_ == other.d->end_ &&
|
||||||
|
d->url_ == other.d->url_ &&
|
||||||
|
d->basefilename_ == other.d->basefilename_ &&
|
||||||
|
d->filetype_ == other.d->filetype_ &&
|
||||||
|
d->filesize_ == other.d->filesize_ &&
|
||||||
|
d->mtime_ == other.d->mtime_ &&
|
||||||
|
d->ctime_ == other.d->ctime_ &&
|
||||||
|
d->mtime_ == other.d->mtime_ &&
|
||||||
|
d->stream_url_ == other.d->stream_url_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Song::IsMetadataEqual(const Song &other) const {
|
bool Song::IsMetadataEqual(const Song &other) const {
|
||||||
|
|
||||||
return d->title_ == other.d->title_ &&
|
return d->title_ == other.d->title_ &&
|
||||||
|
d->titlesort_ == other.d->titlesort_ &&
|
||||||
d->album_ == other.d->album_ &&
|
d->album_ == other.d->album_ &&
|
||||||
|
d->albumsort_ == other.d->albumsort_ &&
|
||||||
d->artist_ == other.d->artist_ &&
|
d->artist_ == other.d->artist_ &&
|
||||||
|
d->artistsort_ == other.d->artistsort_ &&
|
||||||
d->albumartist_ == other.d->albumartist_ &&
|
d->albumartist_ == other.d->albumartist_ &&
|
||||||
|
d->albumartistsort_ == other.d->albumartistsort_ &&
|
||||||
d->track_ == other.d->track_ &&
|
d->track_ == other.d->track_ &&
|
||||||
d->disc_ == other.d->disc_ &&
|
d->disc_ == other.d->disc_ &&
|
||||||
d->year_ == other.d->year_ &&
|
d->year_ == other.d->year_ &&
|
||||||
@@ -936,7 +991,9 @@ bool Song::IsMetadataEqual(const Song &other) const {
|
|||||||
d->genre_ == other.d->genre_ &&
|
d->genre_ == other.d->genre_ &&
|
||||||
d->compilation_ == other.d->compilation_ &&
|
d->compilation_ == other.d->compilation_ &&
|
||||||
d->composer_ == other.d->composer_ &&
|
d->composer_ == other.d->composer_ &&
|
||||||
|
d->composersort_ == other.d->composersort_ &&
|
||||||
d->performer_ == other.d->performer_ &&
|
d->performer_ == other.d->performer_ &&
|
||||||
|
d->performersort_ == other.d->performersort_ &&
|
||||||
d->grouping_ == other.d->grouping_ &&
|
d->grouping_ == other.d->grouping_ &&
|
||||||
d->comment_ == other.d->comment_ &&
|
d->comment_ == other.d->comment_ &&
|
||||||
d->lyrics_ == other.d->lyrics_ &&
|
d->lyrics_ == other.d->lyrics_ &&
|
||||||
@@ -948,6 +1005,9 @@ bool Song::IsMetadataEqual(const Song &other) const {
|
|||||||
d->bitrate_ == other.d->bitrate_ &&
|
d->bitrate_ == other.d->bitrate_ &&
|
||||||
d->samplerate_ == other.d->samplerate_ &&
|
d->samplerate_ == other.d->samplerate_ &&
|
||||||
d->bitdepth_ == other.d->bitdepth_ &&
|
d->bitdepth_ == other.d->bitdepth_ &&
|
||||||
|
d->bpm_ == other.d->bpm_ &&
|
||||||
|
d->mood_ == other.d->mood_ &&
|
||||||
|
d->initial_key_ == other.d->initial_key_ &&
|
||||||
d->cue_path_ == other.d->cue_path_;
|
d->cue_path_ == other.d->cue_path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1008,6 +1068,23 @@ bool Song::IsArtEqual(const Song &other) const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::IsCompilationEqual(const Song &other) const {
|
||||||
|
|
||||||
|
return d->compilation_ == other.d->compilation_ &&
|
||||||
|
d->compilation_detected_ == other.d->compilation_detected_ &&
|
||||||
|
d->compilation_on_ == other.d->compilation_on_ &&
|
||||||
|
d->compilation_off_ == other.d->compilation_off_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Song::IsSettingsEqual(const Song &other) const {
|
||||||
|
|
||||||
|
return d->source_ == other.d->source_ &&
|
||||||
|
d->directory_id_ == other.d->directory_id_ &&
|
||||||
|
d->unavailable_ == other.d->unavailable_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Song::IsAllMetadataEqual(const Song &other) const {
|
bool Song::IsAllMetadataEqual(const Song &other) const {
|
||||||
|
|
||||||
return IsMetadataEqual(other) &&
|
return IsMetadataEqual(other) &&
|
||||||
@@ -1015,7 +1092,18 @@ bool Song::IsAllMetadataEqual(const Song &other) const {
|
|||||||
IsRatingEqual(other) &&
|
IsRatingEqual(other) &&
|
||||||
IsAcoustIdEqual(other) &&
|
IsAcoustIdEqual(other) &&
|
||||||
IsMusicBrainzEqual(other) &&
|
IsMusicBrainzEqual(other) &&
|
||||||
IsArtEqual(other);
|
IsArtEqual(other) &&
|
||||||
|
IsEBUR128Equal(other);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Song::IsEqual(const Song &other) const {
|
||||||
|
|
||||||
|
return IsFileInfoEqual(other) &&
|
||||||
|
IsSettingsEqual(other) &&
|
||||||
|
IsAllMetadataEqual(other) &&
|
||||||
|
IsFingerprintEqual(other) &&
|
||||||
|
IsCompilationEqual(other);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1139,6 +1227,22 @@ QIcon Song::IconForSource(const Source source) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a source to a music service domain name, for ListenBrainz.
|
||||||
|
// See the "Music service names" note on https://listenbrainz.readthedocs.io/en/latest/users/json.html.
|
||||||
|
|
||||||
|
QString Song::DomainForSource(const Source source) {
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case Song::Source::Tidal: return u"tidal.com"_s;
|
||||||
|
case Song::Source::Qobuz: return u"qobuz.com"_s;
|
||||||
|
case Song::Source::SomaFM: return u"somafm.com"_s;
|
||||||
|
case Song::Source::RadioParadise: return u"radioparadise.com"_s;
|
||||||
|
case Song::Source::Spotify: return u"spotify.com"_s;
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QString Song::TextForFiletype(const FileType filetype) {
|
QString Song::TextForFiletype(const FileType filetype) {
|
||||||
|
|
||||||
switch (filetype) {
|
switch (filetype) {
|
||||||
@@ -1166,6 +1270,7 @@ QString Song::TextForFiletype(const FileType filetype) {
|
|||||||
case FileType::CDDA: return u"CDDA"_s;
|
case FileType::CDDA: return u"CDDA"_s;
|
||||||
case FileType::SPC: return u"SNES SPC700"_s;
|
case FileType::SPC: return u"SNES SPC700"_s;
|
||||||
case FileType::VGM: return u"VGM"_s;
|
case FileType::VGM: return u"VGM"_s;
|
||||||
|
case FileType::ALAC: return u"ALAC"_s;
|
||||||
case FileType::Stream: return u"Stream"_s;
|
case FileType::Stream: return u"Stream"_s;
|
||||||
case FileType::Unknown:
|
case FileType::Unknown:
|
||||||
default: return QObject::tr("Unknown");
|
default: return QObject::tr("Unknown");
|
||||||
@@ -1198,6 +1303,7 @@ QString Song::ExtensionForFiletype(const FileType filetype) {
|
|||||||
case FileType::IT: return u"it"_s;
|
case FileType::IT: return u"it"_s;
|
||||||
case FileType::SPC: return u"spc"_s;
|
case FileType::SPC: return u"spc"_s;
|
||||||
case FileType::VGM: return u"vgm"_s;
|
case FileType::VGM: return u"vgm"_s;
|
||||||
|
case FileType::ALAC: return u"m4a"_s;
|
||||||
case FileType::Unknown:
|
case FileType::Unknown:
|
||||||
default: return u"dat"_s;
|
default: return u"dat"_s;
|
||||||
}
|
}
|
||||||
@@ -1230,12 +1336,30 @@ QIcon Song::IconForFiletype(const FileType filetype) {
|
|||||||
case FileType::IT: return IconLoader::Load(u"it"_s);
|
case FileType::IT: return IconLoader::Load(u"it"_s);
|
||||||
case FileType::CDDA: return IconLoader::Load(u"cd"_s);
|
case FileType::CDDA: return IconLoader::Load(u"cd"_s);
|
||||||
case FileType::Stream: return IconLoader::Load(u"applications-internet"_s);
|
case FileType::Stream: return IconLoader::Load(u"applications-internet"_s);
|
||||||
|
case FileType::ALAC: return IconLoader::Load(u"alac"_s);
|
||||||
case FileType::Unknown:
|
case FileType::Unknown:
|
||||||
default: return IconLoader::Load(u"edit-delete"_s);
|
default: return IconLoader::Load(u"edit-delete"_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a URL usable for sharing this song with another user.
|
||||||
|
// This is only applicable when streaming from a streaming service, since we can't link to local content.
|
||||||
|
// Returns a web URL which points to the current streaming track or live stream, or an empty string if that is not applicable.
|
||||||
|
|
||||||
|
QString Song::ShareURL() const {
|
||||||
|
|
||||||
|
switch (source()) {
|
||||||
|
case Song::Source::Stream:
|
||||||
|
case Song::Source::SomaFM: return url().toString();
|
||||||
|
case Song::Source::Tidal: return "https://tidal.com/track/%1"_L1.arg(song_id());
|
||||||
|
case Song::Source::Qobuz: return "https://open.qobuz.com/track/%1"_L1.arg(song_id());
|
||||||
|
case Song::Source::Spotify: return "https://open.spotify.com/track/%1"_L1.arg(song_id());
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Song::IsFileLossless() const {
|
bool Song::IsFileLossless() const {
|
||||||
|
|
||||||
switch (filetype()) {
|
switch (filetype()) {
|
||||||
@@ -1250,6 +1374,7 @@ bool Song::IsFileLossless() const {
|
|||||||
case FileType::TrueAudio:
|
case FileType::TrueAudio:
|
||||||
case FileType::PCM:
|
case FileType::PCM:
|
||||||
case FileType::CDDA:
|
case FileType::CDDA:
|
||||||
|
case FileType::ALAC:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@@ -1279,6 +1404,7 @@ Song::FileType Song::FiletypeByMimetype(const QString &mimetype) {
|
|||||||
if (mimetype.compare("audio/x-s3m"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M;
|
if (mimetype.compare("audio/x-s3m"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M;
|
||||||
if (mimetype.compare("audio/x-spc"_L1, Qt::CaseInsensitive) == 0) return FileType::SPC;
|
if (mimetype.compare("audio/x-spc"_L1, Qt::CaseInsensitive) == 0) return FileType::SPC;
|
||||||
if (mimetype.compare("audio/x-vgm"_L1, Qt::CaseInsensitive) == 0) return FileType::VGM;
|
if (mimetype.compare("audio/x-vgm"_L1, Qt::CaseInsensitive) == 0) return FileType::VGM;
|
||||||
|
if (mimetype.compare("audio/x-alac"_L1, Qt::CaseInsensitive) == 0) return FileType::ALAC;
|
||||||
|
|
||||||
return FileType::Unknown;
|
return FileType::Unknown;
|
||||||
|
|
||||||
@@ -1306,6 +1432,7 @@ Song::FileType Song::FiletypeByDescription(const QString &text) {
|
|||||||
if (text.compare("Module Music Format (MOD)"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M;
|
if (text.compare("Module Music Format (MOD)"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M;
|
||||||
if (text.compare("SNES SPC700"_L1, Qt::CaseInsensitive) == 0) return FileType::SPC;
|
if (text.compare("SNES SPC700"_L1, Qt::CaseInsensitive) == 0) return FileType::SPC;
|
||||||
if (text.compare("VGM"_L1, Qt::CaseInsensitive) == 0) return FileType::VGM;
|
if (text.compare("VGM"_L1, Qt::CaseInsensitive) == 0) return FileType::VGM;
|
||||||
|
if (text.compare("Apple Lossless Audio Codec (ALAC)"_L1, Qt::CaseInsensitive) == 0) return FileType::ALAC;
|
||||||
|
|
||||||
return FileType::Unknown;
|
return FileType::Unknown;
|
||||||
|
|
||||||
@@ -1416,9 +1543,13 @@ void Song::InitFromQuery(const QSqlRecord &r, const bool reliable_metadata, cons
|
|||||||
d->id_ = SqlHelper::ValueToInt(r, ColumnIndex(u"ROWID"_s) + col);
|
d->id_ = SqlHelper::ValueToInt(r, ColumnIndex(u"ROWID"_s) + col);
|
||||||
|
|
||||||
set_title(SqlHelper::ValueToString(r, ColumnIndex(u"title"_s) + col));
|
set_title(SqlHelper::ValueToString(r, ColumnIndex(u"title"_s) + col));
|
||||||
|
set_titlesort(SqlHelper::ValueToString(r, ColumnIndex(u"titlesort"_s) + col));
|
||||||
set_album(SqlHelper::ValueToString(r, ColumnIndex(u"album"_s) + col));
|
set_album(SqlHelper::ValueToString(r, ColumnIndex(u"album"_s) + col));
|
||||||
|
set_albumsort(SqlHelper::ValueToString(r, ColumnIndex(u"albumsort"_s) + col));
|
||||||
set_artist(SqlHelper::ValueToString(r, ColumnIndex(u"artist"_s) + col));
|
set_artist(SqlHelper::ValueToString(r, ColumnIndex(u"artist"_s) + col));
|
||||||
|
set_artistsort(SqlHelper::ValueToString(r, ColumnIndex(u"artistsort"_s) + col));
|
||||||
set_albumartist(SqlHelper::ValueToString(r, ColumnIndex(u"albumartist"_s) + col));
|
set_albumartist(SqlHelper::ValueToString(r, ColumnIndex(u"albumartist"_s) + col));
|
||||||
|
set_albumartistsort(SqlHelper::ValueToString(r, ColumnIndex(u"albumartistsort"_s) + col));
|
||||||
d->track_ = SqlHelper::ValueToInt(r, ColumnIndex(u"track"_s) + col);
|
d->track_ = SqlHelper::ValueToInt(r, ColumnIndex(u"track"_s) + col);
|
||||||
d->disc_ = SqlHelper::ValueToInt(r, ColumnIndex(u"disc"_s) + col);
|
d->disc_ = SqlHelper::ValueToInt(r, ColumnIndex(u"disc"_s) + col);
|
||||||
d->year_ = SqlHelper::ValueToInt(r, ColumnIndex(u"year"_s) + col);
|
d->year_ = SqlHelper::ValueToInt(r, ColumnIndex(u"year"_s) + col);
|
||||||
@@ -1426,7 +1557,9 @@ void Song::InitFromQuery(const QSqlRecord &r, const bool reliable_metadata, cons
|
|||||||
d->genre_ = SqlHelper::ValueToString(r, ColumnIndex(u"genre"_s) + col);
|
d->genre_ = SqlHelper::ValueToString(r, ColumnIndex(u"genre"_s) + col);
|
||||||
d->compilation_ = r.value(ColumnIndex(u"compilation"_s) + col).toBool();
|
d->compilation_ = r.value(ColumnIndex(u"compilation"_s) + col).toBool();
|
||||||
d->composer_ = SqlHelper::ValueToString(r, ColumnIndex(u"composer"_s) + col);
|
d->composer_ = SqlHelper::ValueToString(r, ColumnIndex(u"composer"_s) + col);
|
||||||
|
d->composersort_ = SqlHelper::ValueToString(r, ColumnIndex(u"composersort"_s) + col);
|
||||||
d->performer_ = SqlHelper::ValueToString(r, ColumnIndex(u"performer"_s) + col);
|
d->performer_ = SqlHelper::ValueToString(r, ColumnIndex(u"performer"_s) + col);
|
||||||
|
d->performersort_ = SqlHelper::ValueToString(r, ColumnIndex(u"performersort"_s) + col);
|
||||||
d->grouping_ = SqlHelper::ValueToString(r, ColumnIndex(u"grouping"_s) + col);
|
d->grouping_ = SqlHelper::ValueToString(r, ColumnIndex(u"grouping"_s) + col);
|
||||||
d->comment_ = SqlHelper::ValueToString(r, ColumnIndex(u"comment"_s) + col);
|
d->comment_ = SqlHelper::ValueToString(r, ColumnIndex(u"comment"_s) + col);
|
||||||
d->lyrics_ = SqlHelper::ValueToString(r, ColumnIndex(u"lyrics"_s) + col);
|
d->lyrics_ = SqlHelper::ValueToString(r, ColumnIndex(u"lyrics"_s) + col);
|
||||||
@@ -1468,7 +1601,11 @@ void Song::InitFromQuery(const QSqlRecord &r, const bool reliable_metadata, cons
|
|||||||
d->art_unset_ = SqlHelper::ValueToBool(r, ColumnIndex(u"art_unset"_s) + col);
|
d->art_unset_ = SqlHelper::ValueToBool(r, ColumnIndex(u"art_unset"_s) + col);
|
||||||
|
|
||||||
d->cue_path_ = SqlHelper::ValueToString(r, ColumnIndex(u"cue_path"_s) + col);
|
d->cue_path_ = SqlHelper::ValueToString(r, ColumnIndex(u"cue_path"_s) + col);
|
||||||
|
|
||||||
d->rating_ = SqlHelper::ValueToFloat(r, ColumnIndex(u"rating"_s) + col);
|
d->rating_ = SqlHelper::ValueToFloat(r, ColumnIndex(u"rating"_s) + col);
|
||||||
|
d->bpm_ = SqlHelper::ValueToFloat(r, ColumnIndex(u"bpm"_s) + col);
|
||||||
|
d->mood_ = SqlHelper::ValueToString(r, ColumnIndex(u"mood"_s) + col);
|
||||||
|
d->initial_key_ = SqlHelper::ValueToString(r, ColumnIndex(u"initial_key"_s) + col);
|
||||||
|
|
||||||
d->acoustid_id_ = SqlHelper::ValueToString(r, ColumnIndex(u"acoustid_id"_s) + col);
|
d->acoustid_id_ = SqlHelper::ValueToString(r, ColumnIndex(u"acoustid_id"_s) + col);
|
||||||
d->acoustid_fingerprint_ = SqlHelper::ValueToString(r, ColumnIndex(u"acoustid_fingerprint"_s) + col);
|
d->acoustid_fingerprint_ = SqlHelper::ValueToString(r, ColumnIndex(u"acoustid_fingerprint"_s) + col);
|
||||||
@@ -1734,9 +1871,13 @@ void Song::BindToQuery(SqlQuery *query) const {
|
|||||||
// Remember to bind these in the same order as kBindSpec
|
// Remember to bind these in the same order as kBindSpec
|
||||||
|
|
||||||
query->BindStringValue(u":title"_s, d->title_);
|
query->BindStringValue(u":title"_s, d->title_);
|
||||||
|
query->BindStringValue(u":titlesort"_s, d->titlesort_);
|
||||||
query->BindStringValue(u":album"_s, d->album_);
|
query->BindStringValue(u":album"_s, d->album_);
|
||||||
|
query->BindStringValue(u":albumsort"_s, d->albumsort_);
|
||||||
query->BindStringValue(u":artist"_s, d->artist_);
|
query->BindStringValue(u":artist"_s, d->artist_);
|
||||||
|
query->BindStringValue(u":artistsort"_s, d->artistsort_);
|
||||||
query->BindStringValue(u":albumartist"_s, d->albumartist_);
|
query->BindStringValue(u":albumartist"_s, d->albumartist_);
|
||||||
|
query->BindStringValue(u":albumartistsort"_s, d->albumartistsort_);
|
||||||
query->BindIntValue(u":track"_s, d->track_);
|
query->BindIntValue(u":track"_s, d->track_);
|
||||||
query->BindIntValue(u":disc"_s, d->disc_);
|
query->BindIntValue(u":disc"_s, d->disc_);
|
||||||
query->BindIntValue(u":year"_s, d->year_);
|
query->BindIntValue(u":year"_s, d->year_);
|
||||||
@@ -1744,7 +1885,9 @@ void Song::BindToQuery(SqlQuery *query) const {
|
|||||||
query->BindStringValue(u":genre"_s, d->genre_);
|
query->BindStringValue(u":genre"_s, d->genre_);
|
||||||
query->BindBoolValue(u":compilation"_s, d->compilation_);
|
query->BindBoolValue(u":compilation"_s, d->compilation_);
|
||||||
query->BindStringValue(u":composer"_s, d->composer_);
|
query->BindStringValue(u":composer"_s, d->composer_);
|
||||||
|
query->BindStringValue(u":composersort"_s, d->composersort_);
|
||||||
query->BindStringValue(u":performer"_s, d->performer_);
|
query->BindStringValue(u":performer"_s, d->performer_);
|
||||||
|
query->BindStringValue(u":performersort"_s, d->performersort_);
|
||||||
query->BindStringValue(u":grouping"_s, d->grouping_);
|
query->BindStringValue(u":grouping"_s, d->grouping_);
|
||||||
query->BindStringValue(u":comment"_s, d->comment_);
|
query->BindStringValue(u":comment"_s, d->comment_);
|
||||||
query->BindStringValue(u":lyrics"_s, d->lyrics_);
|
query->BindStringValue(u":lyrics"_s, d->lyrics_);
|
||||||
@@ -1792,6 +1935,9 @@ void Song::BindToQuery(SqlQuery *query) const {
|
|||||||
query->BindValue(u":cue_path"_s, d->cue_path_);
|
query->BindValue(u":cue_path"_s, d->cue_path_);
|
||||||
|
|
||||||
query->BindFloatValue(u":rating"_s, d->rating_);
|
query->BindFloatValue(u":rating"_s, d->rating_);
|
||||||
|
query->BindFloatValue(u":bpm"_s, d->bpm_);
|
||||||
|
query->BindStringValue(u":mood"_s, d->mood_);
|
||||||
|
query->BindStringValue(u":initial_key"_s, d->initial_key_);
|
||||||
|
|
||||||
query->BindStringValue(u":acoustid_id"_s, d->acoustid_id_);
|
query->BindStringValue(u":acoustid_id"_s, d->acoustid_id_);
|
||||||
query->BindStringValue(u":acoustid_fingerprint"_s, d->acoustid_fingerprint_);
|
query->BindStringValue(u":acoustid_fingerprint"_s, d->acoustid_fingerprint_);
|
||||||
@@ -1819,7 +1965,7 @@ void Song::ToXesam(QVariantMap *map) const {
|
|||||||
using mpris::AddMetadataAsList;
|
using mpris::AddMetadataAsList;
|
||||||
using mpris::AsMPRISDateTimeType;
|
using mpris::AsMPRISDateTimeType;
|
||||||
|
|
||||||
AddMetadata(u"xesam:url"_s, effective_stream_url().toString(), map);
|
AddMetadata(u"xesam:url"_s, effective_url().toString(), map);
|
||||||
AddMetadata(u"xesam:title"_s, PrettyTitle(), map);
|
AddMetadata(u"xesam:title"_s, PrettyTitle(), map);
|
||||||
AddMetadataAsList(u"xesam:artist"_s, artist(), map);
|
AddMetadataAsList(u"xesam:artist"_s, artist(), map);
|
||||||
AddMetadata(u"xesam:album"_s, album(), map);
|
AddMetadata(u"xesam:album"_s, album(), map);
|
||||||
@@ -2016,4 +2162,3 @@ QString Song::GetNameForNewPlaylist(const SongList &songs) {
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-2024, 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
|
||||||
@@ -105,6 +105,7 @@ class Song {
|
|||||||
IT = 21,
|
IT = 21,
|
||||||
SPC = 22,
|
SPC = 22,
|
||||||
VGM = 23,
|
VGM = 23,
|
||||||
|
ALAC = 24, // MP4, with ALAC codec
|
||||||
CDDA = 90,
|
CDDA = 90,
|
||||||
Stream = 91
|
Stream = 91
|
||||||
};
|
};
|
||||||
@@ -149,9 +150,13 @@ class Song {
|
|||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
|
|
||||||
const QString &title() const;
|
const QString &title() const;
|
||||||
|
const QString &titlesort() const;
|
||||||
const QString &album() const;
|
const QString &album() const;
|
||||||
|
const QString &albumsort() const;
|
||||||
const QString &artist() const;
|
const QString &artist() const;
|
||||||
|
const QString &artistsort() const;
|
||||||
const QString &albumartist() const;
|
const QString &albumartist() const;
|
||||||
|
const QString &albumartistsort() const;
|
||||||
int track() const;
|
int track() const;
|
||||||
int disc() const;
|
int disc() const;
|
||||||
int year() const;
|
int year() const;
|
||||||
@@ -159,7 +164,9 @@ class Song {
|
|||||||
const QString &genre() const;
|
const QString &genre() const;
|
||||||
bool compilation() const;
|
bool compilation() const;
|
||||||
const QString &composer() const;
|
const QString &composer() const;
|
||||||
|
const QString &composersort() const;
|
||||||
const QString &performer() const;
|
const QString &performer() const;
|
||||||
|
const QString &performersort() const;
|
||||||
const QString &grouping() const;
|
const QString &grouping() const;
|
||||||
const QString &comment() const;
|
const QString &comment() const;
|
||||||
const QString &lyrics() const;
|
const QString &lyrics() const;
|
||||||
@@ -206,6 +213,9 @@ class Song {
|
|||||||
const QString &cue_path() const;
|
const QString &cue_path() const;
|
||||||
|
|
||||||
float rating() const;
|
float rating() const;
|
||||||
|
float bpm() const;
|
||||||
|
const QString &mood() const;
|
||||||
|
const QString &initial_key() const;
|
||||||
|
|
||||||
const QString &acoustid_id() const;
|
const QString &acoustid_id() const;
|
||||||
const QString &acoustid_fingerprint() const;
|
const QString &acoustid_fingerprint() const;
|
||||||
@@ -224,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();
|
||||||
@@ -249,11 +261,6 @@ class Song {
|
|||||||
|
|
||||||
bool init_from_file() const;
|
bool init_from_file() const;
|
||||||
|
|
||||||
const QString &title_sortable() const;
|
|
||||||
const QString &album_sortable() const;
|
|
||||||
const QString &artist_sortable() const;
|
|
||||||
const QString &albumartist_sortable() const;
|
|
||||||
|
|
||||||
const QUrl &stream_url() const;
|
const QUrl &stream_url() const;
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
@@ -261,9 +268,13 @@ class Song {
|
|||||||
void set_valid(const bool v);
|
void set_valid(const bool v);
|
||||||
|
|
||||||
void set_title(const QString &v);
|
void set_title(const QString &v);
|
||||||
|
void set_titlesort(const QString &v);
|
||||||
void set_album(const QString &v);
|
void set_album(const QString &v);
|
||||||
|
void set_albumsort(const QString &v);
|
||||||
void set_artist(const QString &v);
|
void set_artist(const QString &v);
|
||||||
|
void set_artistsort(const QString &v);
|
||||||
void set_albumartist(const QString &v);
|
void set_albumartist(const QString &v);
|
||||||
|
void set_albumartistsort(const QString &v);
|
||||||
void set_track(const int v);
|
void set_track(const int v);
|
||||||
void set_disc(const int v);
|
void set_disc(const int v);
|
||||||
void set_year(const int v);
|
void set_year(const int v);
|
||||||
@@ -271,7 +282,9 @@ class Song {
|
|||||||
void set_genre(const QString &v);
|
void set_genre(const QString &v);
|
||||||
void set_compilation(bool v);
|
void set_compilation(bool v);
|
||||||
void set_composer(const QString &v);
|
void set_composer(const QString &v);
|
||||||
|
void set_composersort(const QString &v);
|
||||||
void set_performer(const QString &v);
|
void set_performer(const QString &v);
|
||||||
|
void set_performersort(const QString &v);
|
||||||
void set_grouping(const QString &v);
|
void set_grouping(const QString &v);
|
||||||
void set_comment(const QString &v);
|
void set_comment(const QString &v);
|
||||||
void set_lyrics(const QString &v);
|
void set_lyrics(const QString &v);
|
||||||
@@ -317,6 +330,9 @@ class Song {
|
|||||||
void set_cue_path(const QString &v);
|
void set_cue_path(const QString &v);
|
||||||
|
|
||||||
void set_rating(const float v);
|
void set_rating(const float v);
|
||||||
|
void set_bpm(const float v);
|
||||||
|
void set_mood(const QString &v);
|
||||||
|
void set_initial_key(const QString &v);
|
||||||
|
|
||||||
void set_acoustid_id(const QString &v);
|
void set_acoustid_id(const QString &v);
|
||||||
void set_acoustid_fingerprint(const QString &v);
|
void set_acoustid_fingerprint(const QString &v);
|
||||||
@@ -335,17 +351,25 @@ 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);
|
||||||
|
|
||||||
void set_title(const TagLib::String &v);
|
void set_title(const TagLib::String &v);
|
||||||
|
void set_titlesort(const TagLib::String &v);
|
||||||
void set_album(const TagLib::String &v);
|
void set_album(const TagLib::String &v);
|
||||||
|
void set_albumsort(const TagLib::String &v);
|
||||||
void set_artist(const TagLib::String &v);
|
void set_artist(const TagLib::String &v);
|
||||||
|
void set_artistsort(const TagLib::String &v);
|
||||||
void set_albumartist(const TagLib::String &v);
|
void set_albumartist(const TagLib::String &v);
|
||||||
|
void set_albumartistsort(const TagLib::String &v);
|
||||||
void set_genre(const TagLib::String &v);
|
void set_genre(const TagLib::String &v);
|
||||||
void set_composer(const TagLib::String &v);
|
void set_composer(const TagLib::String &v);
|
||||||
|
void set_composersort(const TagLib::String &v);
|
||||||
void set_performer(const TagLib::String &v);
|
void set_performer(const TagLib::String &v);
|
||||||
|
void set_performersort(const TagLib::String &v);
|
||||||
void set_grouping(const TagLib::String &v);
|
void set_grouping(const TagLib::String &v);
|
||||||
void set_comment(const TagLib::String &v);
|
void set_comment(const TagLib::String &v);
|
||||||
void set_lyrics(const TagLib::String &v);
|
void set_lyrics(const TagLib::String &v);
|
||||||
@@ -364,14 +388,21 @@ class Song {
|
|||||||
void set_musicbrainz_disc_id(const TagLib::String &v);
|
void set_musicbrainz_disc_id(const TagLib::String &v);
|
||||||
void set_musicbrainz_release_group_id(const TagLib::String &v);
|
void set_musicbrainz_release_group_id(const TagLib::String &v);
|
||||||
void set_musicbrainz_work_id(const TagLib::String &v);
|
void set_musicbrainz_work_id(const TagLib::String &v);
|
||||||
|
void set_mood(const TagLib::String &v);
|
||||||
|
void set_initial_key(const TagLib::String &v);
|
||||||
|
|
||||||
const QUrl &effective_stream_url() const;
|
const QUrl &effective_url() const;
|
||||||
|
const QString &effective_titlesort() const;
|
||||||
const QString &effective_albumartist() const;
|
const QString &effective_albumartist() const;
|
||||||
const QString &effective_albumartist_sortable() const;
|
const QString &effective_albumartistsort() const;
|
||||||
|
const QString &effective_artistsort() const;
|
||||||
const QString &effective_album() const;
|
const QString &effective_album() const;
|
||||||
|
const QString &effective_albumsort() const;
|
||||||
|
const QString &effective_composersort() const;
|
||||||
|
const QString &effective_performersort() const;
|
||||||
int effective_originalyear() const;
|
int effective_originalyear() const;
|
||||||
const QString &playlist_albumartist() const;
|
const QString &playlist_effective_albumartist() const;
|
||||||
const QString &playlist_albumartist_sortable() const;
|
const QString &playlist_effective_albumartistsort() const;
|
||||||
|
|
||||||
bool is_metadata_good() const;
|
bool is_metadata_good() const;
|
||||||
bool is_local_collection_song() const;
|
bool is_local_collection_song() const;
|
||||||
@@ -402,9 +433,18 @@ class Song {
|
|||||||
bool comment_supported() const;
|
bool comment_supported() const;
|
||||||
bool lyrics_supported() const;
|
bool lyrics_supported() const;
|
||||||
|
|
||||||
|
bool albumartistsort_supported() const;
|
||||||
|
bool albumsort_supported() const;
|
||||||
|
bool artistsort_supported() const;
|
||||||
|
bool composersort_supported() const;
|
||||||
|
bool performersort_supported() const;
|
||||||
|
bool titlesort_supported() const;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -430,6 +470,7 @@ class Song {
|
|||||||
bool IsEditable() const;
|
bool IsEditable() const;
|
||||||
|
|
||||||
// Comparison functions
|
// Comparison functions
|
||||||
|
bool IsFileInfoEqual(const Song &other) const;
|
||||||
bool IsMetadataEqual(const Song &other) const;
|
bool IsMetadataEqual(const Song &other) const;
|
||||||
bool IsPlayStatisticsEqual(const Song &other) const;
|
bool IsPlayStatisticsEqual(const Song &other) const;
|
||||||
bool IsRatingEqual(const Song &other) const;
|
bool IsRatingEqual(const Song &other) const;
|
||||||
@@ -438,7 +479,10 @@ class Song {
|
|||||||
bool IsMusicBrainzEqual(const Song &other) const;
|
bool IsMusicBrainzEqual(const Song &other) const;
|
||||||
bool IsEBUR128Equal(const Song &other) const;
|
bool IsEBUR128Equal(const Song &other) const;
|
||||||
bool IsArtEqual(const Song &other) const;
|
bool IsArtEqual(const Song &other) const;
|
||||||
|
bool IsCompilationEqual(const Song &other) const;
|
||||||
|
bool IsSettingsEqual(const Song &other) const;
|
||||||
bool IsAllMetadataEqual(const Song &other) const;
|
bool IsAllMetadataEqual(const Song &other) const;
|
||||||
|
bool IsEqual(const Song &other) const;
|
||||||
|
|
||||||
bool IsOnSameAlbum(const Song &other) const;
|
bool IsOnSameAlbum(const Song &other) const;
|
||||||
bool IsSimilar(const Song &other) const;
|
bool IsSimilar(const Song &other) const;
|
||||||
@@ -448,6 +492,7 @@ class Song {
|
|||||||
static QString DescriptionForSource(const Source source);
|
static QString DescriptionForSource(const Source source);
|
||||||
static Source SourceFromText(const QString &source);
|
static Source SourceFromText(const QString &source);
|
||||||
static QIcon IconForSource(const Source source);
|
static QIcon IconForSource(const Source source);
|
||||||
|
static QString DomainForSource(const Source source);
|
||||||
static QString TextForFiletype(const FileType filetype);
|
static QString TextForFiletype(const FileType filetype);
|
||||||
static QString ExtensionForFiletype(const FileType filetype);
|
static QString ExtensionForFiletype(const FileType filetype);
|
||||||
static QIcon IconForFiletype(const FileType filetype);
|
static QIcon IconForFiletype(const FileType filetype);
|
||||||
@@ -455,9 +500,12 @@ class Song {
|
|||||||
QString TextForSource() const { return TextForSource(source()); }
|
QString TextForSource() const { return TextForSource(source()); }
|
||||||
QString DescriptionForSource() const { return DescriptionForSource(source()); }
|
QString DescriptionForSource() const { return DescriptionForSource(source()); }
|
||||||
QIcon IconForSource() const { return IconForSource(source()); }
|
QIcon IconForSource() const { return IconForSource(source()); }
|
||||||
|
QString DomainForSource() const { return DomainForSource(source()); }
|
||||||
QString TextForFiletype() const { return TextForFiletype(filetype()); }
|
QString TextForFiletype() const { return TextForFiletype(filetype()); }
|
||||||
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
|
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
|
||||||
|
|
||||||
|
QString ShareURL() const;
|
||||||
|
|
||||||
bool IsFileLossless() const;
|
bool IsFileLossless() const;
|
||||||
static FileType FiletypeByMimetype(const QString &mimetype);
|
static FileType FiletypeByMimetype(const QString &mimetype);
|
||||||
static FileType FiletypeByDescription(const QString &text);
|
static FileType FiletypeByDescription(const QString &text);
|
||||||
@@ -521,9 +569,6 @@ class Song {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Private;
|
struct Private;
|
||||||
|
|
||||||
static QString sortable(const QString &v);
|
|
||||||
|
|
||||||
QSharedDataPointer<Private> d;
|
QSharedDataPointer<Private> d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -178,9 +185,11 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
|
|||||||
SongLoader::Result SongLoader::LoadAudioCD() {
|
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) {
|
||||||
|
|
||||||
CddaSongLoader *cdda_song_loader = qobject_cast<CddaSongLoader*>(sender());
|
|
||||||
cdda_song_loader->deleteLater();
|
|
||||||
songs_ = songs;
|
songs_ = songs;
|
||||||
Q_EMIT LoadAudioCDFinished(true);
|
|
||||||
|
Q_EMIT AudioCDTracksLoaded();
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
void SongLoader::AudioCDTracksUpdatedSlot(const SongList &songs) {
|
||||||
|
|
||||||
|
songs_ = songs;
|
||||||
|
|
||||||
|
Q_EMIT AudioCDTracksUpdated();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SongLoader::AudioCDLoadingFinishedSlot() {
|
||||||
|
|
||||||
|
CDDASongLoader *cdda_song_loader = qobject_cast<CDDASongLoader*>(sender());
|
||||||
|
cdda_song_loader->deleteLater();
|
||||||
|
|
||||||
|
Q_EMIT AudioCDLoadingFinished(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class ParserBase;
|
|||||||
class CueParser;
|
class CueParser;
|
||||||
|
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
class CddaSongLoader;
|
class CDDASongLoader;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class SongLoader : public QObject {
|
class SongLoader : public QObject {
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QColor>
|
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#include <QColor>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
@@ -80,20 +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 alt = p.color(QPalette::AlternateBase);
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
if (alt.lightness() > 180) {
|
color_altbase.setAlpha(color_altbase.alpha() >= 180 ? (color_altbase.lightness() > 180 ? 130 : 16) : color_altbase.alpha());
|
||||||
alt.setAlpha(130);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alt.setAlpha(16);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
alt.setAlpha(130);
|
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(alt.red()).arg(alt.green()).arg(alt.blue()).arg(alt.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),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user