Compare commits
277 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f1bda0546 | ||
|
|
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: ''
|
||||||
|
|||||||
185
.github/workflows/build.yml
vendored
185
.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: >
|
||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
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 +97,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 +115,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 +134,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 +156,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:
|
||||||
@@ -209,7 +209,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 +234,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: |
|
||||||
@@ -307,7 +307,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 +333,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: |
|
||||||
@@ -409,7 +409,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 +434,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 +449,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 +499,11 @@ jobs:
|
|||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
qt6-l10n-tools
|
qt6-l10n-tools
|
||||||
rapidjson-dev
|
rapidjson-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 +511,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 +528,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 +542,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 +595,11 @@ jobs:
|
|||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
qt6-l10n-tools
|
qt6-l10n-tools
|
||||||
rapidjson-dev
|
rapidjson-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 +607,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 +624,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 +639,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:
|
||||||
@@ -691,7 +699,7 @@ jobs:
|
|||||||
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 +735,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 +766,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 +793,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 +802,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 +832,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 +896,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 +960,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 +983,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 +1086,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 +1260,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 +1309,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 +1351,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 +1407,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 +1424,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 +1447,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 +1644,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 +1692,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 +1700,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);
|
||||||
|
|||||||
@@ -259,7 +259,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)
|
||||||
@@ -682,6 +693,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 +701,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 +795,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
|
||||||
|
|
||||||
@@ -823,6 +834,12 @@ set(SOURCES
|
|||||||
src/device/devicestatefiltermodel.cpp
|
src/device/devicestatefiltermodel.cpp
|
||||||
src/device/deviceviewcontainer.cpp
|
src/device/deviceviewcontainer.cpp
|
||||||
src/device/deviceview.cpp
|
src/device/deviceview.cpp
|
||||||
|
|
||||||
|
src/artistbio/artistbioview.cpp
|
||||||
|
src/artistbio/artistbiofetcher.cpp
|
||||||
|
src/artistbio/artistbioprovider.cpp
|
||||||
|
src/artistbio/lastfmartistbio.cpp
|
||||||
|
src/artistbio/wikipediaartistbio.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
@@ -979,6 +996,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 +1004,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 +1093,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
|
||||||
|
|
||||||
@@ -1111,6 +1128,12 @@ set(HEADERS
|
|||||||
src/device/devicestatefiltermodel.h
|
src/device/devicestatefiltermodel.h
|
||||||
src/device/deviceviewcontainer.h
|
src/device/deviceviewcontainer.h
|
||||||
src/device/deviceview.h
|
src/device/deviceview.h
|
||||||
|
|
||||||
|
src/artistbio/artistbiofetcher.h
|
||||||
|
src/artistbio/artistbioprovider.h
|
||||||
|
src/artistbio/artistbioview.h
|
||||||
|
src/artistbio/lastfmartistbio.h
|
||||||
|
src/artistbio/wikipediaartistbio.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(UI
|
set(UI
|
||||||
@@ -1452,6 +1475,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,6 +1485,7 @@ 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
|
||||||
@@ -1552,9 +1577,10 @@ 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:${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 +1599,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,8 +12,10 @@
|
|||||||
<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/artistbio.css</file>
|
||||||
<file>style/smartplaylistsearchterm.css</file>
|
<file>style/smartplaylistsearchterm.css</file>
|
||||||
<file>html/oauthsuccess.html</file>
|
<file>html/oauthsuccess.html</file>
|
||||||
<file>pictures/strawberry.png</file>
|
<file>pictures/strawberry.png</file>
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
<file>icons/128x128/somafm.png</file>
|
<file>icons/128x128/somafm.png</file>
|
||||||
<file>icons/128x128/radioparadise.png</file>
|
<file>icons/128x128/radioparadise.png</file>
|
||||||
<file>icons/128x128/musicbrainz.png</file>
|
<file>icons/128x128/musicbrainz.png</file>
|
||||||
|
<file>icons/128x128/guitar.png</file>
|
||||||
<file>icons/64x64/albums.png</file>
|
<file>icons/64x64/albums.png</file>
|
||||||
<file>icons/64x64/alsa.png</file>
|
<file>icons/64x64/alsa.png</file>
|
||||||
<file>icons/64x64/application-exit.png</file>
|
<file>icons/64x64/application-exit.png</file>
|
||||||
@@ -197,6 +198,7 @@
|
|||||||
<file>icons/64x64/somafm.png</file>
|
<file>icons/64x64/somafm.png</file>
|
||||||
<file>icons/64x64/radioparadise.png</file>
|
<file>icons/64x64/radioparadise.png</file>
|
||||||
<file>icons/64x64/musicbrainz.png</file>
|
<file>icons/64x64/musicbrainz.png</file>
|
||||||
|
<file>icons/64x64/guitar.png</file>
|
||||||
<file>icons/48x48/albums.png</file>
|
<file>icons/48x48/albums.png</file>
|
||||||
<file>icons/48x48/alsa.png</file>
|
<file>icons/48x48/alsa.png</file>
|
||||||
<file>icons/48x48/application-exit.png</file>
|
<file>icons/48x48/application-exit.png</file>
|
||||||
@@ -300,6 +302,7 @@
|
|||||||
<file>icons/48x48/somafm.png</file>
|
<file>icons/48x48/somafm.png</file>
|
||||||
<file>icons/48x48/radioparadise.png</file>
|
<file>icons/48x48/radioparadise.png</file>
|
||||||
<file>icons/48x48/musicbrainz.png</file>
|
<file>icons/48x48/musicbrainz.png</file>
|
||||||
|
<file>icons/48x48/guitar.png</file>
|
||||||
<file>icons/32x32/albums.png</file>
|
<file>icons/32x32/albums.png</file>
|
||||||
<file>icons/32x32/alsa.png</file>
|
<file>icons/32x32/alsa.png</file>
|
||||||
<file>icons/32x32/application-exit.png</file>
|
<file>icons/32x32/application-exit.png</file>
|
||||||
@@ -403,6 +406,7 @@
|
|||||||
<file>icons/32x32/somafm.png</file>
|
<file>icons/32x32/somafm.png</file>
|
||||||
<file>icons/32x32/radioparadise.png</file>
|
<file>icons/32x32/radioparadise.png</file>
|
||||||
<file>icons/32x32/musicbrainz.png</file>
|
<file>icons/32x32/musicbrainz.png</file>
|
||||||
|
<file>icons/32x32/guitar.png</file>
|
||||||
<file>icons/22x22/albums.png</file>
|
<file>icons/22x22/albums.png</file>
|
||||||
<file>icons/22x22/alsa.png</file>
|
<file>icons/22x22/alsa.png</file>
|
||||||
<file>icons/22x22/application-exit.png</file>
|
<file>icons/22x22/application-exit.png</file>
|
||||||
@@ -506,5 +510,6 @@
|
|||||||
<file>icons/22x22/somafm.png</file>
|
<file>icons/22x22/somafm.png</file>
|
||||||
<file>icons/22x22/radioparadise.png</file>
|
<file>icons/22x22/radioparadise.png</file>
|
||||||
<file>icons/22x22/musicbrainz.png</file>
|
<file>icons/22x22/musicbrainz.png</file>
|
||||||
|
<file>icons/22x22/guitar.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
BIN
data/icons/128x128/guitar.png
Normal file
BIN
data/icons/128x128/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
data/icons/22x22/guitar.png
Normal file
BIN
data/icons/22x22/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/icons/32x32/guitar.png
Normal file
BIN
data/icons/32x32/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/icons/48x48/guitar.png
Normal file
BIN
data/icons/48x48/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
data/icons/64x64/guitar.png
Normal file
BIN
data/icons/64x64/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
data/icons/full/guitar.png
Normal file
BIN
data/icons/full/guitar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -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
data/style/artistbio.css
Normal file
7
data/style/artistbio.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
QScrollArea {
|
||||||
|
background: qpalette(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextEdit {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
4
debian/control
vendored
4
debian/control
vendored
@@ -60,11 +60,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
|
||||||
|
|||||||
7
dist/unix/strawberry.spec.in
vendored
7
dist/unix/strawberry.spec.in
vendored
@@ -93,16 +93,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
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,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
|
||||||
|
|||||||
126
src/artistbio/artistbiofetcher.cpp
Normal file
126
src/artistbio/artistbiofetcher.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "artistbiofetcher.h"
|
||||||
|
#include "artistbioprovider.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
ArtistBioFetcher::ArtistBioFetcher(QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
timeout_duration_(kDefaultTimeoutDuration),
|
||||||
|
next_id_(1) {}
|
||||||
|
|
||||||
|
void ArtistBioFetcher::AddProvider(ArtistBioProvider *provider) {
|
||||||
|
|
||||||
|
providers_ << provider;
|
||||||
|
connect(provider, SIGNAL(ImageReady(int, QUrl)), SLOT(ImageReady(int, QUrl)), Qt::QueuedConnection);
|
||||||
|
connect(provider, SIGNAL(InfoReady(int, CollapsibleInfoPane::Data)), SLOT(InfoReady(int, CollapsibleInfoPane::Data)), Qt::QueuedConnection);
|
||||||
|
connect(provider, SIGNAL(Finished(int)), SLOT(ProviderFinished(int)), Qt::QueuedConnection);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistBioFetcher::~ArtistBioFetcher() {
|
||||||
|
|
||||||
|
while (!providers_.isEmpty()) {
|
||||||
|
ArtistBioProvider *provider = providers_.takeFirst();
|
||||||
|
provider->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ArtistBioFetcher::FetchInfo(const Song &metadata) {
|
||||||
|
|
||||||
|
const int id = next_id_++;
|
||||||
|
results_[id] = Result();
|
||||||
|
timeout_timers_[id] = new QTimer(this);
|
||||||
|
timeout_timers_[id]->setSingleShot(true);
|
||||||
|
timeout_timers_[id]->setInterval(timeout_duration_);
|
||||||
|
timeout_timers_[id]->start();
|
||||||
|
|
||||||
|
connect(timeout_timers_[id], &QTimer::timeout, [this, id]() { Timeout(id); });
|
||||||
|
|
||||||
|
for (ArtistBioProvider *provider : providers_) {
|
||||||
|
if (provider->is_enabled()) {
|
||||||
|
waiting_for_[id].append(provider);
|
||||||
|
provider->Start(id, metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioFetcher::ImageReady(const int id, const QUrl &url) {
|
||||||
|
|
||||||
|
if (!results_.contains(id)) return;
|
||||||
|
results_[id].images_ << url;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioFetcher::InfoReady(const int id, const CollapsibleInfoPane::Data &data) {
|
||||||
|
|
||||||
|
if (!results_.contains(id)) return;
|
||||||
|
results_[id].info_ << data;
|
||||||
|
|
||||||
|
if (!waiting_for_.contains(id)) return;
|
||||||
|
Q_EMIT InfoResultReady(id, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioFetcher::ProviderFinished(const int id) {
|
||||||
|
|
||||||
|
if (!results_.contains(id)) return;
|
||||||
|
if (!waiting_for_.contains(id)) return;
|
||||||
|
|
||||||
|
ArtistBioProvider *provider = qobject_cast<ArtistBioProvider*>(sender());
|
||||||
|
if (!waiting_for_[id].contains(provider)) return;
|
||||||
|
|
||||||
|
waiting_for_[id].removeAll(provider);
|
||||||
|
if (waiting_for_[id].isEmpty()) {
|
||||||
|
Result result = results_.take(id);
|
||||||
|
Q_EMIT ResultReady(id, result);
|
||||||
|
waiting_for_.remove(id);
|
||||||
|
delete timeout_timers_.take(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioFetcher::Timeout(const int id) {
|
||||||
|
|
||||||
|
if (!results_.contains(id)) return;
|
||||||
|
if (!waiting_for_.contains(id)) return;
|
||||||
|
|
||||||
|
// Emit the results that we have already
|
||||||
|
Q_EMIT ResultReady(id, results_.take(id));
|
||||||
|
|
||||||
|
// Cancel any providers that we're still waiting for
|
||||||
|
for (ArtistBioProvider *provider : waiting_for_[id]) {
|
||||||
|
qLog(Info) << "Request timed out from info provider" << provider->name();
|
||||||
|
provider->Cancel(id);
|
||||||
|
}
|
||||||
|
waiting_for_.remove(id);
|
||||||
|
|
||||||
|
// Remove the timer
|
||||||
|
delete timeout_timers_.take(id);
|
||||||
|
|
||||||
|
}
|
||||||
75
src/artistbio/artistbiofetcher.h
Normal file
75
src/artistbio/artistbiofetcher.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARTISTBIOFETCHER_H
|
||||||
|
#define ARTISTBIOFETCHER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "widgets/collapsibleinfopane.h"
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
class ArtistBioProvider;
|
||||||
|
|
||||||
|
class ArtistBioFetcher : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ArtistBioFetcher(QObject *parent = nullptr);
|
||||||
|
~ArtistBioFetcher() override;
|
||||||
|
|
||||||
|
struct Result {
|
||||||
|
QList<QUrl> images_;
|
||||||
|
QList<CollapsibleInfoPane::Data> info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int kDefaultTimeoutDuration = 25000;
|
||||||
|
|
||||||
|
void AddProvider(ArtistBioProvider *provider);
|
||||||
|
int FetchInfo(const Song &metadata);
|
||||||
|
|
||||||
|
QList<ArtistBioProvider*> providers() const { return providers_; }
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void InfoResultReady(int, CollapsibleInfoPane::Data);
|
||||||
|
void ResultReady(int, ArtistBioFetcher::Result);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void ImageReady(const int id, const QUrl &url);
|
||||||
|
void InfoReady(const int id, const CollapsibleInfoPane::Data &data);
|
||||||
|
void ProviderFinished(const int id);
|
||||||
|
void Timeout(const int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<ArtistBioProvider*> providers_;
|
||||||
|
|
||||||
|
QMap<int, Result> results_;
|
||||||
|
QMap<int, QList<ArtistBioProvider*>> waiting_for_;
|
||||||
|
QMap<int, QTimer*> timeout_timers_;
|
||||||
|
|
||||||
|
int timeout_duration_;
|
||||||
|
int next_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARTISTBIOFETCHER_H
|
||||||
25
src/artistbio/artistbioprovider.cpp
Normal file
25
src/artistbio/artistbioprovider.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "artistbioprovider.h"
|
||||||
|
|
||||||
|
ArtistBioProvider::ArtistBioProvider() : enabled_(true) {}
|
||||||
|
|
||||||
|
QString ArtistBioProvider::name() const { return QString::fromLatin1(metaObject()->className()); }
|
||||||
53
src/artistbio/artistbioprovider.h
Normal file
53
src/artistbio/artistbioprovider.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARTISTBIOPROVIDER_H
|
||||||
|
#define ARTISTBIOPROVIDER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "widgets/collapsibleinfopane.h"
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
class ArtistBioProvider : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ArtistBioProvider();
|
||||||
|
|
||||||
|
virtual void Start(const int id, const Song &song) = 0;
|
||||||
|
virtual void Cancel(const int) {}
|
||||||
|
|
||||||
|
virtual QString name() const;
|
||||||
|
|
||||||
|
bool is_enabled() const { return enabled_; }
|
||||||
|
void set_enabled(bool enabled) { enabled_ = enabled; }
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void ImageReady(int, QUrl);
|
||||||
|
void InfoReady(int, CollapsibleInfoPane::Data);
|
||||||
|
void Finished(int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool enabled_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARTISTBIOPROVIDER_H
|
||||||
287
src/artistbio/artistbioview.cpp
Normal file
287
src/artistbio/artistbioview.cpp
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QShowEvent>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "core/networkaccessmanager.h"
|
||||||
|
#include "widgets/prettyimageview.h"
|
||||||
|
#include "widgets/widgetfadehelper.h"
|
||||||
|
#include "artistbiofetcher.h"
|
||||||
|
#include "lastfmartistbio.h"
|
||||||
|
#include "wikipediaartistbio.h"
|
||||||
|
|
||||||
|
#include "artistbioview.h"
|
||||||
|
|
||||||
|
const char *ArtistBioView::kSettingsGroup = "ArtistBio";
|
||||||
|
|
||||||
|
ArtistBioView::ArtistBioView(QWidget *parent)
|
||||||
|
: QWidget(parent),
|
||||||
|
network_(new NetworkAccessManager(this)),
|
||||||
|
fetcher_(new ArtistBioFetcher(this)),
|
||||||
|
current_request_id_(-1),
|
||||||
|
container_(new QVBoxLayout),
|
||||||
|
section_container_(nullptr),
|
||||||
|
fader_(new WidgetFadeHelper(this, 1000)),
|
||||||
|
dirty_(false) {
|
||||||
|
|
||||||
|
// Add the top-level scroll area
|
||||||
|
QScrollArea *scrollarea = new QScrollArea(this);
|
||||||
|
setLayout(new QVBoxLayout);
|
||||||
|
layout()->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout()->addWidget(scrollarea);
|
||||||
|
|
||||||
|
// Add a container widget to the scroll area
|
||||||
|
QWidget *container_widget = new QWidget;
|
||||||
|
container_widget->setLayout(container_);
|
||||||
|
container_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
container_widget->setBackgroundRole(QPalette::Base);
|
||||||
|
container_->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||||
|
container_->setContentsMargins(0, 0, 0, 0);
|
||||||
|
container_->setSpacing(6);
|
||||||
|
scrollarea->setWidget(container_widget);
|
||||||
|
scrollarea->setWidgetResizable(true);
|
||||||
|
|
||||||
|
// Add a spacer to the bottom of the container
|
||||||
|
container_->addStretch();
|
||||||
|
|
||||||
|
// Set stylesheet
|
||||||
|
QFile stylesheet(QStringLiteral(":/style/artistbio.css"));
|
||||||
|
if (stylesheet.open(QIODevice::ReadOnly)) {
|
||||||
|
setStyleSheet(QString::fromLatin1(stylesheet.readAll()));
|
||||||
|
stylesheet.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher_->AddProvider(new LastFMArtistBio);
|
||||||
|
fetcher_->AddProvider(new WikipediaArtistBio);
|
||||||
|
|
||||||
|
connect(fetcher_, SIGNAL(ResultReady(int, ArtistBioFetcher::Result)), SLOT(ResultReady(int, ArtistBioFetcher::Result)));
|
||||||
|
connect(fetcher_, SIGNAL(InfoResultReady(int, CollapsibleInfoPane::Data)), SLOT(InfoResultReady(int, CollapsibleInfoPane::Data)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistBioView::~ArtistBioView() {}
|
||||||
|
|
||||||
|
void ArtistBioView::showEvent(QShowEvent *e) {
|
||||||
|
|
||||||
|
if (dirty_) {
|
||||||
|
MaybeUpdate(queued_metadata_);
|
||||||
|
dirty_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget::showEvent(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::ReloadSettings() {
|
||||||
|
|
||||||
|
for (CollapsibleInfoPane *pane : sections_) {
|
||||||
|
QWidget *contents = pane->data().contents_;
|
||||||
|
if (!contents) continue;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(contents, "ReloadSettings");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArtistBioView::NeedsUpdate(const Song &old_metadata, const Song &new_metadata) const {
|
||||||
|
|
||||||
|
if (new_metadata.artist().isEmpty()) return false;
|
||||||
|
|
||||||
|
return old_metadata.artist() != new_metadata.artist();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::InfoResultReady(const int id, const CollapsibleInfoPane::Data &_data) {
|
||||||
|
|
||||||
|
if (id != current_request_id_) return;
|
||||||
|
|
||||||
|
AddSection(new CollapsibleInfoPane(_data, this));
|
||||||
|
CollapseSections();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::ResultReady(const int id, const ArtistBioFetcher::Result &result) {
|
||||||
|
|
||||||
|
if (id != current_request_id_) return;
|
||||||
|
|
||||||
|
if (!result.images_.isEmpty()) {
|
||||||
|
// Image view goes at the top
|
||||||
|
PrettyImageView *image_view = new PrettyImageView(network_, this);
|
||||||
|
AddWidget(image_view);
|
||||||
|
|
||||||
|
for (const QUrl& url : result.images_) {
|
||||||
|
image_view->AddImage(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollapseSections();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::Clear() {
|
||||||
|
|
||||||
|
fader_->StartFade();
|
||||||
|
|
||||||
|
qDeleteAll(widgets_);
|
||||||
|
widgets_.clear();
|
||||||
|
if (section_container_) {
|
||||||
|
container_->removeWidget(section_container_);
|
||||||
|
delete section_container_;
|
||||||
|
}
|
||||||
|
sections_.clear();
|
||||||
|
|
||||||
|
// Container for collapsible sections goes below
|
||||||
|
section_container_ = new QWidget;
|
||||||
|
section_container_->setLayout(new QVBoxLayout);
|
||||||
|
section_container_->layout()->setContentsMargins(0, 0, 0, 0);
|
||||||
|
section_container_->layout()->setSpacing(1);
|
||||||
|
section_container_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
container_->insertWidget(0, section_container_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::AddSection(CollapsibleInfoPane *section) {
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (; i < sections_.count(); ++i) {
|
||||||
|
if (section->data() < sections_[i]->data()) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectWidget(section->data().contents_);
|
||||||
|
|
||||||
|
sections_.insert(i, section);
|
||||||
|
qobject_cast<QVBoxLayout*>(section_container_->layout())->insertWidget(i, section);
|
||||||
|
section->show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::AddWidget(QWidget *widget) {
|
||||||
|
|
||||||
|
ConnectWidget(widget);
|
||||||
|
|
||||||
|
container_->insertWidget(container_->count() - 2, widget);
|
||||||
|
widgets_ << widget;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::SongChanged(const Song &metadata) {
|
||||||
|
|
||||||
|
if (isVisible()) {
|
||||||
|
MaybeUpdate(metadata);
|
||||||
|
dirty_ = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queued_metadata_ = metadata;
|
||||||
|
dirty_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::SongFinished() { dirty_ = false; }
|
||||||
|
|
||||||
|
void ArtistBioView::MaybeUpdate(const Song &metadata) {
|
||||||
|
|
||||||
|
if (old_metadata_.is_valid()) {
|
||||||
|
if (!NeedsUpdate(old_metadata_, metadata)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Update(metadata);
|
||||||
|
old_metadata_ = metadata;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::Update(const Song &metadata) {
|
||||||
|
|
||||||
|
current_request_id_ = fetcher_->FetchInfo(metadata);
|
||||||
|
|
||||||
|
// Do this after the new pane has been shown otherwise it'll just grab a black rectangle.
|
||||||
|
Clear();
|
||||||
|
QTimer::singleShot(0, fader_, SLOT(StartBlur()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::CollapseSections() {
|
||||||
|
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
|
// Sections are already sorted by type and relevance, so the algorithm we use to determine which ones to show by default is:
|
||||||
|
// * In the absence of any user preference, show the first (highest relevance section of each type and hide the rest)
|
||||||
|
// * If one or more sections in a type have been explicitly hidden/shown by the user before then hide all sections in that type and show only the ones that are explicitly shown.
|
||||||
|
|
||||||
|
QMultiMap<CollapsibleInfoPane::Data::Type, CollapsibleInfoPane*> types_;
|
||||||
|
QSet<CollapsibleInfoPane::Data::Type> has_user_preference_;
|
||||||
|
for (CollapsibleInfoPane *pane : sections_) {
|
||||||
|
const CollapsibleInfoPane::Data::Type type = pane->data().type_;
|
||||||
|
types_.insert(type, pane);
|
||||||
|
|
||||||
|
QVariant preference = s.value(pane->data().id_);
|
||||||
|
if (preference.isValid()) {
|
||||||
|
has_user_preference_.insert(type);
|
||||||
|
if (preference.toBool()) {
|
||||||
|
pane->Expand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CollapsibleInfoPane::Data::Type type : types_.keys()) {
|
||||||
|
if (!has_user_preference_.contains(type)) {
|
||||||
|
// Expand the first one
|
||||||
|
types_.values(type).last()->Expand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CollapsibleInfoPane *pane : sections_) {
|
||||||
|
connect(pane, SIGNAL(Toggled(bool)), SLOT(SectionToggled(bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::SectionToggled(const bool value) {
|
||||||
|
|
||||||
|
CollapsibleInfoPane *pane = qobject_cast<CollapsibleInfoPane*>(sender());
|
||||||
|
if (!pane || !sections_.contains(pane)) return;
|
||||||
|
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
s.setValue(pane->data().id_, value);
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArtistBioView::ConnectWidget(QWidget *widget) {
|
||||||
|
|
||||||
|
const QMetaObject *m = widget->metaObject();
|
||||||
|
|
||||||
|
if (m->indexOfSignal("ShowSettingsDialog()") != -1) {
|
||||||
|
connect(widget, SIGNAL(ShowSettingsDialog()), SIGNAL(ShowSettingsDialog()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
104
src/artistbio/artistbioview.h
Normal file
104
src/artistbio/artistbioview.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* This file was part of Clementine.
|
||||||
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARTISTBIOVIEW_H
|
||||||
|
#define ARTISTBIOVIEW_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "widgets/collapsibleinfopane.h"
|
||||||
|
#include "widgets/widgetfadehelper.h"
|
||||||
|
#include "widgets/collapsibleinfopane.h"
|
||||||
|
#include "playlist/playlistitem.h"
|
||||||
|
#include "smartplaylists/playlistgenerator_fwd.h"
|
||||||
|
#include "artistbiofetcher.h"
|
||||||
|
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QTimeLine;
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QScrollArea;
|
||||||
|
class QShowEvent;
|
||||||
|
|
||||||
|
class PrettyImageView;
|
||||||
|
class CollapsibleInfoPane;
|
||||||
|
class WidgetFadeHelper;
|
||||||
|
|
||||||
|
class ArtistBioView : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ArtistBioView(QWidget *parent = nullptr);
|
||||||
|
~ArtistBioView() override;
|
||||||
|
|
||||||
|
static const char *kSettingsGroup;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void SongChanged(const Song& metadata);
|
||||||
|
void SongFinished();
|
||||||
|
virtual void ReloadSettings();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void ShowSettingsDialog();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *e) override;
|
||||||
|
|
||||||
|
void Update(const Song &metadata);
|
||||||
|
void AddWidget(QWidget *widget);
|
||||||
|
void AddSection(CollapsibleInfoPane *section);
|
||||||
|
void Clear();
|
||||||
|
void CollapseSections();
|
||||||
|
|
||||||
|
bool NeedsUpdate(const Song& old_metadata, const Song &new_metadata) const;
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void ResultReady(const int id, const ArtistBioFetcher::Result &result);
|
||||||
|
void InfoResultReady(const int id, const CollapsibleInfoPane::Data &data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QNetworkAccessManager *network_;
|
||||||
|
ArtistBioFetcher *fetcher_;
|
||||||
|
int current_request_id_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MaybeUpdate(const Song &metadata);
|
||||||
|
void ConnectWidget(QWidget *widget);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void SectionToggled(const bool value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *container_;
|
||||||
|
QList<QWidget*> widgets_;
|
||||||
|
|
||||||
|
QWidget *section_container_;
|
||||||
|
QList<CollapsibleInfoPane*> sections_;
|
||||||
|
|
||||||
|
WidgetFadeHelper *fader_;
|
||||||
|
|
||||||
|
Song queued_metadata_;
|
||||||
|
Song old_metadata_;
|
||||||
|
bool dirty_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARTISTBIOVIEW_H
|
||||||
209
src/artistbio/lastfmartistbio.cpp
Normal file
209
src/artistbio/lastfmartistbio.cpp
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include "core/networkaccessmanager.h"
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/iconloader.h"
|
||||||
|
|
||||||
|
#include "lastfmartistbio.h"
|
||||||
|
#include "widgets/infotextview.h"
|
||||||
|
#include "scrobbler/scrobblingapi20.h"
|
||||||
|
#include "scrobbler/lastfmscrobbler.h"
|
||||||
|
|
||||||
|
LastFMArtistBio::LastFMArtistBio() : ArtistBioProvider(), network_(new NetworkAccessManager(this)) {}
|
||||||
|
|
||||||
|
LastFMArtistBio::~LastFMArtistBio() {
|
||||||
|
|
||||||
|
while (!replies_.isEmpty()) {
|
||||||
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
|
disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->abort();
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LastFMArtistBio::Start(const int id, const Song &song) {
|
||||||
|
|
||||||
|
ParamList params = ParamList()
|
||||||
|
<< Param(QStringLiteral("api_key"), QString::fromLatin1(ScrobblingAPI20::kApiKey))
|
||||||
|
<< Param(QStringLiteral("lang"), QLocale().name().left(2).toLower())
|
||||||
|
<< Param(QStringLiteral("format"), QStringLiteral("json"))
|
||||||
|
<< Param(QStringLiteral("method"), QStringLiteral("artist.getinfo"))
|
||||||
|
<< Param(QStringLiteral("artist"), song.artist());
|
||||||
|
|
||||||
|
std::sort(params.begin(), params.end());
|
||||||
|
|
||||||
|
QUrlQuery url_query;
|
||||||
|
for (const Param ¶m : params) {
|
||||||
|
url_query.addQueryItem(QString::fromLatin1(QUrl::toPercentEncoding(param.first)), QString::fromLatin1(QUrl::toPercentEncoding(param.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl url(QString::fromLatin1(LastFMScrobbler::kApiUrl));
|
||||||
|
url.setQuery(url_query);
|
||||||
|
QNetworkRequest req(url);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
||||||
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
#else
|
||||||
|
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
#endif
|
||||||
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
|
||||||
|
|
||||||
|
QNetworkReply *reply = network_->get(req);
|
||||||
|
replies_ << reply;
|
||||||
|
connect(reply, &QNetworkReply::finished, [=] { RequestFinished(reply, id); });
|
||||||
|
|
||||||
|
qLog(Debug) << "Sending request" << url_query.toString(QUrl::FullyDecoded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray LastFMArtistBio::GetReplyData(QNetworkReply *reply) {
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
|
||||||
|
data = reply->readAll();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (reply->error() != QNetworkReply::NoError && reply->error() < 200) {
|
||||||
|
// This is a network error, there is nothing more to do.
|
||||||
|
Error(QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QString error;
|
||||||
|
// See if there is Json data containing "error" and "message" - then use that instead.
|
||||||
|
data = reply->readAll();
|
||||||
|
QJsonParseError json_error;
|
||||||
|
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||||
|
int error_code = -1;
|
||||||
|
if (json_error.error == QJsonParseError::NoError && !json_doc.isEmpty() && json_doc.isObject()) {
|
||||||
|
QJsonObject json_obj = json_doc.object();
|
||||||
|
if (json_obj.contains(QLatin1String("error")) && json_obj.contains(QLatin1String("message"))) {
|
||||||
|
error_code = json_obj[QLatin1String("error")].toInt();
|
||||||
|
QString error_message = json_obj[QLatin1String("message")].toString();
|
||||||
|
error = QStringLiteral("%1 (%2)").arg(error_message).arg(error_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error.isEmpty()) {
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
error = QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error = QStringLiteral("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Error(error);
|
||||||
|
}
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject LastFMArtistBio::ExtractJsonObj(const QByteArray &data) {
|
||||||
|
|
||||||
|
if (data.isEmpty()) return QJsonObject();
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument json_doc = QJsonDocument::fromJson(data, &error);
|
||||||
|
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
Error(QStringLiteral("Reply from server missing Json data."), data);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
if (json_doc.isEmpty()) {
|
||||||
|
Error(QStringLiteral("Received empty Json document."), json_doc);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
if (!json_doc.isObject()) {
|
||||||
|
Error(QStringLiteral("Json document is not an object."), json_doc);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
QJsonObject json_obj = json_doc.object();
|
||||||
|
if (json_obj.isEmpty()) {
|
||||||
|
Error(QStringLiteral("Received empty Json object."), json_doc);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_obj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LastFMArtistBio::RequestFinished(QNetworkReply *reply, const int id) {
|
||||||
|
|
||||||
|
if (!replies_.contains(reply)) return;
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
QJsonObject json_obj = ExtractJsonObj(GetReplyData(reply));
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
QString text;
|
||||||
|
if (!json_obj.isEmpty() && json_obj.contains(QLatin1String("artist")) && json_obj[QLatin1String("artist")].isObject()) {
|
||||||
|
json_obj = json_obj[QLatin1String("artist")].toObject();
|
||||||
|
if (json_obj.contains(QLatin1String("bio")) && json_obj[QLatin1String("bio")].isObject()) {
|
||||||
|
title = json_obj[QLatin1String("name")].toString();
|
||||||
|
QJsonObject obj_bio = json_obj[QLatin1String("bio")].toObject();
|
||||||
|
if (obj_bio.contains(QLatin1String("content"))) {
|
||||||
|
text = obj_bio[QLatin1String("content")].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollapsibleInfoPane::Data info_data;
|
||||||
|
info_data.id_ = title;
|
||||||
|
info_data.title_ = tr("Biography");
|
||||||
|
info_data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
||||||
|
info_data.icon_ = IconLoader::Load(QStringLiteral("scrobble"));
|
||||||
|
InfoTextView *editor = new InfoTextView;
|
||||||
|
editor->SetHtml(text);
|
||||||
|
info_data.contents_ = editor;
|
||||||
|
Q_EMIT InfoReady(id, info_data);
|
||||||
|
Q_EMIT Finished(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LastFMArtistBio::Error(const QString &error, const QVariant &debug) {
|
||||||
|
|
||||||
|
qLog(Error) << error;
|
||||||
|
if (debug.isValid()) qLog(Debug) << debug;
|
||||||
|
|
||||||
|
}
|
||||||
66
src/artistbio/lastfmartistbio.h
Normal file
66
src/artistbio/lastfmartistbio.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LASTFMARTISTBIO_H
|
||||||
|
#define LASTFMARTISTBIO_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
#include "artistbioprovider.h"
|
||||||
|
|
||||||
|
class NetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class LastFMArtistBio : public ArtistBioProvider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LastFMArtistBio();
|
||||||
|
~LastFMArtistBio();
|
||||||
|
|
||||||
|
void Start(const int id, const Song &song) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Param = QPair<QString, QString>;
|
||||||
|
using ParamList = QList<Param>;
|
||||||
|
|
||||||
|
QNetworkReply *CreateRequest(const ParamList &request_params);
|
||||||
|
QByteArray GetReplyData(QNetworkReply *reply);
|
||||||
|
QJsonObject ExtractJsonObj(const QByteArray &data);
|
||||||
|
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void RequestFinished(QNetworkReply *reply, const int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkAccessManager *network_;
|
||||||
|
QList<QNetworkReply*> replies_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LASTFMARTISTBIO_H
|
||||||
319
src/artistbio/wikipediaartistbio.cpp
Normal file
319
src/artistbio/wikipediaartistbio.cpp
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/networkaccessmanager.h"
|
||||||
|
#include "core/iconloader.h"
|
||||||
|
#include "core/latch.h"
|
||||||
|
|
||||||
|
#include "widgets/infotextview.h"
|
||||||
|
#include "wikipediaartistbio.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr char kApiUrl[] = "https://en.wikipedia.org/w/api.php";
|
||||||
|
constexpr int kMinimumImageSize = 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
WikipediaArtistBio::WikipediaArtistBio() : ArtistBioProvider(), network_(new NetworkAccessManager(this)) {}
|
||||||
|
|
||||||
|
WikipediaArtistBio::~WikipediaArtistBio() {
|
||||||
|
|
||||||
|
while (!replies_.isEmpty()) {
|
||||||
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
|
disconnect(reply, nullptr, this, nullptr);
|
||||||
|
if (reply->isRunning()) reply->abort();
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply *WikipediaArtistBio::CreateRequest(QList<Param> ¶ms) {
|
||||||
|
|
||||||
|
params << Param(QLatin1String("format"), QLatin1String("json"));
|
||||||
|
params << Param(QLatin1String("action"), QLatin1String("query"));
|
||||||
|
|
||||||
|
QUrlQuery url_query;
|
||||||
|
for (const Param ¶m : params) {
|
||||||
|
url_query.addQueryItem(QString::fromLatin1(QUrl::toPercentEncoding(param.first)), QString::fromLatin1(QUrl::toPercentEncoding(param.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl url(QString::fromLatin1(kApiUrl));
|
||||||
|
url.setQuery(url_query);
|
||||||
|
QNetworkRequest req(url);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
||||||
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
#else
|
||||||
|
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
#endif
|
||||||
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
|
||||||
|
|
||||||
|
QNetworkReply *reply = network_->get(req);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, this, &WikipediaArtistBio::HandleSSLErrors);
|
||||||
|
replies_ << reply;
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray WikipediaArtistBio::GetReplyData(QNetworkReply *reply) {
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
|
||||||
|
data = reply->readAll();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error: Received HTTP code" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error:" << reply->error() << reply->errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject WikipediaArtistBio::ExtractJsonObj(const QByteArray &data) {
|
||||||
|
|
||||||
|
if (data.isEmpty()) return QJsonObject();
|
||||||
|
|
||||||
|
QJsonParseError json_error;
|
||||||
|
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||||
|
|
||||||
|
if (json_error.error != QJsonParseError::NoError) {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error: Failed to parse json data:" << json_error.errorString();
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_doc.isEmpty()) {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error: Received empty Json document.";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_doc.isObject()) {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error: Json document is not an object.";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject json_obj = json_doc.object();
|
||||||
|
if (json_obj.isEmpty()) {
|
||||||
|
qLog(Error) << "Wikipedia artist biography error: Received empty Json object.";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_obj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::HandleSSLErrors(QList<QSslError>) {}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::Start(const int id, const Song &metadata) {
|
||||||
|
|
||||||
|
if (metadata.artist().isEmpty()) {
|
||||||
|
Q_EMIT Finished(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CountdownLatch *latch = new CountdownLatch;
|
||||||
|
connect(latch, &CountdownLatch::Done, [this, id, latch](){
|
||||||
|
latch->deleteLater();
|
||||||
|
Q_EMIT Finished(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
GetImageTitles(id, metadata.artist(), latch);
|
||||||
|
//GetArticle(id, metadata.artist(), latch);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetArticle(const int id, const QString &artist, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
latch->Wait();
|
||||||
|
|
||||||
|
ParamList params = ParamList() << Param(QStringLiteral("titles"), artist)
|
||||||
|
<< Param(QStringLiteral("prop"), QStringLiteral("extracts"));
|
||||||
|
|
||||||
|
QNetworkReply *reply = CreateRequest(params);
|
||||||
|
connect(reply, &QNetworkReply::finished, [this, reply, id, latch]() { GetArticleReply(reply, id, latch); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetArticleReply(QNetworkReply *reply, const int id, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
|
||||||
|
QJsonObject json_obj = ExtractJsonObj(GetReplyData(reply));
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
QString text;
|
||||||
|
if (!json_obj.isEmpty() && json_obj.contains(QLatin1String("query")) && json_obj[QLatin1String("query")].isObject()) {
|
||||||
|
json_obj = json_obj[QLatin1String("query")].toObject();
|
||||||
|
if (json_obj.contains(QLatin1String("pages")) && json_obj[QLatin1String("pages")].isObject()) {
|
||||||
|
QJsonObject value_pages = json_obj[QLatin1String("pages")].toObject();
|
||||||
|
for (const QJsonValue value_page : value_pages) {
|
||||||
|
if (!value_page.isObject()) continue;
|
||||||
|
QJsonObject obj_page = value_page.toObject();
|
||||||
|
if (!obj_page.contains(QLatin1String("title")) || !obj_page.contains(QLatin1String("extract"))) continue;
|
||||||
|
title = obj_page[QLatin1String("title")].toString();
|
||||||
|
text = obj_page[QLatin1String("extract")].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollapsibleInfoPane::Data info_data;
|
||||||
|
info_data.id_ = title;
|
||||||
|
info_data.title_ = tr("Biography");
|
||||||
|
info_data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
||||||
|
info_data.icon_ = IconLoader::Load(QStringLiteral("wikipedia"));
|
||||||
|
InfoTextView *editor = new InfoTextView;
|
||||||
|
editor->SetHtml(text);
|
||||||
|
info_data.contents_ = editor;
|
||||||
|
Q_EMIT InfoReady(id, info_data);
|
||||||
|
|
||||||
|
latch->CountDown();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetImageTitles(const int id, const QString &artist, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
latch->Wait();
|
||||||
|
|
||||||
|
ParamList params = ParamList() << Param(QStringLiteral("titles"), artist)
|
||||||
|
<< Param(QStringLiteral("prop"), QStringLiteral("images"))
|
||||||
|
<< Param(QStringLiteral("imlimit"), QString::number(25));
|
||||||
|
|
||||||
|
QNetworkReply *reply = CreateRequest(params);
|
||||||
|
connect(reply, &QNetworkReply::finished, [this, reply, id, latch]() { GetImageTitlesFinished(reply, id, latch); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetImageTitlesFinished(QNetworkReply *reply, const int id, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
|
||||||
|
QJsonObject json_obj = ExtractJsonObj(GetReplyData(reply));
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
QStringList titles;
|
||||||
|
if (!json_obj.isEmpty() && json_obj.contains(QLatin1String("query")) && json_obj[QLatin1String("query")].isObject()) {
|
||||||
|
json_obj = json_obj[QLatin1String("query")].toObject();
|
||||||
|
if (json_obj.contains(QLatin1String("pages")) && json_obj[QLatin1String("pages")].isObject()) {
|
||||||
|
QJsonObject value_pages = json_obj[QLatin1String("pages")].toObject();
|
||||||
|
for (const QJsonValue value_page : value_pages) {
|
||||||
|
if (!value_page.isObject()) continue;
|
||||||
|
QJsonObject obj_page = value_page.toObject();
|
||||||
|
if (!obj_page.contains(QLatin1String("title")) || !obj_page.contains(QLatin1String("images")) || !obj_page[QLatin1String("images")].isArray()) continue;
|
||||||
|
title = obj_page[QLatin1String("title")].toString();
|
||||||
|
QJsonArray array_images = obj_page[QLatin1String("images")].toArray();
|
||||||
|
for (const QJsonValue value_image : array_images) {
|
||||||
|
if (!value_image.isObject()) continue;
|
||||||
|
QJsonObject obj_image = value_image.toObject();
|
||||||
|
if (!obj_image.contains(QLatin1String("title"))) continue;
|
||||||
|
QString filename = obj_image[QLatin1String("title")].toString();
|
||||||
|
if (filename.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || filename.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) {
|
||||||
|
titles << filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &image_title : titles) {
|
||||||
|
GetImage(id, image_title, latch);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch->CountDown();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetImage(const int id, const QString &title, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
latch->Wait();
|
||||||
|
|
||||||
|
ParamList params2 = ParamList() << Param(QStringLiteral("titles"), title)
|
||||||
|
<< Param(QStringLiteral("prop"), QStringLiteral("imageinfo"))
|
||||||
|
<< Param(QStringLiteral("iiprop"), QStringLiteral("url|size"));
|
||||||
|
|
||||||
|
QNetworkReply *reply = CreateRequest(params2);
|
||||||
|
connect(reply, &QNetworkReply::finished, [this, reply, id, latch]() { GetImageFinished(reply, id, latch); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WikipediaArtistBio::GetImageFinished(QNetworkReply *reply, const int id, CountdownLatch *latch) {
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
replies_.removeAll(reply);
|
||||||
|
|
||||||
|
QJsonObject json_obj = ExtractJsonObj(GetReplyData(reply));
|
||||||
|
|
||||||
|
if (!json_obj.isEmpty()) {
|
||||||
|
QList<QUrl> urls = ExtractImageUrls(json_obj);
|
||||||
|
for (const QUrl &url : urls) {
|
||||||
|
Q_EMIT ImageReady(id, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
latch->CountDown();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QUrl> WikipediaArtistBio::ExtractImageUrls(QJsonObject json_obj) {
|
||||||
|
|
||||||
|
QList<QUrl> urls;
|
||||||
|
if (json_obj.contains(QLatin1String("query")) && json_obj[QLatin1String("query")].isObject()) {
|
||||||
|
json_obj = json_obj[QLatin1String("query")].toObject();
|
||||||
|
if (json_obj.contains(QLatin1String("pages")) && json_obj[QLatin1String("pages")].isObject()) {
|
||||||
|
QJsonObject value_pages = json_obj[QLatin1String("pages")].toObject();
|
||||||
|
for (const QJsonValue value_page : value_pages) {
|
||||||
|
if (!value_page.isObject()) continue;
|
||||||
|
QJsonObject obj_page = value_page.toObject();
|
||||||
|
if (!obj_page.contains(QLatin1String("title")) || !obj_page.contains(QLatin1String("imageinfo")) || !obj_page[QLatin1String("imageinfo")].isArray()) continue;
|
||||||
|
QJsonArray array_images = obj_page[QLatin1String("imageinfo")].toArray();
|
||||||
|
for (const QJsonValue value_image : array_images) {
|
||||||
|
if (!value_image.isObject()) continue;
|
||||||
|
QJsonObject obj_image = value_image.toObject();
|
||||||
|
if (!obj_image.contains(QLatin1String("url")) || !obj_image.contains(QLatin1String("width")) || !obj_image.contains(QLatin1String("height"))) continue;
|
||||||
|
QUrl url(obj_image[QLatin1String("url")].toString());
|
||||||
|
const int width = obj_image[QLatin1String("width")].toInt();
|
||||||
|
const int height = obj_image[QLatin1String("height")].toInt();
|
||||||
|
if (!url.isValid() || width < kMinimumImageSize || height < kMinimumImageSize) continue;
|
||||||
|
urls << url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
|
||||||
|
}
|
||||||
69
src/artistbio/wikipediaartistbio.h
Normal file
69
src/artistbio/wikipediaartistbio.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIKIPEDIAARTISTBIO_H
|
||||||
|
#define WIKIPEDIAARTISTBIO_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QSslError>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "artistbioprovider.h"
|
||||||
|
|
||||||
|
class QNetworkReply;
|
||||||
|
class CountdownLatch;
|
||||||
|
class NetworkAccessManager;
|
||||||
|
|
||||||
|
class WikipediaArtistBio : public ArtistBioProvider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WikipediaArtistBio();
|
||||||
|
~WikipediaArtistBio();
|
||||||
|
|
||||||
|
void Start(const int id, const Song &song) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef QPair<QString, QString> Param;
|
||||||
|
typedef QList<Param> ParamList;
|
||||||
|
|
||||||
|
QNetworkReply *CreateRequest(QList<Param> ¶ms);
|
||||||
|
QByteArray GetReplyData(QNetworkReply *reply);
|
||||||
|
QJsonObject ExtractJsonObj(const QByteArray &data);
|
||||||
|
void GetArticle(const int id, const QString &artist, CountdownLatch* latch);
|
||||||
|
void GetImageTitles(const int id, const QString &artist, CountdownLatch* latch);
|
||||||
|
void GetImage(const int id, const QString &title, CountdownLatch *latch);
|
||||||
|
QList<QUrl> ExtractImageUrls(QJsonObject json_obj);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void HandleSSLErrors(QList<QSslError> ssl_errors);
|
||||||
|
void GetArticleReply(QNetworkReply *reply, const int id, CountdownLatch *latch);
|
||||||
|
void GetImageTitlesFinished(QNetworkReply *reply, const int id, CountdownLatch *latch);
|
||||||
|
void GetImageFinished(QNetworkReply *reply, const int id, CountdownLatch *latch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkAccessManager *network_;
|
||||||
|
QList<QNetworkReply*> replies_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIKIPEDIAARTISTBIO_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();
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
39
src/core/latch.cpp
Normal file
39
src/core/latch.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2016, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
#include "latch.h"
|
||||||
|
|
||||||
|
CountdownLatch::CountdownLatch() : count_(0) {}
|
||||||
|
|
||||||
|
void CountdownLatch::Wait() {
|
||||||
|
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
++count_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountdownLatch::CountDown() {
|
||||||
|
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
Q_ASSERT(count_ > 0);
|
||||||
|
--count_;
|
||||||
|
if (count_ == 0) {
|
||||||
|
emit Done();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
src/core/latch.h
Normal file
39
src/core/latch.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2016, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LATCH_H
|
||||||
|
#define LATCH_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
class CountdownLatch : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CountdownLatch();
|
||||||
|
void Wait();
|
||||||
|
void CountDown();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void Done();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex mutex_;
|
||||||
|
int count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LATCH_H
|
||||||
@@ -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
|
||||||
@@ -207,6 +205,7 @@
|
|||||||
#include "smartplaylists/smartplaylistsviewcontainer.h"
|
#include "smartplaylists/smartplaylistsviewcontainer.h"
|
||||||
|
|
||||||
#include "organize/organizeerrordialog.h"
|
#include "organize/organizeerrordialog.h"
|
||||||
|
#include "artistbio/artistbioview.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
# include "core/windows7thumbbar.h"
|
# include "core/windows7thumbbar.h"
|
||||||
@@ -234,6 +233,7 @@
|
|||||||
|
|
||||||
using std::make_unique;
|
using std::make_unique;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
@@ -280,7 +280,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 +292,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 +310,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)),
|
||||||
@@ -361,6 +359,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
qobuz_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Qobuz), app->albumcover_loader(), QLatin1String(QobuzSettings::kSettingsGroup), this)),
|
qobuz_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Qobuz), app->albumcover_loader(), QLatin1String(QobuzSettings::kSettingsGroup), this)),
|
||||||
#endif
|
#endif
|
||||||
radio_view_(new RadioViewContainer(this)),
|
radio_view_(new RadioViewContainer(this)),
|
||||||
|
artistbio_view_(new ArtistBioView(this)),
|
||||||
lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)),
|
lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)),
|
||||||
collection_show_all_(nullptr),
|
collection_show_all_(nullptr),
|
||||||
collection_show_duplicates_(nullptr),
|
collection_show_duplicates_(nullptr),
|
||||||
@@ -375,9 +374,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 +406,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 +431,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
|
||||||
@@ -445,6 +444,7 @@ MainWindow::MainWindow(Application *app,
|
|||||||
#ifdef HAVE_QOBUZ
|
#ifdef HAVE_QOBUZ
|
||||||
ui_->tabs->AddTab(qobuz_view_, u"qobuz"_s, IconLoader::Load(u"qobuz"_s, true, 0, 32), tr("Qobuz"));
|
ui_->tabs->AddTab(qobuz_view_, u"qobuz"_s, IconLoader::Load(u"qobuz"_s, true, 0, 32), tr("Qobuz"));
|
||||||
#endif
|
#endif
|
||||||
|
ui_->tabs->AddTab(artistbio_view_, QStringLiteral("artistbio"), IconLoader::Load(QStringLiteral("guitar")), tr("Artist biography"));
|
||||||
|
|
||||||
// Add the playing widget to the fancy tab widget
|
// Add the playing widget to the fancy tab widget
|
||||||
ui_->tabs->AddBottomWidget(ui_->widget_playing);
|
ui_->tabs->AddBottomWidget(ui_->widget_playing);
|
||||||
@@ -480,9 +480,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 +552,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
|
||||||
@@ -704,6 +700,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 +717,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 +781,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 +824,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 +843,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 +854,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
|
||||||
@@ -980,34 +976,39 @@ 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
|
||||||
|
|
||||||
|
connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, artistbio_view_, &ArtistBioView::SongChanged);
|
||||||
|
connect(&*app_->player(), &Player::PlaylistFinished, artistbio_view_, &ArtistBioView::SongFinished);
|
||||||
|
connect(&*app_->player(), &Player::Stopped, artistbio_view_, &ArtistBioView::SongFinished);
|
||||||
|
|
||||||
// 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 +1044,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 +1065,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 +1169,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 +1200,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 +1222,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 +1236,19 @@ 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();
|
||||||
|
|
||||||
|
s.beginGroup(BehaviourSettings::kSettingsGroup);
|
||||||
|
bool artistbio = s.value("artistbio", false).toBool();
|
||||||
|
s.endGroup();
|
||||||
|
if (artistbio) {
|
||||||
|
ui_->tabs->EnableTab(artistbio_view_);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui_->tabs->DisableTab(artistbio_view_);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
s.beginGroup(SubsonicSettings::kSettingsGroup);
|
s.beginGroup(SubsonicSettings::kSettingsGroup);
|
||||||
@@ -1352,8 +1365,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 +1393,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 +1451,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 +1460,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 +1493,7 @@ void MainWindow::MediaPaused() {
|
|||||||
track_slider_timer_->start();
|
track_slider_timer_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_icon_->SetPaused();
|
systemtrayicon_->SetPaused();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1498,7 +1514,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 +1531,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 +1552,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 +1578,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 +1610,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 +1722,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 +1736,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 +1744,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 +1774,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 +1807,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 +1821,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 +1943,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 +2022,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 +2065,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 +2128,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 +2138,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 +2218,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 +2367,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 +2379,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 +2397,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 +2784,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 +2793,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 +2852,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 +2868,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 +2920,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 +2944,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 +3037,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 +3316,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 +3339,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 +3354,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 +3365,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 ArtistBioView;
|
||||||
|
|
||||||
#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();
|
||||||
@@ -286,7 +284,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
void Raise();
|
void Raise();
|
||||||
|
|
||||||
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 +309,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 +325,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_;
|
||||||
@@ -364,6 +359,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
|
|
||||||
RadioViewContainer *radio_view_;
|
RadioViewContainer *radio_view_;
|
||||||
|
|
||||||
|
ArtistBioView *artistbio_view_;
|
||||||
|
|
||||||
LastFMImportDialog *lastfm_import_dialog_;
|
LastFMImportDialog *lastfm_import_dialog_;
|
||||||
|
|
||||||
QAction *collection_show_all_;
|
QAction *collection_show_all_;
|
||||||
@@ -380,9 +377,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 +391,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 +414,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>
|
||||||
@@ -580,7 +576,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 +640,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 +655,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 +781,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">
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
#include "smartplaylists/playlistgenerator_fwd.h"
|
#include "smartplaylists/playlistgenerator_fwd.h"
|
||||||
|
|
||||||
#include "radios/radiochannel.h"
|
#include "radios/radiochannel.h"
|
||||||
|
#include "widgets/collapsibleinfopane.h"
|
||||||
|
|
||||||
#ifdef HAVE_MTP
|
#ifdef HAVE_MTP
|
||||||
# include "device/mtpconnection.h"
|
# include "device/mtpconnection.h"
|
||||||
@@ -147,6 +148,8 @@ void RegisterMetaTypes() {
|
|||||||
qRegisterMetaType<RadioChannel>("RadioChannel");
|
qRegisterMetaType<RadioChannel>("RadioChannel");
|
||||||
qRegisterMetaType<RadioChannelList>("RadioChannelList");
|
qRegisterMetaType<RadioChannelList>("RadioChannelList");
|
||||||
|
|
||||||
|
qRegisterMetaType<CollapsibleInfoPane::Data>("CollapsibleInfoPane::Data");
|
||||||
|
|
||||||
#ifdef HAVE_MTP
|
#ifdef HAVE_MTP
|
||||||
qRegisterMetaType<MtpConnection*>("MtpConnection*");
|
qRegisterMetaType<MtpConnection*>("MtpConnection*");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user