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
|
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:
|
install_debian_dependencies:
|
||||||
description: Install Debian dependencies
|
description: Install Debian dependencies
|
||||||
steps:
|
steps:
|
||||||
@@ -326,7 +255,7 @@ jobs:
|
|||||||
|
|
||||||
build_source:
|
build_source:
|
||||||
docker:
|
docker:
|
||||||
- image: opensuse/leap:15.1
|
- image: opensuse/leap:15.2
|
||||||
steps:
|
steps:
|
||||||
- install_opensuse_dependencies
|
- install_opensuse_dependencies
|
||||||
- checkout
|
- checkout
|
||||||
@@ -359,18 +288,6 @@ jobs:
|
|||||||
- build_rpm
|
- 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:
|
build_fedora_32:
|
||||||
docker:
|
docker:
|
||||||
- image: fedora:32
|
- image: fedora:32
|
||||||
@@ -383,14 +300,13 @@ jobs:
|
|||||||
- build_source
|
- build_source
|
||||||
- build_rpm
|
- build_rpm
|
||||||
|
|
||||||
|
build_fedora_33:
|
||||||
build_centos_8:
|
|
||||||
docker:
|
docker:
|
||||||
- image: centos:8
|
- image: fedora:33
|
||||||
environment:
|
environment:
|
||||||
RPM_BUILD_NCPUS: "2"
|
RPM_BUILD_NCPUS: "2"
|
||||||
steps:
|
steps:
|
||||||
- install_centos_dependencies
|
- install_fedora_dependencies
|
||||||
- checkout
|
- checkout
|
||||||
- cmake
|
- cmake
|
||||||
- build_source
|
- build_source
|
||||||
@@ -464,17 +380,11 @@ workflows:
|
|||||||
only: /.*/
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
- build_fedora_31:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build_fedora_32:
|
- build_fedora_32:
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
- build_fedora_33:
|
||||||
|
|
||||||
- build_centos_8:
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
|||||||
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
|||||||
github: jonaski
|
github: jonaski
|
||||||
|
patreon: jonaskvinge
|
||||||
|
|||||||
237
.github/workflows/ccpp.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
name: Create source tarball
|
name: Create source tarball
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: opensuse/leap:15.1
|
image: opensuse/leap:15.2
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
- name: Update packages
|
- name: Update packages
|
||||||
@@ -223,8 +223,8 @@ jobs:
|
|||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
build_opensuse_tumbleweed:
|
build_opensuse_tumbleweed_qt5:
|
||||||
name: Build openSUSE Tumbleweed
|
name: Build openSUSE Tumbleweed Qt 5
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: opensuse/tumbleweed
|
image: opensuse/tumbleweed
|
||||||
@@ -287,7 +287,7 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: build
|
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
|
- name: Create source tarball
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: ../dist/scripts/maketarball.sh
|
run: ../dist/scripts/maketarball.sh
|
||||||
@@ -301,8 +301,8 @@ jobs:
|
|||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
build_opensuse_qt6:
|
build_opensuse_tumbleweed_qt6:
|
||||||
name: Build openSUSE Qt 6
|
name: Build openSUSE Tumbleweed Qt 6
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: opensuse/tumbleweed
|
image: opensuse/tumbleweed
|
||||||
@@ -352,7 +352,7 @@ jobs:
|
|||||||
qt6-x11extras-devel
|
qt6-x11extras-devel
|
||||||
qt6-base-common-devel
|
qt6-base-common-devel
|
||||||
qt6-sql-sqlite
|
qt6-sql-sqlite
|
||||||
qt6-qt5compat-devel
|
qt6-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
@@ -368,9 +368,17 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
|
||||||
- name: Build
|
- name: Create source tarball
|
||||||
working-directory: build
|
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:
|
build_fedora_32:
|
||||||
@@ -615,15 +623,21 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
|
|
||||||
|
- name: Install dnf-plugins-core
|
||||||
|
run: dnf install -y dnf-plugins-core
|
||||||
- name: Install epel-release
|
- name: Install epel-release
|
||||||
run: dnf install -y epel-release
|
run: dnf install -y epel-release
|
||||||
|
|
||||||
- name: Install config-manager
|
- name: Install config-manager
|
||||||
run: dnf install -y 'dnf-command(config-manager)'
|
run: dnf install -y 'dnf-command(config-manager)'
|
||||||
- name: Enable PowerTools
|
|
||||||
run: dnf config-manager --set-enabled PowerTools
|
- name: Enable powertools
|
||||||
- name: DNF Clean All
|
run: dnf config-manager --set-enabled powertools
|
||||||
|
|
||||||
|
- name: Clean all
|
||||||
run: dnf clean all
|
run: dnf clean all
|
||||||
- name: DNF Update
|
|
||||||
|
- name: Update
|
||||||
run: dnf update -y
|
run: dnf update -y
|
||||||
|
|
||||||
- name: Install CentOS dependencies
|
- name: Install CentOS dependencies
|
||||||
@@ -649,10 +663,9 @@ jobs:
|
|||||||
gnutls-devel
|
gnutls-devel
|
||||||
sqlite-devel
|
sqlite-devel
|
||||||
protobuf-devel
|
protobuf-devel
|
||||||
protobuf-compiler
|
protobuf-c
|
||||||
alsa-lib-devel
|
alsa-lib-devel
|
||||||
pulseaudio-libs-devel
|
pulseaudio-libs-devel
|
||||||
qt5-devel
|
|
||||||
qt5-qtbase-devel
|
qt5-qtbase-devel
|
||||||
qt5-qtx11extras-devel
|
qt5-qtx11extras-devel
|
||||||
qt5-qttools-devel
|
qt5-qttools-devel
|
||||||
@@ -673,7 +686,89 @@ jobs:
|
|||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
gstreamer1-devel
|
gstreamer1-devel
|
||||||
gstreamer1-plugins-base-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
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -995,15 +1090,11 @@ jobs:
|
|||||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||||
|
|
||||||
|
|
||||||
build-macos:
|
build-macos-catalina:
|
||||||
name: Build macOS
|
name: Build macOS Catalina
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
#- name: Update
|
|
||||||
# run: brew update
|
|
||||||
#- name: Upgrade
|
|
||||||
# run: brew upgrade
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: >
|
run: >
|
||||||
brew install
|
brew install
|
||||||
@@ -1061,8 +1152,73 @@ jobs:
|
|||||||
run: make dmg
|
run: make dmg
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: upload-macos
|
name: upload-macos-catalina
|
||||||
path: build/strawberry-*.dmg
|
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:
|
build-windows:
|
||||||
@@ -1179,7 +1335,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Copy extra binaries
|
- name: Copy extra binaries
|
||||||
working-directory: build
|
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
|
- name: Copy dependencies
|
||||||
working-directory: build
|
working-directory: build
|
||||||
@@ -1208,12 +1364,12 @@ jobs:
|
|||||||
run: makensis strawberry.nsi
|
run: makensis strawberry.nsi
|
||||||
|
|
||||||
|
|
||||||
upload-macos:
|
upload-macos-catalina:
|
||||||
name: Upload macOS DMG
|
name: Upload macOS Catalina DMG
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
needs:
|
needs:
|
||||||
- build-macos
|
- build-macos-catalina
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
- uses: actions/download-artifact@v2
|
- uses: actions/download-artifact@v2
|
||||||
@@ -1227,6 +1383,29 @@ jobs:
|
|||||||
- name: rsync
|
- name: rsync
|
||||||
run: |
|
run: |
|
||||||
set -x
|
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/
|
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/catalina/
|
||||||
done
|
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
|
sudo: required
|
||||||
language: C++
|
language: C++
|
||||||
os:
|
os: osx
|
||||||
- osx
|
osx_image: xcode11.3
|
||||||
compiler:
|
compiler: clang
|
||||||
- gcc
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||||
@@ -13,8 +12,8 @@ before_install:
|
|||||||
- git fetch --unshallow
|
- git fetch --unshallow
|
||||||
- git pull
|
- git pull
|
||||||
- brew update
|
- brew update
|
||||||
- travis_wait 120 brew upgrade || echo "Failed"
|
- travis_wait 400 brew upgrade || echo "Failed"
|
||||||
- travis_wait 120 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 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 gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
|
||||||
- brew install libcdio libmtp
|
- brew install libcdio libmtp
|
||||||
@@ -32,14 +31,12 @@ before_script:
|
|||||||
script:
|
script:
|
||||||
- make -j8
|
- make -j8
|
||||||
- make install
|
- make install
|
||||||
- make dmg
|
- make dmg2
|
||||||
after_success:
|
after_success:
|
||||||
- ls -lh strawberry*.dmg
|
- ls -lh strawberry*.dmg
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||||
if [[ "$TRAVIS_BRANCH" == "master" ]]; 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/highsierra/;
|
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
|
||||||
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
|
|
||||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
9
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -16,11 +16,6 @@ else()
|
|||||||
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||||
endif()
|
endif()
|
||||||
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
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
|
target_include_directories(singleapplication PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
@@ -39,10 +34,6 @@ else()
|
|||||||
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||||
endif()
|
endif()
|
||||||
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
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
|
target_include_directories(singlecoreapplication PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|||||||
1
3rdparty/utf8-cpp/CMakeLists.txt
vendored
@@ -1,2 +1 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
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/Summary.cmake)
|
||||||
include(cmake/OptionalSource.cmake)
|
include(cmake/OptionalSource.cmake)
|
||||||
include(cmake/ParseArguments.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")
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(LINUX ON)
|
set(LINUX ON)
|
||||||
@@ -29,13 +22,22 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
|||||||
set(OPENBSD ON)
|
set(OPENBSD ON)
|
||||||
endif()
|
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)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
list(APPEND COMPILE_OPTIONS
|
list(APPEND COMPILE_OPTIONS
|
||||||
$<$<COMPILE_LANGUAGE:C>:--std=c99>
|
$<$<COMPILE_LANGUAGE:C>:-std=c99>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:--std=c++11>
|
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
|
||||||
-U__STRICT_ANSI__
|
|
||||||
-Wall
|
-Wall
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wpedantic
|
-Wpedantic
|
||||||
@@ -133,6 +135,11 @@ pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
|
|||||||
find_package(Gettext)
|
find_package(Gettext)
|
||||||
find_package(FFTW3)
|
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_QT5 "Use Qt 5" OFF)
|
||||||
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
||||||
|
|
||||||
@@ -140,79 +147,66 @@ if(WITH_QT6)
|
|||||||
set(BUILD_WITH_QT6 ON)
|
set(BUILD_WITH_QT6 ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_WITH_QT5 AND NOT BUILD_WITH_QT6)
|
if(BUILD_WITH_QT5)
|
||||||
set(BUILD_WITH_QT5 ON)
|
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()
|
endif()
|
||||||
|
|
||||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||||
if(X11_FOUND)
|
unset(OPTIONAL_COMPONENTS)
|
||||||
list(APPEND QT_COMPONENTS X11Extras)
|
|
||||||
endif()
|
if(QT_MAJOR_VERSION EQUAL 5)
|
||||||
if(DBUS_FOUND)
|
set(QT_MIN_VERSION 5.8)
|
||||||
list(APPEND QT_COMPONENTS DBus)
|
|
||||||
endif()
|
|
||||||
if(WIN32)
|
|
||||||
list(APPEND QT_COMPONENTS WinExtras)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_WITH_QT6)
|
if(DBUS_FOUND AND NOT WIN32)
|
||||||
list(APPEND QT_COMPONENTS Core5Compat)
|
list(APPEND QT_COMPONENTS DBus)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
endif()
|
||||||
set(QtCore_LIBRARIES Qt6::Core)
|
if(X11_FOUND)
|
||||||
set(QtConcurrent_LIBRARIES Qt6::Concurrent)
|
list(APPEND OPTIONAL_COMPONENTS X11Extras)
|
||||||
set(QtWidgets_LIBRARIES Qt6::Widgets)
|
endif()
|
||||||
set(QtNetwork_LIBRARIES Qt6::Network)
|
if(WIN32)
|
||||||
set(QtSql_LIBRARIES Qt6::Sql)
|
list(APPEND OPTIONAL_COMPONENTS WinExtras)
|
||||||
set(QT_LIBRARIES Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Core5Compat)
|
endif()
|
||||||
if(Qt6DBus_FOUND)
|
|
||||||
set(QtDBus_LIBRARIES Qt6::DBus)
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${OPTIONAL_COMPONENTS})
|
||||||
list(APPEND QT_LIBRARIES Qt6::DBus)
|
|
||||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt6::qdbusxml2cpp LOCATION)
|
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
|
||||||
endif()
|
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
|
||||||
if(Qt6X11Extras_FOUND)
|
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
|
||||||
set(QtX11Extras_LIBRARIES Qt6::X11Extras)
|
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
|
||||||
list(APPEND QT_LIBRARIES Qt6::X11Extras)
|
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
|
||||||
endif()
|
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
|
||||||
if(Qt6WinExtras_FOUND)
|
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)
|
||||||
set(QtWinExtras_LIBRARIES Qt6::WinExtras)
|
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
|
||||||
list(APPEND QT_LIBRARIES Qt6::WinExtras)
|
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||||
endif()
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||||
find_package(Qt6 QUIET COMPONENTS LinguistTools CONFIG)
|
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
|
||||||
if (Qt6LinguistTools_FOUND)
|
endif()
|
||||||
set(QT_LCONVERT_EXECUTABLE Qt6::lconvert)
|
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
|
||||||
endif()
|
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||||
elseif(BUILD_WITH_QT5)
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||||
set(QT_MIN_VERSION 5.8)
|
set(HAVE_X11EXTRAS ON)
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
|
endif()
|
||||||
set(QtCore_LIBRARIES ${Qt5Core_LIBRARIES})
|
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
|
||||||
set(QtConcurrent_LIBRARIES ${Qt5Concurrent_LIBRARIES})
|
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||||
set(QtWidgets_LIBRARIES ${Qt5Widgets_LIBRARIES})
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||||
set(QtNetwork_LIBRARIES ${Qt5Network_LIBRARIES})
|
set(HAVE_WINEXTRAS ON)
|
||||||
set(QtSql_LIBRARIES ${Qt5Sql_LIBRARIES})
|
endif()
|
||||||
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})
|
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||||
if(Qt5DBus_FOUND)
|
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
|
||||||
set(QtDBus_LIBRARIES ${Qt5DBus_LIBRARIES})
|
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
|
||||||
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")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(X11_FOUND)
|
if(X11_FOUND)
|
||||||
@@ -230,6 +224,15 @@ if(X11_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
endif(X11_FOUND)
|
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
|
# TAGLIB
|
||||||
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
|
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
|
||||||
if(USE_SYSTEM_TAGLIB)
|
if(USE_SYSTEM_TAGLIB)
|
||||||
@@ -324,6 +327,10 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
|
|||||||
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
|
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"
|
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||||
)
|
)
|
||||||
@@ -392,9 +399,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
|||||||
# Check that we have sqlite3 with FTS5
|
# Check that we have sqlite3 with FTS5
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
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_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${QtCore_INCLUDE_DIRS} ${QtSql_INCLUDE_DIRS})
|
|
||||||
check_cxx_source_runs("
|
check_cxx_source_runs("
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
|||||||
45
Changelog
@@ -2,6 +2,51 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
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:
|
0.8.3:
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
: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.
|
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.
|
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.
|
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
|
### :wrench: Compiling from source
|
||||||
|
|
||||||
### Get the code:
|
### Get the code:
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
|||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
|
|
||||||
if (DEB_CODENAME)
|
if (DEB_CODENAME AND DEB_DATE)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
|
|
||||||
add_custom_target(deb
|
add_custom_target(deb
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us
|
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
|
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}
|
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
|
# From http://www.cmake.org/Wiki/CMakeMacroParseArguments
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.6)
|
|
||||||
|
|
||||||
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
|
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
|
||||||
SET(DEFAULT_ARGS)
|
SET(DEFAULT_ARGS)
|
||||||
FOREACH(arg_name ${arg_names})
|
FOREACH(arg_name ${arg_names})
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
|||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
if (DIST_NAME)
|
if (DIST_NAME)
|
||||||
|
|
||||||
message(STATUS "Distro Name: ${DIST_NAME}")
|
message(STATUS "Distro Name: ${DIST_NAME}")
|
||||||
if (DIST_RELEASE)
|
if (DIST_RELEASE)
|
||||||
message(STATUS "Distro Release: ${DIST_RELEASE}")
|
message(STATUS "Distro Release: ${DIST_RELEASE}")
|
||||||
@@ -24,45 +25,40 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
|||||||
if (DIST_VERSION)
|
if (DIST_VERSION)
|
||||||
message(STATUS "Distro Version: ${DIST_VERSION}")
|
message(STATUS "Distro Version: ${DIST_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
|
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
|
||||||
if (${DIST_NAME} STREQUAL "opensuse")
|
|
||||||
if (DIST_RELEASE)
|
if (${DIST_NAME} STREQUAL "opensuse" AND DIST_RELEASE)
|
||||||
if (${DIST_RELEASE} STREQUAL "leap")
|
if (${DIST_RELEASE} STREQUAL "leap")
|
||||||
if (DIST_VERSION)
|
if (DIST_VERSION)
|
||||||
set(RPM_DISTRO "lp${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO "lp${DIST_VERSION}")
|
||||||
else()
|
else()
|
||||||
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO ${DIST_RELEASE})
|
||||||
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")
|
|
||||||
endif()
|
endif()
|
||||||
else()
|
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO ${DIST_RELEASE})
|
||||||
endif()
|
endif()
|
||||||
elseif (${DIST_NAME} STREQUAL "fedora")
|
elseif (${DIST_NAME} STREQUAL "fedora" AND DIST_VERSION)
|
||||||
if (DIST_VERSION)
|
set(RPM_DISTRO "fc${DIST_VERSION}")
|
||||||
set(RPM_DISTRO "fc${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
elseif (${DIST_NAME} STREQUAL "centos" AND DIST_VERSION)
|
||||||
else ()
|
set(RPM_DISTRO "el${DIST_VERSION}")
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
elseif (${DIST_NAME} STREQUAL "mageia" AND DIST_RELEASE)
|
||||||
endif()
|
set(RPM_DISTRO "mga${DIST_RELEASE}")
|
||||||
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")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT RPM_DISTRO)
|
||||||
|
set(RPM_DISTRO ${DIST_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
|
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
|
add_custom_target(rpm
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
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} -bs ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||||
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
|
|
||||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||||
message(FATAL_ERROR "Could not find xgettext executable")
|
message(FATAL_ERROR "Could not find xgettext executable")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||||
set(STRAWBERRY_VERSION_MINOR 8)
|
set(STRAWBERRY_VERSION_MINOR 8)
|
||||||
set(STRAWBERRY_VERSION_PATCH 3)
|
set(STRAWBERRY_VERSION_PATCH 5)
|
||||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||||
|
|
||||||
set(INCLUDE_GIT_REVISION OFF)
|
set(INCLUDE_GIT_REVISION OFF)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<file>style/smartplaylistsearchterm.css</file>
|
<file>style/smartplaylistsearchterm.css</file>
|
||||||
<file>html/oauthsuccess.html</file>
|
<file>html/oauthsuccess.html</file>
|
||||||
<file>pictures/strawberry.png</file>
|
<file>pictures/strawberry.png</file>
|
||||||
|
<file>pictures/strawberry-grey.png</file>
|
||||||
<file>pictures/strawberry-faded.png</file>
|
<file>pictures/strawberry-faded.png</file>
|
||||||
<file>pictures/strawbs.png</file>
|
<file>pictures/strawbs.png</file>
|
||||||
<file>pictures/nomusic.png</file>
|
<file>pictures/nomusic.png</file>
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
<file>icons/128x128/star-grey.png</file>
|
<file>icons/128x128/star-grey.png</file>
|
||||||
<file>icons/128x128/star.png</file>
|
<file>icons/128x128/star.png</file>
|
||||||
<file>icons/128x128/strawberry.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/tools-wizard.png</file>
|
||||||
<file>icons/128x128/view-choose.png</file>
|
<file>icons/128x128/view-choose.png</file>
|
||||||
<file>icons/128x128/view-fullscreen.png</file>
|
<file>icons/128x128/view-fullscreen.png</file>
|
||||||
@@ -164,6 +165,7 @@
|
|||||||
<file>icons/64x64/star-grey.png</file>
|
<file>icons/64x64/star-grey.png</file>
|
||||||
<file>icons/64x64/star.png</file>
|
<file>icons/64x64/star.png</file>
|
||||||
<file>icons/64x64/strawberry.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/tools-wizard.png</file>
|
||||||
<file>icons/64x64/view-choose.png</file>
|
<file>icons/64x64/view-choose.png</file>
|
||||||
<file>icons/64x64/view-fullscreen.png</file>
|
<file>icons/64x64/view-fullscreen.png</file>
|
||||||
@@ -260,6 +262,7 @@
|
|||||||
<file>icons/48x48/star-grey.png</file>
|
<file>icons/48x48/star-grey.png</file>
|
||||||
<file>icons/48x48/star.png</file>
|
<file>icons/48x48/star.png</file>
|
||||||
<file>icons/48x48/strawberry.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/tools-wizard.png</file>
|
||||||
<file>icons/48x48/view-choose.png</file>
|
<file>icons/48x48/view-choose.png</file>
|
||||||
<file>icons/48x48/view-fullscreen.png</file>
|
<file>icons/48x48/view-fullscreen.png</file>
|
||||||
@@ -356,6 +359,7 @@
|
|||||||
<file>icons/32x32/star-grey.png</file>
|
<file>icons/32x32/star-grey.png</file>
|
||||||
<file>icons/32x32/star.png</file>
|
<file>icons/32x32/star.png</file>
|
||||||
<file>icons/32x32/strawberry.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/tools-wizard.png</file>
|
||||||
<file>icons/32x32/view-choose.png</file>
|
<file>icons/32x32/view-choose.png</file>
|
||||||
<file>icons/32x32/view-fullscreen.png</file>
|
<file>icons/32x32/view-fullscreen.png</file>
|
||||||
@@ -452,6 +456,7 @@
|
|||||||
<file>icons/22x22/star-grey.png</file>
|
<file>icons/22x22/star-grey.png</file>
|
||||||
<file>icons/22x22/star.png</file>
|
<file>icons/22x22/star.png</file>
|
||||||
<file>icons/22x22/strawberry.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/tools-wizard.png</file>
|
||||||
<file>icons/22x22/view-choose.png</file>
|
<file>icons/22x22/view-choose.png</file>
|
||||||
<file>icons/22x22/view-fullscreen.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)
|
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)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|||||||
1
dist/macos/macdeploy.py
vendored
@@ -88,7 +88,6 @@ GSTREAMER_PLUGINS = [
|
|||||||
'libgstosxaudio.dylib',
|
'libgstosxaudio.dylib',
|
||||||
'libgstplayback.dylib',
|
'libgstplayback.dylib',
|
||||||
'libgstrawparse.dylib',
|
'libgstrawparse.dylib',
|
||||||
'libgstrealmedia.dylib',
|
|
||||||
'libgstreplaygain.dylib',
|
'libgstreplaygain.dylib',
|
||||||
'libgstsoup.dylib',
|
'libgstsoup.dylib',
|
||||||
'libgstspectrum.dylib',
|
'libgstspectrum.dylib',
|
||||||
|
|||||||
24
dist/macos/macversion.sh
vendored
@@ -1,14 +1,22 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
macos_version=$(sw_vers -productVersion| awk -F '[.]' '{print $2}')
|
macos_version=$(sw_vers -productVersion)
|
||||||
macos_codenames=(
|
macos_version_major=$(echo $macos_version | awk -F '[.]' '{print $1}')
|
||||||
["13"]="highsierra"
|
macos_version_minor=$(echo $macos_version | awk -F '[.]' '{print $2}')
|
||||||
["14"]="mojave"
|
|
||||||
["15"]="catalina"
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ -n "${macos_codenames[$macos_version]}" ]]; then
|
if [ "${macos_version_major}" = "10" ]; then
|
||||||
echo "${macos_codenames[$macos_version]}"
|
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
|
else
|
||||||
echo "unknown"
|
echo "unknown"
|
||||||
fi
|
fi
|
||||||
|
|||||||
64
dist/unix/strawberry.spec.in
vendored
@@ -1,12 +1,15 @@
|
|||||||
Name: strawberry
|
Name: strawberry
|
||||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
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@
|
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||||
|
%endif
|
||||||
Summary: A music player and music collection organizer
|
Summary: A music player and music collection organizer
|
||||||
Group: Applications/Multimedia
|
Group: Productivity/Multimedia/Sound/Players
|
||||||
License: GPL-3.0+
|
License: GPL-3.0+
|
||||||
URL: https://www.strawberrymusicplayer.org/
|
URL: https://www.strawberrymusicplayer.org/
|
||||||
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
||||||
BuildRequires: libboost_headers-devel
|
BuildRequires: libboost_headers-devel
|
||||||
@@ -26,14 +29,11 @@ BuildRequires: update-desktop-files
|
|||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
BuildRequires: appstream-glib
|
BuildRequires: appstream-glib
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
BuildRequires: libappstream-glib
|
BuildRequires: libappstream-glib
|
||||||
%else
|
%else
|
||||||
BuildRequires: appstream-util
|
BuildRequires: appstream-util
|
||||||
%endif
|
%endif
|
||||||
%endif
|
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
|
||||||
BuildRequires: cmake(Qt5LinguistTools)
|
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig
|
BuildRequires: pkgconfig
|
||||||
BuildRequires: pkgconfig(glib-2.0)
|
BuildRequires: pkgconfig(glib-2.0)
|
||||||
@@ -49,15 +49,29 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
|
|||||||
BuildRequires: pkgconfig(taglib)
|
BuildRequires: pkgconfig(taglib)
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig(fftw3)
|
BuildRequires: pkgconfig(fftw3)
|
||||||
BuildRequires: pkgconfig(Qt5Core)
|
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
||||||
BuildRequires: pkgconfig(Qt5Gui)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
|
||||||
BuildRequires: pkgconfig(Qt5Widgets)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
|
||||||
BuildRequires: pkgconfig(Qt5Concurrent)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
|
||||||
BuildRequires: pkgconfig(Qt5Network)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||||
BuildRequires: pkgconfig(Qt5Sql)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
|
||||||
BuildRequires: pkgconfig(Qt5X11Extras)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
|
||||||
BuildRequires: pkgconfig(Qt5DBus)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
|
||||||
BuildRequires: pkgconfig(Qt5Test)
|
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-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
||||||
@@ -70,14 +84,17 @@ BuildRequires: pkgconfig(libpulse)
|
|||||||
BuildRequires: pkgconfig(libcdio)
|
BuildRequires: pkgconfig(libcdio)
|
||||||
BuildRequires: pkgconfig(libgpod-1.0)
|
BuildRequires: pkgconfig(libgpod-1.0)
|
||||||
BuildRequires: pkgconfig(libmtp)
|
BuildRequires: pkgconfig(libmtp)
|
||||||
BuildRequires: pkgconfig(libnotify)
|
|
||||||
BuildRequires: pkgconfig(libudf)
|
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version}
|
%if 0%{?suse_version} || 0%{?fedora_version}
|
||||||
BuildRequires: pkgconfig(libvlc)
|
BuildRequires: pkgconfig(libvlc)
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%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
|
%endif
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@@ -107,8 +124,11 @@ Features:
|
|||||||
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
%if 0%{?centos} || 0%{?mageia}
|
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
|
%make_build
|
||||||
%else
|
%else
|
||||||
%cmake_build
|
%cmake_build
|
||||||
|
|||||||
16
dist/windows/strawberry.nsi.in
vendored
@@ -98,13 +98,13 @@ Name "${PRODUCT_NAME}"
|
|||||||
!ifdef with_qt6
|
!ifdef with_qt6
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
||||||
!else
|
!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
|
!endif
|
||||||
!else
|
!else
|
||||||
!ifdef with_qt6
|
!ifdef with_qt6
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
||||||
!else
|
!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
|
!endif
|
||||||
!endif
|
!endif
|
||||||
@@ -114,13 +114,13 @@ Name "${PRODUCT_NAME}"
|
|||||||
!ifdef with_qt6
|
!ifdef with_qt6
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
||||||
!else
|
!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
|
!endif
|
||||||
!else
|
!else
|
||||||
!ifdef with_qt6
|
!ifdef with_qt6
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
||||||
!else
|
!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
|
!endif
|
||||||
!endif
|
!endif
|
||||||
@@ -175,6 +175,7 @@ Section "Strawberry" Strawberry
|
|||||||
File "strawberry-tagreader.exe"
|
File "strawberry-tagreader.exe"
|
||||||
File "strawberry.ico"
|
File "strawberry.ico"
|
||||||
File "sqlite3.exe"
|
File "sqlite3.exe"
|
||||||
|
File "gst-launch-1.0.exe"
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
File "libgcc_s_sjlj-1.dll"
|
File "libgcc_s_sjlj-1.dll"
|
||||||
@@ -238,7 +239,7 @@ Section "Strawberry" Strawberry
|
|||||||
File "libpcre-1.dll"
|
File "libpcre-1.dll"
|
||||||
File "libpcre2-16-0.dll"
|
File "libpcre2-16-0.dll"
|
||||||
File "libpng16-16.dll"
|
File "libpng16-16.dll"
|
||||||
File "libprotobuf-24.dll"
|
File "libprotobuf-25.dll"
|
||||||
File "libpsl-5.dll"
|
File "libpsl-5.dll"
|
||||||
File "libsoup-2.4-1.dll"
|
File "libsoup-2.4-1.dll"
|
||||||
File "libspeex-1.dll"
|
File "libspeex-1.dll"
|
||||||
@@ -266,7 +267,7 @@ Section "Strawberry" Strawberry
|
|||||||
File "Qt6Network.dll"
|
File "Qt6Network.dll"
|
||||||
File "Qt6Sql.dll"
|
File "Qt6Sql.dll"
|
||||||
File "Qt6Widgets.dll"
|
File "Qt6Widgets.dll"
|
||||||
File "Qt6WinExtras.dll"
|
;File "Qt6WinExtras.dll"
|
||||||
File "libqtsparkle-qt6.dll"
|
File "libqtsparkle-qt6.dll"
|
||||||
!else
|
!else
|
||||||
File "Qt5Concurrent.dll"
|
File "Qt5Concurrent.dll"
|
||||||
@@ -443,6 +444,7 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\strawberry.exe"
|
Delete "$INSTDIR\strawberry.exe"
|
||||||
Delete "$INSTDIR\strawberry-tagreader.exe"
|
Delete "$INSTDIR\strawberry-tagreader.exe"
|
||||||
Delete "$INSTDIR\sqlite3.exe"
|
Delete "$INSTDIR\sqlite3.exe"
|
||||||
|
Delete "$INSTDIR\gst-launch-1.0.exe"
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||||
@@ -506,7 +508,7 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libpcre-1.dll"
|
Delete "$INSTDIR\libpcre-1.dll"
|
||||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||||
Delete "$INSTDIR\libpng16-16.dll"
|
Delete "$INSTDIR\libpng16-16.dll"
|
||||||
Delete "$INSTDIR\libprotobuf-24.dll"
|
Delete "$INSTDIR\libprotobuf-25.dll"
|
||||||
Delete "$INSTDIR\libpsl-5.dll"
|
Delete "$INSTDIR\libpsl-5.dll"
|
||||||
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
||||||
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ link_directories(
|
|||||||
${GSTREAMER_BASE_LIBRARY_DIRS}
|
${GSTREAMER_BASE_LIBRARY_DIRS}
|
||||||
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
||||||
${FFTW3_LIBRARY_DIRS}
|
${FFTW3_LIBRARY_DIRS}
|
||||||
${QtCore_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(gstmoodbar STATIC ${SOURCES})
|
add_library(gstmoodbar STATIC ${SOURCES})
|
||||||
@@ -21,7 +20,6 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
|
|||||||
${GSTREAMER_BASE_INCLUDE_DIRS}
|
${GSTREAMER_BASE_INCLUDE_DIRS}
|
||||||
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
||||||
${FFTW3_INCLUDE_DIR}
|
${FFTW3_INCLUDE_DIR}
|
||||||
${QtCore_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|||||||
@@ -28,16 +28,12 @@ endif()
|
|||||||
|
|
||||||
link_directories(
|
link_directories(
|
||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${QtCore_LIBRARY_DIRS}
|
|
||||||
${QtNetwork_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
||||||
|
|
||||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${QtCore_INCLUDE_DIRS}
|
|
||||||
${QtNetwork_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(libstrawberry-common PRIVATE
|
target_include_directories(libstrawberry-common PRIVATE
|
||||||
|
|||||||
@@ -107,10 +107,10 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
|
|||||||
else if (flush_local_socket_) {
|
else if (flush_local_socket_) {
|
||||||
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _MessageHandlerBase::DeviceClosed() {
|
void _MessageHandlerBase::DeviceClosed() {
|
||||||
is_device_closed_ = true;
|
is_device_closed_ = true;
|
||||||
AbortAll();
|
AbortAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class QIODevice;
|
|||||||
class _MessageHandlerBase : public QObject {
|
class _MessageHandlerBase : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
||||||
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
||||||
|
|
||||||
@@ -52,16 +52,16 @@ public:
|
|||||||
// After this is true, messages cannot be sent to the handler any more.
|
// After this is true, messages cannot be sent to the handler any more.
|
||||||
bool is_device_closed() const { return is_device_closed_; }
|
bool is_device_closed() const { return is_device_closed_; }
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void WriteMessage(const QByteArray &data);
|
void WriteMessage(const QByteArray &data);
|
||||||
void DeviceReadyRead();
|
void DeviceReadyRead();
|
||||||
virtual void DeviceClosed();
|
virtual void DeviceClosed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
||||||
virtual void AbortAll() = 0;
|
virtual void AbortAll() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
||||||
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ protected:
|
|||||||
// You should subclass this and implement the MessageArrived(MessageType) method.
|
// You should subclass this and implement the MessageArrived(MessageType) method.
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
class AbstractMessageHandler : public _MessageHandlerBase {
|
class AbstractMessageHandler : public _MessageHandlerBase {
|
||||||
public:
|
public:
|
||||||
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
||||||
~AbstractMessageHandler() override { AbortAll(); }
|
~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.
|
// 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);
|
void SendReply(const MessageType &request, MessageType *reply);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Called when a message is received from the socket.
|
// Called when a message is received from the socket.
|
||||||
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
|
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ protected:
|
|||||||
bool RawMessageArrived(const QByteArray &data) override;
|
bool RawMessageArrived(const QByteArray &data) override;
|
||||||
void AbortAll() override;
|
void AbortAll() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<int, ReplyType*> pending_replies_;
|
QMap<qint64, ReplyType*> pending_replies_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
@@ -146,6 +146,7 @@ void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageTy
|
|||||||
|
|
||||||
template<typename MT>
|
template<typename MT>
|
||||||
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
||||||
|
|
||||||
MessageType message;
|
MessageType message;
|
||||||
if (!message.ParseFromArray(data.constData(), data.size())) {
|
if (!message.ParseFromArray(data.constData(), data.size())) {
|
||||||
return false;
|
return false;
|
||||||
@@ -161,14 +162,17 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MT>
|
template<typename MT>
|
||||||
void AbstractMessageHandler<MT>::AbortAll() {
|
void AbstractMessageHandler<MT>::AbortAll() {
|
||||||
|
|
||||||
for (ReplyType *reply : pending_replies_) {
|
for (ReplyType *reply : pending_replies_) {
|
||||||
reply->Abort();
|
reply->Abort();
|
||||||
}
|
}
|
||||||
pending_replies_.clear();
|
pending_replies_.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MESSAGEHANDLER_H
|
#endif // MESSAGEHANDLER_H
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
#include <QSemaphore>
|
#include <QSemaphore>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ class _MessageReplyBase : public QObject {
|
|||||||
public:
|
public:
|
||||||
explicit _MessageReplyBase(QObject *parent = nullptr);
|
explicit _MessageReplyBase(QObject *parent = nullptr);
|
||||||
|
|
||||||
virtual int id() const = 0;
|
virtual qint64 id() const = 0;
|
||||||
bool is_finished() const { return finished_; }
|
bool is_finished() const { return finished_; }
|
||||||
bool is_successful() const { return success_; }
|
bool is_successful() const { return success_; }
|
||||||
|
|
||||||
@@ -57,29 +58,27 @@ class _MessageReplyBase : public QObject {
|
|||||||
template <typename MessageType>
|
template <typename MessageType>
|
||||||
class MessageReply : public _MessageReplyBase {
|
class MessageReply : public _MessageReplyBase {
|
||||||
public:
|
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(); }
|
qint64 id() const override { return request_message_.id(); }
|
||||||
const MessageType& request_message() const { return request_message_; }
|
const MessageType &request_message() const { return request_message_; }
|
||||||
const MessageType& message() const { return reply_message_; }
|
const MessageType &message() const { return reply_message_; }
|
||||||
|
|
||||||
void SetReply(const MessageType& message);
|
void SetReply(const MessageType &message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageType request_message_;
|
MessageType request_message_;
|
||||||
MessageType reply_message_;
|
MessageType reply_message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename MessageType>
|
template<typename MessageType>
|
||||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
|
MessageReply<MessageType>::MessageReply(const MessageType &request_message, QObject *parent) : _MessageReplyBase(parent) {
|
||||||
: _MessageReplyBase(parent)
|
|
||||||
{
|
|
||||||
request_message_.MergeFrom(request_message);
|
request_message_.MergeFrom(request_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MessageType>
|
template<typename MessageType>
|
||||||
void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
void MessageReply<MessageType>::SetReply(const MessageType &message) {
|
||||||
|
|
||||||
Q_ASSERT(!finished_);
|
Q_ASSERT(!finished_);
|
||||||
|
|
||||||
@@ -90,6 +89,9 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
|||||||
qLog(Debug) << "Releasing ID" << id() << "(finished)";
|
qLog(Debug) << "Releasing ID" << id() << "(finished)";
|
||||||
semaphore_.release();
|
semaphore_.release();
|
||||||
|
|
||||||
|
// The signal is not always emitted without this.
|
||||||
|
QThread::usleep(10);
|
||||||
|
|
||||||
emit Finished(success_);
|
emit Finished(success_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInteger>
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
# include <QRandomGenerator>
|
# include <QRandomGenerator>
|
||||||
#endif
|
#endif
|
||||||
@@ -48,11 +48,11 @@ class _WorkerPoolBase : public QObject {
|
|||||||
public:
|
public:
|
||||||
explicit _WorkerPoolBase(QObject *parent = nullptr);
|
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.
|
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
|
||||||
void WorkerFailedToStart();
|
void WorkerFailedToStart();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void DoStart() {}
|
virtual void DoStart() {}
|
||||||
virtual void NewConnection() {}
|
virtual void NewConnection() {}
|
||||||
virtual void ProcessError(QProcess::ProcessError) {}
|
virtual void ProcessError(QProcess::ProcessError) {}
|
||||||
@@ -78,7 +78,7 @@ class WorkerPool : public _WorkerPoolBase {
|
|||||||
void SetExecutableName(const QString &executable_name);
|
void SetExecutableName(const QString &executable_name);
|
||||||
|
|
||||||
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
|
// 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).
|
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
|
||||||
// Defaults to QApplication::applicationName().
|
// Defaults to QApplication::applicationName().
|
||||||
@@ -93,14 +93,14 @@ class WorkerPool : public _WorkerPoolBase {
|
|||||||
// Can be called from any thread.
|
// Can be called from any thread.
|
||||||
ReplyType *SendMessageWithReply(MessageType *message);
|
ReplyType *SendMessageWithReply(MessageType *message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
||||||
void DoStart() override;
|
void DoStart() override;
|
||||||
void NewConnection() override;
|
void NewConnection() override;
|
||||||
void ProcessError(QProcess::ProcessError error) override;
|
void ProcessError(QProcess::ProcessError error) override;
|
||||||
void SendQueuedMessages() override;
|
void SendQueuedMessages() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Worker {
|
struct Worker {
|
||||||
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
|
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.
|
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
|
||||||
HandlerType *NextHandler() const;
|
HandlerType *NextHandler() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString local_server_name_;
|
QString local_server_name_;
|
||||||
QString executable_name_;
|
QString executable_name_;
|
||||||
QString executable_path_;
|
QString executable_path_;
|
||||||
@@ -147,7 +147,7 @@ private:
|
|||||||
mutable int next_worker_;
|
mutable int next_worker_;
|
||||||
QList<Worker> workers_;
|
QList<Worker> workers_;
|
||||||
|
|
||||||
QAtomicInt next_id_;
|
QAtomicInteger<qint64> next_id_;
|
||||||
|
|
||||||
QMutex message_queue_mutex_;
|
QMutex message_queue_mutex_;
|
||||||
QQueue<ReplyType*> message_queue_;
|
QQueue<ReplyType*> message_queue_;
|
||||||
@@ -170,6 +170,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
|||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
WorkerPool<HandlerType>::~WorkerPool() {
|
WorkerPool<HandlerType>::~WorkerPool() {
|
||||||
|
|
||||||
for (const Worker &worker : workers_) {
|
for (const Worker &worker : workers_) {
|
||||||
if (worker.local_socket_ && worker.process_) {
|
if (worker.local_socket_ && worker.process_) {
|
||||||
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
||||||
@@ -193,10 +194,11 @@ WorkerPool<HandlerType>::~WorkerPool() {
|
|||||||
for (ReplyType *reply : message_queue_) {
|
for (ReplyType *reply : message_queue_) {
|
||||||
reply->Abort();
|
reply->Abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SetWorkerCount(int count) {
|
void WorkerPool<HandlerType>::SetWorkerCount(const int count) {
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
worker_count_ = count;
|
worker_count_ = count;
|
||||||
}
|
}
|
||||||
@@ -220,6 +222,7 @@ void WorkerPool<HandlerType>::Start() {
|
|||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::DoStart() {
|
void WorkerPool<HandlerType>::DoStart() {
|
||||||
|
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
Q_ASSERT(!executable_name_.isEmpty());
|
Q_ASSERT(!executable_name_.isEmpty());
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
@@ -248,10 +251,12 @@ void WorkerPool<HandlerType>::DoStart() {
|
|||||||
|
|
||||||
workers_ << worker;
|
workers_ << worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||||
|
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
DeleteQObjectPointerLater(&worker->local_server_);
|
DeleteQObjectPointerLater(&worker->local_server_);
|
||||||
@@ -284,6 +289,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
|||||||
// Start the process
|
// Start the process
|
||||||
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
|
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
@@ -338,20 +344,24 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
|||||||
StartOneWorker(worker);
|
StartOneWorker(worker);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
typename WorkerPool<HandlerType>::ReplyType*
|
typename WorkerPool<HandlerType>::ReplyType*
|
||||||
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
||||||
const int id = next_id_.fetchAndAddOrdered(1);
|
|
||||||
|
const qint64 id = next_id_.fetchAndAddOrdered(1);
|
||||||
message->set_id(id);
|
message->set_id(id);
|
||||||
|
|
||||||
return new ReplyType(*message);
|
return new ReplyType(*message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
typename WorkerPool<HandlerType>::ReplyType*
|
typename WorkerPool<HandlerType>::ReplyType*
|
||||||
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||||
|
|
||||||
ReplyType *reply = NewReply(message);
|
ReplyType *reply = NewReply(message);
|
||||||
|
|
||||||
// Add the pending reply to the queue
|
// Add the pending reply to the queue
|
||||||
@@ -364,10 +374,12 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
|||||||
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SendQueuedMessages() {
|
void WorkerPool<HandlerType>::SendQueuedMessages() {
|
||||||
|
|
||||||
QMutexLocker l(&message_queue_mutex_);
|
QMutexLocker l(&message_queue_mutex_);
|
||||||
|
|
||||||
while (!message_queue_.isEmpty()) {
|
while (!message_queue_.isEmpty()) {
|
||||||
@@ -384,10 +396,12 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
|
|||||||
|
|
||||||
handler->SendRequest(reply);
|
handler->SendRequest(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
||||||
|
|
||||||
for (int i = 0; i < workers_.count(); ++i) {
|
for (int i = 0; i < workers_.count(); ++i) {
|
||||||
const int worker_index = (next_worker_ + i) % workers_.count();
|
const int worker_index = (next_worker_ + i) % workers_.count();
|
||||||
|
|
||||||
@@ -398,6 +412,7 @@ HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WORKERPOOL_H
|
#endif // WORKERPOOL_H
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ link_directories(
|
|||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${PROTOBUF_LIBRARY_DIRS}
|
${PROTOBUF_LIBRARY_DIRS}
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
${Qt5Core_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
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
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${QtCore_INCLUDE_DIRS}
|
|
||||||
${QtNetwork_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||||
@@ -38,7 +35,3 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
|||||||
${QtNetwork_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
)
|
)
|
||||||
|
|
||||||
if(BUILD_WITH_QT6)
|
|
||||||
target_link_libraries(libstrawberry-tagreader PRIVATE Qt6::Core5Compat)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -84,7 +84,6 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
@@ -138,6 +137,15 @@ TagReader::~TagReader() {
|
|||||||
delete factory_;
|
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 {
|
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;
|
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();
|
TagLib::Tag *tag = fileref->tag();
|
||||||
if (tag) {
|
if (tag) {
|
||||||
Decode(tag->title(), nullptr, song->mutable_title());
|
Decode(tag->title(), song->mutable_title());
|
||||||
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
|
Decode(tag->artist(), song->mutable_artist()); // TPE1
|
||||||
Decode(tag->album(), nullptr, song->mutable_album());
|
Decode(tag->album(), song->mutable_album());
|
||||||
Decode(tag->genre(), nullptr, song->mutable_genre());
|
Decode(tag->genre(), song->mutable_genre());
|
||||||
song->set_year(tag->year());
|
song->set_year(tag->year());
|
||||||
song->set_track(tag->track());
|
song->set_track(tag->track());
|
||||||
song->set_valid(true);
|
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;
|
// 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.
|
// 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())) {
|
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()) {
|
if (!tag_ogg->pictureList().isEmpty()) {
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
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());
|
song->set_bitdepth(file_flac->audioProperties()->bitsPerSample());
|
||||||
|
|
||||||
if (file_flac->xiphComment()) {
|
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()) {
|
if (!file_flac->pictureList().isEmpty()) {
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
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())) {
|
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
||||||
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
|
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
|
||||||
if (file_wavpack->APETag()) {
|
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())) {
|
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||||
if (file_ape->APETag()) {
|
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());
|
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())) {
|
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();
|
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
|
||||||
|
|
||||||
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
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
|
// 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
|
// 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
|
// 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
|
// Skip TPE1 (which is the artist) here because we already fetched it
|
||||||
|
|
||||||
|
|
||||||
// non-standard: Apple, Microsoft
|
// 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();
|
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()) {
|
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()) {
|
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);
|
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||||
|
|
||||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
// 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]);
|
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||||
|
|
||||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
||||||
Decode(frame->text(), nullptr, song->mutable_comment());
|
Decode(frame->text(), song->mutable_comment());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +320,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
if (mp4_tag->item("aART").isValid()) {
|
if (mp4_tag->item("aART").isValid()) {
|
||||||
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
|
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
|
||||||
if (!album_artists.isEmpty()) {
|
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()) {
|
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()) {
|
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()) {
|
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()) {
|
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
|
||||||
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
|
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());
|
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
|
||||||
|
|
||||||
if (file_asf->tag()) {
|
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();
|
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()) {
|
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) {
|
else if (tag) {
|
||||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!disc.isEmpty()) {
|
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) {
|
void TagReader::Decode(const TagLib::String &tag, 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
QString tmp = TStringToQString(tag).trimmed();
|
||||||
output->assign(DataCommaSizeFromQString(tmp));
|
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));
|
||||||
output->assign(DataCommaSizeFromQString(tag));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const QString decoded(codec->toUnicode(tag.toUtf8()));
|
|
||||||
output->assign(DataCommaSizeFromQString(decoded));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
||||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
||||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
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());
|
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), song->mutable_albumartist());
|
||||||
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), codec, 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());
|
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());
|
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["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());
|
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), song->mutable_lyrics());
|
||||||
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), codec, 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 {
|
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
Q_UNUSED(codec);
|
|
||||||
|
|
||||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||||
if (it != map.end()) {
|
if (it != map.end()) {
|
||||||
TagLib::StringList album_artists = it->second.values();
|
TagLib::StringList album_artists = it->second.values();
|
||||||
if (!album_artists.isEmpty()) {
|
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")) {
|
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")) {
|
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")) {
|
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")) {
|
if (map.contains("LYRICS")) {
|
||||||
Decode(map["LYRICS"].toString(), nullptr, song->mutable_lyrics());
|
Decode(map["LYRICS"].toString(), song->mutable_lyrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("FMPS_PLAYCOUNT")) {
|
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("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), 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("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), 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
|
// 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));;
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||||
if (!fileref || fileref->isNull()) return false;
|
if (!fileref || fileref->isNull()) return false;
|
||||||
|
|
||||||
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
|
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
|
||||||
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
|
fileref->tag()->setArtist(song.artist().empty() ? TagLib::String() : StdStringToTaglibString(song.artist()));
|
||||||
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
|
fileref->tag()->setAlbum(song.album().empty() ? TagLib::String() : StdStringToTaglibString(song.album()));
|
||||||
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
|
fileref->tag()->setGenre(song.genre().empty() ? TagLib::String() : StdStringToTaglibString(song.genre()));
|
||||||
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
|
fileref->tag()->setComment(song.comment().empty() ? TagLib::String() : StdStringToTaglibString(song.comment()));
|
||||||
fileref->tag()->setYear(song.year());
|
fileref->tag()->setYear(song.year() <= 0 ? 0 : song.year());
|
||||||
fileref->tag()->setTrack(song.track());
|
fileref->tag()->setTrack(song.track() <= 0 ? 0 : song.track());
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
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())) {
|
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||||
if (!tag) return false;
|
if (!tag) return false;
|
||||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
||||||
SetTextFrame("TCOM", song.composer(), tag);
|
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
||||||
SetTextFrame("TIT1", song.grouping(), tag);
|
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
||||||
SetTextFrame("TOPE", song.performer(), tag);
|
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
||||||
// Skip TPE1 (which is the artist) here because we already set it
|
// Skip TPE1 (which is the artist) here because we already set it
|
||||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
||||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
||||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
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);
|
SetVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = fileref->save();
|
result = fileref->save();
|
||||||
#ifdef Q_OS_LINUX
|
#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)
|
// 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);
|
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_LINUX
|
#endif // Q_OS_LINUX
|
||||||
|
|
||||||
return ret;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
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("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("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("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("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
||||||
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
||||||
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
|
tag->addValue("compilation", QStringToTaglibString(song.compilation() ? QString::number(1) : QString()), true);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,6 +618,7 @@ void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2
|
|||||||
|
|
||||||
const QByteArray utf8(value.toUtf8());
|
const QByteArray utf8(value.toUtf8());
|
||||||
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
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());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
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
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// 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 {
|
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return QByteArray();
|
if (filename.isEmpty()) return QByteArray();
|
||||||
@@ -711,8 +671,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||||||
if (flac_file && flac_file->xiphComment()) {
|
if (flac_file && flac_file->xiphComment()) {
|
||||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||||
if (!pics.isEmpty()) {
|
if (!pics.isEmpty()) {
|
||||||
// Use the first picture in the file - this could be made cleverer and
|
// Use the first picture in the file - this could be made cleverer and pick the front cover if it's present.
|
||||||
// pick the front cover if it's present.
|
|
||||||
|
|
||||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||||
TagLib::FLAC::Picture *picture = *it;
|
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");
|
TagLib::ByteVector id_vector("USLT");
|
||||||
QVector<TagLib::ByteVector> frames_buffer;
|
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());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
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
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// add frame takes ownership and clears the memory
|
||||||
|
|||||||
@@ -35,8 +35,6 @@
|
|||||||
|
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
class QTextCodec;
|
|
||||||
|
|
||||||
#ifndef USE_SYSTEM_TAGLIB
|
#ifndef USE_SYSTEM_TAGLIB
|
||||||
using namespace Strawberry_TagLib;
|
using namespace Strawberry_TagLib;
|
||||||
#endif
|
#endif
|
||||||
@@ -52,27 +50,24 @@ class TagReader {
|
|||||||
explicit TagReader();
|
explicit TagReader();
|
||||||
~TagReader();
|
~TagReader();
|
||||||
|
|
||||||
|
bool IsMediaFile(const QString &filename) const;
|
||||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
||||||
bool SaveFile(const QString &filename, const 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 LoadEmbeddedArt(const QString &filename) const;
|
||||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) 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 TagLib::String &tag, std::string *output);
|
||||||
static void Decode(const QString &tag, const QTextCodec *codec, 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 ParseOggTag(const TagLib::Ogg::FieldListMap &map, 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 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 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 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 QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetTextFrame(const char *id, const std::string &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;
|
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ message LoadEmbeddedArtResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Message {
|
message Message {
|
||||||
optional int32 id = 1;
|
optional int64 id = 1;
|
||||||
|
|
||||||
optional ReadFileRequest read_file_request = 2;
|
optional ReadFileRequest read_file_request = 2;
|
||||||
optional ReadFileResponse read_file_response = 3;
|
optional ReadFileResponse read_file_response = 3;
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ endif()
|
|||||||
link_directories(
|
link_directories(
|
||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
${QtCore_LIBRARY_DIRS}
|
|
||||||
${QtNetwork_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
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
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${QtCore_INCLUDE_DIRS}
|
|
||||||
${QtNetwork_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(strawberry-tagreader PRIVATE
|
target_include_directories(strawberry-tagreader PRIVATE
|
||||||
|
|||||||
@@ -27,11 +27,9 @@
|
|||||||
#include "tagreaderworker.h"
|
#include "tagreaderworker.h"
|
||||||
|
|
||||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
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;
|
pb::tagreader::Message reply;
|
||||||
|
|
||||||
@@ -51,6 +49,7 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SendReply(message, &reply);
|
SendReply(message, &reply);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,14 @@
|
|||||||
class QIODevice;
|
class QIODevice;
|
||||||
|
|
||||||
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
|
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
|
||||||
public:
|
public:
|
||||||
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
|
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void MessageArrived(const pb::tagreader::Message &message) override;
|
void MessageArrived(const pb::tagreader::Message &message) override;
|
||||||
void DeviceClosed() override;
|
void DeviceClosed() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TagReader tag_reader_;
|
TagReader tag_reader_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ set(SOURCES
|
|||||||
core/multisortfilterproxy.cpp
|
core/multisortfilterproxy.cpp
|
||||||
core/musicstorage.cpp
|
core/musicstorage.cpp
|
||||||
core/network.cpp
|
core/network.cpp
|
||||||
|
core/threadsafenetworkdiskcache.cpp
|
||||||
core/networktimeouts.cpp
|
core/networktimeouts.cpp
|
||||||
core/networkproxyfactory.cpp
|
core/networkproxyfactory.cpp
|
||||||
core/qtfslistener.cpp
|
core/qtfslistener.cpp
|
||||||
@@ -257,6 +258,7 @@ set(HEADERS
|
|||||||
core/filesystemwatcherinterface.h
|
core/filesystemwatcherinterface.h
|
||||||
core/mergedproxymodel.h
|
core/mergedproxymodel.h
|
||||||
core/network.h
|
core/network.h
|
||||||
|
core/threadsafenetworkdiskcache.h
|
||||||
core/networktimeouts.h
|
core/networktimeouts.h
|
||||||
core/qtfslistener.h
|
core/qtfslistener.h
|
||||||
core/songloader.h
|
core/songloader.h
|
||||||
@@ -548,19 +550,19 @@ if(HAVE_GLOBALSHORTCUTS)
|
|||||||
HEADERS globalshortcuts/globalshortcuts.h globalshortcuts/globalshortcutbackend.h globalshortcuts/globalshortcutgrabber.h settings/shortcutssettingspage.h
|
HEADERS globalshortcuts/globalshortcuts.h globalshortcuts/globalshortcutbackend.h globalshortcuts/globalshortcutgrabber.h settings/shortcutssettingspage.h
|
||||||
UI globalshortcuts/globalshortcutgrabber.ui settings/shortcutssettingspage.ui
|
UI globalshortcuts/globalshortcutgrabber.ui settings/shortcutssettingspage.ui
|
||||||
)
|
)
|
||||||
if (X11_FOUND OR WIN32)
|
if(HAVE_X11EXTRAS OR WIN32)
|
||||||
set(X11_OR_WIN ON)
|
set(X11_OR_WIN ON)
|
||||||
endif()
|
endif()
|
||||||
optional_source(X11_OR_WIN
|
optional_source(X11_OR_WIN
|
||||||
SOURCES globalshortcuts/globalshortcutbackend-system.cpp globalshortcuts/globalshortcut.cpp
|
SOURCES globalshortcuts/globalshortcutbackend-system.cpp globalshortcuts/globalshortcut.cpp
|
||||||
HEADERS globalshortcuts/globalshortcutbackend-system.h globalshortcuts/globalshortcut.h
|
HEADERS globalshortcuts/globalshortcutbackend-system.h globalshortcuts/globalshortcut.h
|
||||||
)
|
)
|
||||||
optional_source(X11_FOUND
|
optional_source(HAVE_X11EXTRAS
|
||||||
SOURCES globalshortcuts/globalshortcut-x11.cpp
|
SOURCES globalshortcuts/globalshortcut-x11.cpp
|
||||||
)
|
)
|
||||||
optional_source(HAVE_DBUS
|
optional_source(HAVE_DBUS
|
||||||
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp
|
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp globalshortcuts/globalshortcutbackend-kde.cpp
|
||||||
HEADERS globalshortcuts/globalshortcutbackend-gsd.h
|
HEADERS globalshortcuts/globalshortcutbackend-gsd.h globalshortcuts/globalshortcutbackend-kde.h
|
||||||
)
|
)
|
||||||
optional_source(WIN32
|
optional_source(WIN32
|
||||||
SOURCES globalshortcuts/globalshortcut-win.cpp
|
SOURCES globalshortcuts/globalshortcut-win.cpp
|
||||||
@@ -634,6 +636,14 @@ if(UNIX AND HAVE_DBUS)
|
|||||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||||
dbus/gnomesettingsdaemon)
|
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()
|
else()
|
||||||
|
|
||||||
# MPRIS 2.0 DBUS interfaces
|
# MPRIS 2.0 DBUS interfaces
|
||||||
@@ -662,6 +672,14 @@ if(UNIX AND HAVE_DBUS)
|
|||||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||||
dbus/gnomesettingsdaemon)
|
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()
|
endif()
|
||||||
|
|
||||||
# org.freedesktop.Avahi.Server interface
|
# org.freedesktop.Avahi.Server interface
|
||||||
@@ -1033,7 +1051,6 @@ link_directories(
|
|||||||
${GNUTLS_LIBRARY_DIRS}
|
${GNUTLS_LIBRARY_DIRS}
|
||||||
${SQLITE_LIBRARY_DIRS}
|
${SQLITE_LIBRARY_DIRS}
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
${QT_LIBRARY_DIRS}
|
|
||||||
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
||||||
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
|
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
|
||||||
${QTSPARKLE_LIBRARY_DIRS}
|
${QTSPARKLE_LIBRARY_DIRS}
|
||||||
@@ -1108,7 +1125,6 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
|
|||||||
${GOBJECT_INCLUDE_DIRS}
|
${GOBJECT_INCLUDE_DIRS}
|
||||||
${GNUTLS_INCLUDE_DIRS}
|
${GNUTLS_INCLUDE_DIRS}
|
||||||
${SQLITE_INCLUDE_DIRS}
|
${SQLITE_INCLUDE_DIRS}
|
||||||
${QT_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(strawberry_lib PUBLIC
|
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_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
|
||||||
connect(app_->player(), SIGNAL(Stopped()), SLOT(Stopped()));
|
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)));
|
connect(app_->lastfm_import(), SIGNAL(UpdatePlayCount(QString, QString, int)), backend_, SLOT(UpdatePlayCount(QString, QString, int)));
|
||||||
|
|
||||||
// This will start the watcher checking for updates
|
// 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);
|
SongList songs = GetSongsBy(artist, album, title);
|
||||||
if (songs.isEmpty()) {
|
if (songs.isEmpty()) {
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||||
|
|
||||||
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
|
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 UpdatePlayCount(const QString &artist, const QString &title, const int playcount);
|
||||||
|
|
||||||
void UpdateSongRating(const int id, const float rating);
|
void UpdateSongRating(const int id, const float rating);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QMetaType>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -892,7 +893,11 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, const bool signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CollectionModel::ResetAsync() {
|
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);
|
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
|
// Create the divider entry if we're supposed to
|
||||||
if (create_divider && show_dividers_) {
|
if (create_divider && show_dividers_) {
|
||||||
QString divider_key = DividerKey(type, item);
|
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()))
|
if (!divider_key.isEmpty()) {
|
||||||
item->sort_text.prepend(divider_key);
|
item->sort_text.prepend(divider_key + " ");
|
||||||
|
}
|
||||||
|
|
||||||
if (!divider_key.isEmpty() && !divider_nodes_.contains(divider_key)) {
|
if (!divider_key.isEmpty() && !divider_nodes_.contains(divider_key)) {
|
||||||
if (signal)
|
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_);
|
CollectionItem *divider = new CollectionItem(CollectionItem::Type_Divider, root_);
|
||||||
divider->key = divider_key;
|
divider->key = divider_key;
|
||||||
divider->display_text = DividerDisplayText(type, 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->lazy_loaded = true;
|
||||||
|
|
||||||
divider_nodes_[divider_key] = divider;
|
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 left(data(a, CollectionModel::Role_SortText));
|
||||||
QVariant right(data(b, CollectionModel::Role_SortText));
|
QVariant right(data(b, CollectionModel::Role_SortText));
|
||||||
|
|
||||||
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
return left.toString() < right.toString();
|
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
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QMetaType>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -133,10 +134,20 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
// 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) {
|
if (value.type() == QVariant::Int) {
|
||||||
|
#endif
|
||||||
where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString());
|
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);
|
where_clauses_ << QString("%1 %2 ?").arg(column, op);
|
||||||
bound_values_ << QString("");
|
bound_values_ << QString("");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
|||||||
scan_on_startup_(true),
|
scan_on_startup_(true),
|
||||||
monitor_(true),
|
monitor_(true),
|
||||||
mark_songs_unavailable_(false),
|
mark_songs_unavailable_(false),
|
||||||
live_scanning_(false),
|
|
||||||
stop_requested_(false),
|
stop_requested_(false),
|
||||||
rescan_in_progress_(false),
|
rescan_in_progress_(false),
|
||||||
rescan_timer_(new QTimer(this)),
|
rescan_timer_(new QTimer(this)),
|
||||||
@@ -480,8 +479,6 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
|||||||
|
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
|
|
||||||
if (live_scanning_) t->CommitNewOrUpdatedSongs();
|
|
||||||
|
|
||||||
// Recurse into the new subdirs that we found
|
// Recurse into the new subdirs that we found
|
||||||
t->AddToProgressMax(my_new_subdirs.count());
|
t->AddToProgressMax(my_new_subdirs.count());
|
||||||
for (const Subdirectory &my_new_subdir : my_new_subdirs) {
|
for (const Subdirectory &my_new_subdir : my_new_subdirs) {
|
||||||
|
|||||||
@@ -195,7 +195,6 @@ class CollectionWatcher : public QObject {
|
|||||||
bool scan_on_startup_;
|
bool scan_on_startup_;
|
||||||
bool monitor_;
|
bool monitor_;
|
||||||
bool mark_songs_unavailable_;
|
bool mark_songs_unavailable_;
|
||||||
bool live_scanning_;
|
|
||||||
|
|
||||||
bool stop_requested_;
|
bool stop_requested_;
|
||||||
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.
|
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.
|
||||||
|
|||||||
@@ -64,4 +64,8 @@
|
|||||||
#cmakedefine INSTALL_TRANSLATIONS
|
#cmakedefine INSTALL_TRANSLATIONS
|
||||||
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/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
|
#endif // CONFIG_H_IN
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
|
#include <QMetaType>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -444,8 +445,13 @@ bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionI
|
|||||||
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
|
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
|
||||||
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
|
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
|
||||||
|
|
||||||
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
return left.toString() < right.toString();
|
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() {
|
void ContextAlbumsView::CopyToDevice() {
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
if (!organize_dialog_)
|
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_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
||||||
organize_dialog_->SetCopy(true);
|
organize_dialog_->SetCopy(true);
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ ContextView::ContextView(QWidget *parent) :
|
|||||||
label_top_->setWordWrap(true);
|
label_top_->setWordWrap(true);
|
||||||
label_top_->setMinimumHeight(50);
|
label_top_->setMinimumHeight(50);
|
||||||
label_top_->setContentsMargins(0, 0, 32, 0);
|
label_top_->setContentsMargins(0, 0, 32, 0);
|
||||||
|
label_top_->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
|
||||||
layout_scrollarea_->setObjectName("context-layout-scrollarea");
|
layout_scrollarea_->setObjectName("context-layout-scrollarea");
|
||||||
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
|
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
|
||||||
@@ -485,7 +486,7 @@ void ContextView::UpdateFonts() {
|
|||||||
void ContextView::SetSong() {
|
void ContextView::SetSong() {
|
||||||
|
|
||||||
label_top_->setStyleSheet(QString("font: %2pt \"%1\"; font-weight: regular;").arg(font_headline_).arg(font_size_headline_));
|
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();
|
label_stop_summary_->clear();
|
||||||
|
|
||||||
@@ -643,7 +644,7 @@ void ContextView::SetSong() {
|
|||||||
|
|
||||||
void ContextView::UpdateSong(const Song &song) {
|
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 (action_show_data_->isChecked()) {
|
||||||
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());
|
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());
|
||||||
|
|||||||
@@ -44,36 +44,37 @@ const char *CommandlineOptions::kHelpText =
|
|||||||
"%1: strawberry [%2] [%3]\n"
|
"%1: strawberry [%2] [%3]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"%4:\n"
|
"%4:\n"
|
||||||
" -p, --play %5\n"
|
" -p, --play %5\n"
|
||||||
" -t, --play-pause %6\n"
|
" -t, --play-pause %6\n"
|
||||||
" -u, --pause %7\n"
|
" -u, --pause %7\n"
|
||||||
" -s, --stop %8\n"
|
" -s, --stop %8\n"
|
||||||
" -q, --stop-after-current %9\n"
|
" -q, --stop-after-current %9\n"
|
||||||
" -r, --previous %10\n"
|
" -r, --previous %10\n"
|
||||||
" -f, --next %11\n"
|
" -f, --next %11\n"
|
||||||
" -v, --volume <value> %12\n"
|
" -v, --volume <value> %12\n"
|
||||||
" --volume-up %13\n"
|
" --volume-up %13\n"
|
||||||
" --volume-down %14\n"
|
" --volume-down %14\n"
|
||||||
" --volume-increase-by %15\n"
|
" --volume-increase-by %15\n"
|
||||||
" --volume-decrease-by %16\n"
|
" --volume-decrease-by %16\n"
|
||||||
" --seek-to <seconds> %17\n"
|
" --seek-to <seconds> %17\n"
|
||||||
" --seek-by <seconds> %18\n"
|
" --seek-by <seconds> %18\n"
|
||||||
" --restart-or-previous %19\n"
|
" --restart-or-previous %19\n"
|
||||||
"\n"
|
"\n"
|
||||||
"%20:\n"
|
"%20:\n"
|
||||||
" -c, --create <name> %21\n"
|
" -c, --create <name> %21\n"
|
||||||
" -a, --append %22\n"
|
" -a, --append %22\n"
|
||||||
" -l, --load %23\n"
|
" -l, --load %23\n"
|
||||||
" -k, --play-track <n> %24\n"
|
" -k, --play-track <n> %24\n"
|
||||||
|
" -i, --play-playlist <name> %25\n"
|
||||||
"\n"
|
"\n"
|
||||||
"%25:\n"
|
"%26:\n"
|
||||||
" -o, --show-osd %26\n"
|
" -o, --show-osd %27\n"
|
||||||
" -y, --toggle-pretty-osd %27\n"
|
" -y, --toggle-pretty-osd %28\n"
|
||||||
" -g, --language <lang> %28\n"
|
" -g, --language <lang> %29\n"
|
||||||
" --quiet %29\n"
|
" --quiet %30\n"
|
||||||
" --verbose %30\n"
|
" --verbose %31\n"
|
||||||
" --log-levels <levels> %31\n"
|
" --log-levels <levels> %32\n"
|
||||||
" --version %32\n";
|
" --version %33\n";
|
||||||
|
|
||||||
const char *CommandlineOptions::kVersionText = "Strawberry %1";
|
const char *CommandlineOptions::kVersionText = "Strawberry %1";
|
||||||
|
|
||||||
@@ -138,6 +139,7 @@ bool CommandlineOptions::Parse() {
|
|||||||
{"append", no_argument, nullptr, 'a'},
|
{"append", no_argument, nullptr, 'a'},
|
||||||
{"load", no_argument, nullptr, 'l'},
|
{"load", no_argument, nullptr, 'l'},
|
||||||
{"play-track", required_argument, nullptr, 'k'},
|
{"play-track", required_argument, nullptr, 'k'},
|
||||||
|
{"play-playlist", required_argument, nullptr, 'i'},
|
||||||
{"show-osd", no_argument, nullptr, 'o'},
|
{"show-osd", no_argument, nullptr, 'o'},
|
||||||
{"toggle-pretty-osd", no_argument, nullptr, 'y'},
|
{"toggle-pretty-osd", no_argument, nullptr, 'y'},
|
||||||
{"language", required_argument, nullptr, 'g'},
|
{"language", required_argument, nullptr, 'g'},
|
||||||
@@ -150,7 +152,7 @@ bool CommandlineOptions::Parse() {
|
|||||||
// Parse the arguments
|
// Parse the arguments
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
forever {
|
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
|
// End of the options
|
||||||
if (c == -1) break;
|
if (c == -1) break;
|
||||||
@@ -179,7 +181,8 @@ bool CommandlineOptions::Parse() {
|
|||||||
tr("Create a new playlist with files"),
|
tr("Create a new playlist with files"),
|
||||||
tr("Append files/URLs to the playlist"),
|
tr("Append files/URLs to the playlist"),
|
||||||
tr("Loads files/URLs, replacing current 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"),
|
.arg(tr("Other options"), tr("Display the on-screen-display"),
|
||||||
tr("Toggle visibility for the pretty on-screen-display"),
|
tr("Toggle visibility for the pretty on-screen-display"),
|
||||||
tr("Change the language"),
|
tr("Change the language"),
|
||||||
@@ -213,6 +216,10 @@ bool CommandlineOptions::Parse() {
|
|||||||
case 'f':
|
case 'f':
|
||||||
player_action_ = Player_Next;
|
player_action_ = Player_Next;
|
||||||
break;
|
break;
|
||||||
|
case 'i':
|
||||||
|
player_action_ = Player_PlayPlaylist;
|
||||||
|
playlist_name_ = QString(optarg);
|
||||||
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
url_list_action_ = UrlList_CreateNew;
|
url_list_action_ = UrlList_CreateNew;
|
||||||
playlist_name_ = QString(optarg);
|
playlist_name_ = QString(optarg);
|
||||||
@@ -362,7 +369,8 @@ QDataStream& operator<<(QDataStream &s, const CommandlineOptions &a) {
|
|||||||
<< a.show_osd_
|
<< a.show_osd_
|
||||||
<< a.urls_
|
<< a.urls_
|
||||||
<< a.log_levels_
|
<< a.log_levels_
|
||||||
<< a.toggle_pretty_osd_;
|
<< a.toggle_pretty_osd_
|
||||||
|
<< a.playlist_name_;
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
@@ -382,7 +390,8 @@ QDataStream& operator>>(QDataStream &s, CommandlineOptions &a) {
|
|||||||
>> a.show_osd_
|
>> a.show_osd_
|
||||||
>> a.urls_
|
>> a.urls_
|
||||||
>> a.log_levels_
|
>> a.log_levels_
|
||||||
>> a.toggle_pretty_osd_;
|
>> a.toggle_pretty_osd_
|
||||||
|
>> a.playlist_name_;
|
||||||
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
|
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
|
||||||
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);
|
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class CommandlineOptions {
|
|||||||
Player_Next = 6,
|
Player_Next = 6,
|
||||||
Player_RestartOrPrevious = 7,
|
Player_RestartOrPrevious = 7,
|
||||||
Player_StopAfterCurrent = 8,
|
Player_StopAfterCurrent = 8,
|
||||||
|
Player_PlayPlaylist = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Parse();
|
bool Parse();
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ QSqlDatabase Database::Connect() {
|
|||||||
if (db.isOpen()) {
|
if (db.isOpen()) {
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
db.setConnectOptions("QSQLITE_BUSY_TIMEOUT=30000");
|
||||||
//qLog(Debug) << "Opened database with connection id" << connection_id;
|
//qLog(Debug) << "Opened database with connection id" << connection_id;
|
||||||
|
|
||||||
if (!injected_database_name_.isNull())
|
if (!injected_database_name_.isNull())
|
||||||
@@ -518,11 +519,13 @@ void Database::DoBackup() {
|
|||||||
|
|
||||||
QSqlDatabase db(this->Connect());
|
QSqlDatabase db(this->Connect());
|
||||||
|
|
||||||
|
if (!db.isOpen()) return;
|
||||||
|
|
||||||
// Before we overwrite anything, make sure the database is not corrupt
|
// Before we overwrite anything, make sure the database is not corrupt
|
||||||
QMutexLocker l(&mutex_);
|
QMutexLocker l(&mutex_);
|
||||||
const bool ok = IntegrityCheck(db);
|
|
||||||
|
|
||||||
if (ok) {
|
const bool ok = IntegrityCheck(db);
|
||||||
|
if (ok && SchemaVersion(&db) == kSchemaVersion) {
|
||||||
BackupFile(db.databaseName());
|
BackupFile(db.databaseName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPixmap normal_icon_;
|
QPixmap normal_icon_;
|
||||||
|
QPixmap grey_icon_;
|
||||||
std::unique_ptr<MacSystemTrayIconPrivate> p_;
|
std::unique_ptr<MacSystemTrayIconPrivate> p_;
|
||||||
Q_DISABLE_COPY(MacSystemTrayIcon);
|
Q_DISABLE_COPY(MacSystemTrayIcon);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ class MacSystemTrayIconPrivate {
|
|||||||
|
|
||||||
MacSystemTrayIcon::MacSystemTrayIcon(QObject* parent)
|
MacSystemTrayIcon::MacSystemTrayIcon(QObject* parent)
|
||||||
: SystemTrayIcon(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_);
|
QApplication::setWindowIcon(normal_icon_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1023,6 +1023,7 @@ void MainWindow::ReloadSettings() {
|
|||||||
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
||||||
keep_running_ = s.value("keeprunning", false).toBool();
|
keep_running_ = s.value("keeprunning", false).toBool();
|
||||||
playing_widget_ = s.value("playing_widget", true).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();
|
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
|
||||||
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(s.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
|
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(s.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
|
||||||
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_Never).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();
|
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
|
if (tray_icon_) tray_icon_->SetTrayiconProgress(trayicon_progress);
|
||||||
|
|
||||||
ui_->back_button->setIconSize(QSize(iconsize, iconsize));
|
ui_->back_button->setIconSize(QSize(iconsize, iconsize));
|
||||||
ui_->pause_play_button->setIconSize(QSize(iconsize, iconsize));
|
ui_->pause_play_button->setIconSize(QSize(iconsize, iconsize));
|
||||||
ui_->stop_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;
|
if (!idx.isValid()) return;
|
||||||
|
|
||||||
int row = idx.row();
|
QModelIndex source_idx = idx;
|
||||||
if (idx.model() == app_->playlist_manager()->current()->proxy()) {
|
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.
|
// 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_) {
|
switch (doubleclick_playlist_addmode_) {
|
||||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Play:
|
case BehaviourSettingsPage::PlaylistAddBehaviour_Play:
|
||||||
app_->playlist_manager()->SetActiveToCurrent();
|
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;
|
break;
|
||||||
|
|
||||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Enqueue:
|
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) {
|
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);
|
app_->player()->PlayAt(app_->playlist_manager()->current()->queue()->TakeNext(), Engine::Manual, Playlist::AutoScroll_Never, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1530,19 +1534,25 @@ void MainWindow::showEvent(QShowEvent *e) {
|
|||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *e) {
|
void MainWindow::closeEvent(QCloseEvent *e) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
Exit();
|
||||||
|
#else
|
||||||
if (!hidden_ && keep_running_ && e->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable()) {
|
if (!hidden_ && keep_running_ && e->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||||
e->ignore();
|
|
||||||
SetHiddenInTray(true);
|
SetHiddenInTray(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QMainWindow::closeEvent(e);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::SetHiddenInTray(const bool hidden) {
|
void MainWindow::SetHiddenInTray(const bool hidden) {
|
||||||
|
|
||||||
hidden_ = 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.
|
// Some window managers don't remember maximized state between calls to hide() and show(), so we have to remember it ourself.
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
@@ -2265,6 +2275,14 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
|
|||||||
case CommandlineOptions::Player_Next:
|
case CommandlineOptions::Player_Next:
|
||||||
app_->player()->Next();
|
app_->player()->Next();
|
||||||
break;
|
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:
|
case CommandlineOptions::Player_RestartOrPrevious:
|
||||||
app_->player()->RestartOrPrevious();
|
app_->player()->RestartOrPrevious();
|
||||||
break;
|
break;
|
||||||
@@ -2531,9 +2549,9 @@ void MainWindow::PlaylistCopyUrl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (urls.count() > 0) {
|
if (urls.count() > 0) {
|
||||||
QMimeData *mime_data = new QMimeData;
|
QMimeData mime_data;
|
||||||
mime_data->setUrls(urls);
|
mime_data.setUrls(urls);
|
||||||
QApplication::clipboard()->setMimeData(mime_data);
|
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)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
|
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
|
||||||
#else
|
#else
|
||||||
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) {
|
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Q_UNUSED(eventType);
|
if (exit_count_ == 0 && message) {
|
||||||
Q_UNUSED(result);
|
MSG *msg = static_cast<MSG*>(message);
|
||||||
|
thumbbar_->HandleWinEvent(msg);
|
||||||
#ifdef Q_OS_WIN
|
}
|
||||||
MSG *msg = static_cast<MSG*>(message);
|
return QMainWindow::nativeEvent(eventType, message, result);
|
||||||
thumbbar_->HandleWinEvent(msg);
|
|
||||||
#else
|
|
||||||
Q_UNUSED(message);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
void MainWindow::AutoCompleteTags() {
|
void MainWindow::AutoCompleteTags() {
|
||||||
|
|
||||||
|
|||||||
@@ -117,10 +117,12 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||||||
void showEvent(QShowEvent *e) override;
|
void showEvent(QShowEvent *e) override;
|
||||||
void closeEvent(QCloseEvent *e) override;
|
void closeEvent(QCloseEvent *e) override;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||||
#else
|
#else
|
||||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// PlatformInterface
|
// 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.
|
// Copied from the QSortFilterProxyModel::lessThan implementation, but returns -1, 0 or 1 instead of true or false.
|
||||||
switch (left.userType()) {
|
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::Invalid: return (right.type() != QVariant::Invalid) ? -1 : 0;
|
||||||
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
|
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
|
||||||
case QVariant::UInt: return DoCompare(left.toUInt(), right.toUInt());
|
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::Time: return DoCompare(left.toTime(), right.toTime());
|
||||||
case QVariant::DateTime: return DoCompare(left.toDateTime(), right.toDateTime());
|
case QVariant::DateTime: return DoCompare(left.toDateTime(), right.toDateTime());
|
||||||
case QVariant::String:
|
case QVariant::String:
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
if (isSortLocaleAware())
|
if (isSortLocaleAware())
|
||||||
return left.toString().localeAwareCompare(right.toString());
|
return left.toString().localeAwareCompare(right.toString());
|
||||||
|
|||||||
@@ -21,89 +21,18 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QMutex>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkDiskCache>
|
|
||||||
#include <QNetworkCacheMetaData>
|
|
||||||
#include <QAbstractNetworkCache>
|
|
||||||
|
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
#include "threadsafenetworkdiskcache.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkAccessManager::NetworkAccessManager(QObject *parent)
|
NetworkAccessManager::NetworkAccessManager(QObject *parent)
|
||||||
: QNetworkAccessManager(parent) {
|
: QNetworkAccessManager(parent) {
|
||||||
@@ -144,4 +73,5 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
|
|||||||
}
|
}
|
||||||
|
|
||||||
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,10 @@
|
|||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QAbstractNetworkCache>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkCacheMetaData>
|
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QNetworkDiskCache;
|
|
||||||
|
|
||||||
class NetworkAccessManager : public QNetworkAccessManager {
|
class NetworkAccessManager : public QNetworkAccessManager {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -47,25 +42,4 @@ class NetworkAccessManager : public QNetworkAccessManager {
|
|||||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override;
|
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
|
#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) {
|
bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
if (app_->playlist_manager()->active()->stop_after_current()) {
|
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.
|
// Skips this track. Might load more of the current radio station.
|
||||||
virtual void Next() = 0;
|
virtual void Next() = 0;
|
||||||
|
|
||||||
virtual void Previous() = 0;
|
virtual void Previous() = 0;
|
||||||
|
virtual void PlayPlaylist(const QString &playlist_name) = 0;
|
||||||
virtual void SetVolume(const int value) = 0;
|
virtual void SetVolume(const int value) = 0;
|
||||||
virtual void VolumeUp() = 0;
|
virtual void VolumeUp() = 0;
|
||||||
virtual void VolumeDown() = 0;
|
virtual void VolumeDown() = 0;
|
||||||
@@ -163,6 +163,7 @@ class Player : public PlayerInterface {
|
|||||||
void RestartOrPrevious() override;
|
void RestartOrPrevious() override;
|
||||||
void Next() override;
|
void Next() override;
|
||||||
void Previous() override;
|
void Previous() override;
|
||||||
|
void PlayPlaylist(const QString &playlist_name) override;
|
||||||
void SetVolume(const int value) override;
|
void SetVolume(const int value) override;
|
||||||
void VolumeUp() override { SetVolume(GetVolume() + 5); }
|
void VolumeUp() override { SetVolume(GetVolume() + 5); }
|
||||||
void VolumeDown() 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 PreviousItem(const Engine::TrackChangeFlags change);
|
||||||
|
|
||||||
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
|
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
|
||||||
|
void PlayPlaylistInternal(Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
||||||
|
|
||||||
void FatalError();
|
void FatalError();
|
||||||
void ValidSongRequested(const QUrl&);
|
void ValidSongRequested(const QUrl&);
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ QtSystemTrayIcon::QtSystemTrayIcon(QObject *parent)
|
|||||||
action_mute_(nullptr) {
|
action_mute_(nullptr) {
|
||||||
|
|
||||||
app_name_[0] = app_name_[0].toUpper();
|
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_->setIcon(normal_icon_);
|
||||||
tray_->installEventFilter(this);
|
tray_->installEventFilter(this);
|
||||||
ClearNowPlaying();
|
ClearNowPlaying();
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
@@ -745,14 +744,6 @@ void Song::set_genre_id3(int id) {
|
|||||||
set_genre(TStringToQString(TagLib::ID3v1::genre(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) {
|
void Song::InitFromProtobuf(const pb::tagreader::SongMetadata &pb) {
|
||||||
|
|
||||||
if (d->source_ == Source_Unknown) d->source_ = Source_LocalFile;
|
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 Song::TitleWithCompilationArtist() const {
|
||||||
|
|
||||||
QString title(d->title_);
|
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();
|
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) {
|
uint qHash(const Song &song) {
|
||||||
|
#endif
|
||||||
// Should compare the same fields as operator==
|
// Should compare the same fields as operator==
|
||||||
return qHash(song.url().toString()) ^ qHash(song.beginning_nanosec());
|
return qHash(song.url().toString()) ^ qHash(song.beginning_nanosec());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
class QTextCodec;
|
|
||||||
class QSqlQuery;
|
class QSqlQuery;
|
||||||
|
|
||||||
namespace Engine {
|
namespace Engine {
|
||||||
@@ -177,8 +176,6 @@ class Song {
|
|||||||
// Useful when you want updated tags from disk but you want to keep user stats.
|
// Useful when you want updated tags from disk but you want to keep user stats.
|
||||||
void MergeUserSetData(const Song &other);
|
void MergeUserSetData(const Song &other);
|
||||||
|
|
||||||
static QString Decode(const QString &tag, const QTextCodec *codec = nullptr);
|
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
void BindToQuery(QSqlQuery *query) const;
|
void BindToQuery(QSqlQuery *query) const;
|
||||||
void BindToFtsQuery(QSqlQuery *query) const;
|
void BindToFtsQuery(QSqlQuery *query) const;
|
||||||
@@ -285,6 +282,7 @@ class Song {
|
|||||||
QString PrettyTitleWithArtist() const;
|
QString PrettyTitleWithArtist() const;
|
||||||
QString PrettyLength() const;
|
QString PrettyLength() const;
|
||||||
QString PrettyYear() const;
|
QString PrettyYear() const;
|
||||||
|
QString PrettyOriginalYear() const;
|
||||||
|
|
||||||
QString TitleWithCompilationArtist() const;
|
QString TitleWithCompilationArtist() const;
|
||||||
|
|
||||||
@@ -381,7 +379,11 @@ Q_DECLARE_METATYPE(Song)
|
|||||||
typedef QList<Song> SongList;
|
typedef QList<Song> SongList;
|
||||||
Q_DECLARE_METATYPE(QList<Song>)
|
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);
|
uint qHash(const Song &song);
|
||||||
|
#endif
|
||||||
// Hash function using field checked in IsSimilar function
|
// Hash function using field checked in IsSimilar function
|
||||||
uint HashSimilar(const Song &song);
|
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());
|
errors_ << tr("Couldn't create gstreamer source element for %1").arg(url_.toString());
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
g_object_set(source, "ssl-strict", FALSE, nullptr);
|
||||||
|
|
||||||
// Create the other elements and link them up
|
// Create the other elements and link them up
|
||||||
GstElement *typefind = gst_element_factory_make("typefind", nullptr);
|
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) {
|
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &icon, const QPixmap &grey_icon) {
|
||||||
|
|
||||||
Q_UNUSED(grey_icon);
|
|
||||||
|
|
||||||
QRect rect(icon.rect());
|
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);
|
QPixmap ret(icon);
|
||||||
QPainter p(&ret);
|
QPainter p(&ret);
|
||||||
|
|
||||||
// Draw the grey bit
|
if (trayicon_progress_) {
|
||||||
//p.setClipRegion(mask);
|
// The angle of the line that's used to cover the icon.
|
||||||
//p.drawPixmap(0, 0, grey_icon);
|
// Centered on rect.topLeft()
|
||||||
//p.setClipping(false);
|
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
|
// Draw the playing or paused icon in the top-right
|
||||||
if (!current_state_icon().isNull()) {
|
if (!current_state_icon().isNull()) {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class SystemTrayIcon : public QObject {
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetProgress(int percentage);
|
void SetProgress(int percentage);
|
||||||
|
void SetTrayiconProgress(bool enabled) { trayicon_progress_ = enabled; }
|
||||||
virtual void SetPaused();
|
virtual void SetPaused();
|
||||||
virtual void SetPlaying(bool enable_play_pause = false);
|
virtual void SetPlaying(bool enable_play_pause = false);
|
||||||
virtual void SetStopped();
|
virtual void SetStopped();
|
||||||
@@ -85,6 +86,7 @@ class SystemTrayIcon : public QObject {
|
|||||||
QPixmap playing_icon_;
|
QPixmap playing_icon_;
|
||||||
QPixmap paused_icon_;
|
QPixmap paused_icon_;
|
||||||
QPixmap current_state_icon_;
|
QPixmap current_state_icon_;
|
||||||
|
bool trayicon_progress_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SYSTEMTRAYICON_H
|
#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_);
|
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_);
|
QMutexLocker l(&mutex_);
|
||||||
@@ -90,7 +90,7 @@ void TaskManager::SetTaskProgress(int id, int progress, int max) {
|
|||||||
emit TasksChanged();
|
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_);
|
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;
|
bool resume_collection_watchers = false;
|
||||||
|
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ class TaskManager : public QObject {
|
|||||||
explicit TaskManager(QObject *parent = nullptr);
|
explicit TaskManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
struct Task {
|
struct Task {
|
||||||
|
Task() : id(0), progress(0), progress_max(0), blocks_collection_scans(false) {}
|
||||||
int id;
|
int id;
|
||||||
QString name;
|
QString name;
|
||||||
int progress;
|
qint64 progress;
|
||||||
int progress_max;
|
qint64 progress_max;
|
||||||
bool blocks_collection_scans;
|
bool blocks_collection_scans;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,13 +62,13 @@ class TaskManager : public QObject {
|
|||||||
QList<Task> GetTasks();
|
QList<Task> GetTasks();
|
||||||
|
|
||||||
int StartTask(const QString &name);
|
int StartTask(const QString &name);
|
||||||
void SetTaskBlocksCollectionScans(int id);
|
void SetTaskBlocksCollectionScans(const int id);
|
||||||
void SetTaskProgress(int id, int progress, int max = 0);
|
void SetTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
|
||||||
void IncreaseTaskProgress(int id, int progress, int max = 0);
|
void IncreaseTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
|
||||||
void SetTaskFinished(int id);
|
void SetTaskFinished(const int id);
|
||||||
int GetTaskProgress(int id);
|
int GetTaskProgress(const int id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TasksChanged();
|
void TasksChanged();
|
||||||
|
|
||||||
void PauseCollectionWatchers();
|
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);
|
return quint64(fs_info.f_blocks) * quint64(fs_info.f_bsize);
|
||||||
#elif defined(Q_OS_WIN32)
|
#elif defined(Q_OS_WIN32)
|
||||||
_ULARGE_INTEGER ret;
|
_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;
|
return ret.QuadPart;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -231,7 +236,12 @@ quint64 FileSystemFreeSpace(const QString &path) {
|
|||||||
return quint64(fs_info.f_bavail) * quint64(fs_info.f_bsize);
|
return quint64(fs_info.f_bavail) * quint64(fs_info.f_bsize);
|
||||||
#elif defined(Q_OS_WIN32)
|
#elif defined(Q_OS_WIN32)
|
||||||
_ULARGE_INTEGER ret;
|
_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)
|
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), &ret, nullptr, nullptr) != 0)
|
||||||
|
# endif
|
||||||
return ret.QuadPart;
|
return ret.QuadPart;
|
||||||
#endif
|
#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]+[%]");
|
QRegularExpression variable_replacer("[%][a-z]+[%]");
|
||||||
QString copy(message);
|
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)) {
|
for (match = variable_replacer.match(message, pos) ; match.hasMatch() ; match = variable_replacer.match(message, pos)) {
|
||||||
pos = match.capturedStart();
|
pos = match.capturedStart();
|
||||||
QStringList captured = match.capturedTexts();
|
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();
|
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);
|
if (index_of >= 0) copy = copy.remove(index_of, 3);
|
||||||
|
|
||||||
return copy;
|
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;
|
QString value = variable;
|
||||||
if (variable == "%artist%") {
|
|
||||||
return song.artist().toHtmlEscaped();
|
if (variable == "%title%") {
|
||||||
|
value = song.PrettyTitle();
|
||||||
}
|
}
|
||||||
else if (variable == "%album%") {
|
else if (variable == "%album%") {
|
||||||
return song.album().toHtmlEscaped();
|
value = song.album();
|
||||||
}
|
}
|
||||||
else if (variable == "%title%") {
|
else if (variable == "%artist%") {
|
||||||
return song.PrettyTitle().toHtmlEscaped();
|
value = song.artist();
|
||||||
}
|
}
|
||||||
else if (variable == "%albumartist%") {
|
else if (variable == "%albumartist%") {
|
||||||
return song.effective_albumartist().toHtmlEscaped();
|
value = song.effective_albumartist();
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
else if (variable == "%track%") {
|
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%") {
|
else if (variable == "%genre%") {
|
||||||
return song.genre().toHtmlEscaped();
|
value = song.genre();
|
||||||
}
|
}
|
||||||
else if (variable == "%playcount%") {
|
else if (variable == "%composer%") {
|
||||||
return return_value.setNum(song.playcount()).toHtmlEscaped();
|
value = song.composer();
|
||||||
}
|
}
|
||||||
else if (variable == "%skipcount%") {
|
else if (variable == "%performer%") {
|
||||||
return return_value.setNum(song.skipcount()).toHtmlEscaped();
|
value = song.performer();
|
||||||
|
}
|
||||||
|
else if (variable == "%grouping%") {
|
||||||
|
value = song.grouping();
|
||||||
|
}
|
||||||
|
else if (variable == "%length%") {
|
||||||
|
value = song.PrettyLength();
|
||||||
}
|
}
|
||||||
else if (variable == "%filename%") {
|
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%") {
|
else if (variable == "%newline%") {
|
||||||
return QString(newline);
|
return QString(newline); // No HTML escaping, return immediately.
|
||||||
}
|
}
|
||||||
|
|
||||||
//if the variable is not recognized, just return it
|
if (html_escaped) {
|
||||||
return variable;
|
value = value.toHtmlEscaped();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsColorDark(const QColor &color) {
|
bool IsColorDark(const QColor &color) {
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ QString UnicodeToAscii(const QString &unicode);
|
|||||||
|
|
||||||
QString MacAddress();
|
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 = false);
|
||||||
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 = false);
|
||||||
|
|
||||||
bool IsColorDark(const QColor &color);
|
bool IsColorDark(const QColor &color);
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ void Windows7ThumbBar::HandleWinEvent(MSG *msg) {
|
|||||||
|
|
||||||
if (button_created_message_id_ == 0) {
|
if (button_created_message_id_ == 0) {
|
||||||
// Compute the value for the TaskbarButtonCreated message
|
// 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_;
|
qLog(Debug) << "TaskbarButtonCreated message ID registered" << button_created_message_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,7 +23,6 @@
|
|||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
|
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),
|
: QObject(parent),
|
||||||
cover_providers_(cover_providers),
|
cover_providers_(cover_providers),
|
||||||
network_(network ? network : new NetworkAccessManager(this)),
|
network_(network ? network : new NetworkAccessManager(this)),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class QNetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class CoverProviders;
|
class CoverProviders;
|
||||||
class AlbumCoverFetcherSearch;
|
class AlbumCoverFetcherSearch;
|
||||||
struct CoverSearchStatistics;
|
struct CoverSearchStatistics;
|
||||||
@@ -103,7 +104,7 @@ class AlbumCoverFetcher : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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;
|
~AlbumCoverFetcher() override;
|
||||||
|
|
||||||
static const int kMaxConcurrentRequests;
|
static const int kMaxConcurrentRequests;
|
||||||
@@ -126,7 +127,7 @@ class AlbumCoverFetcher : public QObject {
|
|||||||
void AddRequest(const CoverSearchRequest &req);
|
void AddRequest(const CoverSearchRequest &req);
|
||||||
|
|
||||||
CoverProviders *cover_providers_;
|
CoverProviders *cover_providers_;
|
||||||
QNetworkAccessManager *network_;
|
NetworkAccessManager *network_;
|
||||||
quint64 next_id_;
|
quint64 next_id_;
|
||||||
|
|
||||||
QQueue<CoverSearchRequest> queued_requests_;
|
QQueue<CoverSearchRequest> queued_requests_;
|
||||||
|
|||||||
@@ -34,13 +34,13 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/utilities.h"
|
#include "core/utilities.h"
|
||||||
|
#include "core/network.h"
|
||||||
#include "core/networktimeouts.h"
|
#include "core/networktimeouts.h"
|
||||||
#include "albumcoverfetcher.h"
|
#include "albumcoverfetcher.h"
|
||||||
#include "albumcoverfetchersearch.h"
|
#include "albumcoverfetchersearch.h"
|
||||||
@@ -52,8 +52,7 @@ const int AlbumCoverFetcherSearch::kImageLoadTimeoutMs = 6000;
|
|||||||
const int AlbumCoverFetcherSearch::kTargetSize = 500;
|
const int AlbumCoverFetcherSearch::kTargetSize = 500;
|
||||||
const float AlbumCoverFetcherSearch::kGoodScore = 4.0;
|
const float AlbumCoverFetcherSearch::kGoodScore = 4.0;
|
||||||
|
|
||||||
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(
|
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent)
|
||||||
const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent)
|
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
request_(request),
|
request_(request),
|
||||||
image_load_timeout_(new NetworkTimeouts(kImageLoadTimeoutMs, this)),
|
image_load_timeout_(new NetworkTimeouts(kImageLoadTimeoutMs, this)),
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
#include "albumcoverfetcher.h"
|
#include "albumcoverfetcher.h"
|
||||||
#include "coversearchstatistics.h"
|
#include "coversearchstatistics.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class CoverProvider;
|
class CoverProvider;
|
||||||
class CoverProviders;
|
class CoverProviders;
|
||||||
|
class NetworkAccessManager;
|
||||||
class NetworkTimeouts;
|
class NetworkTimeouts;
|
||||||
|
|
||||||
// This class encapsulates a single search for covers initiated by an AlbumCoverFetcher.
|
// This class encapsulates a single search for covers initiated by an AlbumCoverFetcher.
|
||||||
@@ -49,7 +49,7 @@ class AlbumCoverFetcherSearch : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent);
|
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent);
|
||||||
~AlbumCoverFetcherSearch() override;
|
~AlbumCoverFetcherSearch() override;
|
||||||
|
|
||||||
void Start(CoverProviders *cover_providers);
|
void Start(CoverProviders *cover_providers);
|
||||||
@@ -107,7 +107,7 @@ class AlbumCoverFetcherSearch : public QObject {
|
|||||||
typedef QPair<CoverSearchResult, QImage> CandidateImage;
|
typedef QPair<CoverSearchResult, QImage> CandidateImage;
|
||||||
QMultiMap<float, CandidateImage> candidate_images_;
|
QMultiMap<float, CandidateImage> candidate_images_;
|
||||||
|
|
||||||
QNetworkAccessManager *network_;
|
NetworkAccessManager *network_;
|
||||||
|
|
||||||
bool cancel_requested_;
|
bool cancel_requested_;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -621,6 +622,7 @@ void AlbumCoverManager::ShowCover() {
|
|||||||
if (!song.is_valid()) return;
|
if (!song.is_valid()) return;
|
||||||
|
|
||||||
album_cover_choice_controller_->ShowCover(song);
|
album_cover_choice_controller_->ShowCover(song);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlbumCoverManager::FetchSingleCover() {
|
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);
|
QListWidgetItem *item = static_cast<QListWidgetItem*>(idx.internalPointer());
|
||||||
if (mimedata) {
|
if (!item) return;
|
||||||
mimedata->from_doubleclick_ = true;
|
album_cover_choice_controller_->ShowCover(ItemAsSong(item));
|
||||||
emit AddToPlaylist(mimedata);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -151,7 +152,7 @@ class AlbumCoverManager : public QMainWindow {
|
|||||||
void ShowCover();
|
void ShowCover();
|
||||||
|
|
||||||
// For adding albums to the playlist
|
// For adding albums to the playlist
|
||||||
void AlbumDoubleClicked(const QModelIndex &index);
|
void AlbumDoubleClicked(const QModelIndex &idx);
|
||||||
void AddSelectedToPlaylist();
|
void AddSelectedToPlaylist();
|
||||||
void LoadSelectedToPlaylist();
|
void LoadSelectedToPlaylist();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ void DeezerCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
|
|||||||
|
|
||||||
QMap<QUrl, CoverSearchResult> results;
|
QMap<QUrl, CoverSearchResult> results;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const QJsonValue &json_value : array_data) {
|
for (const QJsonValue json_value : array_data) {
|
||||||
|
|
||||||
if (!json_value.isObject()) {
|
if (!json_value.isObject()) {
|
||||||
Error("Invalid Json reply, data array value is not a object.", json_value);
|
Error("Invalid Json reply, data array value is not a object.", json_value);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
#include "jsoncoverprovider.h"
|
#include "jsoncoverprovider.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class Application;
|
class Application;
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class DeezerCoverProvider : public JsonCoverProvider {
|
|||||||
static const char *kApiUrl;
|
static const char *kApiUrl;
|
||||||
static const int kLimit;
|
static const int kLimit;
|
||||||
|
|
||||||
QNetworkAccessManager *network_;
|
NetworkAccessManager *network_;
|
||||||
QList<QNetworkReply*> replies_;
|
QList<QNetworkReply*> replies_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ void DiscogsCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
|
|||||||
array_results = value_results.toArray();
|
array_results = value_results.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QJsonValue &value_result : array_results) {
|
for (const QJsonValue value_result : array_results) {
|
||||||
|
|
||||||
if (!value_result.isObject()) {
|
if (!value_result.isObject()) {
|
||||||
Error("Invalid Json reply, results value is not a object.", value_result);
|
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();
|
QJsonArray array_artists = value_artists.toArray();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
QString artist;
|
QString artist;
|
||||||
for (const QJsonValue &value_artist : array_artists) {
|
for (const QJsonValue value_artist : array_artists) {
|
||||||
if (!value_artist.isObject()) {
|
if (!value_artist.isObject()) {
|
||||||
Error("Invalid Json reply, atists array value is not a object.", value_artist);
|
Error("Invalid Json reply, atists array value is not a object.", value_artist);
|
||||||
continue;
|
continue;
|
||||||
@@ -421,7 +421,7 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, const int se
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QJsonValue &value_image : array_images) {
|
for (const QJsonValue value_image : array_images) {
|
||||||
|
|
||||||
if (!value_image.isObject()) {
|
if (!value_image.isObject()) {
|
||||||
Error("Invalid Json reply, images array value is not an object.", value_image);
|
Error("Invalid Json reply, images array value is not an object.", value_image);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
#include "jsoncoverprovider.h"
|
#include "jsoncoverprovider.h"
|
||||||
#include "albumcoverfetcher.h"
|
#include "albumcoverfetcher.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class Application;
|
class Application;
|
||||||
@@ -99,7 +99,7 @@ class DiscogsCoverProvider : public JsonCoverProvider {
|
|||||||
static const char *kSecretKeyB64;
|
static const char *kSecretKeyB64;
|
||||||
static const int kRequestsDelay;
|
static const int kRequestsDelay;
|
||||||
|
|
||||||
QNetworkAccessManager *network_;
|
NetworkAccessManager *network_;
|
||||||
QTimer *timer_flush_requests_;
|
QTimer *timer_flush_requests_;
|
||||||
QQueue<std::shared_ptr<DiscogsCoverSearchContext>> queue_search_requests_;
|
QQueue<std::shared_ptr<DiscogsCoverSearchContext>> queue_search_requests_;
|
||||||
QQueue<DiscogsCoverReleaseContext> queue_release_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();
|
QJsonArray array_type = value_type.toArray();
|
||||||
|
|
||||||
for (const QJsonValue &value : array_type) {
|
for (const QJsonValue value : array_type) {
|
||||||
|
|
||||||
if (!value.isObject()) {
|
if (!value.isObject()) {
|
||||||
Error("Invalid Json reply, value in albummatches/trackmatches array is not a object.", value);
|
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();
|
QJsonArray array_image = json_image.toArray();
|
||||||
QUrl url;
|
QUrl url;
|
||||||
LastFmImageSize size(LastFmImageSize::Unknown);
|
LastFmImageSize size(LastFmImageSize::Unknown);
|
||||||
for (const QJsonValue &value_image : array_image) {
|
for (const QJsonValue value_image : array_image) {
|
||||||
if (!value_image.isObject()) {
|
if (!value_image.isObject()) {
|
||||||
Error("Invalid Json reply, album image value is not an object.", value_image);
|
Error("Invalid Json reply, album image value is not an object.", value_image);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#include "jsoncoverprovider.h"
|
#include "jsoncoverprovider.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class Application;
|
class Application;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class LastFmCoverProvider : public JsonCoverProvider {
|
|||||||
static const char *kApiKey;
|
static const char *kApiKey;
|
||||||
static const char *kSecret;
|
static const char *kSecret;
|
||||||
|
|
||||||
QNetworkAccessManager *network_;
|
NetworkAccessManager *network_;
|
||||||
QList<QNetworkReply*> replies_;
|
QList<QNetworkReply*> replies_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QJsonValue &value_release : array_releases) {
|
for (const QJsonValue value_release : array_releases) {
|
||||||
|
|
||||||
if (!value_release.isObject()) {
|
if (!value_release.isObject()) {
|
||||||
Error("Invalid Json reply, releases array value is not an object.", value_release);
|
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();
|
QJsonArray array_artists = json_artists.toArray();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
QString artist;
|
QString artist;
|
||||||
for (const QJsonValue &value_artist : array_artists) {
|
for (const QJsonValue value_artist : array_artists) {
|
||||||
if (!value_artist.isObject()) {
|
if (!value_artist.isObject()) {
|
||||||
Error("Invalid Json reply, artist is not a object.", value_artist);
|
Error("Invalid Json reply, artist is not a object.", value_artist);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||