Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3faf18251 | ||
|
|
ddcc296af4 | ||
|
|
ee765bc210 | ||
|
|
630c67f7d0 | ||
|
|
e78bb1b29c | ||
|
|
db312a7380 | ||
|
|
48b4cbe1b9 | ||
|
|
edb1d4c747 | ||
|
|
128935b55e | ||
|
|
d5a0d67c7b | ||
|
|
a86640fdee | ||
|
|
03291b27d1 | ||
|
|
acf81f116f | ||
|
|
6564c405c1 | ||
|
|
1bd586268c | ||
|
|
497952611d | ||
|
|
9149d1baa3 | ||
|
|
66c60ac5c7 | ||
|
|
971fad4560 | ||
|
|
8ed1ce4103 | ||
|
|
3112c34d11 | ||
|
|
7ebf3cecc6 | ||
|
|
660fa99f9c | ||
|
|
a997e53d8b | ||
|
|
1d1dd583e2 | ||
|
|
d490b29265 | ||
|
|
e8ca64f16b | ||
|
|
380aa7d884 | ||
|
|
0a31a94ee8 | ||
|
|
e1d50ce28f | ||
|
|
38cc39f23d | ||
|
|
8110035d71 | ||
|
|
9af409d6d6 | ||
|
|
8646829853 | ||
|
|
b9e87813b1 | ||
|
|
138e032746 | ||
|
|
d94c6b5aba | ||
|
|
7c03a39316 | ||
|
|
c482e264f4 | ||
|
|
3a9a1f0a94 | ||
|
|
5b5f728f49 | ||
|
|
179aac6b35 | ||
|
|
25de9126ca | ||
|
|
87be5662ad | ||
|
|
053a59e3f4 | ||
|
|
250857b3d4 | ||
|
|
dc288c584c | ||
|
|
fc02543f15 | ||
|
|
0808db706f | ||
|
|
1e649fc565 | ||
|
|
c523e959d7 | ||
|
|
1422f988c1 | ||
|
|
e86252b934 | ||
|
|
e9c59b5c31 | ||
|
|
f7bddee0ce | ||
|
|
d56b76c9e3 | ||
|
|
a41353ed03 | ||
|
|
7ea12cf2f9 | ||
|
|
5bc5170112 | ||
|
|
946ed0c0b5 | ||
|
|
fcd4e5aca2 | ||
|
|
6ab6e6d3a8 | ||
|
|
0893d01b4a | ||
|
|
bf04c45ebf | ||
|
|
2786a1a97e | ||
|
|
cd9a4f1aa9 | ||
|
|
91e5cafe76 | ||
|
|
47754951f0 | ||
|
|
fbef8ef32e | ||
|
|
abd5bf3158 | ||
|
|
fd024368a3 | ||
|
|
cf4856a0c9 | ||
|
|
1b791c82fc | ||
|
|
3a27a3e868 | ||
|
|
7e6c16b287 | ||
|
|
f877639ed7 | ||
|
|
846309d4dd | ||
|
|
f2239ddfdf | ||
|
|
742d455b0e | ||
|
|
20b08a78e7 | ||
|
|
042da74955 | ||
|
|
4dcae4ce21 | ||
|
|
fa0f07f553 | ||
|
|
39792d76cf | ||
|
|
d181ee9101 | ||
|
|
335ba1a5b0 | ||
|
|
4934a360f1 | ||
|
|
0559a4c6f2 | ||
|
|
fc07919a75 | ||
|
|
d7661edf67 | ||
|
|
1c91693294 | ||
|
|
3fc3cbc6d5 | ||
|
|
11b5895e69 | ||
|
|
b4c614edbf | ||
|
|
deddaed04a | ||
|
|
a155e503f4 | ||
|
|
c0663bc19f | ||
|
|
7ffa51b83d | ||
|
|
6d397b9988 | ||
|
|
571a7fa26b | ||
|
|
b3b5a38c3a | ||
|
|
a4b115f89b | ||
|
|
3d672bb145 | ||
|
|
15b656b753 | ||
|
|
f5785db163 | ||
|
|
1ff1bf3292 | ||
|
|
b062febea0 | ||
|
|
35301dc79e | ||
|
|
30c336726b | ||
|
|
3821680817 | ||
|
|
74242ea24f | ||
|
|
73c7024e11 | ||
|
|
bbdec92dc6 | ||
|
|
b4c289101c | ||
|
|
722d0797f6 | ||
|
|
deb27d5b55 | ||
|
|
9cc4ffdf6e | ||
|
|
c76d63d1d9 | ||
|
|
21bb4f33ad | ||
|
|
2897b881d6 | ||
|
|
e9b89d0929 | ||
|
|
e801254b2e | ||
|
|
6f49918ee9 | ||
|
|
0ae7c18f1f | ||
|
|
c9c3fb396a | ||
|
|
91db4f1934 | ||
|
|
9bbed6e95c | ||
|
|
748bc27b25 | ||
|
|
f42708e8bc | ||
|
|
160e4570a2 | ||
|
|
6272965143 | ||
|
|
1e9613bf7f | ||
|
|
a061dac298 | ||
|
|
914dee8571 | ||
|
|
47e2905edf | ||
|
|
ee6675aee0 | ||
|
|
62e0d9fe64 | ||
|
|
a174c142c1 | ||
|
|
95afc5fdec | ||
|
|
04d69f66c0 | ||
|
|
0347141edd | ||
|
|
1d6baae6e0 | ||
|
|
fb0f48f08a | ||
|
|
76e5e03d31 | ||
|
|
4cab743634 | ||
|
|
7c10ec97b7 | ||
|
|
8718a16889 | ||
|
|
75a0b924c3 | ||
|
|
83a90e0c05 | ||
|
|
e5eadd1315 | ||
|
|
f0142d90d4 | ||
|
|
cabd6e6e9d | ||
|
|
4804a05736 | ||
|
|
c258e5a3af | ||
|
|
e8492940a5 | ||
|
|
4bccb1ab47 | ||
|
|
b6d219e232 | ||
|
|
23ee17594d | ||
|
|
d9d39d8e25 | ||
|
|
224d5d46c1 | ||
|
|
09e0059930 | ||
|
|
0ddff2b087 | ||
|
|
2e6a29eacc | ||
|
|
ad2fb82aa9 | ||
|
|
a3f91c11e8 | ||
|
|
a50c978ce3 | ||
|
|
27d6f881cd | ||
|
|
944cd020af | ||
|
|
bbe5d64b99 | ||
|
|
f7b36ac4c7 | ||
|
|
1d555ca17e |
@@ -155,77 +155,6 @@ commands:
|
||||
hicolor-icon-theme
|
||||
|
||||
|
||||
install_centos_dependencies:
|
||||
description: Install CentOS dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Install epel-release
|
||||
command: dnf install -y epel-release
|
||||
- run:
|
||||
name: Install epel-release-latest-8.noarch.rpm
|
||||
command: dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
|
||||
- run:
|
||||
name: Install config-manager
|
||||
command: dnf install -y 'dnf-command(config-manager)'
|
||||
- run:
|
||||
name: PowerTools
|
||||
command: dnf config-manager --set-enabled PowerTools
|
||||
- run:
|
||||
name: DNF Clean All
|
||||
command: dnf clean all
|
||||
- run:
|
||||
name: Update packages
|
||||
command: dnf update -y
|
||||
- run:
|
||||
name: Install CentOS dependencies
|
||||
command: >
|
||||
dnf install -y
|
||||
glibc
|
||||
gcc-c++
|
||||
make
|
||||
libtool
|
||||
cmake3
|
||||
rpmdevtools
|
||||
redhat-lsb-core
|
||||
git
|
||||
man
|
||||
tar
|
||||
gettext
|
||||
boost-devel
|
||||
fuse-devel
|
||||
dbus-devel
|
||||
libnotify-devel
|
||||
gnutls-devel
|
||||
sqlite-devel
|
||||
protobuf-devel
|
||||
protobuf-compiler
|
||||
alsa-lib-devel
|
||||
pulseaudio-libs-devel
|
||||
qt5-devel
|
||||
qt5-qtbase-devel
|
||||
qt5-qtx11extras-devel
|
||||
qt5-qttools-devel
|
||||
fftw-devel
|
||||
libchromaprint-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libmtp-devel
|
||||
libjpeg-devel
|
||||
cairo-devel
|
||||
dbus-x11
|
||||
xorg-x11-server-Xvfb
|
||||
xorg-x11-xauth
|
||||
vim-common
|
||||
desktop-file-utils
|
||||
libappstream-glib
|
||||
appstream-data
|
||||
hicolor-icon-theme
|
||||
python3-pip
|
||||
python3-devel
|
||||
gstreamer1-devel
|
||||
gstreamer1-plugins-base-devel
|
||||
|
||||
|
||||
install_debian_dependencies:
|
||||
description: Install Debian dependencies
|
||||
steps:
|
||||
@@ -326,7 +255,7 @@ jobs:
|
||||
|
||||
build_source:
|
||||
docker:
|
||||
- image: opensuse/leap:15.1
|
||||
- image: opensuse/leap:15.2
|
||||
steps:
|
||||
- install_opensuse_dependencies
|
||||
- checkout
|
||||
@@ -359,18 +288,6 @@ jobs:
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_fedora_31:
|
||||
docker:
|
||||
- image: fedora:31
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_fedora_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
build_fedora_32:
|
||||
docker:
|
||||
- image: fedora:32
|
||||
@@ -383,14 +300,13 @@ jobs:
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_centos_8:
|
||||
build_fedora_33:
|
||||
docker:
|
||||
- image: centos:8
|
||||
- image: fedora:33
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_centos_dependencies
|
||||
- install_fedora_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
@@ -464,17 +380,11 @@ workflows:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_fedora_31:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_fedora_32:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_centos_8:
|
||||
- build_fedora_33:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
||||
github: jonaski
|
||||
patreon: jonaskvinge
|
||||
|
||||
237
.github/workflows/ccpp.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Create source tarball
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: opensuse/leap:15.1
|
||||
image: opensuse/leap:15.2
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- name: Update packages
|
||||
@@ -223,8 +223,8 @@ jobs:
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_opensuse_tumbleweed:
|
||||
name: Build openSUSE Tumbleweed
|
||||
build_opensuse_tumbleweed_qt5:
|
||||
name: Build openSUSE Tumbleweed Qt 5
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: opensuse/tumbleweed
|
||||
@@ -287,7 +287,7 @@ jobs:
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT5=ON
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
@@ -301,8 +301,8 @@ jobs:
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_opensuse_qt6:
|
||||
name: Build openSUSE Qt 6
|
||||
build_opensuse_tumbleweed_qt6:
|
||||
name: Build openSUSE Tumbleweed Qt 6
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: opensuse/tumbleweed
|
||||
@@ -352,7 +352,7 @@ jobs:
|
||||
qt6-x11extras-devel
|
||||
qt6-base-common-devel
|
||||
qt6-sql-sqlite
|
||||
qt6-qt5compat-devel
|
||||
qt6-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libmtp-devel
|
||||
@@ -368,9 +368,17 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
|
||||
- name: Build
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
- name: Create RPM build sources directories
|
||||
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- name: Copy source tarball
|
||||
working-directory: build
|
||||
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||
- name: Build RPM
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_fedora_32:
|
||||
@@ -615,15 +623,21 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
|
||||
- name: Install dnf-plugins-core
|
||||
run: dnf install -y dnf-plugins-core
|
||||
- name: Install epel-release
|
||||
run: dnf install -y epel-release
|
||||
|
||||
- name: Install config-manager
|
||||
run: dnf install -y 'dnf-command(config-manager)'
|
||||
- name: Enable PowerTools
|
||||
run: dnf config-manager --set-enabled PowerTools
|
||||
- name: DNF Clean All
|
||||
|
||||
- name: Enable powertools
|
||||
run: dnf config-manager --set-enabled powertools
|
||||
|
||||
- name: Clean all
|
||||
run: dnf clean all
|
||||
- name: DNF Update
|
||||
|
||||
- name: Update
|
||||
run: dnf update -y
|
||||
|
||||
- name: Install CentOS dependencies
|
||||
@@ -649,10 +663,9 @@ jobs:
|
||||
gnutls-devel
|
||||
sqlite-devel
|
||||
protobuf-devel
|
||||
protobuf-compiler
|
||||
protobuf-c
|
||||
alsa-lib-devel
|
||||
pulseaudio-libs-devel
|
||||
qt5-devel
|
||||
qt5-qtbase-devel
|
||||
qt5-qtx11extras-devel
|
||||
qt5-qttools-devel
|
||||
@@ -673,7 +686,89 @@ jobs:
|
||||
hicolor-icon-theme
|
||||
gstreamer1-devel
|
||||
gstreamer1-plugins-base-devel
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
- name: Create RPM build sources directories
|
||||
working-directory: build
|
||||
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- name: Copy source tarball
|
||||
working-directory: build
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_mageia_7:
|
||||
name: Build Mageia 7
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mageia:7
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
|
||||
- name: Update packages
|
||||
run: urpmi.update --auto -a
|
||||
|
||||
- name: Configure auto update
|
||||
run: urpmi --auto --auto-update
|
||||
|
||||
- name: Install Mageia dependencies
|
||||
run: >
|
||||
urpmi --auto --force
|
||||
urpmi-debuginfo-install
|
||||
git
|
||||
tar
|
||||
rpmdevtools
|
||||
make
|
||||
cmake
|
||||
glibc
|
||||
binutils
|
||||
gcc-c++
|
||||
man
|
||||
gettext
|
||||
notification-daemon
|
||||
dbus-devel
|
||||
libgnutls-devel
|
||||
lib64boost-devel
|
||||
lib64protobuf-devel
|
||||
protobuf-compiler
|
||||
lib64sqlite3-devel
|
||||
lib64alsa2-devel
|
||||
lib64pulseaudio-devel
|
||||
lib64notify-devel
|
||||
lib64qt5core-devel
|
||||
lib64qt5gui-devel
|
||||
lib64qt5widgets-devel
|
||||
lib64qt5network-devel
|
||||
lib64qt5concurrent-devel
|
||||
lib64qt5sql-devel
|
||||
lib64qt5dbus-devel
|
||||
lib64qt5x11extras-devel
|
||||
lib64qt5help-devel
|
||||
libqt5test-devel
|
||||
lib64gstreamer1.0-devel
|
||||
lib64gstreamer-plugins-base1.0-devel
|
||||
lib64cdio-devel
|
||||
lib64gpod-devel
|
||||
lib64mtp-devel
|
||||
lib64raw1394-devel
|
||||
lib64chromaprint-devel
|
||||
libfftw-devel
|
||||
desktop-file-utils
|
||||
appstream-util
|
||||
libappstream-glib8
|
||||
hicolor-icon-theme
|
||||
qt5ct
|
||||
lib64mesaegl1
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -995,15 +1090,11 @@ jobs:
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||
|
||||
|
||||
build-macos:
|
||||
name: Build macOS
|
||||
runs-on: macos-latest
|
||||
build-macos-catalina:
|
||||
name: Build macOS Catalina
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
#- name: Update
|
||||
# run: brew update
|
||||
#- name: Upgrade
|
||||
# run: brew upgrade
|
||||
- name: Install packages
|
||||
run: >
|
||||
brew install
|
||||
@@ -1061,8 +1152,73 @@ jobs:
|
||||
run: make dmg
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: upload-macos
|
||||
path: build/strawberry-*.dmg
|
||||
name: upload-macos-catalina
|
||||
path: build/strawberry-*-catalina.dmg
|
||||
|
||||
build-macos-bigsur:
|
||||
name: Build macOS Big Sur
|
||||
runs-on: macos-11.0
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- name: Install packages
|
||||
run: >
|
||||
brew install
|
||||
glib
|
||||
pkgconfig
|
||||
boost
|
||||
libffi
|
||||
protobuf
|
||||
protobuf-c
|
||||
qt
|
||||
gettext
|
||||
gnutls
|
||||
fftw
|
||||
sqlite
|
||||
chromaprint
|
||||
gstreamer
|
||||
gst-plugins-base
|
||||
gst-plugins-good
|
||||
gst-plugins-bad
|
||||
gst-plugins-ugly
|
||||
gst-libav
|
||||
libcdio
|
||||
libmtp
|
||||
create-dmg
|
||||
taglib
|
||||
|
||||
- name: Delete conflicting taglib system headers
|
||||
shell: bash
|
||||
run: rm -rf /usr/local/include/taglib
|
||||
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
env:
|
||||
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
|
||||
Qt5_DIR: /usr/local/opt/qt5/lib/cmake
|
||||
Qt5LinguistTools_DIR: /usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
|
||||
GST_SCANNER_PATH: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
|
||||
GST_PLUGIN_PATH: /usr/local/lib/gstreamer-1.0
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLE=ON
|
||||
- name: Build
|
||||
working-directory: build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
- name: Install
|
||||
working-directory: build
|
||||
shell: bash
|
||||
run: make install
|
||||
- name: Create DMG
|
||||
working-directory: build
|
||||
shell: bash
|
||||
run: make dmg
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: upload-macos-bigsur
|
||||
path: build/strawberry-*-bigsur.dmg
|
||||
|
||||
|
||||
build-windows:
|
||||
@@ -1179,7 +1335,7 @@ jobs:
|
||||
|
||||
- name: Copy extra binaries
|
||||
working-directory: build
|
||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,liborc-0.4-0.dll} .
|
||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,gst-launch-1.0.exe} .
|
||||
|
||||
- name: Copy dependencies
|
||||
working-directory: build
|
||||
@@ -1208,12 +1364,12 @@ jobs:
|
||||
run: makensis strawberry.nsi
|
||||
|
||||
|
||||
upload-macos:
|
||||
name: Upload macOS DMG
|
||||
upload-macos-catalina:
|
||||
name: Upload macOS Catalina DMG
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
needs:
|
||||
- build-macos
|
||||
- build-macos-catalina
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- uses: actions/download-artifact@v2
|
||||
@@ -1227,6 +1383,29 @@ jobs:
|
||||
- name: rsync
|
||||
run: |
|
||||
set -x
|
||||
for i in $(find uploads -type f -name '*.dmg'); do
|
||||
for i in $(find uploads -type f -name 'strawberry-*-catalina.dmg'); do
|
||||
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/catalina/
|
||||
done
|
||||
|
||||
upload-macos-bigsur:
|
||||
name: Upload macOS Big Sur DMG
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
needs:
|
||||
- build-macos-bigsur
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: uploads
|
||||
- name: Install SSH keys
|
||||
uses: shimataro/ssh-key-action@v2
|
||||
with:
|
||||
known_hosts: ${{ secrets.KNOWN_HOSTS2 }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
- name: rsync
|
||||
run: |
|
||||
set -x
|
||||
for i in $(find uploads -type f -name 'strawberry-*-bigsur.dmg'); do
|
||||
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/bigsur/
|
||||
done
|
||||
|
||||
21
.travis.yml
@@ -1,9 +1,8 @@
|
||||
sudo: required
|
||||
language: C++
|
||||
os:
|
||||
- osx
|
||||
compiler:
|
||||
- gcc
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
|
||||
before_install:
|
||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||
@@ -13,8 +12,8 @@ before_install:
|
||||
- git fetch --unshallow
|
||||
- git pull
|
||||
- brew update
|
||||
- travis_wait 120 brew upgrade || echo "Failed"
|
||||
- travis_wait 120 brew upgrade || echo "Failed"
|
||||
- travis_wait 400 brew upgrade || echo "Failed"
|
||||
- travis_wait 400 brew upgrade || echo "Failed"
|
||||
- brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib
|
||||
- brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
|
||||
- brew install libcdio libmtp
|
||||
@@ -32,14 +31,12 @@ before_script:
|
||||
script:
|
||||
- make -j8
|
||||
- make install
|
||||
- make dmg
|
||||
- make dmg2
|
||||
after_success:
|
||||
- ls -lh strawberry*.dmg
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||
if [[ "$TRAVIS_BRANCH" == "master" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
|
||||
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
9
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -16,11 +16,6 @@ else()
|
||||
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
endif()
|
||||
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
||||
target_include_directories(singleapplication SYSTEM PRIVATE
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtWidgets_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(singleapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
@@ -39,10 +34,6 @@ else()
|
||||
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
endif()
|
||||
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||
target_include_directories(singlecoreapplication SYSTEM PRIVATE
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(singlecoreapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
||||
1
3rdparty/utf8-cpp/CMakeLists.txt
vendored
@@ -1,2 +1 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
168
CMakeLists.txt
@@ -11,13 +11,6 @@ include(cmake/Version.cmake)
|
||||
include(cmake/Summary.cmake)
|
||||
include(cmake/OptionalSource.cmake)
|
||||
include(cmake/ParseArguments.cmake)
|
||||
include(cmake/Rpm.cmake)
|
||||
include(cmake/Deb.cmake)
|
||||
if(APPLE)
|
||||
include(cmake/Dmg.cmake)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(LINUX ON)
|
||||
@@ -29,13 +22,22 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
set(OPENBSD ON)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
if(LINUX)
|
||||
include(cmake/Rpm.cmake)
|
||||
include(cmake/Deb.cmake)
|
||||
endif()
|
||||
if(APPLE)
|
||||
include(cmake/Dmg.cmake)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
list(APPEND COMPILE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:C>:--std=c99>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:--std=c++11>
|
||||
-U__STRICT_ANSI__
|
||||
$<$<COMPILE_LANGUAGE:C>:-std=c99>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
@@ -133,6 +135,11 @@ pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
|
||||
find_package(Gettext)
|
||||
find_package(FFTW3)
|
||||
|
||||
if(NOT QT_DEFAULT_MAJOR_VERSION)
|
||||
set(QT_DEFAULT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
set(QT_MAJOR_VERSION ${QT_DEFAULT_MAJOR_VERSION} CACHE STRING "Qt version to use (5 or 6), defaults to ${QT_DEFAULT_MAJOR_VERSION}")
|
||||
|
||||
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
|
||||
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
||||
|
||||
@@ -140,79 +147,66 @@ if(WITH_QT6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_WITH_QT5 AND NOT BUILD_WITH_QT6)
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
if(BUILD_WITH_QT5)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
elseif(BUILD_WITH_QT6)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
if(QT_MAJOR_VERSION EQUAL 5)
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
elseif(QT_MAJOR_VERSION EQUAL 6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
else()
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||
if(X11_FOUND)
|
||||
list(APPEND QT_COMPONENTS X11Extras)
|
||||
endif()
|
||||
if(DBUS_FOUND)
|
||||
list(APPEND QT_COMPONENTS DBus)
|
||||
endif()
|
||||
if(WIN32)
|
||||
list(APPEND QT_COMPONENTS WinExtras)
|
||||
unset(OPTIONAL_COMPONENTS)
|
||||
|
||||
if(QT_MAJOR_VERSION EQUAL 5)
|
||||
set(QT_MIN_VERSION 5.8)
|
||||
endif()
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
list(APPEND QT_COMPONENTS Core5Compat)
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
set(QtCore_LIBRARIES Qt6::Core)
|
||||
set(QtConcurrent_LIBRARIES Qt6::Concurrent)
|
||||
set(QtWidgets_LIBRARIES Qt6::Widgets)
|
||||
set(QtNetwork_LIBRARIES Qt6::Network)
|
||||
set(QtSql_LIBRARIES Qt6::Sql)
|
||||
set(QT_LIBRARIES Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Core5Compat)
|
||||
if(Qt6DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES Qt6::DBus)
|
||||
list(APPEND QT_LIBRARIES Qt6::DBus)
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt6::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt6X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES Qt6::X11Extras)
|
||||
list(APPEND QT_LIBRARIES Qt6::X11Extras)
|
||||
endif()
|
||||
if(Qt6WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES Qt6::WinExtras)
|
||||
list(APPEND QT_LIBRARIES Qt6::WinExtras)
|
||||
endif()
|
||||
find_package(Qt6 QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if (Qt6LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt6::lconvert)
|
||||
endif()
|
||||
elseif(BUILD_WITH_QT5)
|
||||
set(QT_MIN_VERSION 5.8)
|
||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
set(QtCore_LIBRARIES ${Qt5Core_LIBRARIES})
|
||||
set(QtConcurrent_LIBRARIES ${Qt5Concurrent_LIBRARIES})
|
||||
set(QtWidgets_LIBRARIES ${Qt5Widgets_LIBRARIES})
|
||||
set(QtNetwork_LIBRARIES ${Qt5Network_LIBRARIES})
|
||||
set(QtSql_LIBRARIES ${Qt5Sql_LIBRARIES})
|
||||
set(QT_LIBRARIES ${QtCore_LIBRARIES} ${QtConcurrent_LIBRARIES} ${QtWidgets_LIBRARIES} ${QtNetwork_LIBRARIES} ${QtSql_LIBRARIES})
|
||||
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
||||
if(Qt5DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt5X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Qt5WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
|
||||
endif()
|
||||
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if (Qt5LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Set BUILD_WITH_QT5 or BUILD_WITH_QT6")
|
||||
if(DBUS_FOUND AND NOT WIN32)
|
||||
list(APPEND QT_COMPONENTS DBus)
|
||||
endif()
|
||||
if(X11_FOUND)
|
||||
list(APPEND OPTIONAL_COMPONENTS X11Extras)
|
||||
endif()
|
||||
if(WIN32)
|
||||
list(APPEND OPTIONAL_COMPONENTS WinExtras)
|
||||
endif()
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${OPTIONAL_COMPONENTS})
|
||||
|
||||
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
|
||||
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
|
||||
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
|
||||
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
|
||||
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
|
||||
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
|
||||
set(QT_LIBRARIES Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Concurrent Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Sql)
|
||||
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||
set(HAVE_X11EXTRAS ON)
|
||||
endif()
|
||||
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||
set(HAVE_WINEXTRAS ON)
|
||||
endif()
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
@@ -230,6 +224,15 @@ if(X11_FOUND)
|
||||
endif()
|
||||
endif(X11_FOUND)
|
||||
|
||||
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
if(QPA_QPLATFORMNATIVEINTERFACE_H)
|
||||
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
|
||||
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
|
||||
else()
|
||||
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
|
||||
endif()
|
||||
|
||||
# TAGLIB
|
||||
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
|
||||
if(USE_SYSTEM_TAGLIB)
|
||||
@@ -324,6 +327,10 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
|
||||
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
|
||||
)
|
||||
|
||||
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
||||
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND
|
||||
)
|
||||
|
||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||
)
|
||||
@@ -392,9 +399,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
||||
# Check that we have sqlite3 with FTS5
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_REQUIRED_FLAGS "--std=c++11")
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_INCLUDES ${QtCore_INCLUDE_DIRS} ${QtSql_INCLUDE_DIRS})
|
||||
check_cxx_source_runs("
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
|
||||
45
Changelog
@@ -2,6 +2,51 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
0.8.5:
|
||||
|
||||
Bugfixes:
|
||||
* Fix return type of SmartPlaylistQueryWizardPlugin::type().
|
||||
* Fix comparison between QChar and integer to use QChar::unicode().
|
||||
* Fix return type of qHash with Qt 6 to use size_t instead of uint.
|
||||
* Fix tag edit dialog save process sometimes stuck.
|
||||
* Fix repeat and shuffle buttons greyed out when a dynamic playlist is open.
|
||||
* Fix CUE parser handling of values with empty quotes.
|
||||
* Fix broken year and disc collection groupings with CUE songs.
|
||||
* Fix HTML escaping showing up in OSD notifications when using custom text.
|
||||
|
||||
Enhancements:
|
||||
* Add Swedish translation.
|
||||
* Made Qt X11Extras and WinExtras modules optional.
|
||||
* Save and restore geometry in edit tag dialog.
|
||||
* Add command line option to play a playlist based on name.
|
||||
* Change double-click behaviour in cover manager to open fullsize cover.
|
||||
|
||||
0.8.4:
|
||||
|
||||
Bugfixes:
|
||||
* Fix preventing session logout when window is maxmimized.
|
||||
* Fix empty space in organize window when copying songs/playlists to devices.
|
||||
* Fix crash when opening about dialog in a wayland session.
|
||||
* Fix stretched fancy/side tabbar style issue with adwaita style (Fedora/Gnome).
|
||||
* Fix centering star icon on playlist tabbar.
|
||||
* Fix network proxy settings for streaming.
|
||||
* Fix copy URL to clipboard to handle non-ASCII characters.
|
||||
* Fix HiDPI scaling for glow animation and drag over playlist.
|
||||
* Fix smart playlist search by filename.
|
||||
* Fix single letter collection nodes showing before dividers.
|
||||
|
||||
Enhancements:
|
||||
* Add support for native global shortcuts on KDE.
|
||||
* Add track progress in system tray icon as an option.
|
||||
* Only strip problematic characters in suggested filename when saving a playlist to file.
|
||||
* Change star/unstar playlist to doubleclick instead of singleclick.
|
||||
* Don't edit playlist name on doubleclick in playlists view.
|
||||
* Make context view top label text selectable.
|
||||
* Add setting to change Qt style.
|
||||
* Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags.
|
||||
* Remove remaining uses of QTextCodec.
|
||||
* Remove Core5Compat dependency.
|
||||
|
||||
0.8.3:
|
||||
|
||||
Bugfixes:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||
[](https://paypal.me/jonaskvinge)
|
||||
[](https://paypal.me/jonaskvinge)
|
||||
[](https://patreon.com/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 toolkit.
|
||||
@@ -84,8 +85,6 @@ Optional dependencies:
|
||||
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented so far.
|
||||
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
||||
|
||||
With Qt 6 we also depend on the Core5Compat module for QTextCodec.
|
||||
|
||||
### :wrench: Compiling from source
|
||||
|
||||
### Get the code:
|
||||
|
||||
@@ -9,8 +9,7 @@ if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if (DEB_CODENAME)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
|
||||
if (DEB_CODENAME AND DEB_DATE)
|
||||
add_custom_target(deb
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us
|
||||
|
||||
@@ -6,3 +6,10 @@ add_custom_target(dmg
|
||||
COMMAND create-dmg --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(dmg2
|
||||
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||
COMMAND create-dmg --skip-jenkins --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# From http://www.cmake.org/Wiki/CMakeMacroParseArguments
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
|
||||
SET(DEFAULT_ARGS)
|
||||
FOREACH(arg_name ${arg_names})
|
||||
|
||||
@@ -17,6 +17,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if (DIST_NAME)
|
||||
|
||||
message(STATUS "Distro Name: ${DIST_NAME}")
|
||||
if (DIST_RELEASE)
|
||||
message(STATUS "Distro Release: ${DIST_RELEASE}")
|
||||
@@ -24,45 +25,40 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
||||
if (DIST_VERSION)
|
||||
message(STATUS "Distro Version: ${DIST_VERSION}")
|
||||
endif()
|
||||
|
||||
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
|
||||
if (${DIST_NAME} STREQUAL "opensuse")
|
||||
if (DIST_RELEASE)
|
||||
if (${DIST_RELEASE} STREQUAL "leap")
|
||||
if (DIST_VERSION)
|
||||
set(RPM_DISTRO "lp${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
||||
else()
|
||||
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
|
||||
endif()
|
||||
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
|
||||
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
|
||||
else ()
|
||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
||||
|
||||
if (${DIST_NAME} STREQUAL "opensuse" AND DIST_RELEASE)
|
||||
if (${DIST_RELEASE} STREQUAL "leap")
|
||||
if (DIST_VERSION)
|
||||
set(RPM_DISTRO "lp${DIST_VERSION}")
|
||||
else()
|
||||
set(RPM_DISTRO ${DIST_RELEASE})
|
||||
endif()
|
||||
else()
|
||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
||||
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
|
||||
set(RPM_DISTRO ${DIST_RELEASE})
|
||||
endif()
|
||||
elseif (${DIST_NAME} STREQUAL "fedora")
|
||||
if (DIST_VERSION)
|
||||
set(RPM_DISTRO "fc${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
||||
else ()
|
||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
||||
endif()
|
||||
elseif (${DIST_NAME} STREQUAL "mageia")
|
||||
if (DIST_RELEASE)
|
||||
set(RPM_DISTRO "mga${DIST_RELEASE}" CACHE STRING "Suffix of the rpm file")
|
||||
else ()
|
||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
||||
endif()
|
||||
else()
|
||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
||||
elseif (${DIST_NAME} STREQUAL "fedora" AND DIST_VERSION)
|
||||
set(RPM_DISTRO "fc${DIST_VERSION}")
|
||||
elseif (${DIST_NAME} STREQUAL "centos" AND DIST_VERSION)
|
||||
set(RPM_DISTRO "el${DIST_VERSION}")
|
||||
elseif (${DIST_NAME} STREQUAL "mageia" AND DIST_RELEASE)
|
||||
set(RPM_DISTRO "mga${DIST_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(NOT RPM_DISTRO)
|
||||
set(RPM_DISTRO ${DIST_NAME})
|
||||
endif()
|
||||
|
||||
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec.in ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec @ONLY)
|
||||
|
||||
add_custom_target(rpm
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
||||
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
||||
COMMAND ${RPMBUILD_EXEC} -bs ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could not find xgettext executable")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 8)
|
||||
set(STRAWBERRY_VERSION_PATCH 3)
|
||||
set(STRAWBERRY_VERSION_PATCH 5)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<file>style/smartplaylistsearchterm.css</file>
|
||||
<file>html/oauthsuccess.html</file>
|
||||
<file>pictures/strawberry.png</file>
|
||||
<file>pictures/strawberry-grey.png</file>
|
||||
<file>pictures/strawberry-faded.png</file>
|
||||
<file>pictures/strawbs.png</file>
|
||||
<file>pictures/nomusic.png</file>
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<file>icons/128x128/star-grey.png</file>
|
||||
<file>icons/128x128/star.png</file>
|
||||
<file>icons/128x128/strawberry.png</file>
|
||||
<file>icons/128x128/strawberry-grey.png</file>
|
||||
<file>icons/128x128/tools-wizard.png</file>
|
||||
<file>icons/128x128/view-choose.png</file>
|
||||
<file>icons/128x128/view-fullscreen.png</file>
|
||||
@@ -164,6 +165,7 @@
|
||||
<file>icons/64x64/star-grey.png</file>
|
||||
<file>icons/64x64/star.png</file>
|
||||
<file>icons/64x64/strawberry.png</file>
|
||||
<file>icons/64x64/strawberry-grey.png</file>
|
||||
<file>icons/64x64/tools-wizard.png</file>
|
||||
<file>icons/64x64/view-choose.png</file>
|
||||
<file>icons/64x64/view-fullscreen.png</file>
|
||||
@@ -260,6 +262,7 @@
|
||||
<file>icons/48x48/star-grey.png</file>
|
||||
<file>icons/48x48/star.png</file>
|
||||
<file>icons/48x48/strawberry.png</file>
|
||||
<file>icons/48x48/strawberry-grey.png</file>
|
||||
<file>icons/48x48/tools-wizard.png</file>
|
||||
<file>icons/48x48/view-choose.png</file>
|
||||
<file>icons/48x48/view-fullscreen.png</file>
|
||||
@@ -356,6 +359,7 @@
|
||||
<file>icons/32x32/star-grey.png</file>
|
||||
<file>icons/32x32/star.png</file>
|
||||
<file>icons/32x32/strawberry.png</file>
|
||||
<file>icons/32x32/strawberry-grey.png</file>
|
||||
<file>icons/32x32/tools-wizard.png</file>
|
||||
<file>icons/32x32/view-choose.png</file>
|
||||
<file>icons/32x32/view-fullscreen.png</file>
|
||||
@@ -452,6 +456,7 @@
|
||||
<file>icons/22x22/star-grey.png</file>
|
||||
<file>icons/22x22/star.png</file>
|
||||
<file>icons/22x22/strawberry.png</file>
|
||||
<file>icons/22x22/strawberry-grey.png</file>
|
||||
<file>icons/22x22/tools-wizard.png</file>
|
||||
<file>icons/22x22/view-choose.png</file>
|
||||
<file>icons/22x22/view-fullscreen.png</file>
|
||||
|
||||
BIN
data/icons/128x128/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
data/icons/22x22/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/icons/32x32/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
data/icons/48x48/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
data/icons/64x64/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
data/icons/full/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 352 KiB |
BIN
data/pictures/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
6
dist/CMakeLists.txt
vendored
@@ -1,4 +1,10 @@
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
|
||||
if(RPM_DISTRO AND RPM_DATE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec @ONLY)
|
||||
endif(RPM_DISTRO AND RPM_DATE)
|
||||
if(DEB_CODENAME AND DEB_DATE)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
|
||||
endif(DEB_CODENAME AND DEB_DATE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
|
||||
|
||||
if (APPLE)
|
||||
|
||||
1
dist/macos/macdeploy.py
vendored
@@ -88,7 +88,6 @@ GSTREAMER_PLUGINS = [
|
||||
'libgstosxaudio.dylib',
|
||||
'libgstplayback.dylib',
|
||||
'libgstrawparse.dylib',
|
||||
'libgstrealmedia.dylib',
|
||||
'libgstreplaygain.dylib',
|
||||
'libgstsoup.dylib',
|
||||
'libgstspectrum.dylib',
|
||||
|
||||
24
dist/macos/macversion.sh
vendored
@@ -1,14 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
macos_version=$(sw_vers -productVersion| awk -F '[.]' '{print $2}')
|
||||
macos_codenames=(
|
||||
["13"]="highsierra"
|
||||
["14"]="mojave"
|
||||
["15"]="catalina"
|
||||
)
|
||||
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 [[ -n "${macos_codenames[$macos_version]}" ]]; then
|
||||
echo "${macos_codenames[$macos_version]}"
|
||||
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
|
||||
|
||||
64
dist/unix/strawberry.spec.in
vendored
@@ -1,12 +1,15 @@
|
||||
Name: strawberry
|
||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
|
||||
%else
|
||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||
%endif
|
||||
Summary: A music player and music collection organizer
|
||||
Group: Applications/Multimedia
|
||||
Group: Productivity/Multimedia/Sound/Players
|
||||
License: GPL-3.0+
|
||||
URL: https://www.strawberrymusicplayer.org/
|
||||
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
||||
BuildRequires: libboost_headers-devel
|
||||
@@ -26,14 +29,11 @@ BuildRequires: update-desktop-files
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: appstream-glib
|
||||
%else
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
BuildRequires: libappstream-glib
|
||||
%else
|
||||
%else
|
||||
BuildRequires: appstream-util
|
||||
%endif
|
||||
%endif
|
||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
||||
BuildRequires: cmake(Qt5LinguistTools)
|
||||
%endif
|
||||
%endif
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
@@ -49,15 +49,29 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
|
||||
BuildRequires: pkgconfig(taglib)
|
||||
%endif
|
||||
BuildRequires: pkgconfig(fftw3)
|
||||
BuildRequires: pkgconfig(Qt5Core)
|
||||
BuildRequires: pkgconfig(Qt5Gui)
|
||||
BuildRequires: pkgconfig(Qt5Widgets)
|
||||
BuildRequires: pkgconfig(Qt5Concurrent)
|
||||
BuildRequires: pkgconfig(Qt5Network)
|
||||
BuildRequires: pkgconfig(Qt5Sql)
|
||||
BuildRequires: pkgconfig(Qt5X11Extras)
|
||||
BuildRequires: pkgconfig(Qt5DBus)
|
||||
BuildRequires: pkgconfig(Qt5Test)
|
||||
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@DBus)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Test)
|
||||
%else
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Core)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Gui)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Widgets)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Network)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Sql)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@DBus)
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Test)
|
||||
%endif
|
||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@LinguistTools)
|
||||
%endif
|
||||
BuildRequires: pkgconfig(gstreamer-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
||||
@@ -70,14 +84,17 @@ BuildRequires: pkgconfig(libpulse)
|
||||
BuildRequires: pkgconfig(libcdio)
|
||||
BuildRequires: pkgconfig(libgpod-1.0)
|
||||
BuildRequires: pkgconfig(libmtp)
|
||||
BuildRequires: pkgconfig(libnotify)
|
||||
BuildRequires: pkgconfig(libudf)
|
||||
%if 0%{?suse_version} || 0%{?fedora_version}
|
||||
BuildRequires: pkgconfig(libvlc)
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version}
|
||||
Requires: libQt5Sql5-sqlite
|
||||
%if "@QT_MAJOR_VERSION@" == "6"
|
||||
Requires: qt6-sql-sqlite
|
||||
%endif
|
||||
%if "@QT_MAJOR_VERSION@" == "5"
|
||||
Requires: libQt5Sql5-sqlite
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%description
|
||||
@@ -107,8 +124,11 @@ Features:
|
||||
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
||||
|
||||
%build
|
||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release
|
||||
%if 0%{?centos} || 0%{?mageia}
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
||||
%endif
|
||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_MAJOR_VERSION=@QT_MAJOR_VERSION@
|
||||
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7)
|
||||
%make_build
|
||||
%else
|
||||
%cmake_build
|
||||
|
||||
16
dist/windows/strawberry.nsi.in
vendored
@@ -98,13 +98,13 @@ Name "${PRODUCT_NAME}"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x86.exe"
|
||||
!endif
|
||||
!else
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x86.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
@@ -114,13 +114,13 @@ Name "${PRODUCT_NAME}"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x64.exe"
|
||||
!endif
|
||||
!else
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x64.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
@@ -175,6 +175,7 @@ Section "Strawberry" Strawberry
|
||||
File "strawberry-tagreader.exe"
|
||||
File "strawberry.ico"
|
||||
File "sqlite3.exe"
|
||||
File "gst-launch-1.0.exe"
|
||||
|
||||
!ifdef arch_x86
|
||||
File "libgcc_s_sjlj-1.dll"
|
||||
@@ -238,7 +239,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-24.dll"
|
||||
File "libprotobuf-25.dll"
|
||||
File "libpsl-5.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "libspeex-1.dll"
|
||||
@@ -266,7 +267,7 @@ Section "Strawberry" Strawberry
|
||||
File "Qt6Network.dll"
|
||||
File "Qt6Sql.dll"
|
||||
File "Qt6Widgets.dll"
|
||||
File "Qt6WinExtras.dll"
|
||||
;File "Qt6WinExtras.dll"
|
||||
File "libqtsparkle-qt6.dll"
|
||||
!else
|
||||
File "Qt5Concurrent.dll"
|
||||
@@ -443,6 +444,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\strawberry.exe"
|
||||
Delete "$INSTDIR\strawberry-tagreader.exe"
|
||||
Delete "$INSTDIR\sqlite3.exe"
|
||||
Delete "$INSTDIR\gst-launch-1.0.exe"
|
||||
|
||||
!ifdef arch_x86
|
||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||
@@ -506,7 +508,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-24.dll"
|
||||
Delete "$INSTDIR\libprotobuf-25.dll"
|
||||
Delete "$INSTDIR\libpsl-5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||
|
||||
@@ -9,7 +9,6 @@ link_directories(
|
||||
${GSTREAMER_BASE_LIBRARY_DIRS}
|
||||
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
||||
${FFTW3_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_library(gstmoodbar STATIC ${SOURCES})
|
||||
@@ -21,7 +20,6 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
|
||||
${GSTREAMER_BASE_INCLUDE_DIRS}
|
||||
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
||||
${FFTW3_INCLUDE_DIR}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@@ -28,16 +28,12 @@ endif()
|
||||
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
${QtNetwork_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
||||
|
||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-common PRIVATE
|
||||
|
||||
@@ -107,10 +107,10 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
|
||||
else if (flush_local_socket_) {
|
||||
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _MessageHandlerBase::DeviceClosed() {
|
||||
is_device_closed_ = true;
|
||||
AbortAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class QIODevice;
|
||||
class _MessageHandlerBase : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
public:
|
||||
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
||||
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
||||
|
||||
@@ -52,16 +52,16 @@ public:
|
||||
// After this is true, messages cannot be sent to the handler any more.
|
||||
bool is_device_closed() const { return is_device_closed_; }
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
void WriteMessage(const QByteArray &data);
|
||||
void DeviceReadyRead();
|
||||
virtual void DeviceClosed();
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
||||
virtual void AbortAll() = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
||||
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
||||
|
||||
@@ -80,7 +80,7 @@ protected:
|
||||
// You should subclass this and implement the MessageArrived(MessageType) method.
|
||||
template <typename MT>
|
||||
class AbstractMessageHandler : public _MessageHandlerBase {
|
||||
public:
|
||||
public:
|
||||
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
||||
~AbstractMessageHandler() override { AbortAll(); }
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
// Sets the "id" field of reply to the same as the request, and sends the reply on the socket. Used on the worker side.
|
||||
void SendReply(const MessageType &request, MessageType *reply);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// Called when a message is received from the socket.
|
||||
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
|
||||
|
||||
@@ -110,8 +110,8 @@ protected:
|
||||
bool RawMessageArrived(const QByteArray &data) override;
|
||||
void AbortAll() override;
|
||||
|
||||
private:
|
||||
QMap<int, ReplyType*> pending_replies_;
|
||||
private:
|
||||
QMap<qint64, ReplyType*> pending_replies_;
|
||||
};
|
||||
|
||||
template <typename MT>
|
||||
@@ -146,6 +146,7 @@ void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageTy
|
||||
|
||||
template<typename MT>
|
||||
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
||||
|
||||
MessageType message;
|
||||
if (!message.ParseFromArray(data.constData(), data.size())) {
|
||||
return false;
|
||||
@@ -161,14 +162,17 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
void AbstractMessageHandler<MT>::AbortAll() {
|
||||
|
||||
for (ReplyType *reply : pending_replies_) {
|
||||
reply->Abort();
|
||||
}
|
||||
pending_replies_.clear();
|
||||
|
||||
}
|
||||
|
||||
#endif // MESSAGEHANDLER_H
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
#include <QString>
|
||||
|
||||
@@ -33,7 +34,7 @@ class _MessageReplyBase : public QObject {
|
||||
public:
|
||||
explicit _MessageReplyBase(QObject *parent = nullptr);
|
||||
|
||||
virtual int id() const = 0;
|
||||
virtual qint64 id() const = 0;
|
||||
bool is_finished() const { return finished_; }
|
||||
bool is_successful() const { return success_; }
|
||||
|
||||
@@ -57,29 +58,27 @@ class _MessageReplyBase : public QObject {
|
||||
template <typename MessageType>
|
||||
class MessageReply : public _MessageReplyBase {
|
||||
public:
|
||||
explicit MessageReply(const MessageType& request_message, QObject *parent = nullptr);
|
||||
explicit MessageReply(const MessageType &request_message, QObject *parent = nullptr);
|
||||
|
||||
int id() const override { return request_message_.id(); }
|
||||
const MessageType& request_message() const { return request_message_; }
|
||||
const MessageType& message() const { return reply_message_; }
|
||||
qint64 id() const override { return request_message_.id(); }
|
||||
const MessageType &request_message() const { return request_message_; }
|
||||
const MessageType &message() const { return reply_message_; }
|
||||
|
||||
void SetReply(const MessageType& message);
|
||||
void SetReply(const MessageType &message);
|
||||
|
||||
private:
|
||||
private:
|
||||
MessageType request_message_;
|
||||
MessageType reply_message_;
|
||||
};
|
||||
|
||||
|
||||
template<typename MessageType>
|
||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
|
||||
: _MessageReplyBase(parent)
|
||||
{
|
||||
MessageReply<MessageType>::MessageReply(const MessageType &request_message, QObject *parent) : _MessageReplyBase(parent) {
|
||||
request_message_.MergeFrom(request_message);
|
||||
}
|
||||
|
||||
template<typename MessageType>
|
||||
void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
||||
void MessageReply<MessageType>::SetReply(const MessageType &message) {
|
||||
|
||||
Q_ASSERT(!finished_);
|
||||
|
||||
@@ -90,6 +89,9 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
||||
qLog(Debug) << "Releasing ID" << id() << "(finished)";
|
||||
semaphore_.release();
|
||||
|
||||
// The signal is not always emitted without this.
|
||||
QThread::usleep(10);
|
||||
|
||||
emit Finished(success_);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QQueue>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QAtomicInt>
|
||||
#include <QAtomicInteger>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#endif
|
||||
@@ -48,11 +48,11 @@ class _WorkerPoolBase : public QObject {
|
||||
public:
|
||||
explicit _WorkerPoolBase(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
|
||||
void WorkerFailedToStart();
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
virtual void DoStart() {}
|
||||
virtual void NewConnection() {}
|
||||
virtual void ProcessError(QProcess::ProcessError) {}
|
||||
@@ -78,7 +78,7 @@ class WorkerPool : public _WorkerPoolBase {
|
||||
void SetExecutableName(const QString &executable_name);
|
||||
|
||||
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
|
||||
void SetWorkerCount(int count);
|
||||
void SetWorkerCount(const int count);
|
||||
|
||||
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
|
||||
// Defaults to QApplication::applicationName().
|
||||
@@ -93,14 +93,14 @@ class WorkerPool : public _WorkerPoolBase {
|
||||
// Can be called from any thread.
|
||||
ReplyType *SendMessageWithReply(MessageType *message);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
||||
void DoStart() override;
|
||||
void NewConnection() override;
|
||||
void ProcessError(QProcess::ProcessError error) override;
|
||||
void SendQueuedMessages() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
struct Worker {
|
||||
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
|
||||
|
||||
@@ -138,7 +138,7 @@ private:
|
||||
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
|
||||
HandlerType *NextHandler() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
QString local_server_name_;
|
||||
QString executable_name_;
|
||||
QString executable_path_;
|
||||
@@ -147,7 +147,7 @@ private:
|
||||
mutable int next_worker_;
|
||||
QList<Worker> workers_;
|
||||
|
||||
QAtomicInt next_id_;
|
||||
QAtomicInteger<qint64> next_id_;
|
||||
|
||||
QMutex message_queue_mutex_;
|
||||
QQueue<ReplyType*> message_queue_;
|
||||
@@ -170,6 +170,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||
|
||||
template <typename HandlerType>
|
||||
WorkerPool<HandlerType>::~WorkerPool() {
|
||||
|
||||
for (const Worker &worker : workers_) {
|
||||
if (worker.local_socket_ && worker.process_) {
|
||||
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
||||
@@ -193,10 +194,11 @@ WorkerPool<HandlerType>::~WorkerPool() {
|
||||
for (ReplyType *reply : message_queue_) {
|
||||
reply->Abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::SetWorkerCount(int count) {
|
||||
void WorkerPool<HandlerType>::SetWorkerCount(const int count) {
|
||||
Q_ASSERT(workers_.isEmpty());
|
||||
worker_count_ = count;
|
||||
}
|
||||
@@ -220,6 +222,7 @@ void WorkerPool<HandlerType>::Start() {
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::DoStart() {
|
||||
|
||||
Q_ASSERT(workers_.isEmpty());
|
||||
Q_ASSERT(!executable_name_.isEmpty());
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
@@ -248,10 +251,12 @@ void WorkerPool<HandlerType>::DoStart() {
|
||||
|
||||
workers_ << worker;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
DeleteQObjectPointerLater(&worker->local_server_);
|
||||
@@ -284,6 +289,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||
// Start the process
|
||||
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
@@ -338,20 +344,24 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||
StartOneWorker(worker);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
typename WorkerPool<HandlerType>::ReplyType*
|
||||
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
||||
const int id = next_id_.fetchAndAddOrdered(1);
|
||||
|
||||
const qint64 id = next_id_.fetchAndAddOrdered(1);
|
||||
message->set_id(id);
|
||||
|
||||
return new ReplyType(*message);
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
typename WorkerPool<HandlerType>::ReplyType*
|
||||
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||
|
||||
ReplyType *reply = NewReply(message);
|
||||
|
||||
// Add the pending reply to the queue
|
||||
@@ -364,10 +374,12 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
||||
|
||||
return reply;
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::SendQueuedMessages() {
|
||||
|
||||
QMutexLocker l(&message_queue_mutex_);
|
||||
|
||||
while (!message_queue_.isEmpty()) {
|
||||
@@ -384,10 +396,12 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
|
||||
|
||||
handler->SendRequest(reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
||||
|
||||
for (int i = 0; i < workers_.count(); ++i) {
|
||||
const int worker_index = (next_worker_ + i) % workers_.count();
|
||||
|
||||
@@ -398,6 +412,7 @@ HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
#endif // WORKERPOOL_H
|
||||
|
||||
@@ -9,7 +9,6 @@ link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${PROTOBUF_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
${Qt5Core_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||
@@ -17,8 +16,6 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||
@@ -38,7 +35,3 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||
${QtNetwork_LIBRARIES}
|
||||
libstrawberry-common
|
||||
)
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE Qt6::Core5Compat)
|
||||
endif()
|
||||
|
||||
@@ -84,7 +84,6 @@
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QTextCodec>
|
||||
#include <QVector>
|
||||
#include <QtDebug>
|
||||
|
||||
@@ -138,6 +137,15 @@ TagReader::~TagReader() {
|
||||
delete factory_;
|
||||
}
|
||||
|
||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
||||
|
||||
qLog(Debug) << "Checking for valid file" << filename;
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
return !fileref->isNull() && fileref->tag();
|
||||
|
||||
}
|
||||
|
||||
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
||||
@@ -198,10 +206,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
|
||||
TagLib::Tag *tag = fileref->tag();
|
||||
if (tag) {
|
||||
Decode(tag->title(), nullptr, song->mutable_title());
|
||||
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
|
||||
Decode(tag->album(), nullptr, song->mutable_album());
|
||||
Decode(tag->genre(), nullptr, song->mutable_genre());
|
||||
Decode(tag->title(), song->mutable_title());
|
||||
Decode(tag->artist(), song->mutable_artist()); // TPE1
|
||||
Decode(tag->album(), song->mutable_album());
|
||||
Decode(tag->genre(), song->mutable_genre());
|
||||
song->set_year(tag->year());
|
||||
song->set_track(tag->track());
|
||||
song->set_valid(true);
|
||||
@@ -214,7 +222,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
||||
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
|
||||
if (TagLib::Ogg::XiphComment *tag_ogg = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
ParseOggTag(tag_ogg->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
ParseOggTag(tag_ogg->fieldListMap(), &disc, &compilation, song);
|
||||
if (!tag_ogg->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
@@ -225,28 +233,28 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
song->set_bitdepth(file_flac->audioProperties()->bitsPerSample());
|
||||
|
||||
if (file_flac->xiphComment()) {
|
||||
ParseOggTag(file_flac->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
ParseOggTag(file_flac->xiphComment()->fieldListMap(), &disc, &compilation, song);
|
||||
if (!file_flac->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
||||
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
|
||||
if (file_wavpack->APETag()) {
|
||||
ParseAPETag(file_wavpack->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
ParseAPETag(file_wavpack->APETag()->itemListMap(), &disc, &compilation, song);
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
if (file_ape->APETag()) {
|
||||
ParseAPETag(file_ape->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
ParseAPETag(file_ape->APETag()->itemListMap(), &disc, &compilation, song);
|
||||
}
|
||||
song->set_bitdepth(file_ape->audioProperties()->bitsPerSample());
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
@@ -255,22 +263,22 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
|
||||
|
||||
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
||||
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
|
||||
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), song->mutable_composer());
|
||||
|
||||
// content group
|
||||
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), nullptr, song->mutable_grouping());
|
||||
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), song->mutable_grouping());
|
||||
|
||||
// ID3v2: lead performer/soloist
|
||||
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), nullptr, song->mutable_performer());
|
||||
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), song->mutable_performer());
|
||||
|
||||
// original artist/performer
|
||||
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), nullptr, song->mutable_performer());
|
||||
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), song->mutable_performer());
|
||||
|
||||
// Skip TPE1 (which is the artist) here because we already fetched it
|
||||
|
||||
|
||||
// non-standard: Apple, Microsoft
|
||||
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), nullptr, song->mutable_albumartist());
|
||||
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), song->mutable_albumartist());
|
||||
|
||||
if (!map["TCMP"].isEmpty()) compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
||||
|
||||
@@ -280,20 +288,20 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
|
||||
if (!map["USLT"].isEmpty()) {
|
||||
Decode(map["USLT"].front()->toString(), nullptr, song->mutable_lyrics());
|
||||
Decode(map["USLT"].front()->toString(), song->mutable_lyrics());
|
||||
}
|
||||
else if (!map["SYLT"].isEmpty()) {
|
||||
Decode(map["SYLT"].front()->toString(), nullptr, song->mutable_lyrics());
|
||||
Decode(map["SYLT"].front()->toString(), song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
|
||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
||||
for (uint i = 0 ; i < map["COMM"].size() ; ++i) {
|
||||
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||
|
||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
||||
Decode(frame->text(), nullptr, song->mutable_comment());
|
||||
Decode(frame->text(), song->mutable_comment());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -312,7 +320,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
if (mp4_tag->item("aART").isValid()) {
|
||||
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
|
||||
if (!album_artists.isEmpty()) {
|
||||
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
|
||||
Decode(album_artists.front(), song->mutable_albumartist());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,20 +334,20 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
|
||||
if (mp4_tag->item("\251wrt").isValid()) {
|
||||
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), nullptr, song->mutable_composer());
|
||||
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), song->mutable_composer());
|
||||
}
|
||||
if (mp4_tag->item("\251grp").isValid()) {
|
||||
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), nullptr, song->mutable_grouping());
|
||||
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), song->mutable_grouping());
|
||||
}
|
||||
if (mp4_tag->item("\251lyr").isValid()) {
|
||||
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), nullptr, song->mutable_lyrics());
|
||||
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
|
||||
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
|
||||
}
|
||||
|
||||
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
|
||||
Decode(mp4_tag->comment(), song->mutable_comment());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +356,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
|
||||
|
||||
if (file_asf->tag()) {
|
||||
Decode(file_asf->tag()->comment(), nullptr, song->mutable_comment());
|
||||
Decode(file_asf->tag()->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
|
||||
@@ -367,15 +375,15 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::MPC::File* file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
else if (TagLib::MPC::File *file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
if (file_mpc->APETag()) {
|
||||
ParseAPETag(file_mpc->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
ParseAPETag(file_mpc->APETag()->itemListMap(), &disc, &compilation, song);
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (tag) {
|
||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
Decode(tag->comment(), song->mutable_comment());
|
||||
}
|
||||
|
||||
if (!disc.isEmpty()) {
|
||||
@@ -414,42 +422,27 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
|
||||
}
|
||||
|
||||
void TagReader::Decode(const TagLib::String &tag, const QTextCodec *codec, std::string *output) {
|
||||
|
||||
QString tmp;
|
||||
|
||||
if (codec && tag.isLatin1()) { // Never override UTF-8.
|
||||
const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString();
|
||||
tmp = codec->toUnicode(fixed.c_str()).trimmed();
|
||||
}
|
||||
else {
|
||||
tmp = TStringToQString(tag).trimmed();
|
||||
}
|
||||
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
|
||||
|
||||
QString tmp = TStringToQString(tag).trimmed();
|
||||
output->assign(DataCommaSizeFromQString(tmp));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::Decode(const QString &tag, const QTextCodec *codec, std::string *output) {
|
||||
void TagReader::Decode(const QString &tag, std::string *output) {
|
||||
|
||||
if (!codec) {
|
||||
output->assign(DataCommaSizeFromQString(tag));
|
||||
}
|
||||
else {
|
||||
const QString decoded(codec->toUnicode(tag.toUtf8()));
|
||||
output->assign(DataCommaSizeFromQString(decoded));
|
||||
}
|
||||
output->assign(DataCommaSizeFromQString(tag));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), song->mutable_grouping());
|
||||
|
||||
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
|
||||
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
|
||||
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), song->mutable_albumartist());
|
||||
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), song->mutable_albumartist());
|
||||
|
||||
if (!map["ORIGINALDATE"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
||||
else if (!map["ORIGINALYEAR"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
||||
@@ -461,20 +454,18 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCod
|
||||
|
||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
||||
|
||||
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
|
||||
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
|
||||
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), song->mutable_lyrics());
|
||||
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), song->mutable_lyrics());
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
Q_UNUSED(codec);
|
||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||
if (it != map.end()) {
|
||||
TagLib::StringList album_artists = it->second.values();
|
||||
if (!album_artists.isEmpty()) {
|
||||
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
|
||||
Decode(album_artists.front(), song->mutable_albumartist());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,19 +479,19 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCode
|
||||
}
|
||||
|
||||
if (map.contains("PERFORMER")) {
|
||||
Decode(map["PERFORMER"].values().toString(", "), nullptr, song->mutable_performer());
|
||||
Decode(map["PERFORMER"].values().toString(", "), song->mutable_performer());
|
||||
}
|
||||
|
||||
if (map.contains("COMPOSER")) {
|
||||
Decode(map["COMPOSER"].values().toString(", "), nullptr, song->mutable_composer());
|
||||
Decode(map["COMPOSER"].values().toString(", "), song->mutable_composer());
|
||||
}
|
||||
|
||||
if (map.contains("GROUPING")) {
|
||||
Decode(map["GROUPING"].values().toString(" "), nullptr, song->mutable_grouping());
|
||||
Decode(map["GROUPING"].values().toString(" "), song->mutable_grouping());
|
||||
}
|
||||
|
||||
if (map.contains("LYRICS")) {
|
||||
Decode(map["LYRICS"].toString(), nullptr, song->mutable_lyrics());
|
||||
Decode(map["LYRICS"].toString(), song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (map.contains("FMPS_PLAYCOUNT")) {
|
||||
@@ -517,8 +508,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
|
||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
|
||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||
vorbis_comments->addField("COMPILATION", QStringToTaglibString(song.compilation() ? "1" : QString()), true);
|
||||
|
||||
// Try to be coherent, the two forms are used but the first one is preferred
|
||||
|
||||
@@ -538,13 +529,15 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
|
||||
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
|
||||
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
|
||||
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
|
||||
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
|
||||
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
|
||||
fileref->tag()->setYear(song.year());
|
||||
fileref->tag()->setTrack(song.track());
|
||||
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
|
||||
fileref->tag()->setArtist(song.artist().empty() ? TagLib::String() : StdStringToTaglibString(song.artist()));
|
||||
fileref->tag()->setAlbum(song.album().empty() ? TagLib::String() : StdStringToTaglibString(song.album()));
|
||||
fileref->tag()->setGenre(song.genre().empty() ? TagLib::String() : StdStringToTaglibString(song.genre()));
|
||||
fileref->tag()->setComment(song.comment().empty() ? TagLib::String() : StdStringToTaglibString(song.comment()));
|
||||
fileref->tag()->setYear(song.year() <= 0 ? 0 : song.year());
|
||||
fileref->tag()->setTrack(song.track() <= 0 ? 0 : song.track());
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
||||
@@ -572,14 +565,14 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
||||
SetTextFrame("TCOM", song.composer(), tag);
|
||||
SetTextFrame("TIT1", song.grouping(), tag);
|
||||
SetTextFrame("TOPE", song.performer(), tag);
|
||||
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
||||
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
||||
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
||||
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
||||
// Skip TPE1 (which is the artist) here because we already set it
|
||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
||||
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
||||
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
||||
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
||||
}
|
||||
|
||||
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
@@ -598,53 +591,26 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
SetVorbisComments(tag, song);
|
||||
}
|
||||
|
||||
bool ret = fileref->save();
|
||||
result = fileref->save();
|
||||
#ifdef Q_OS_LINUX
|
||||
if (ret) {
|
||||
if (result) {
|
||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
||||
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||
tag->setItem("disc", TagLib::APE::Item("disc", TagLib::String::number(song.disc() <= 0 - 1 ? 0 : song.disc())));
|
||||
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
||||
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
||||
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
||||
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
||||
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
const QByteArray descr_utf8(description.toUtf8());
|
||||
const QByteArray value_utf8(value.toUtf8());
|
||||
qLog(Debug) << "Setting FMPSFrame:" << description << ", " << value;
|
||||
SetUserTextFrame(std::string(descr_utf8.constData(), descr_utf8.length()), std::string(value_utf8.constData(), value_utf8.length()), tag);
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
const TagLib::String t_description = StdStringToTaglibString(description);
|
||||
// Remove the frame if it already exists
|
||||
TagLib::ID3v2::UserTextIdentificationFrame *frame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, t_description);
|
||||
if (frame) {
|
||||
tag->removeFrame(frame);
|
||||
}
|
||||
|
||||
// Create and add a new frame
|
||||
frame = new TagLib::ID3v2::UserTextIdentificationFrame(TagLib::String::UTF8);
|
||||
|
||||
frame->setDescription(t_description);
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
tag->addFrame(frame);
|
||||
tag->addValue("compilation", QStringToTaglibString(song.compilation() ? QString::number(1) : QString()), true);
|
||||
|
||||
}
|
||||
|
||||
@@ -652,6 +618,7 @@ void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2
|
||||
|
||||
const QByteArray utf8(value.toUtf8());
|
||||
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
@@ -665,6 +632,8 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||
}
|
||||
|
||||
if (value.empty()) return;
|
||||
|
||||
// If no frames stored create empty frame
|
||||
if (frames_buffer.isEmpty()) {
|
||||
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
||||
@@ -672,9 +641,9 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
||||
}
|
||||
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
||||
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||
TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(i));
|
||||
if (i == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
@@ -683,15 +652,6 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
||||
|
||||
}
|
||||
|
||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
||||
|
||||
qLog(Debug) << "Checking for valid file" << filename;
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
return !fileref->isNull() && fileref->tag();
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
if (filename.isEmpty()) return QByteArray();
|
||||
@@ -711,8 +671,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
if (flac_file && flac_file->xiphComment()) {
|
||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||
if (!pics.isEmpty()) {
|
||||
// Use the first picture in the file - this could be made cleverer and
|
||||
// pick the front cover if it's present.
|
||||
// Use the first picture in the file - this could be made cleverer and pick the front cover if it's present.
|
||||
|
||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||
TagLib::FLAC::Picture *picture = *it;
|
||||
@@ -817,7 +776,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
TagLib::ByteVector id_vector("USLT");
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
@@ -828,6 +787,8 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||
}
|
||||
|
||||
if (value.empty()) return;
|
||||
|
||||
// If no frames stored create empty frame
|
||||
if (frames_buffer.isEmpty()) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
||||
@@ -836,9 +797,9 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
||||
}
|
||||
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(i));
|
||||
if (i == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
class QTextCodec;
|
||||
|
||||
#ifndef USE_SYSTEM_TAGLIB
|
||||
using namespace Strawberry_TagLib;
|
||||
#endif
|
||||
@@ -52,27 +50,24 @@ class TagReader {
|
||||
explicit TagReader();
|
||||
~TagReader();
|
||||
|
||||
bool IsMediaFile(const QString &filename) const;
|
||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||
|
||||
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
||||
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
||||
|
||||
bool IsMediaFile(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||
|
||||
static void Decode(const TagLib::String& tag, const QTextCodec *codec, std::string *output);
|
||||
static void Decode(const QString &tag, const QTextCodec *codec, std::string *output);
|
||||
static void Decode(const TagLib::String &tag, std::string *output);
|
||||
static void Decode(const QString &tag, std::string *output);
|
||||
|
||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
|
||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
||||
|
||||
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;
|
||||
|
||||
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||
|
||||
@@ -101,7 +101,7 @@ message LoadEmbeddedArtResponse {
|
||||
}
|
||||
|
||||
message Message {
|
||||
optional int32 id = 1;
|
||||
optional int64 id = 1;
|
||||
|
||||
optional ReadFileRequest read_file_request = 2;
|
||||
optional ReadFileResponse read_file_response = 3;
|
||||
|
||||
@@ -15,8 +15,6 @@ endif()
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
${QtNetwork_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||
@@ -24,8 +22,6 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(strawberry-tagreader PRIVATE
|
||||
|
||||
@@ -27,11 +27,9 @@
|
||||
#include "tagreaderworker.h"
|
||||
|
||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
||||
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent)
|
||||
{
|
||||
}
|
||||
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent) {}
|
||||
|
||||
void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||
void TagReaderWorker::MessageArrived(const pb::tagreader::Message &message) {
|
||||
|
||||
pb::tagreader::Message reply;
|
||||
|
||||
@@ -51,6 +49,7 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||
}
|
||||
|
||||
SendReply(message, &reply);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
class QIODevice;
|
||||
|
||||
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
|
||||
public:
|
||||
public:
|
||||
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void MessageArrived(const pb::tagreader::Message &message) override;
|
||||
void DeviceClosed() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
TagReader tag_reader_;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ set(SOURCES
|
||||
core/multisortfilterproxy.cpp
|
||||
core/musicstorage.cpp
|
||||
core/network.cpp
|
||||
core/threadsafenetworkdiskcache.cpp
|
||||
core/networktimeouts.cpp
|
||||
core/networkproxyfactory.cpp
|
||||
core/qtfslistener.cpp
|
||||
@@ -257,6 +258,7 @@ set(HEADERS
|
||||
core/filesystemwatcherinterface.h
|
||||
core/mergedproxymodel.h
|
||||
core/network.h
|
||||
core/threadsafenetworkdiskcache.h
|
||||
core/networktimeouts.h
|
||||
core/qtfslistener.h
|
||||
core/songloader.h
|
||||
@@ -548,19 +550,19 @@ if(HAVE_GLOBALSHORTCUTS)
|
||||
HEADERS globalshortcuts/globalshortcuts.h globalshortcuts/globalshortcutbackend.h globalshortcuts/globalshortcutgrabber.h settings/shortcutssettingspage.h
|
||||
UI globalshortcuts/globalshortcutgrabber.ui settings/shortcutssettingspage.ui
|
||||
)
|
||||
if (X11_FOUND OR WIN32)
|
||||
if(HAVE_X11EXTRAS OR WIN32)
|
||||
set(X11_OR_WIN ON)
|
||||
endif()
|
||||
optional_source(X11_OR_WIN
|
||||
SOURCES globalshortcuts/globalshortcutbackend-system.cpp globalshortcuts/globalshortcut.cpp
|
||||
HEADERS globalshortcuts/globalshortcutbackend-system.h globalshortcuts/globalshortcut.h
|
||||
)
|
||||
optional_source(X11_FOUND
|
||||
optional_source(HAVE_X11EXTRAS
|
||||
SOURCES globalshortcuts/globalshortcut-x11.cpp
|
||||
)
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp
|
||||
HEADERS globalshortcuts/globalshortcutbackend-gsd.h
|
||||
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp globalshortcuts/globalshortcutbackend-kde.cpp
|
||||
HEADERS globalshortcuts/globalshortcutbackend-gsd.h globalshortcuts/globalshortcutbackend-kde.h
|
||||
)
|
||||
optional_source(WIN32
|
||||
SOURCES globalshortcuts/globalshortcut-win.cpp
|
||||
@@ -634,6 +636,14 @@ if(UNIX AND HAVE_DBUS)
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
|
||||
# org.kde.KGlobalAccel interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.xml
|
||||
dbus/kglobalaccel)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.Component.xml
|
||||
dbus/kglobalaccelcomponent)
|
||||
|
||||
else()
|
||||
|
||||
# MPRIS 2.0 DBUS interfaces
|
||||
@@ -662,6 +672,14 @@ if(UNIX AND HAVE_DBUS)
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
|
||||
# org.kde.KGlobalAccel interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.xml
|
||||
dbus/kglobalaccel)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.Component.xml
|
||||
dbus/kglobalaccelcomponent)
|
||||
|
||||
endif()
|
||||
|
||||
# org.freedesktop.Avahi.Server interface
|
||||
@@ -1033,7 +1051,6 @@ link_directories(
|
||||
${GNUTLS_LIBRARY_DIRS}
|
||||
${SQLITE_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
${QT_LIBRARY_DIRS}
|
||||
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
||||
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
|
||||
${QTSPARKLE_LIBRARY_DIRS}
|
||||
@@ -1108,7 +1125,6 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
|
||||
${GOBJECT_INCLUDE_DIRS}
|
||||
${GNUTLS_INCLUDE_DIRS}
|
||||
${SQLITE_INCLUDE_DIRS}
|
||||
${QT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(strawberry_lib PUBLIC
|
||||
|
||||
@@ -110,7 +110,7 @@ void SCollection::Init() {
|
||||
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
|
||||
connect(app_->player(), SIGNAL(Stopped()), SLOT(Stopped()));
|
||||
|
||||
connect(app_->lastfm_import(), SIGNAL(UpdateLastPlayed(QString, QString, QString, int)), backend_, SLOT(UpdateLastPlayed(QString, QString, QString, int)));
|
||||
connect(app_->lastfm_import(), SIGNAL(UpdateLastPlayed(QString, QString, QString, qint64)), backend_, SLOT(UpdateLastPlayed(QString, QString, QString, qint64)));
|
||||
connect(app_->lastfm_import(), SIGNAL(UpdatePlayCount(QString, QString, int)), backend_, SLOT(UpdatePlayCount(QString, QString, int)));
|
||||
|
||||
// This will start the watcher checking for updates
|
||||
|
||||
@@ -1368,7 +1368,7 @@ SongList CollectionBackend::GetSongsBy(const QString &artist, const QString &alb
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed) {
|
||||
void CollectionBackend::UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed) {
|
||||
|
||||
SongList songs = GetSongsBy(artist, album, title);
|
||||
if (songs.isEmpty()) {
|
||||
|
||||
@@ -213,7 +213,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||
|
||||
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
|
||||
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed);
|
||||
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
|
||||
void UpdatePlayCount(const QString &artist, const QString &title, const int playcount);
|
||||
|
||||
void UpdateSongRating(const int id, const float rating);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
* Copyright 2018-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
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
@@ -892,7 +893,11 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, const bool signal) {
|
||||
}
|
||||
|
||||
void CollectionModel::ResetAsync() {
|
||||
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(std::bind(&CollectionModel::RunQuery, this, root_));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(&CollectionModel::RunQuery, this, root_);
|
||||
#else
|
||||
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_);
|
||||
#endif
|
||||
NewClosure(future, this, SLOT(ResetAsyncQueryFinished(QFuture<CollectionModel::QueryResult>)), future);
|
||||
}
|
||||
|
||||
@@ -1566,8 +1571,9 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo
|
||||
// Create the divider entry if we're supposed to
|
||||
if (create_divider && show_dividers_) {
|
||||
QString divider_key = DividerKey(type, item);
|
||||
if (!divider_key.isEmpty() && (item->sort_text.isEmpty() || item->sort_text[0].toLower() != divider_key || divider_key[0].isDigit()))
|
||||
item->sort_text.prepend(divider_key);
|
||||
if (!divider_key.isEmpty()) {
|
||||
item->sort_text.prepend(divider_key + " ");
|
||||
}
|
||||
|
||||
if (!divider_key.isEmpty() && !divider_nodes_.contains(divider_key)) {
|
||||
if (signal)
|
||||
@@ -1576,7 +1582,7 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo
|
||||
CollectionItem *divider = new CollectionItem(CollectionItem::Type_Divider, root_);
|
||||
divider->key = divider_key;
|
||||
divider->display_text = DividerDisplayText(type, divider_key);
|
||||
divider->sort_text = divider_key + "0000";
|
||||
divider->sort_text = divider_key + " ";
|
||||
divider->lazy_loaded = true;
|
||||
|
||||
divider_nodes_[divider_key] = divider;
|
||||
@@ -1730,8 +1736,14 @@ bool CollectionModel::CompareItems(const CollectionItem *a, const CollectionItem
|
||||
QVariant left(data(a, CollectionModel::Role_SortText));
|
||||
QVariant right(data(b, CollectionModel::Role_SortText));
|
||||
|
||||
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
|
||||
return left.toString() < right.toString();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (left.metaType().id() == QMetaType::Int)
|
||||
#else
|
||||
if (left.type() == QVariant::Int)
|
||||
#endif
|
||||
return left.toInt() < right.toInt();
|
||||
else
|
||||
return left.toString() < right.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QMetaType>
|
||||
#include <QDateTime>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
@@ -133,10 +134,20 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
|
||||
}
|
||||
else {
|
||||
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (value.metaType().id() == QMetaType::Int) {
|
||||
#else
|
||||
if (value.type() == QVariant::Int) {
|
||||
#endif
|
||||
where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString());
|
||||
}
|
||||
else if (value.type() == QVariant::String && value.toString().isNull()) {
|
||||
else if (
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
value.metaType().id() == QMetaType::QString
|
||||
#else
|
||||
value.type() == QVariant::String
|
||||
#endif
|
||||
&& value.toString().isNull()) {
|
||||
where_clauses_ << QString("%1 %2 ?").arg(column, op);
|
||||
bound_values_ << QString("");
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
||||
scan_on_startup_(true),
|
||||
monitor_(true),
|
||||
mark_songs_unavailable_(false),
|
||||
live_scanning_(false),
|
||||
stop_requested_(false),
|
||||
rescan_in_progress_(false),
|
||||
rescan_timer_(new QTimer(this)),
|
||||
@@ -480,8 +479,6 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
||||
|
||||
t->AddToProgress(1);
|
||||
|
||||
if (live_scanning_) t->CommitNewOrUpdatedSongs();
|
||||
|
||||
// Recurse into the new subdirs that we found
|
||||
t->AddToProgressMax(my_new_subdirs.count());
|
||||
for (const Subdirectory &my_new_subdir : my_new_subdirs) {
|
||||
|
||||
@@ -195,7 +195,6 @@ class CollectionWatcher : public QObject {
|
||||
bool scan_on_startup_;
|
||||
bool monitor_;
|
||||
bool mark_songs_unavailable_;
|
||||
bool live_scanning_;
|
||||
|
||||
bool stop_requested_;
|
||||
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.
|
||||
|
||||
@@ -64,4 +64,8 @@
|
||||
#cmakedefine INSTALL_TRANSLATIONS
|
||||
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/translations"
|
||||
|
||||
#cmakedefine HAVE_X11EXTRAS
|
||||
#cmakedefine HAVE_WINEXTRAS
|
||||
#cmakedefine HAVE_QPA_QPLATFORMNATIVEINTERFACE_H
|
||||
|
||||
#endif // CONFIG_H_IN
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QMutex>
|
||||
#include <QMimeData>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
@@ -444,8 +445,13 @@ bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionI
|
||||
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
|
||||
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
|
||||
|
||||
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
|
||||
return left.toString() < right.toString();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (left.metaType().id() == QMetaType::Int)
|
||||
#else
|
||||
if (left.type() == QVariant::Int)
|
||||
#endif
|
||||
return left.toInt() < right.toInt();
|
||||
else return left.toString() < right.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ void ContextAlbumsView::EditTracks() {
|
||||
void ContextAlbumsView::CopyToDevice() {
|
||||
#ifndef Q_OS_WIN
|
||||
if (!organize_dialog_)
|
||||
organize_dialog_.reset(new OrganizeDialog(app_->task_manager()));
|
||||
organize_dialog_.reset(new OrganizeDialog(app_->task_manager(), nullptr, this));
|
||||
|
||||
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
||||
organize_dialog_->SetCopy(true);
|
||||
|
||||
@@ -151,6 +151,7 @@ ContextView::ContextView(QWidget *parent) :
|
||||
label_top_->setWordWrap(true);
|
||||
label_top_->setMinimumHeight(50);
|
||||
label_top_->setContentsMargins(0, 0, 32, 0);
|
||||
label_top_->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
layout_scrollarea_->setObjectName("context-layout-scrollarea");
|
||||
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
|
||||
@@ -485,7 +486,7 @@ void ContextView::UpdateFonts() {
|
||||
void ContextView::SetSong() {
|
||||
|
||||
label_top_->setStyleSheet(QString("font: %2pt \"%1\"; font-weight: regular;").arg(font_headline_).arg(font_size_headline_));
|
||||
label_top_->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song_playing_, "<br/>")));
|
||||
label_top_->setText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song_playing_, "<br />", true)));
|
||||
|
||||
label_stop_summary_->clear();
|
||||
|
||||
@@ -643,7 +644,7 @@ void ContextView::SetSong() {
|
||||
|
||||
void ContextView::UpdateSong(const Song &song) {
|
||||
|
||||
label_top_->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song, "<br/>")));
|
||||
label_top_->setText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song, "<br />", true)));
|
||||
|
||||
if (action_show_data_->isChecked()) {
|
||||
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());
|
||||
|
||||
@@ -44,36 +44,37 @@ const char *CommandlineOptions::kHelpText =
|
||||
"%1: strawberry [%2] [%3]\n"
|
||||
"\n"
|
||||
"%4:\n"
|
||||
" -p, --play %5\n"
|
||||
" -t, --play-pause %6\n"
|
||||
" -u, --pause %7\n"
|
||||
" -s, --stop %8\n"
|
||||
" -q, --stop-after-current %9\n"
|
||||
" -r, --previous %10\n"
|
||||
" -f, --next %11\n"
|
||||
" -v, --volume <value> %12\n"
|
||||
" --volume-up %13\n"
|
||||
" --volume-down %14\n"
|
||||
" --volume-increase-by %15\n"
|
||||
" --volume-decrease-by %16\n"
|
||||
" --seek-to <seconds> %17\n"
|
||||
" --seek-by <seconds> %18\n"
|
||||
" --restart-or-previous %19\n"
|
||||
" -p, --play %5\n"
|
||||
" -t, --play-pause %6\n"
|
||||
" -u, --pause %7\n"
|
||||
" -s, --stop %8\n"
|
||||
" -q, --stop-after-current %9\n"
|
||||
" -r, --previous %10\n"
|
||||
" -f, --next %11\n"
|
||||
" -v, --volume <value> %12\n"
|
||||
" --volume-up %13\n"
|
||||
" --volume-down %14\n"
|
||||
" --volume-increase-by %15\n"
|
||||
" --volume-decrease-by %16\n"
|
||||
" --seek-to <seconds> %17\n"
|
||||
" --seek-by <seconds> %18\n"
|
||||
" --restart-or-previous %19\n"
|
||||
"\n"
|
||||
"%20:\n"
|
||||
" -c, --create <name> %21\n"
|
||||
" -a, --append %22\n"
|
||||
" -l, --load %23\n"
|
||||
" -k, --play-track <n> %24\n"
|
||||
" -c, --create <name> %21\n"
|
||||
" -a, --append %22\n"
|
||||
" -l, --load %23\n"
|
||||
" -k, --play-track <n> %24\n"
|
||||
" -i, --play-playlist <name> %25\n"
|
||||
"\n"
|
||||
"%25:\n"
|
||||
" -o, --show-osd %26\n"
|
||||
" -y, --toggle-pretty-osd %27\n"
|
||||
" -g, --language <lang> %28\n"
|
||||
" --quiet %29\n"
|
||||
" --verbose %30\n"
|
||||
" --log-levels <levels> %31\n"
|
||||
" --version %32\n";
|
||||
"%26:\n"
|
||||
" -o, --show-osd %27\n"
|
||||
" -y, --toggle-pretty-osd %28\n"
|
||||
" -g, --language <lang> %29\n"
|
||||
" --quiet %30\n"
|
||||
" --verbose %31\n"
|
||||
" --log-levels <levels> %32\n"
|
||||
" --version %33\n";
|
||||
|
||||
const char *CommandlineOptions::kVersionText = "Strawberry %1";
|
||||
|
||||
@@ -138,6 +139,7 @@ bool CommandlineOptions::Parse() {
|
||||
{"append", no_argument, nullptr, 'a'},
|
||||
{"load", no_argument, nullptr, 'l'},
|
||||
{"play-track", required_argument, nullptr, 'k'},
|
||||
{"play-playlist", required_argument, nullptr, 'i'},
|
||||
{"show-osd", no_argument, nullptr, 'o'},
|
||||
{"toggle-pretty-osd", no_argument, nullptr, 'y'},
|
||||
{"language", required_argument, nullptr, 'g'},
|
||||
@@ -150,7 +152,7 @@ bool CommandlineOptions::Parse() {
|
||||
// Parse the arguments
|
||||
bool ok = false;
|
||||
forever {
|
||||
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:oyg:", kOptions, nullptr);
|
||||
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:i:oyg:", kOptions, nullptr);
|
||||
|
||||
// End of the options
|
||||
if (c == -1) break;
|
||||
@@ -179,7 +181,8 @@ bool CommandlineOptions::Parse() {
|
||||
tr("Create a new playlist with files"),
|
||||
tr("Append files/URLs to the playlist"),
|
||||
tr("Loads files/URLs, replacing current playlist"),
|
||||
tr("Play the <n>th track in the playlist"))
|
||||
tr("Play the <n>th track in the playlist"),
|
||||
tr("Play given playlist"))
|
||||
.arg(tr("Other options"), tr("Display the on-screen-display"),
|
||||
tr("Toggle visibility for the pretty on-screen-display"),
|
||||
tr("Change the language"),
|
||||
@@ -213,6 +216,10 @@ bool CommandlineOptions::Parse() {
|
||||
case 'f':
|
||||
player_action_ = Player_Next;
|
||||
break;
|
||||
case 'i':
|
||||
player_action_ = Player_PlayPlaylist;
|
||||
playlist_name_ = QString(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
url_list_action_ = UrlList_CreateNew;
|
||||
playlist_name_ = QString(optarg);
|
||||
@@ -362,7 +369,8 @@ QDataStream& operator<<(QDataStream &s, const CommandlineOptions &a) {
|
||||
<< a.show_osd_
|
||||
<< a.urls_
|
||||
<< a.log_levels_
|
||||
<< a.toggle_pretty_osd_;
|
||||
<< a.toggle_pretty_osd_
|
||||
<< a.playlist_name_;
|
||||
|
||||
return s;
|
||||
|
||||
@@ -382,7 +390,8 @@ QDataStream& operator>>(QDataStream &s, CommandlineOptions &a) {
|
||||
>> a.show_osd_
|
||||
>> a.urls_
|
||||
>> a.log_levels_
|
||||
>> a.toggle_pretty_osd_;
|
||||
>> a.toggle_pretty_osd_
|
||||
>> a.playlist_name_;
|
||||
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
|
||||
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ class CommandlineOptions {
|
||||
Player_Next = 6,
|
||||
Player_RestartOrPrevious = 7,
|
||||
Player_StopAfterCurrent = 8,
|
||||
Player_PlayPlaylist = 9,
|
||||
};
|
||||
|
||||
bool Parse();
|
||||
|
||||
@@ -132,6 +132,7 @@ QSqlDatabase Database::Connect() {
|
||||
if (db.isOpen()) {
|
||||
return db;
|
||||
}
|
||||
db.setConnectOptions("QSQLITE_BUSY_TIMEOUT=30000");
|
||||
//qLog(Debug) << "Opened database with connection id" << connection_id;
|
||||
|
||||
if (!injected_database_name_.isNull())
|
||||
@@ -518,11 +519,13 @@ void Database::DoBackup() {
|
||||
|
||||
QSqlDatabase db(this->Connect());
|
||||
|
||||
if (!db.isOpen()) return;
|
||||
|
||||
// Before we overwrite anything, make sure the database is not corrupt
|
||||
QMutexLocker l(&mutex_);
|
||||
const bool ok = IntegrityCheck(db);
|
||||
|
||||
if (ok) {
|
||||
const bool ok = IntegrityCheck(db);
|
||||
if (ok && SchemaVersion(&db) == kSchemaVersion) {
|
||||
BackupFile(db.databaseName());
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ protected:
|
||||
|
||||
private:
|
||||
QPixmap normal_icon_;
|
||||
QPixmap grey_icon_;
|
||||
std::unique_ptr<MacSystemTrayIconPrivate> p_;
|
||||
Q_DISABLE_COPY(MacSystemTrayIcon);
|
||||
};
|
||||
|
||||
@@ -165,7 +165,8 @@ class MacSystemTrayIconPrivate {
|
||||
|
||||
MacSystemTrayIcon::MacSystemTrayIcon(QObject* parent)
|
||||
: SystemTrayIcon(parent),
|
||||
normal_icon_(QPixmap(":/pictures/strawberry.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)) {
|
||||
normal_icon_(QPixmap(":/pictures/strawberry.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)),
|
||||
grey_icon_(QPixmap(":/pictures/strawberry-grey.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)) {
|
||||
QApplication::setWindowIcon(normal_icon_);
|
||||
}
|
||||
|
||||
|
||||
@@ -1023,6 +1023,7 @@ void MainWindow::ReloadSettings() {
|
||||
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
||||
keep_running_ = s.value("keeprunning", false).toBool();
|
||||
playing_widget_ = s.value("playing_widget", true).toBool();
|
||||
bool trayicon_progress = s.value("trayicon_progress", false).toBool();
|
||||
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
|
||||
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(s.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
|
||||
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_Never).toInt());
|
||||
@@ -1034,6 +1035,8 @@ void MainWindow::ReloadSettings() {
|
||||
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt();
|
||||
s.endGroup();
|
||||
|
||||
if (tray_icon_) tray_icon_->SetTrayiconProgress(trayicon_progress);
|
||||
|
||||
ui_->back_button->setIconSize(QSize(iconsize, iconsize));
|
||||
ui_->pause_play_button->setIconSize(QSize(iconsize, iconsize));
|
||||
ui_->stop_button->setIconSize(QSize(iconsize, iconsize));
|
||||
@@ -1461,21 +1464,22 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &idx) {
|
||||
|
||||
if (!idx.isValid()) return;
|
||||
|
||||
int row = idx.row();
|
||||
QModelIndex source_idx = idx;
|
||||
if (idx.model() == app_->playlist_manager()->current()->proxy()) {
|
||||
// The index was in the proxy model (might've been filtered), so we need to get the actual row in the source model.
|
||||
row = app_->playlist_manager()->current()->proxy()->mapToSource(idx).row();
|
||||
source_idx = app_->playlist_manager()->current()->proxy()->mapToSource(idx);
|
||||
}
|
||||
|
||||
switch (doubleclick_playlist_addmode_) {
|
||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Play:
|
||||
app_->playlist_manager()->SetActiveToCurrent();
|
||||
app_->player()->PlayAt(row, Engine::Manual, Playlist::AutoScroll_Never, true, true);
|
||||
app_->player()->PlayAt(source_idx.row(), Engine::Manual, Playlist::AutoScroll_Never, true, true);
|
||||
break;
|
||||
|
||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Enqueue:
|
||||
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << idx);
|
||||
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx);
|
||||
if (app_->player()->GetState() != Engine::Playing) {
|
||||
app_->playlist_manager()->SetActiveToCurrent();
|
||||
app_->player()->PlayAt(app_->playlist_manager()->current()->queue()->TakeNext(), Engine::Manual, Playlist::AutoScroll_Never, true);
|
||||
}
|
||||
break;
|
||||
@@ -1530,19 +1534,25 @@ void MainWindow::showEvent(QShowEvent *e) {
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
Exit();
|
||||
#else
|
||||
if (!hidden_ && keep_running_ && e->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
e->ignore();
|
||||
SetHiddenInTray(true);
|
||||
}
|
||||
else {
|
||||
Exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
QMainWindow::closeEvent(e);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::SetHiddenInTray(const bool hidden) {
|
||||
|
||||
hidden_ = hidden;
|
||||
settings_.setValue("hidden", hidden_);
|
||||
|
||||
// Some window managers don't remember maximized state between calls to hide() and show(), so we have to remember it ourself.
|
||||
if (hidden) {
|
||||
@@ -2265,6 +2275,14 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
|
||||
case CommandlineOptions::Player_Next:
|
||||
app_->player()->Next();
|
||||
break;
|
||||
case CommandlineOptions::Player_PlayPlaylist:
|
||||
if (options.playlist_name().isEmpty()) {
|
||||
qLog(Error) << "ERROR: playlist name missing";
|
||||
}
|
||||
else {
|
||||
app_->player()->PlayPlaylist(options.playlist_name());
|
||||
}
|
||||
break;
|
||||
case CommandlineOptions::Player_RestartOrPrevious:
|
||||
app_->player()->RestartOrPrevious();
|
||||
break;
|
||||
@@ -2531,9 +2549,9 @@ void MainWindow::PlaylistCopyUrl() {
|
||||
}
|
||||
|
||||
if (urls.count() > 0) {
|
||||
QMimeData *mime_data = new QMimeData;
|
||||
mime_data->setUrls(urls);
|
||||
QApplication::clipboard()->setMimeData(mime_data);
|
||||
QMimeData mime_data;
|
||||
mime_data.setUrls(urls);
|
||||
QApplication::clipboard()->setText(mime_data.text());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2743,25 +2761,21 @@ void MainWindow::Raise() {
|
||||
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
|
||||
#else
|
||||
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) {
|
||||
#endif
|
||||
|
||||
Q_UNUSED(eventType);
|
||||
Q_UNUSED(result);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
MSG *msg = static_cast<MSG*>(message);
|
||||
thumbbar_->HandleWinEvent(msg);
|
||||
#else
|
||||
Q_UNUSED(message);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
if (exit_count_ == 0 && message) {
|
||||
MSG *msg = static_cast<MSG*>(message);
|
||||
thumbbar_->HandleWinEvent(msg);
|
||||
}
|
||||
return QMainWindow::nativeEvent(eventType, message, result);
|
||||
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
void MainWindow::AutoCompleteTags() {
|
||||
|
||||
@@ -2958,7 +2972,7 @@ void MainWindow::SetToggleScrobblingIcon(const bool value) {
|
||||
if (value) {
|
||||
if (app_->playlist_manager()->active() && app_->playlist_manager()->active()->scrobbled())
|
||||
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22));
|
||||
else
|
||||
else
|
||||
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22)); // TODO: Create a faint version of the icon
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -117,10 +117,12 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
void showEvent(QShowEvent *e) override;
|
||||
void closeEvent(QCloseEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
#else
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// PlatformInterface
|
||||
|
||||
@@ -69,6 +69,20 @@ int MultiSortFilterProxy::Compare(const QVariant &left, const QVariant &right) c
|
||||
|
||||
// Copied from the QSortFilterProxyModel::lessThan implementation, but returns -1, 0 or 1 instead of true or false.
|
||||
switch (left.userType()) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
case QMetaType::UnknownType: return (right.metaType().id() != QMetaType::UnknownType) ? -1 : 0;
|
||||
case QMetaType::Int: return DoCompare(left.toInt(), right.toInt());
|
||||
case QMetaType::UInt: return DoCompare(left.toUInt(), right.toUInt());
|
||||
case QMetaType::LongLong: return DoCompare(left.toLongLong(), right.toLongLong());
|
||||
case QMetaType::ULongLong: return DoCompare(left.toULongLong(), right.toULongLong());
|
||||
case QMetaType::Float: return DoCompare(left.toFloat(), right.toFloat());
|
||||
case QMetaType::Double: return DoCompare(left.toDouble(), right.toDouble());
|
||||
case QMetaType::Char: return DoCompare(left.toChar(), right.toChar());
|
||||
case QMetaType::QDate: return DoCompare(left.toDate(), right.toDate());
|
||||
case QMetaType::QTime: return DoCompare(left.toTime(), right.toTime());
|
||||
case QMetaType::QDateTime: return DoCompare(left.toDateTime(), right.toDateTime());
|
||||
case QMetaType::QString:
|
||||
#else
|
||||
case QVariant::Invalid: return (right.type() != QVariant::Invalid) ? -1 : 0;
|
||||
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
|
||||
case QVariant::UInt: return DoCompare(left.toUInt(), right.toUInt());
|
||||
@@ -81,6 +95,7 @@ int MultiSortFilterProxy::Compare(const QVariant &left, const QVariant &right) c
|
||||
case QVariant::Time: return DoCompare(left.toTime(), right.toTime());
|
||||
case QVariant::DateTime: return DoCompare(left.toDateTime(), right.toDateTime());
|
||||
case QVariant::String:
|
||||
#endif
|
||||
default:
|
||||
if (isSortLocaleAware())
|
||||
return left.toString().localeAwareCompare(right.toString());
|
||||
|
||||
@@ -21,89 +21,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QNetworkCacheMetaData>
|
||||
#include <QAbstractNetworkCache>
|
||||
|
||||
#include "network.h"
|
||||
|
||||
QMutex ThreadSafeNetworkDiskCache::sMutex;
|
||||
ThreadSafeNetworkDiskCache *ThreadSafeNetworkDiskCache::sInstance = nullptr;
|
||||
QNetworkDiskCache *ThreadSafeNetworkDiskCache::sCache = nullptr;
|
||||
|
||||
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstractNetworkCache(parent) {
|
||||
|
||||
QMutexLocker l(&sMutex);
|
||||
if (!sCache) {
|
||||
sInstance = this;
|
||||
sCache = new QNetworkDiskCache;
|
||||
#ifdef Q_OS_WIN32
|
||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/strawberry/networkcache");
|
||||
#else
|
||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/networkcache");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThreadSafeNetworkDiskCache::~ThreadSafeNetworkDiskCache() {
|
||||
if (this == sInstance) delete sCache;
|
||||
}
|
||||
|
||||
qint64 ThreadSafeNetworkDiskCache::cacheSize() const {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->cacheSize();
|
||||
}
|
||||
|
||||
QIODevice *ThreadSafeNetworkDiskCache::data(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->data(url);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::insert(QIODevice *device) {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->insert(device);
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData ThreadSafeNetworkDiskCache::metaData(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->metaData(url);
|
||||
}
|
||||
|
||||
QIODevice *ThreadSafeNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->prepare(metaData);
|
||||
}
|
||||
|
||||
bool ThreadSafeNetworkDiskCache::remove(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->remove(url);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData) {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->updateMetaData(metaData);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::clear() {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->clear();
|
||||
}
|
||||
#include "threadsafenetworkdiskcache.h"
|
||||
|
||||
NetworkAccessManager::NetworkAccessManager(QObject *parent)
|
||||
: QNetworkAccessManager(parent) {
|
||||
@@ -144,4 +73,5 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
|
||||
}
|
||||
|
||||
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
||||
|
||||
}
|
||||
|
||||
@@ -27,15 +27,10 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QAbstractNetworkCache>
|
||||
#include <QMutex>
|
||||
#include <QUrl>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkCacheMetaData>
|
||||
|
||||
class QIODevice;
|
||||
class QNetworkReply;
|
||||
class QNetworkDiskCache;
|
||||
|
||||
class NetworkAccessManager : public QNetworkAccessManager {
|
||||
Q_OBJECT
|
||||
@@ -47,25 +42,4 @@ class NetworkAccessManager : public QNetworkAccessManager {
|
||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override;
|
||||
};
|
||||
|
||||
class ThreadSafeNetworkDiskCache : public QAbstractNetworkCache {
|
||||
public:
|
||||
explicit ThreadSafeNetworkDiskCache(QObject *parent);
|
||||
~ThreadSafeNetworkDiskCache() override;
|
||||
|
||||
qint64 cacheSize() const override;
|
||||
QIODevice *data(const QUrl &url) override;
|
||||
void insert(QIODevice *device) override;
|
||||
QNetworkCacheMetaData metaData(const QUrl &url) override;
|
||||
QIODevice *prepare(const QNetworkCacheMetaData &metaData) override;
|
||||
bool remove(const QUrl &url) override;
|
||||
void updateMetaData(const QNetworkCacheMetaData &metaData) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
private:
|
||||
static QMutex sMutex;
|
||||
static ThreadSafeNetworkDiskCache *sInstance;
|
||||
static QNetworkDiskCache *sCache;
|
||||
};
|
||||
|
||||
#endif // NETWORK_H
|
||||
|
||||
@@ -398,6 +398,36 @@ void Player::NextItem(const Engine::TrackChangeFlags change, const Playlist::Aut
|
||||
|
||||
}
|
||||
|
||||
void Player::PlayPlaylist(const QString &playlist_name) {
|
||||
PlayPlaylistInternal(Engine::Manual, Playlist::AutoScroll_Always, playlist_name);
|
||||
}
|
||||
|
||||
void Player::PlayPlaylistInternal(Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const QString &playlist_name) {
|
||||
Playlist *playlist = nullptr;
|
||||
for (Playlist *p : app_->playlist_manager()->GetAllPlaylists()) {
|
||||
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
|
||||
playlist = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (playlist == nullptr) {
|
||||
qLog(Warning) << "Playlist '" << playlist_name << "' not found.";
|
||||
return;
|
||||
}
|
||||
|
||||
app_->playlist_manager()->SetActivePlaylist(playlist->id());
|
||||
app_->playlist_manager()->SetCurrentPlaylist(playlist->id());
|
||||
if (playlist->rowCount() == 0) return;
|
||||
|
||||
int i = app_->playlist_manager()->active()->current_row();
|
||||
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
|
||||
if (i == -1) i = 0;
|
||||
|
||||
PlayAt(i, change, autoscroll, true);
|
||||
}
|
||||
|
||||
|
||||
bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
|
||||
|
||||
if (app_->playlist_manager()->active()->stop_after_current()) {
|
||||
|
||||
@@ -81,8 +81,8 @@ class PlayerInterface : public QObject {
|
||||
|
||||
// Skips this track. Might load more of the current radio station.
|
||||
virtual void Next() = 0;
|
||||
|
||||
virtual void Previous() = 0;
|
||||
virtual void PlayPlaylist(const QString &playlist_name) = 0;
|
||||
virtual void SetVolume(const int value) = 0;
|
||||
virtual void VolumeUp() = 0;
|
||||
virtual void VolumeDown() = 0;
|
||||
@@ -163,6 +163,7 @@ class Player : public PlayerInterface {
|
||||
void RestartOrPrevious() override;
|
||||
void Next() override;
|
||||
void Previous() override;
|
||||
void PlayPlaylist(const QString &playlist_name) override;
|
||||
void SetVolume(const int value) override;
|
||||
void VolumeUp() override { SetVolume(GetVolume() + 5); }
|
||||
void VolumeDown() override { SetVolume(GetVolume() - 5); }
|
||||
@@ -195,6 +196,7 @@ class Player : public PlayerInterface {
|
||||
void PreviousItem(const Engine::TrackChangeFlags change);
|
||||
|
||||
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
|
||||
void PlayPlaylistInternal(Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
||||
|
||||
void FatalError();
|
||||
void ValidSongRequested(const QUrl&);
|
||||
|
||||
@@ -54,7 +54,10 @@ QtSystemTrayIcon::QtSystemTrayIcon(QObject *parent)
|
||||
action_mute_(nullptr) {
|
||||
|
||||
app_name_[0] = app_name_[0].toUpper();
|
||||
|
||||
QIcon theme_icon_grey = IconLoader::Load("strawberry-grey");
|
||||
if (!theme_icon_grey.isNull()) {
|
||||
grey_icon_ = theme_icon_grey.pixmap(48, QIcon::Disabled);
|
||||
}
|
||||
tray_->setIcon(normal_icon_);
|
||||
tray_->installEventFilter(this);
|
||||
ClearNowPlaying();
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QIcon>
|
||||
#include <QTextCodec>
|
||||
#include <QSqlQuery>
|
||||
#include <QStandardPaths>
|
||||
#include <QtDebug>
|
||||
@@ -745,14 +744,6 @@ void Song::set_genre_id3(int id) {
|
||||
set_genre(TStringToQString(TagLib::ID3v1::genre(id)));
|
||||
}
|
||||
|
||||
QString Song::Decode(const QString &tag, const QTextCodec *codec) {
|
||||
if (!codec) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
return codec->toUnicode(tag.toUtf8());
|
||||
}
|
||||
|
||||
void Song::InitFromProtobuf(const pb::tagreader::SongMetadata &pb) {
|
||||
|
||||
if (d->source_ == Source_Unknown) d->source_ = Source_LocalFile;
|
||||
@@ -1436,6 +1427,14 @@ QString Song::PrettyYear() const {
|
||||
|
||||
}
|
||||
|
||||
QString Song::PrettyOriginalYear() const {
|
||||
|
||||
if (effective_originalyear() == -1) return QString();
|
||||
|
||||
return QString::number(effective_originalyear());
|
||||
|
||||
}
|
||||
|
||||
QString Song::TitleWithCompilationArtist() const {
|
||||
|
||||
QString title(d->title_);
|
||||
@@ -1509,7 +1508,11 @@ bool Song::operator!=(const Song &other) const {
|
||||
return source() != other.source() || url() != other.url() || beginning_nanosec() != other.beginning_nanosec();
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
size_t qHash(const Song &song) {
|
||||
#else
|
||||
uint qHash(const Song &song) {
|
||||
#endif
|
||||
// Should compare the same fields as operator==
|
||||
return qHash(song.url().toString()) ^ qHash(song.beginning_nanosec());
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <QImage>
|
||||
#include <QIcon>
|
||||
|
||||
class QTextCodec;
|
||||
class QSqlQuery;
|
||||
|
||||
namespace Engine {
|
||||
@@ -177,8 +176,6 @@ class Song {
|
||||
// Useful when you want updated tags from disk but you want to keep user stats.
|
||||
void MergeUserSetData(const Song &other);
|
||||
|
||||
static QString Decode(const QString &tag, const QTextCodec *codec = nullptr);
|
||||
|
||||
// Save
|
||||
void BindToQuery(QSqlQuery *query) const;
|
||||
void BindToFtsQuery(QSqlQuery *query) const;
|
||||
@@ -285,6 +282,7 @@ class Song {
|
||||
QString PrettyTitleWithArtist() const;
|
||||
QString PrettyLength() const;
|
||||
QString PrettyYear() const;
|
||||
QString PrettyOriginalYear() const;
|
||||
|
||||
QString TitleWithCompilationArtist() const;
|
||||
|
||||
@@ -381,7 +379,11 @@ Q_DECLARE_METATYPE(Song)
|
||||
typedef QList<Song> SongList;
|
||||
Q_DECLARE_METATYPE(QList<Song>)
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
size_t qHash(const Song &song);
|
||||
#else
|
||||
uint qHash(const Song &song);
|
||||
#endif
|
||||
// Hash function using field checked in IsSimilar function
|
||||
uint HashSimilar(const Song &song);
|
||||
|
||||
|
||||
@@ -439,6 +439,7 @@ SongLoader::Result SongLoader::LoadRemote() {
|
||||
errors_ << tr("Couldn't create gstreamer source element for %1").arg(url_.toString());
|
||||
return Error;
|
||||
}
|
||||
g_object_set(source, "ssl-strict", FALSE, nullptr);
|
||||
|
||||
// Create the other elements and link them up
|
||||
GstElement *typefind = gst_element_factory_make("typefind", nullptr);
|
||||
|
||||
@@ -47,31 +47,31 @@ SystemTrayIcon::SystemTrayIcon(QObject *parent)
|
||||
|
||||
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &icon, const QPixmap &grey_icon) {
|
||||
|
||||
Q_UNUSED(grey_icon);
|
||||
|
||||
QRect rect(icon.rect());
|
||||
|
||||
// The angle of the line that's used to cover the icon.
|
||||
// Centered on rect.topRight()
|
||||
double angle = double(100 - song_progress()) / 100.0 * M_PI_2 + M_PI;
|
||||
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
|
||||
|
||||
QPolygon mask;
|
||||
mask << rect.topRight();
|
||||
mask << rect.topRight() + QPoint(length * sin(angle), -length * cos(angle));
|
||||
|
||||
if (song_progress() > 50) mask << rect.bottomLeft();
|
||||
|
||||
mask << rect.topLeft();
|
||||
mask << rect.topRight();
|
||||
|
||||
QPixmap ret(icon);
|
||||
QPainter p(&ret);
|
||||
|
||||
// Draw the grey bit
|
||||
//p.setClipRegion(mask);
|
||||
//p.drawPixmap(0, 0, grey_icon);
|
||||
//p.setClipping(false);
|
||||
if (trayicon_progress_) {
|
||||
// The angle of the line that's used to cover the icon.
|
||||
// Centered on rect.topLeft()
|
||||
double angle = double(100 - song_progress()) / 100.0 * M_PI_2;
|
||||
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
|
||||
|
||||
QPolygon mask;
|
||||
mask << rect.topLeft();
|
||||
mask << rect.topLeft() + QPoint(length * sin(angle), length * cos(angle));
|
||||
|
||||
if (song_progress() > 50) mask << rect.bottomRight();
|
||||
|
||||
mask << rect.topRight();
|
||||
mask << rect.topLeft();
|
||||
|
||||
// Draw the grey bit
|
||||
p.setClipRegion(mask);
|
||||
p.drawPixmap(0, 0, grey_icon);
|
||||
p.setClipping(false);
|
||||
}
|
||||
|
||||
// Draw the playing or paused icon in the top-right
|
||||
if (!current_state_icon().isNull()) {
|
||||
|
||||
@@ -57,6 +57,7 @@ class SystemTrayIcon : public QObject {
|
||||
|
||||
public slots:
|
||||
void SetProgress(int percentage);
|
||||
void SetTrayiconProgress(bool enabled) { trayicon_progress_ = enabled; }
|
||||
virtual void SetPaused();
|
||||
virtual void SetPlaying(bool enable_play_pause = false);
|
||||
virtual void SetStopped();
|
||||
@@ -85,6 +86,7 @@ class SystemTrayIcon : public QObject {
|
||||
QPixmap playing_icon_;
|
||||
QPixmap paused_icon_;
|
||||
QPixmap current_state_icon_;
|
||||
bool trayicon_progress_;
|
||||
};
|
||||
|
||||
#endif // SYSTEMTRAYICON_H
|
||||
|
||||
@@ -61,7 +61,7 @@ QList<TaskManager::Task> TaskManager::GetTasks() {
|
||||
|
||||
}
|
||||
|
||||
void TaskManager::SetTaskBlocksCollectionScans(int id) {
|
||||
void TaskManager::SetTaskBlocksCollectionScans(const int id) {
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
@@ -76,7 +76,7 @@ void TaskManager::SetTaskBlocksCollectionScans(int id) {
|
||||
|
||||
}
|
||||
|
||||
void TaskManager::SetTaskProgress(int id, int progress, int max) {
|
||||
void TaskManager::SetTaskProgress(const int id, const qint64 progress, const qint64 max) {
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
@@ -90,7 +90,7 @@ void TaskManager::SetTaskProgress(int id, int progress, int max) {
|
||||
emit TasksChanged();
|
||||
}
|
||||
|
||||
void TaskManager::IncreaseTaskProgress(int id, int progress, int max) {
|
||||
void TaskManager::IncreaseTaskProgress(const int id, const qint64 progress, const qint64 max) {
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
@@ -105,7 +105,7 @@ void TaskManager::IncreaseTaskProgress(int id, int progress, int max) {
|
||||
|
||||
}
|
||||
|
||||
void TaskManager::SetTaskFinished(int id) {
|
||||
void TaskManager::SetTaskFinished(const int id) {
|
||||
|
||||
bool resume_collection_watchers = false;
|
||||
|
||||
|
||||
@@ -37,10 +37,11 @@ class TaskManager : public QObject {
|
||||
explicit TaskManager(QObject *parent = nullptr);
|
||||
|
||||
struct Task {
|
||||
Task() : id(0), progress(0), progress_max(0), blocks_collection_scans(false) {}
|
||||
int id;
|
||||
QString name;
|
||||
int progress;
|
||||
int progress_max;
|
||||
qint64 progress;
|
||||
qint64 progress_max;
|
||||
bool blocks_collection_scans;
|
||||
};
|
||||
|
||||
@@ -61,13 +62,13 @@ class TaskManager : public QObject {
|
||||
QList<Task> GetTasks();
|
||||
|
||||
int StartTask(const QString &name);
|
||||
void SetTaskBlocksCollectionScans(int id);
|
||||
void SetTaskProgress(int id, int progress, int max = 0);
|
||||
void IncreaseTaskProgress(int id, int progress, int max = 0);
|
||||
void SetTaskFinished(int id);
|
||||
int GetTaskProgress(int id);
|
||||
void SetTaskBlocksCollectionScans(const int id);
|
||||
void SetTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
|
||||
void IncreaseTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
|
||||
void SetTaskFinished(const int id);
|
||||
int GetTaskProgress(const int id);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void TasksChanged();
|
||||
|
||||
void PauseCollectionWatchers();
|
||||
|
||||
108
src/core/threadsafenetworkdiskcache.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, 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 <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QNetworkCacheMetaData>
|
||||
#include <QAbstractNetworkCache>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "threadsafenetworkdiskcache.h"
|
||||
|
||||
QMutex ThreadSafeNetworkDiskCache::sMutex;
|
||||
int ThreadSafeNetworkDiskCache::sInstances = 0;
|
||||
QNetworkDiskCache *ThreadSafeNetworkDiskCache::sCache = nullptr;
|
||||
|
||||
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstractNetworkCache(parent) {
|
||||
|
||||
QMutexLocker l(&sMutex);
|
||||
++sInstances;
|
||||
|
||||
if (!sCache) {
|
||||
sCache = new QNetworkDiskCache;
|
||||
#ifdef Q_OS_WIN32
|
||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/strawberry/networkcache");
|
||||
#else
|
||||
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/networkcache");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThreadSafeNetworkDiskCache::~ThreadSafeNetworkDiskCache() {
|
||||
|
||||
QMutexLocker l(&sMutex);
|
||||
--sInstances;
|
||||
|
||||
if (sCache && sInstances == 0) {
|
||||
sCache->deleteLater();
|
||||
sCache = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
qint64 ThreadSafeNetworkDiskCache::cacheSize() const {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->cacheSize();
|
||||
}
|
||||
|
||||
QIODevice *ThreadSafeNetworkDiskCache::data(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->data(url);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::insert(QIODevice *device) {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->insert(device);
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData ThreadSafeNetworkDiskCache::metaData(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->metaData(url);
|
||||
}
|
||||
|
||||
QIODevice *ThreadSafeNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->prepare(metaData);
|
||||
}
|
||||
|
||||
bool ThreadSafeNetworkDiskCache::remove(const QUrl &url) {
|
||||
QMutexLocker l(&sMutex);
|
||||
return sCache->remove(url);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData) {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->updateMetaData(metaData);
|
||||
}
|
||||
|
||||
void ThreadSafeNetworkDiskCache::clear() {
|
||||
QMutexLocker l(&sMutex);
|
||||
sCache->clear();
|
||||
}
|
||||
61
src/core/threadsafenetworkdiskcache.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, 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 THREADSAFENETWORKDISKCACHE_H
|
||||
#define THREADSAFENETWORKDISKCACHE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QAbstractNetworkCache>
|
||||
#include <QMutex>
|
||||
#include <QUrl>
|
||||
#include <QNetworkCacheMetaData>
|
||||
|
||||
class QIODevice;
|
||||
class QNetworkDiskCache;
|
||||
|
||||
class ThreadSafeNetworkDiskCache : public QAbstractNetworkCache {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ThreadSafeNetworkDiskCache(QObject *parent);
|
||||
~ThreadSafeNetworkDiskCache() override;
|
||||
|
||||
qint64 cacheSize() const override;
|
||||
QIODevice *data(const QUrl &url) override;
|
||||
void insert(QIODevice *device) override;
|
||||
QNetworkCacheMetaData metaData(const QUrl &url) override;
|
||||
QIODevice *prepare(const QNetworkCacheMetaData &metaData) override;
|
||||
bool remove(const QUrl &url) override;
|
||||
void updateMetaData(const QNetworkCacheMetaData &metaData) override;
|
||||
|
||||
public slots:
|
||||
void clear() override;
|
||||
|
||||
private:
|
||||
static QMutex sMutex;
|
||||
static int sInstances;
|
||||
static QNetworkDiskCache *sCache;
|
||||
};
|
||||
|
||||
#endif // THREADSAFENETWORKDISKCACHE_H
|
||||
@@ -215,7 +215,12 @@ quint64 FileSystemCapacity(const QString &path) {
|
||||
return quint64(fs_info.f_blocks) * quint64(fs_info.f_bsize);
|
||||
#elif defined(Q_OS_WIN32)
|
||||
_ULARGE_INTEGER ret;
|
||||
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), nullptr,&ret, nullptr) != 0)
|
||||
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
ScopedWCharArray wchar(QDir::toNativeSeparators(path));
|
||||
if (GetDiskFreeSpaceEx(wchar.get(), nullptr, &ret, nullptr) != 0)
|
||||
# else
|
||||
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), nullptr, &ret, nullptr) != 0)
|
||||
# endif
|
||||
return ret.QuadPart;
|
||||
#endif
|
||||
|
||||
@@ -231,7 +236,12 @@ quint64 FileSystemFreeSpace(const QString &path) {
|
||||
return quint64(fs_info.f_bavail) * quint64(fs_info.f_bsize);
|
||||
#elif defined(Q_OS_WIN32)
|
||||
_ULARGE_INTEGER ret;
|
||||
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
ScopedWCharArray wchar(QDir::toNativeSeparators(path));
|
||||
if (GetDiskFreeSpaceEx(wchar.get(), &ret, nullptr, nullptr) != 0)
|
||||
# else
|
||||
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), &ret, nullptr, nullptr) != 0)
|
||||
# endif
|
||||
return ret.QuadPart;
|
||||
#endif
|
||||
|
||||
@@ -924,7 +934,7 @@ QString MacAddress() {
|
||||
|
||||
}
|
||||
|
||||
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline) {
|
||||
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline, const bool html_escaped) {
|
||||
|
||||
QRegularExpression variable_replacer("[%][a-z]+[%]");
|
||||
QString copy(message);
|
||||
@@ -935,7 +945,7 @@ QString ReplaceMessage(const QString &message, const Song &song, const QString &
|
||||
for (match = variable_replacer.match(message, pos) ; match.hasMatch() ; match = variable_replacer.match(message, pos)) {
|
||||
pos = match.capturedStart();
|
||||
QStringList captured = match.capturedTexts();
|
||||
copy.replace(captured[0], ReplaceVariable(captured[0], song, newline));
|
||||
copy.replace(captured[0], ReplaceVariable(captured[0], song, newline, html_escaped));
|
||||
pos += match.capturedLength();
|
||||
}
|
||||
|
||||
@@ -943,62 +953,76 @@ QString ReplaceMessage(const QString &message, const Song &song, const QString &
|
||||
if (index_of >= 0) copy = copy.remove(index_of, 3);
|
||||
|
||||
return copy;
|
||||
|
||||
}
|
||||
|
||||
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline) {
|
||||
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline, const bool html_escaped) {
|
||||
|
||||
QString return_value;
|
||||
if (variable == "%artist%") {
|
||||
return song.artist().toHtmlEscaped();
|
||||
QString value = variable;
|
||||
|
||||
if (variable == "%title%") {
|
||||
value = song.PrettyTitle();
|
||||
}
|
||||
else if (variable == "%album%") {
|
||||
return song.album().toHtmlEscaped();
|
||||
value = song.album();
|
||||
}
|
||||
else if (variable == "%title%") {
|
||||
return song.PrettyTitle().toHtmlEscaped();
|
||||
else if (variable == "%artist%") {
|
||||
value = song.artist();
|
||||
}
|
||||
else if (variable == "%albumartist%") {
|
||||
return song.effective_albumartist().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%year%") {
|
||||
return song.PrettyYear().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%composer%") {
|
||||
return song.composer().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%performer%") {
|
||||
return song.performer().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%grouping%") {
|
||||
return song.grouping().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%length%") {
|
||||
return song.PrettyLength().toHtmlEscaped();
|
||||
}
|
||||
else if (variable == "%disc%") {
|
||||
return return_value.setNum(song.disc()).toHtmlEscaped();
|
||||
value = song.effective_albumartist();
|
||||
}
|
||||
else if (variable == "%track%") {
|
||||
return return_value.setNum(song.track()).toHtmlEscaped();
|
||||
value.setNum(song.track());
|
||||
}
|
||||
else if (variable == "%disc%") {
|
||||
value.setNum(song.disc());
|
||||
}
|
||||
else if (variable == "%year%") {
|
||||
value = song.PrettyYear();
|
||||
}
|
||||
else if (variable == "%originalyear%") {
|
||||
value = song.PrettyOriginalYear();
|
||||
}
|
||||
else if (variable == "%genre%") {
|
||||
return song.genre().toHtmlEscaped();
|
||||
value = song.genre();
|
||||
}
|
||||
else if (variable == "%playcount%") {
|
||||
return return_value.setNum(song.playcount()).toHtmlEscaped();
|
||||
else if (variable == "%composer%") {
|
||||
value = song.composer();
|
||||
}
|
||||
else if (variable == "%skipcount%") {
|
||||
return return_value.setNum(song.skipcount()).toHtmlEscaped();
|
||||
else if (variable == "%performer%") {
|
||||
value = song.performer();
|
||||
}
|
||||
else if (variable == "%grouping%") {
|
||||
value = song.grouping();
|
||||
}
|
||||
else if (variable == "%length%") {
|
||||
value = song.PrettyLength();
|
||||
}
|
||||
else if (variable == "%filename%") {
|
||||
return song.basefilename().toHtmlEscaped();
|
||||
value = song.basefilename();
|
||||
}
|
||||
else if (variable == "%url%") {
|
||||
value = song.url().toString();
|
||||
}
|
||||
else if (variable == "%playcount%") {
|
||||
value.setNum(song.playcount());
|
||||
}
|
||||
else if (variable == "%skipcount%") {
|
||||
value.setNum(song.skipcount());
|
||||
}
|
||||
else if (variable == "%rating%") {
|
||||
value = song.PrettyRating();
|
||||
}
|
||||
else if (variable == "%newline%") {
|
||||
return QString(newline);
|
||||
return QString(newline); // No HTML escaping, return immediately.
|
||||
}
|
||||
|
||||
//if the variable is not recognized, just return it
|
||||
return variable;
|
||||
if (html_escaped) {
|
||||
value = value.toHtmlEscaped();
|
||||
}
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
bool IsColorDark(const QColor &color) {
|
||||
|
||||
@@ -147,8 +147,8 @@ QString UnicodeToAscii(const QString &unicode);
|
||||
|
||||
QString MacAddress();
|
||||
|
||||
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline);
|
||||
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline);
|
||||
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline, const bool html_escaped = false);
|
||||
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline, const bool html_escaped = false);
|
||||
|
||||
bool IsColorDark(const QColor &color);
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ void Windows7ThumbBar::HandleWinEvent(MSG *msg) {
|
||||
|
||||
if (button_created_message_id_ == 0) {
|
||||
// Compute the value for the TaskbarButtonCreated message
|
||||
button_created_message_id_ = RegisterWindowMessage("TaskbarButtonCreated");
|
||||
button_created_message_id_ = RegisterWindowMessageA(LPCSTR("TaskbarButtonCreated"));
|
||||
qLog(Debug) << "TaskbarButtonCreated message ID registered" << button_created_message_id_;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
@@ -22,7 +23,6 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
|
||||
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent, QNetworkAccessManager *network)
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent, NetworkAccessManager *network)
|
||||
: QObject(parent),
|
||||
cover_providers_(cover_providers),
|
||||
network_(network ? network : new NetworkAccessManager(this)),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
@@ -35,7 +36,7 @@
|
||||
#include <QImage>
|
||||
|
||||
class QTimer;
|
||||
class QNetworkAccessManager;
|
||||
class NetworkAccessManager;
|
||||
class CoverProviders;
|
||||
class AlbumCoverFetcherSearch;
|
||||
struct CoverSearchStatistics;
|
||||
@@ -103,7 +104,7 @@ class AlbumCoverFetcher : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent = nullptr, QNetworkAccessManager *network = nullptr);
|
||||
explicit AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent = nullptr, NetworkAccessManager *network = nullptr);
|
||||
~AlbumCoverFetcher() override;
|
||||
|
||||
static const int kMaxConcurrentRequests;
|
||||
@@ -126,7 +127,7 @@ class AlbumCoverFetcher : public QObject {
|
||||
void AddRequest(const CoverSearchRequest &req);
|
||||
|
||||
CoverProviders *cover_providers_;
|
||||
QNetworkAccessManager *network_;
|
||||
NetworkAccessManager *network_;
|
||||
quint64 next_id_;
|
||||
|
||||
QQueue<CoverSearchRequest> queued_requests_;
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/network.h"
|
||||
#include "core/networktimeouts.h"
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "albumcoverfetchersearch.h"
|
||||
@@ -52,8 +52,7 @@ const int AlbumCoverFetcherSearch::kImageLoadTimeoutMs = 6000;
|
||||
const int AlbumCoverFetcherSearch::kTargetSize = 500;
|
||||
const float AlbumCoverFetcherSearch::kGoodScore = 4.0;
|
||||
|
||||
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(
|
||||
const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent)
|
||||
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent)
|
||||
: QObject(parent),
|
||||
request_(request),
|
||||
image_load_timeout_(new NetworkTimeouts(kImageLoadTimeoutMs, this)),
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "coversearchstatistics.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class CoverProvider;
|
||||
class CoverProviders;
|
||||
class NetworkAccessManager;
|
||||
class NetworkTimeouts;
|
||||
|
||||
// This class encapsulates a single search for covers initiated by an AlbumCoverFetcher.
|
||||
@@ -49,7 +49,7 @@ class AlbumCoverFetcherSearch : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent);
|
||||
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent);
|
||||
~AlbumCoverFetcherSearch() override;
|
||||
|
||||
void Start(CoverProviders *cover_providers);
|
||||
@@ -107,7 +107,7 @@ class AlbumCoverFetcherSearch : public QObject {
|
||||
typedef QPair<CoverSearchResult, QImage> CandidateImage;
|
||||
QMultiMap<float, CandidateImage> candidate_images_;
|
||||
|
||||
QNetworkAccessManager *network_;
|
||||
NetworkAccessManager *network_;
|
||||
|
||||
bool cancel_requested_;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
@@ -621,6 +622,7 @@ void AlbumCoverManager::ShowCover() {
|
||||
if (!song.is_valid()) return;
|
||||
|
||||
album_cover_choice_controller_->ShowCover(song);
|
||||
|
||||
}
|
||||
|
||||
void AlbumCoverManager::FetchSingleCover() {
|
||||
@@ -809,13 +811,11 @@ SongMimeData *AlbumCoverManager::GetMimeDataForAlbums(const QModelIndexList &ind
|
||||
|
||||
}
|
||||
|
||||
void AlbumCoverManager::AlbumDoubleClicked(const QModelIndex &index) {
|
||||
void AlbumCoverManager::AlbumDoubleClicked(const QModelIndex &idx) {
|
||||
|
||||
SongMimeData *mimedata = GetMimeDataForAlbums(QModelIndexList() << index);
|
||||
if (mimedata) {
|
||||
mimedata->from_doubleclick_ = true;
|
||||
emit AddToPlaylist(mimedata);
|
||||
}
|
||||
QListWidgetItem *item = static_cast<QListWidgetItem*>(idx.internalPointer());
|
||||
if (!item) return;
|
||||
album_cover_choice_controller_->ShowCover(ItemAsSong(item));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
@@ -151,7 +152,7 @@ class AlbumCoverManager : public QMainWindow {
|
||||
void ShowCover();
|
||||
|
||||
// For adding albums to the playlist
|
||||
void AlbumDoubleClicked(const QModelIndex &index);
|
||||
void AlbumDoubleClicked(const QModelIndex &idx);
|
||||
void AddSelectedToPlaylist();
|
||||
void LoadSelectedToPlaylist();
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-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
|
||||
|
||||
@@ -223,7 +223,7 @@ void DeezerCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
|
||||
|
||||
QMap<QUrl, CoverSearchResult> results;
|
||||
int i = 0;
|
||||
for (const QJsonValue &json_value : array_data) {
|
||||
for (const QJsonValue json_value : array_data) {
|
||||
|
||||
if (!json_value.isObject()) {
|
||||
Error("Invalid Json reply, data array value is not a object.", json_value);
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "jsoncoverprovider.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class Application;
|
||||
|
||||
@@ -58,7 +58,7 @@ class DeezerCoverProvider : public JsonCoverProvider {
|
||||
static const char *kApiUrl;
|
||||
static const int kLimit;
|
||||
|
||||
QNetworkAccessManager *network_;
|
||||
NetworkAccessManager *network_;
|
||||
QList<QNetworkReply*> replies_;
|
||||
|
||||
};
|
||||
|
||||
@@ -273,7 +273,7 @@ void DiscogsCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
|
||||
array_results = value_results.toArray();
|
||||
}
|
||||
|
||||
for (const QJsonValue &value_result : array_results) {
|
||||
for (const QJsonValue value_result : array_results) {
|
||||
|
||||
if (!value_result.isObject()) {
|
||||
Error("Invalid Json reply, results value is not a object.", value_result);
|
||||
@@ -380,7 +380,7 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, const int se
|
||||
QJsonArray array_artists = value_artists.toArray();
|
||||
int i = 0;
|
||||
QString artist;
|
||||
for (const QJsonValue &value_artist : array_artists) {
|
||||
for (const QJsonValue value_artist : array_artists) {
|
||||
if (!value_artist.isObject()) {
|
||||
Error("Invalid Json reply, atists array value is not a object.", value_artist);
|
||||
continue;
|
||||
@@ -421,7 +421,7 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, const int se
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QJsonValue &value_image : array_images) {
|
||||
for (const QJsonValue value_image : array_images) {
|
||||
|
||||
if (!value_image.isObject()) {
|
||||
Error("Invalid Json reply, images array value is not an object.", value_image);
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include "jsoncoverprovider.h"
|
||||
#include "albumcoverfetcher.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QTimer;
|
||||
class Application;
|
||||
@@ -99,7 +99,7 @@ class DiscogsCoverProvider : public JsonCoverProvider {
|
||||
static const char *kSecretKeyB64;
|
||||
static const int kRequestsDelay;
|
||||
|
||||
QNetworkAccessManager *network_;
|
||||
NetworkAccessManager *network_;
|
||||
QTimer *timer_flush_requests_;
|
||||
QQueue<std::shared_ptr<DiscogsCoverSearchContext>> queue_search_requests_;
|
||||
QQueue<DiscogsCoverReleaseContext> queue_release_requests_;
|
||||
|
||||
@@ -230,7 +230,7 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, const int id, cons
|
||||
}
|
||||
QJsonArray array_type = value_type.toArray();
|
||||
|
||||
for (const QJsonValue &value : array_type) {
|
||||
for (const QJsonValue value : array_type) {
|
||||
|
||||
if (!value.isObject()) {
|
||||
Error("Invalid Json reply, value in albummatches/trackmatches array is not a object.", value);
|
||||
@@ -255,7 +255,7 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, const int id, cons
|
||||
QJsonArray array_image = json_image.toArray();
|
||||
QUrl url;
|
||||
LastFmImageSize size(LastFmImageSize::Unknown);
|
||||
for (const QJsonValue &value_image : array_image) {
|
||||
for (const QJsonValue value_image : array_image) {
|
||||
if (!value_image.isObject()) {
|
||||
Error("Invalid Json reply, album image value is not an object.", value_image);
|
||||
continue;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#include "jsoncoverprovider.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class Application;
|
||||
|
||||
@@ -65,7 +65,7 @@ class LastFmCoverProvider : public JsonCoverProvider {
|
||||
static const char *kApiKey;
|
||||
static const char *kSecret;
|
||||
|
||||
QNetworkAccessManager *network_;
|
||||
NetworkAccessManager *network_;
|
||||
QList<QNetworkReply*> replies_;
|
||||
|
||||
};
|
||||
|
||||
@@ -167,7 +167,7 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QJsonValue &value_release : array_releases) {
|
||||
for (const QJsonValue value_release : array_releases) {
|
||||
|
||||
if (!value_release.isObject()) {
|
||||
Error("Invalid Json reply, releases array value is not an object.", value_release);
|
||||
@@ -187,7 +187,7 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int
|
||||
QJsonArray array_artists = json_artists.toArray();
|
||||
int i = 0;
|
||||
QString artist;
|
||||
for (const QJsonValue &value_artist : array_artists) {
|
||||
for (const QJsonValue value_artist : array_artists) {
|
||||
if (!value_artist.isObject()) {
|
||||
Error("Invalid Json reply, artist is not a object.", value_artist);
|
||||
continue;
|
||||
|
||||