Compare commits
454 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d18f40c5f | ||
|
|
47e771ee37 | ||
|
|
20760dcf0e | ||
|
|
2edf12e4cd | ||
|
|
c946c1d1c4 | ||
|
|
bb5fe5a6d1 | ||
|
|
814d2fa9fc | ||
|
|
10d89cb6a6 | ||
|
|
a6569d09ac | ||
|
|
b38ad81928 | ||
|
|
637772f8f0 | ||
|
|
0093eb6c2c | ||
|
|
f3b70d1cb2 | ||
|
|
5152f57f6e | ||
|
|
e6112a311d | ||
|
|
e069d069d2 | ||
|
|
fd74bbc868 | ||
|
|
3b37aea3da | ||
|
|
2fd4bbbd49 | ||
|
|
17226b524b | ||
|
|
f38b33ee50 | ||
|
|
0da0e0a6db | ||
|
|
de2b6d1dee | ||
|
|
c9a501ddc8 | ||
|
|
c5babbbb22 | ||
|
|
1fd0d1a330 | ||
|
|
84d9b30fec | ||
|
|
d0f1522f0d | ||
|
|
6b2e25122d | ||
|
|
ce3026f91c | ||
|
|
4b384b3b59 | ||
|
|
da7605a234 | ||
|
|
3abdec1b91 | ||
|
|
ef34199887 | ||
|
|
08eca88e2f | ||
|
|
4636cc74dc | ||
|
|
4cc54a2f05 | ||
|
|
9bd728889b | ||
|
|
d870115467 | ||
|
|
2b445fb563 | ||
|
|
44a3bad278 | ||
|
|
8d2615547d | ||
|
|
3292db8b77 | ||
|
|
8c6ad52437 | ||
|
|
62e53b53f0 | ||
|
|
35db157617 | ||
|
|
cfb137a94b | ||
|
|
9b4a33a612 | ||
|
|
918b7c1e03 | ||
|
|
8fb354705c | ||
|
|
b3826064b7 | ||
|
|
acba68dab9 | ||
|
|
32ea709350 | ||
|
|
c15dfc7c5d | ||
|
|
92f6a0c6c8 | ||
|
|
0235be538b | ||
|
|
abf19e7a27 | ||
|
|
1d3540dca6 | ||
|
|
ddfc9d911b | ||
|
|
b952256988 | ||
|
|
71868936d1 | ||
|
|
23ef4fb132 | ||
|
|
f74f2ca3ef | ||
|
|
f4f005cdd6 | ||
|
|
3137652280 | ||
|
|
88c6f0340a | ||
|
|
86146c7292 | ||
|
|
fad39b6d67 | ||
|
|
09a9330f3e | ||
|
|
d2d7f32c45 | ||
|
|
120b18b399 | ||
|
|
32b9e6b73d | ||
|
|
8d6e5272b9 | ||
|
|
b6f90a9715 | ||
|
|
e3d0b777fc | ||
|
|
79a8450462 | ||
|
|
1a967597e8 | ||
|
|
d5b0794b00 | ||
|
|
d78ee3d62b | ||
|
|
03037a9a6b | ||
|
|
0637b65846 | ||
|
|
e77e914f44 | ||
|
|
68c44daef2 | ||
|
|
8ca0b54b18 | ||
|
|
b77d01baca | ||
|
|
cd509bbbdc | ||
|
|
24a3ac9811 | ||
|
|
d35d3aabc3 | ||
|
|
9e624a6c0d | ||
|
|
9823cd25d2 | ||
|
|
416a8c8f5d | ||
|
|
c55bdd423e | ||
|
|
e45e202c5e | ||
|
|
2280a30ba9 | ||
|
|
9ac7518156 | ||
|
|
0e6dbaf71a | ||
|
|
fe018ff8f7 | ||
|
|
9b01e09302 | ||
|
|
3127474fd7 | ||
|
|
b849edfcca | ||
|
|
20a23c2868 | ||
|
|
49d9ded684 | ||
|
|
fc57b437c2 | ||
|
|
1ba20561ed | ||
|
|
2c8b26e091 | ||
|
|
62d2a97f38 | ||
|
|
44aa292bb5 | ||
|
|
5273b52c31 | ||
|
|
f143efb810 | ||
|
|
978fb06349 | ||
|
|
e274e8070d | ||
|
|
46bd5b42fa | ||
|
|
0b15e29324 | ||
|
|
75b6669371 | ||
|
|
c1c34017e4 | ||
|
|
ed08818b6f | ||
|
|
71adfc0a74 | ||
|
|
49ccbddb17 | ||
|
|
be3c7eef60 | ||
|
|
336c6cdd9d | ||
|
|
709a706853 | ||
|
|
dad62faf88 | ||
|
|
6e0a5fb5c6 | ||
|
|
4b9551d27f | ||
|
|
2a67bc9926 | ||
|
|
d02241d32c | ||
|
|
55e038d345 | ||
|
|
ea2bfbda44 | ||
|
|
ed7794f396 | ||
|
|
086646f311 | ||
|
|
ad169ca5a5 | ||
|
|
671e636aef | ||
|
|
72d381e9ed | ||
|
|
c076933b52 | ||
|
|
fdb5c813ad | ||
|
|
1bda6633b1 | ||
|
|
249a5bf3b7 | ||
|
|
679b468618 | ||
|
|
cf2a0af3d9 | ||
|
|
82be53224d | ||
|
|
9cc995cb52 | ||
|
|
42d414797a | ||
|
|
b071a4df70 | ||
|
|
46d927291c | ||
|
|
50ab01d9c9 | ||
|
|
dc8fe63acf | ||
|
|
4fc5863888 | ||
|
|
e2d8149dcf | ||
|
|
5f156d6bab | ||
|
|
816cdcf4bf | ||
|
|
75c94ae092 | ||
|
|
ecf2c50a26 | ||
|
|
cecb9293f6 | ||
|
|
1ebfa0ad7e | ||
|
|
35c7b57308 | ||
|
|
36bfeffbcc | ||
|
|
e6858719c9 | ||
|
|
f2d52f83fe | ||
|
|
f1d3cadb3b | ||
|
|
7d61d8e646 | ||
|
|
6ede400f3a | ||
|
|
78de45fee2 | ||
|
|
4559e33331 | ||
|
|
43875dd3fe | ||
|
|
fa10384a92 | ||
|
|
7e144da6b9 | ||
|
|
54af17e7bf | ||
|
|
d4a9f5bb2e | ||
|
|
8040813da8 | ||
|
|
871e40c5c0 | ||
|
|
7ce922b084 | ||
|
|
0c9989695a | ||
|
|
88d7cb3ed5 | ||
|
|
5b7fc80f26 | ||
|
|
553e8baa8b | ||
|
|
62a5031ccf | ||
|
|
4abac65316 | ||
|
|
c6e42e1032 | ||
|
|
a353631892 | ||
|
|
f144c982e3 | ||
|
|
16625b1dc7 | ||
|
|
78ccce7d1a | ||
|
|
ffba5f7d31 | ||
|
|
2cb8fa62df | ||
|
|
853b936cdd | ||
|
|
4f0fdbab62 | ||
|
|
190b23b702 | ||
|
|
e0bb79b2c4 | ||
|
|
2eab763d74 | ||
|
|
f64c1dd9e5 | ||
|
|
fab1d94d34 | ||
|
|
facf49b2b7 | ||
|
|
da86b86776 | ||
|
|
a79d9d4e77 | ||
|
|
d9e378211a | ||
|
|
99fbbf70de | ||
|
|
50a616457d | ||
|
|
2ad30ebe88 | ||
|
|
68dbc29f2c | ||
|
|
a87863229f | ||
|
|
f39ffcb997 | ||
|
|
f20bb388be | ||
|
|
fc45015b13 | ||
|
|
7a5f047f8e | ||
|
|
4251bee3ca | ||
|
|
f02741284c | ||
|
|
fec7419fcc | ||
|
|
e8694531f6 | ||
|
|
461491f742 | ||
|
|
af29acf462 | ||
|
|
b47f29e87c | ||
|
|
fc8ec6d7fa | ||
|
|
1d4c736cfa | ||
|
|
600450082a | ||
|
|
c6da0864f2 | ||
|
|
10fc6b4562 | ||
|
|
e48b7d83a3 | ||
|
|
a6742d401c | ||
|
|
87b9a8c4c8 | ||
|
|
134dc55891 | ||
|
|
295cf98e70 | ||
|
|
7333155b8c | ||
|
|
3960c7d8e6 | ||
|
|
b053f99690 | ||
|
|
5700c3f72e | ||
|
|
cdb3729a88 | ||
|
|
c33f2a1d27 | ||
|
|
9b9a32b053 | ||
|
|
95eec369b5 | ||
|
|
5d5860683b | ||
|
|
434b31b932 | ||
|
|
f69e42e520 | ||
|
|
c5cadfe0c6 | ||
|
|
f21be30004 | ||
|
|
b8b21d53e1 | ||
|
|
09ec39c87a | ||
|
|
dbf18db3a3 | ||
|
|
d8f0ae0980 | ||
|
|
09bbf1f4d7 | ||
|
|
d07aff9872 | ||
|
|
4cb3f9d177 | ||
|
|
c1a815778b | ||
|
|
5b003b09ac | ||
|
|
22d0697c77 | ||
|
|
432b0f3e54 | ||
|
|
2bee41e90e | ||
|
|
2a7312f2b4 | ||
|
|
f5091339ad | ||
|
|
3d06d68196 | ||
|
|
1a643bfa8c | ||
|
|
cd82b0a669 | ||
|
|
57312e29e2 | ||
|
|
592df4819b | ||
|
|
f36997c7c5 | ||
|
|
b1640d3626 | ||
|
|
825c62c62b | ||
|
|
a4b0c6f37d | ||
|
|
239b88aa3b | ||
|
|
88819611f4 | ||
|
|
e6ff8368a9 | ||
|
|
8315f572ea | ||
|
|
89e2070419 | ||
|
|
b5f4df0912 | ||
|
|
b9f3f80d50 | ||
|
|
0e8ae1a206 | ||
|
|
32729174bb | ||
|
|
2cf6fe8da7 | ||
|
|
929b031f09 | ||
|
|
57a36491ee | ||
|
|
f3a8dde5f0 | ||
|
|
ffa2489998 | ||
|
|
6cd7bcdae6 | ||
|
|
e4f684f411 | ||
|
|
fdc9a71f9e | ||
|
|
bb134ee7ac | ||
|
|
18dc6f3c88 | ||
|
|
9327fd3aa1 | ||
|
|
aa859b9002 | ||
|
|
20a15ecd35 | ||
|
|
062c59b321 | ||
|
|
9ace66edb7 | ||
|
|
a97d784f26 | ||
|
|
a2e7173983 | ||
|
|
0869651dc3 | ||
|
|
ca42286354 | ||
|
|
174bfcc597 | ||
|
|
244d25ce53 | ||
|
|
8287efd51f | ||
|
|
df31b5d59f | ||
|
|
53cc47a8b1 | ||
|
|
55f8294a38 | ||
|
|
67a5e3f37e | ||
|
|
06cd2f3a57 | ||
|
|
d614a94203 | ||
|
|
f66aca2164 | ||
|
|
b071ecb45e | ||
|
|
83a70fecca | ||
|
|
dbc7c224c1 | ||
|
|
7876f9a8a5 | ||
|
|
215057ce6f | ||
|
|
6d8b0b3ab6 | ||
|
|
e5c85ddd32 | ||
|
|
6bf3c34fe5 | ||
|
|
584f5e5935 | ||
|
|
58a5367015 | ||
|
|
8c2b907ff5 | ||
|
|
e3ab0c0192 | ||
|
|
8572e3eabb | ||
|
|
d4f10c61ef | ||
|
|
59e11d6caa | ||
|
|
67778198de | ||
|
|
ebfd8cd6f7 | ||
|
|
83b54f2ae6 | ||
|
|
7ea60ed178 | ||
|
|
c61d1ce6b4 | ||
|
|
6a7959547e | ||
|
|
af3834672e | ||
|
|
f95ff6cb89 | ||
|
|
e85c13b3fe | ||
|
|
ed09627fdb | ||
|
|
99ba2c2e8b | ||
|
|
589bdf5dcd | ||
|
|
1abbd5ecbc | ||
|
|
7619e8427a | ||
|
|
069e24d713 | ||
|
|
8e9b45f80f | ||
|
|
2d604a80c9 | ||
|
|
1f59a1b952 | ||
|
|
6e5ff01db5 | ||
|
|
50be44adf8 | ||
|
|
e1bf4347ab | ||
|
|
3a3305c020 | ||
|
|
a83f174e1a | ||
|
|
1295033fae | ||
|
|
755abec636 | ||
|
|
310995fb87 | ||
|
|
bee30e572f | ||
|
|
cd37a40bab | ||
|
|
8699790e78 | ||
|
|
d2d3f58a14 | ||
|
|
081df59ed7 | ||
|
|
ec3bcdcb26 | ||
|
|
8265cf8a6a | ||
|
|
dfb53fb3dd | ||
|
|
705fc920e5 | ||
|
|
99764741e4 | ||
|
|
35b3bc4522 | ||
|
|
91eee99bab | ||
|
|
e56defdc50 | ||
|
|
fbe4d3ce9f | ||
|
|
8cf5707575 | ||
|
|
f786a17014 | ||
|
|
427b9c1ebc | ||
|
|
141957e4b5 | ||
|
|
b911f4f34e | ||
|
|
0caf76dfae | ||
|
|
00f09168d7 | ||
|
|
d636359c10 | ||
|
|
8fd32aba4f | ||
|
|
9db59d5deb | ||
|
|
d98321c703 | ||
|
|
a6424c005f | ||
|
|
80d127c277 | ||
|
|
3ca0c828c6 | ||
|
|
d3a8e03b2c | ||
|
|
6e75de0dcb | ||
|
|
a7bed9741a | ||
|
|
f70e52a7a5 | ||
|
|
95d7de0654 | ||
|
|
3398d25b35 | ||
|
|
80a4a43680 | ||
|
|
01ac61f38c | ||
|
|
a98be36684 | ||
|
|
7f085c4012 | ||
|
|
5f008d1560 | ||
|
|
0782b579d5 | ||
|
|
f8ed2afef1 | ||
|
|
a883508eca | ||
|
|
a4a20ec220 | ||
|
|
faed63712f | ||
|
|
0032ec5036 | ||
|
|
9df24a841f | ||
|
|
d2aa4ff2bd | ||
|
|
171db01401 | ||
|
|
0112f1e11d | ||
|
|
8f7b09b58e | ||
|
|
479c2b6cec | ||
|
|
e6f50e6637 | ||
|
|
548f8bf23e | ||
|
|
b6bfe81f9a | ||
|
|
ab95bcc028 | ||
|
|
8b7b3df0a3 | ||
|
|
69a6416146 | ||
|
|
1ced4e277b | ||
|
|
0c5236ebcb | ||
|
|
66c55c5007 | ||
|
|
7f5a0f1b0c | ||
|
|
0c766938f1 | ||
|
|
1455e87aef | ||
|
|
7c50eef8ad | ||
|
|
aa9f972ccf | ||
|
|
45df99bb56 | ||
|
|
a2968e57cf | ||
|
|
67f831beba | ||
|
|
264d47caf4 | ||
|
|
ef72b3273e | ||
|
|
7d3ade7ae1 | ||
|
|
59897fdf55 | ||
|
|
0b4ea04c91 | ||
|
|
094b3b4636 | ||
|
|
0298beeb06 | ||
|
|
971542d6bc | ||
|
|
5bdbc9f40d | ||
|
|
efcd35d4a1 | ||
|
|
a01541d7ca | ||
|
|
7d96d7c066 | ||
|
|
639ea35921 | ||
|
|
dee1905e16 | ||
|
|
1803705749 | ||
|
|
5d96ee5492 | ||
|
|
4a0a1a32a4 | ||
|
|
c2108a35d0 | ||
|
|
31ecdbae18 | ||
|
|
e9c733e78f | ||
|
|
2320c5e04c | ||
|
|
fb7b3b0295 | ||
|
|
316a3d51ee | ||
|
|
240dcf2e5c | ||
|
|
5fbdbffa23 | ||
|
|
932c8b65fb | ||
|
|
e18e174947 | ||
|
|
f2c7df3a3b | ||
|
|
2b15dae806 | ||
|
|
2625d72818 | ||
|
|
a8b4d72421 | ||
|
|
88562890ae | ||
|
|
86c267eb89 | ||
|
|
3d1928cca9 | ||
|
|
8c64d3b55c | ||
|
|
41b238e87b | ||
|
|
da83025fb0 | ||
|
|
84d3b190c8 | ||
|
|
272fc5a72d | ||
|
|
0f709059e3 | ||
|
|
0f91bb28a9 | ||
|
|
63d75a8e17 | ||
|
|
be95d8409b | ||
|
|
9d3000498b | ||
|
|
48f8468e65 | ||
|
|
9b60559855 | ||
|
|
e97acf7938 | ||
|
|
1cfb92e75c | ||
|
|
4c617ccd49 | ||
|
|
494517e767 |
@@ -1,417 +0,0 @@
|
||||
version: 2.1
|
||||
commands:
|
||||
|
||||
cmake:
|
||||
description: Configure build
|
||||
steps:
|
||||
- run:
|
||||
name: Configure build
|
||||
command: cmake ..
|
||||
working_directory: build
|
||||
|
||||
|
||||
build_source:
|
||||
description: Create source tarball
|
||||
steps:
|
||||
- run:
|
||||
name: Create source tarball
|
||||
command: ../dist/scripts/maketarball.sh
|
||||
working_directory: build
|
||||
|
||||
|
||||
build_rpm:
|
||||
description: Build RPM
|
||||
steps:
|
||||
- run:
|
||||
name: Create RPM build sources directories
|
||||
command: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- run:
|
||||
name: Copy source tarball 1
|
||||
command: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
working_directory: build
|
||||
- run:
|
||||
name: Copy source tarball 2
|
||||
command: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||
working_directory: build
|
||||
- run:
|
||||
name: Build RPM
|
||||
command: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
working_directory: build
|
||||
|
||||
|
||||
build_deb:
|
||||
description: Build Deb
|
||||
steps:
|
||||
- run:
|
||||
name: make deb
|
||||
command: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||
|
||||
|
||||
install_opensuse_dependencies:
|
||||
description: Install openSUSE dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Update packages
|
||||
command: zypper --non-interactive --gpg-auto-import-keys ref
|
||||
- run:
|
||||
name: Install openSUSE dependencies
|
||||
command: >
|
||||
zypper --non-interactive --gpg-auto-import-keys install
|
||||
lsb-release
|
||||
rpm-build
|
||||
git
|
||||
tar
|
||||
make
|
||||
cmake
|
||||
gcc
|
||||
gcc-c++
|
||||
gettext-tools
|
||||
glibc-devel
|
||||
libboost_headers-devel
|
||||
boost-devel
|
||||
glib2-devel
|
||||
glib2-tools
|
||||
dbus-1-devel
|
||||
alsa-devel
|
||||
libnotify-devel
|
||||
libgnutls-devel
|
||||
protobuf-devel
|
||||
sqlite3-devel
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
taglib-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
libQt5Widgets-devel
|
||||
libQt5Concurrent-devel
|
||||
libQt5Network-devel
|
||||
libQt5Sql-devel
|
||||
libQt5DBus-devel
|
||||
libQt5Test-devel
|
||||
libqt5-qtx11extras-devel
|
||||
libqt5-qtbase-common-devel
|
||||
libQt5Sql5-sqlite
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libmtp-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
appstream-glib
|
||||
hicolor-icon-theme
|
||||
|
||||
|
||||
install_fedora_dependencies:
|
||||
description: Install Fedora dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Update packages
|
||||
command: yum update --assumeyes
|
||||
- run:
|
||||
name: Upgrade packages
|
||||
command: yum upgrade --assumeyes
|
||||
- run:
|
||||
name: Install Fedora dependencies
|
||||
command: >
|
||||
dnf install --assumeyes
|
||||
@development-tools
|
||||
redhat-lsb-core
|
||||
git
|
||||
glibc
|
||||
gcc-c++
|
||||
rpmdevtools
|
||||
make
|
||||
cmake
|
||||
pkgconfig
|
||||
glib
|
||||
man
|
||||
tar
|
||||
gettext
|
||||
openssh
|
||||
boost-devel
|
||||
dbus-devel
|
||||
protobuf-devel
|
||||
protobuf-compiler
|
||||
sqlite-devel
|
||||
alsa-lib-devel
|
||||
pulseaudio-libs-devel
|
||||
libnotify-devel
|
||||
gnutls-devel
|
||||
qt5-qtbase-devel
|
||||
qt5-qtx11extras-devel
|
||||
qt5-qttools-devel
|
||||
gstreamer1-devel
|
||||
gstreamer1-plugins-base-devel
|
||||
taglib-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libmtp-devel
|
||||
libchromaprint-devel
|
||||
fftw-devel
|
||||
desktop-file-utils
|
||||
libappstream-glib
|
||||
hicolor-icon-theme
|
||||
|
||||
|
||||
install_debian_dependencies:
|
||||
description: Install Debian dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Install Debian dependencies
|
||||
command: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
cmake
|
||||
gcc
|
||||
pkg-config
|
||||
fakeroot
|
||||
gettext
|
||||
lsb-release
|
||||
libglib2.0-dev
|
||||
dpkg-dev
|
||||
libdbus-1-dev
|
||||
libboost-dev
|
||||
libprotobuf-dev
|
||||
protobuf-compiler
|
||||
libsqlite3-dev
|
||||
libgnutls28-dev
|
||||
libasound2-dev
|
||||
libpulse-dev
|
||||
libtag1-dev
|
||||
qtbase5-dev
|
||||
qtbase5-dev-tools
|
||||
qtbase5-private-dev
|
||||
libqt5x11extras5-dev
|
||||
qttools5-dev
|
||||
libgstreamer1.0-dev
|
||||
libgstreamer-plugins-base1.0-dev
|
||||
gstreamer1.0-alsa
|
||||
gstreamer1.0-pulseaudio
|
||||
libchromaprint-dev
|
||||
libfftw3-dev
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
|
||||
|
||||
install_ubuntu_dependencies:
|
||||
description: Install Ubuntu dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Setup environment
|
||||
command: |
|
||||
echo 'export DEBIAN_FRONTEND=noninteractive' >> $BASH_ENV
|
||||
source $BASH_ENV
|
||||
- run:
|
||||
name: Install Ubuntu dependencies
|
||||
command: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
cmake
|
||||
pkg-config
|
||||
gcc
|
||||
fakeroot
|
||||
wget
|
||||
curl
|
||||
gettext
|
||||
lsb-release
|
||||
dpkg-dev
|
||||
libglib2.0-dev
|
||||
libboost-dev
|
||||
libdbus-1-dev
|
||||
libprotobuf-dev
|
||||
protobuf-compiler
|
||||
libsqlite3-dev
|
||||
libgnutls28-dev
|
||||
libasound2-dev
|
||||
libpulse-dev
|
||||
libtag1-dev
|
||||
qtbase5-dev
|
||||
qtbase5-dev-tools
|
||||
qtbase5-private-dev
|
||||
libqt5x11extras5-dev
|
||||
qttools5-dev
|
||||
libgstreamer1.0-dev
|
||||
libgstreamer-plugins-base1.0-dev
|
||||
libgstreamer-plugins-good1.0-dev
|
||||
gstreamer1.0-alsa
|
||||
gstreamer1.0-pulseaudio
|
||||
libchromaprint-dev
|
||||
libfftw3-dev
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
build_source:
|
||||
docker:
|
||||
- image: opensuse/leap:15.2
|
||||
steps:
|
||||
- install_opensuse_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
|
||||
|
||||
build_opensuse_lp151:
|
||||
docker:
|
||||
- image: opensuse/leap:15.1
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_opensuse_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
build_opensuse_lp152:
|
||||
docker:
|
||||
- image: opensuse/leap:15.2
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_opensuse_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_fedora_32:
|
||||
docker:
|
||||
- image: fedora:32
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_fedora_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
build_fedora_33:
|
||||
docker:
|
||||
- image: fedora:33
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_fedora_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_debian_buster:
|
||||
docker:
|
||||
- image: debian:buster
|
||||
steps:
|
||||
- install_debian_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
build_debian_bullseye:
|
||||
docker:
|
||||
- image: debian:bullseye
|
||||
steps:
|
||||
- install_debian_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
|
||||
build_ubuntu_bionic:
|
||||
docker:
|
||||
- image: ubuntu:bionic
|
||||
steps:
|
||||
- install_ubuntu_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
build_ubuntu_focal:
|
||||
docker:
|
||||
- image: ubuntu:focal
|
||||
steps:
|
||||
- install_ubuntu_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
build_ubuntu_groovy:
|
||||
docker:
|
||||
- image: ubuntu:groovy
|
||||
steps:
|
||||
- install_ubuntu_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_all:
|
||||
jobs:
|
||||
|
||||
- build_source:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_opensuse_lp151:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_opensuse_lp152:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_fedora_32:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_fedora_33:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_debian_buster:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_debian_bullseye:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_ubuntu_bionic:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_ubuntu_focal:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_ubuntu_groovy:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
967
.github/workflows/ccpp.yml
vendored
46
.travis.yml
@@ -1,46 +0,0 @@
|
||||
sudo: required
|
||||
language: C++
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
|
||||
before_install:
|
||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
|
||||
chmod 600 ~/.ssh/id_rsa ;
|
||||
fi
|
||||
- git fetch --unshallow
|
||||
- git pull
|
||||
- brew update
|
||||
- travis_wait 400 brew upgrade || echo "Failed"
|
||||
- travis_wait 400 brew upgrade || echo "Failed"
|
||||
- brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib
|
||||
- brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
|
||||
- brew install libcdio libmtp
|
||||
- brew install create-dmg
|
||||
- brew install --cask sparkle
|
||||
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
|
||||
- export Qt6_DIR=/usr/local/opt/qt6/lib/cmake
|
||||
- export Qt6LinguistTools_DIR=/usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
|
||||
- ls /usr/local/lib/gstreamer-1.0
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_WITH_QT6=ON -DBUILD_WERROR=ON -DUSE_BUNDLE=ON
|
||||
script:
|
||||
- make -j8
|
||||
- make install
|
||||
- make dmg2
|
||||
after_success:
|
||||
- ls -lh strawberry*.dmg
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
|
||||
fi
|
||||
fi
|
||||
|
||||
branches:
|
||||
except:
|
||||
- # Do not build tags that we create when we upload to GitHub Releases
|
||||
- /^(?i:continuous)$/
|
||||
|
||||
56
3rdparty/macdeployqt/main.cpp
vendored
@@ -64,7 +64,6 @@ int main(int argc, char **argv)
|
||||
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
|
||||
qDebug() << " -libpath=<path> : Add the given path to the library search path";
|
||||
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
|
||||
qDebug() << " -plugins-dir=<path> : Set plugins directory";
|
||||
qDebug() << "";
|
||||
qDebug() << "macdeployqt takes an application bundle as input and makes it";
|
||||
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
|
||||
@@ -110,7 +109,6 @@ int main(int argc, char **argv)
|
||||
extern bool appstoreCompliant;
|
||||
extern bool deployFramework;
|
||||
extern bool secureTimestamp;
|
||||
QString plugin_dir;
|
||||
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
QByteArray argument = QByteArray(argv[i]);
|
||||
@@ -198,7 +196,7 @@ int main(int argc, char **argv)
|
||||
LogDebug() << "Argument found:" << argument;
|
||||
appstoreCompliant = true;
|
||||
|
||||
// Undocumented option, may not work as intented
|
||||
// Undocumented option, may not work as intended
|
||||
} else if (argument == QByteArray("-deploy-framework")) {
|
||||
LogDebug() << "Argument found:" << argument;
|
||||
deployFramework = true;
|
||||
@@ -210,13 +208,6 @@ int main(int argc, char **argv)
|
||||
LogError() << "Missing filesystem type";
|
||||
else
|
||||
filesystem = argument.mid(index+1);
|
||||
} else if (argument.startsWith(QByteArray("-plugins-dir"))) {
|
||||
LogDebug() << "Argument found:" << argument;
|
||||
int index = argument.indexOf('=');
|
||||
if (index == -1)
|
||||
LogError() << "Missing filesystem type";
|
||||
else
|
||||
plugin_dir = argument.mid(index+1);
|
||||
} else if (argument.startsWith("-")) {
|
||||
LogError() << "Unknown argument" << argument << "\n";
|
||||
return 1;
|
||||
@@ -231,7 +222,7 @@ int main(int argc, char **argv)
|
||||
if (deployFramework && deploymentInfo.isFramework)
|
||||
fixupFramework(appBundlePath);
|
||||
|
||||
// Convenience: Look for .qml files in the current directoty if no -qmldir specified.
|
||||
// Convenience: Look for .qml files in the current directory if no -qmldir specified.
|
||||
if (qmlDirs.isEmpty()) {
|
||||
QDir dir;
|
||||
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
|
||||
@@ -252,40 +243,37 @@ int main(int argc, char **argv)
|
||||
deploymentInfo.deployedFrameworks.end()).values();
|
||||
}
|
||||
|
||||
// Handle plugins
|
||||
if (plugins) {
|
||||
if (plugin_dir.isEmpty()) {
|
||||
// Set the plugins search directory
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
|
||||
#else
|
||||
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
deploymentInfo.pluginPath = plugin_dir;
|
||||
}
|
||||
if (deploymentInfo.pluginPath.isEmpty()) {
|
||||
LogError() << "Missing Qt plugins path\n";
|
||||
return 1;
|
||||
}
|
||||
if (!QDir(deploymentInfo.pluginPath).exists()) {
|
||||
LogError() << "Plugins path does not exist\n" << deploymentInfo.pluginPath;
|
||||
return 1;
|
||||
}
|
||||
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
|
||||
if (!deploymentInfo.pluginPath.isEmpty()) {
|
||||
LogNormal();
|
||||
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
|
||||
createQtConf(appBundlePath);
|
||||
}
|
||||
// Sanity checks
|
||||
if (deploymentInfo.pluginPath.isEmpty()) {
|
||||
LogError() << "Missing Qt plugins path\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!QDir(deploymentInfo.pluginPath).exists()) {
|
||||
LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Deploy plugins
|
||||
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
|
||||
if (!deploymentInfo.pluginPath.isEmpty()) {
|
||||
LogNormal();
|
||||
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
|
||||
createQtConf(appBundlePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (runStripEnabled)
|
||||
stripAppBinary(appBundlePath);
|
||||
|
||||
if (!FinalCheck(appBundlePath)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (runCodesign)
|
||||
codesign(codesignIdentiy, appBundlePath);
|
||||
|
||||
|
||||
339
3rdparty/macdeployqt/shared.cpp
vendored
@@ -35,9 +35,11 @@
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QStack>
|
||||
#include <QDirIterator>
|
||||
#include <QLibraryInfo>
|
||||
@@ -197,22 +199,19 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
|
||||
}
|
||||
|
||||
outputLines.removeFirst(); // remove line containing the binary path
|
||||
|
||||
if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) {
|
||||
const auto match = regexp.match(outputLines.first());
|
||||
if (match.hasMatch()) {
|
||||
QString installname = match.captured(1);
|
||||
if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) {
|
||||
info.installName = installname;
|
||||
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
|
||||
info.currentVersion = QVersionNumber::fromString(match.captured(3));
|
||||
outputLines.removeFirst();
|
||||
info.installName = installname;
|
||||
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
|
||||
info.currentVersion = QVersionNumber::fromString(match.captured(3));
|
||||
outputLines.removeFirst();
|
||||
} else {
|
||||
info.installName = binaryPath;
|
||||
}
|
||||
else {
|
||||
info.installName = binaryPath;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
LogError() << "Could not parse otool output line:" << outputLines.first();
|
||||
outputLines.removeFirst();
|
||||
}
|
||||
@@ -234,7 +233,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
|
||||
return info;
|
||||
}
|
||||
|
||||
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
|
||||
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
|
||||
{
|
||||
FrameworkInfo info;
|
||||
QString trimmed = line.trimmed();
|
||||
@@ -252,7 +251,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
|
||||
if (trimmed.startsWith("@rpath/")) {
|
||||
QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length());
|
||||
bool foundInsideBundle = false;
|
||||
foreach (const QString &rpath, rpaths) {
|
||||
for (const QString &rpath : std::as_const(rpaths)) {
|
||||
QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath);
|
||||
// Skip paths already inside the bundle.
|
||||
if (!appBundlePath.isEmpty()) {
|
||||
@@ -293,7 +292,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
|
||||
// Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/
|
||||
QStringList parts = trimmed.split("/");
|
||||
while (part < parts.count()) {
|
||||
const QString currentPart = parts.at(part).simplified() ;
|
||||
const QString currentPart = parts.at(part).simplified();
|
||||
++part;
|
||||
if (currentPart == "")
|
||||
continue;
|
||||
@@ -311,7 +310,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
|
||||
} else if (trimmed.startsWith("/") == false) { // If the line does not contain a full path, the app is using a binary Qt package.
|
||||
QStringList partsCopy = parts;
|
||||
partsCopy.removeLast();
|
||||
foreach (QString path, librarySearchPath) {
|
||||
for (QString &path : librarySearchPath) {
|
||||
if (!path.endsWith("/"))
|
||||
path += '/';
|
||||
QString nameInPath = path + parts.join(QLatin1Char('/'));
|
||||
@@ -505,7 +504,7 @@ QString findEntitlementsFile(const QString& path)
|
||||
return QString();
|
||||
}
|
||||
|
||||
QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
|
||||
QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
|
||||
{
|
||||
QList<FrameworkInfo> libraries;
|
||||
for (const DylibInfo &dylibInfo : dependencies) {
|
||||
@@ -545,9 +544,9 @@ QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const
|
||||
return path;
|
||||
}
|
||||
|
||||
QSet<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString())
|
||||
QList<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString())
|
||||
{
|
||||
QSet<QString> rpaths;
|
||||
QList<QString> rpaths;
|
||||
|
||||
QProcess otool;
|
||||
otool.start("otool", QStringList() << "-l" << path);
|
||||
@@ -584,18 +583,20 @@ QSet<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString
|
||||
return rpaths;
|
||||
}
|
||||
|
||||
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
|
||||
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
|
||||
{
|
||||
const OtoolInfo info = findDependencyInfo(path);
|
||||
return getQtFrameworks(info.dependencies, appBundlePath, rpaths + getBinaryRPaths(path), useDebugLibs);
|
||||
QList<QString> allRPaths = rpaths + getBinaryRPaths(path);
|
||||
allRPaths.removeDuplicates();
|
||||
return getQtFrameworks(info.dependencies, appBundlePath, allRPaths, useDebugLibs);
|
||||
}
|
||||
|
||||
QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
|
||||
QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
|
||||
{
|
||||
QList<FrameworkInfo> result;
|
||||
QSet<QString> existing;
|
||||
foreach (const QString &path, paths) {
|
||||
foreach (const FrameworkInfo &info, getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
|
||||
for (const QString &path : paths) {
|
||||
for (const FrameworkInfo &info : getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
|
||||
if (!existing.contains(info.frameworkPath)) { // avoid duplicates
|
||||
existing.insert(info.frameworkPath);
|
||||
result << info;
|
||||
@@ -614,10 +615,10 @@ QStringList getBinaryDependencies(const QString executablePath,
|
||||
const auto dependencies = findDependencyInfo(path).dependencies;
|
||||
|
||||
bool rpathsLoaded = false;
|
||||
QSet<QString> rpaths;
|
||||
QList<QString> rpaths;
|
||||
|
||||
// return bundle-local dependencies. (those starting with @executable_path)
|
||||
foreach (const DylibInfo &info, dependencies) {
|
||||
for (const DylibInfo &info : dependencies) {
|
||||
QString trimmedLine = info.binaryPath;
|
||||
if (trimmedLine.startsWith("@executable_path/")) {
|
||||
QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
|
||||
@@ -626,14 +627,14 @@ QStringList getBinaryDependencies(const QString executablePath,
|
||||
} else if (trimmedLine.startsWith("@rpath/")) {
|
||||
if (!rpathsLoaded) {
|
||||
rpaths = getBinaryRPaths(path, true, executablePath);
|
||||
foreach (const QString &binaryPath, additionalBinariesContainingRpaths) {
|
||||
QSet<QString> binaryRpaths = getBinaryRPaths(binaryPath, true);
|
||||
rpaths += binaryRpaths;
|
||||
for (const QString &binaryPath : additionalBinariesContainingRpaths) {
|
||||
rpaths += getBinaryRPaths(binaryPath, true);
|
||||
}
|
||||
rpaths.removeDuplicates();
|
||||
rpathsLoaded = true;
|
||||
}
|
||||
bool resolved = false;
|
||||
foreach (const QString &rpath, rpaths) {
|
||||
for (const QString &rpath : std::as_const(rpaths)) {
|
||||
QString binary = QDir::cleanPath(rpath + "/" + trimmedLine.mid(QStringLiteral("@rpath/").length()));
|
||||
LogDebug() << "Checking for" << binary;
|
||||
if (QFile::exists(binary)) {
|
||||
@@ -662,20 +663,20 @@ bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
|
||||
LogNormal() << "copy:" << sourcePath << destinationPath;
|
||||
|
||||
QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot);
|
||||
foreach (QString file, files) {
|
||||
for (const QString &file : files) {
|
||||
const QString fileSourcePath = sourcePath + "/" + file;
|
||||
const QString fileDestinationPath = destinationPath + "/" + file;
|
||||
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
|
||||
}
|
||||
|
||||
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (QString dir, subdirs) {
|
||||
for (const QString &dir : subdirs) {
|
||||
recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &rpaths, const QString &sourcePath, const QString &destinationPath)
|
||||
void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &rpaths, const QString &sourcePath, const QString &destinationPath)
|
||||
{
|
||||
QDir().mkpath(destinationPath);
|
||||
|
||||
@@ -683,7 +684,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
|
||||
const bool isDwarfPath = sourcePath.endsWith("DWARF");
|
||||
|
||||
QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
|
||||
foreach (QString file, files) {
|
||||
for (const QString &file : files) {
|
||||
const QString fileSourcePath = sourcePath + QLatin1Char('/') + file;
|
||||
|
||||
if (file.endsWith("_debug.dylib")) {
|
||||
@@ -729,7 +730,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
|
||||
}
|
||||
|
||||
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (QString dir, subdirs) {
|
||||
for (const QString &dir : subdirs) {
|
||||
recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + QLatin1Char('/') + dir, destinationPath + QLatin1Char('/') + dir);
|
||||
}
|
||||
}
|
||||
@@ -752,7 +753,7 @@ QString copyDylib(const FrameworkInfo &framework, const QString path)
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Retrun if the dylib has aleardy been deployed
|
||||
// Return if the dylib has already been deployed
|
||||
if (QFileInfo(dylibDestinationBinaryPath).exists() && !alwaysOwerwriteEnabled)
|
||||
return dylibDestinationBinaryPath;
|
||||
|
||||
@@ -786,13 +787,13 @@ QString copyFramework(const FrameworkInfo &framework, const QString path)
|
||||
|
||||
// Now copy the framework. Some parts should be left out (headers/, .prl files).
|
||||
// Some parts should be included (Resources/, symlink structure). We want this
|
||||
// function to make as few assumtions about the framework as possible while at
|
||||
// function to make as few assumptions about the framework as possible while at
|
||||
// the same time producing a codesign-compatible framework.
|
||||
|
||||
// Copy framework binary
|
||||
copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath);
|
||||
|
||||
// Copy Resouces/, Libraries/ and Helpers/
|
||||
// Copy Resources/, Libraries/ and Helpers/
|
||||
const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
|
||||
const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
|
||||
recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
|
||||
@@ -850,7 +851,7 @@ void changeIdentification(const QString &id, const QString &binaryPath)
|
||||
void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath)
|
||||
{
|
||||
const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath();
|
||||
foreach (const QString &binary, binaryPaths) {
|
||||
for (const QString &binary : binaryPaths) {
|
||||
QString deployedInstallName;
|
||||
if (useLoaderPath) {
|
||||
deployedInstallName = QLatin1String("@loader_path/")
|
||||
@@ -858,17 +859,11 @@ void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework
|
||||
} else {
|
||||
deployedInstallName = framework.deployedInstallName;
|
||||
}
|
||||
changeInstallName(framework.installName, deployedInstallName, binary);
|
||||
// Workaround for the case when the library ID name is a symlink, while the dependencies
|
||||
// specified using the canonical path to the library (QTBUG-56814)
|
||||
QString canonicalInstallName = QFileInfo(framework.installName).canonicalFilePath();
|
||||
if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) {
|
||||
changeInstallName(canonicalInstallName, deployedInstallName, binary);
|
||||
if (!framework.sourceFilePath.isEmpty()) {
|
||||
changeInstallName(framework.sourceFilePath, deployedInstallName, binary);
|
||||
}
|
||||
// Homebrew workaround, resolve symlink /usr/local/opt/library to /usr/local/Cellar/library
|
||||
if (framework.installName.startsWith("/usr/local/opt/") && framework.installName.count('/') >= 5) {
|
||||
canonicalInstallName = QFileInfo(framework.installName.section('/', 0, 4)).canonicalFilePath() + "/" + framework.installName.section('/', 5);
|
||||
changeInstallName(canonicalInstallName, deployedInstallName, binary);
|
||||
if (!framework.installName.isEmpty() && framework.installName != framework.sourceFilePath) {
|
||||
changeInstallName(framework.installName, deployedInstallName, binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,7 +873,7 @@ void addRPath(const QString &rpath, const QString &binaryPath)
|
||||
runInstallNameTool(QStringList() << "-add_rpath" << rpath << binaryPath);
|
||||
}
|
||||
|
||||
void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
|
||||
void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
|
||||
{
|
||||
const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath()
|
||||
+ QLatin1String("/Contents/Frameworks");
|
||||
@@ -886,7 +881,8 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
|
||||
const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath;
|
||||
bool rpathToFrameworksFound = false;
|
||||
QStringList args;
|
||||
foreach (const QString &rpath, getBinaryRPaths(binaryPath, false)) {
|
||||
QList<QString> binaryRPaths = getBinaryRPaths(binaryPath, false);
|
||||
for (const QString &rpath : std::as_const(binaryRPaths)) {
|
||||
if (rpath == "@executable_path/../Frameworks" ||
|
||||
rpath == loaderPathToFrameworks) {
|
||||
rpathToFrameworksFound = true;
|
||||
@@ -912,9 +908,9 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
|
||||
runInstallNameTool(QStringList() << args << binaryPath);
|
||||
}
|
||||
|
||||
void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath)
|
||||
void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath)
|
||||
{
|
||||
foreach (const QString &binary, binaryPaths) {
|
||||
for (const QString &binary : binaryPaths) {
|
||||
deployRPaths(bundlePath, rpaths, binary, useLoaderPath);
|
||||
}
|
||||
}
|
||||
@@ -980,7 +976,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
|
||||
deploymentInfo.useLoaderPath = useLoaderPath;
|
||||
deploymentInfo.isFramework = bundlePath.contains(".framework");
|
||||
deploymentInfo.isDebug = false;
|
||||
QSet<QString> rpathsUsed;
|
||||
QList<QString> rpathsUsed;
|
||||
|
||||
while (frameworks.isEmpty() == false) {
|
||||
const FrameworkInfo framework = frameworks.takeFirst();
|
||||
@@ -1005,8 +1001,9 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!framework.rpathUsed.isEmpty())
|
||||
rpathsUsed << framework.rpathUsed;
|
||||
if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) {
|
||||
rpathsUsed.append(framework.rpathUsed);
|
||||
}
|
||||
|
||||
// Copy the framework/dylib to the app bundle.
|
||||
const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath)
|
||||
@@ -1029,11 +1026,11 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
|
||||
// Check for framework dependencies
|
||||
QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs);
|
||||
|
||||
foreach (FrameworkInfo dependency, dependencies) {
|
||||
for (const FrameworkInfo &dependency : dependencies) {
|
||||
if (dependency.rpathUsed.isEmpty()) {
|
||||
changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath);
|
||||
} else {
|
||||
rpathsUsed << dependency.rpathUsed;
|
||||
rpathsUsed.append(dependency.rpathUsed);
|
||||
}
|
||||
|
||||
// Deploy framework if necessary.
|
||||
@@ -1045,6 +1042,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
|
||||
deploymentInfo.deployedFrameworks = copiedFrameworks;
|
||||
deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath);
|
||||
deploymentInfo.rpathsUsed += rpathsUsed;
|
||||
deploymentInfo.rpathsUsed.removeDuplicates();
|
||||
return deploymentInfo;
|
||||
}
|
||||
|
||||
@@ -1056,12 +1054,14 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
|
||||
applicationBundle.libraryPaths = findAppLibraries(appBundlePath);
|
||||
QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths
|
||||
<< additionalExecutables;
|
||||
QSet<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
|
||||
QList<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
allLibraryPaths.insert(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
|
||||
allLibraryPaths.append(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
|
||||
#else
|
||||
allLibraryPaths.insert(QLibraryInfo::location(QLibraryInfo::LibrariesPath));
|
||||
allLibraryPaths.append(QLibraryInfo::location(QLibraryInfo::LibrariesPath));
|
||||
#endif
|
||||
allLibraryPaths.removeDuplicates();
|
||||
|
||||
QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs);
|
||||
if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) {
|
||||
LogWarning();
|
||||
@@ -1077,7 +1077,7 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
|
||||
QString getLibInfix(const QStringList &deployedFrameworks)
|
||||
{
|
||||
QString libInfix;
|
||||
foreach (const QString &framework, deployedFrameworks) {
|
||||
for (const QString &framework : deployedFrameworks) {
|
||||
if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework"))) {
|
||||
Q_ASSERT(framework.length() >= 16);
|
||||
// 16 == "QtCore" + ".framework"
|
||||
@@ -1132,7 +1132,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
|
||||
// Network
|
||||
if (deploymentInfo.containsModule("Network", libInfix))
|
||||
addPlugins(QStringLiteral("bearer"));
|
||||
addPlugins(QStringLiteral("tls"));
|
||||
|
||||
// All image formats (svg if QtSvg is used)
|
||||
const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
|
||||
@@ -1199,7 +1199,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const QString &plugin, pluginList) {
|
||||
for (const QString &plugin : pluginList) {
|
||||
QString sourcePath = pluginSourcePath + "/" + plugin;
|
||||
const QString destinationPath = pluginDestinationPath + "/" + plugin;
|
||||
QDir dir;
|
||||
@@ -1216,7 +1216,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
{
|
||||
QString sourcePath = qgetenv("GIO_EXTRA_MODULES");
|
||||
if (sourcePath.isEmpty()) {
|
||||
sourcePath = "/usr/local/lib/gio/modules/libgiognutls.so";
|
||||
if (QFileInfo::exists("/usr/local/lib/gio/modules/libgiognutls.so")) {
|
||||
sourcePath = "/usr/local/lib/gio/modules/libgiognutls.so";
|
||||
}
|
||||
else if (QFileInfo::exists("/opt/local/lib/gio/modules/libgiognutls.so")) {
|
||||
sourcePath = "/opt/local/lib/gio/modules/libgiognutls.so";
|
||||
}
|
||||
else {
|
||||
qFatal("Missing GIO_EXTRA_MODULES");
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourcePath = sourcePath + "/libgiognutls.so";
|
||||
@@ -1234,7 +1242,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
{
|
||||
QString sourcePath = qgetenv("GST_PLUGIN_SCANNER");
|
||||
if (sourcePath.isEmpty()) {
|
||||
sourcePath = "/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner";
|
||||
if (QFileInfo::exists("/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner")) {
|
||||
sourcePath = "/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner";
|
||||
}
|
||||
else if (QFileInfo::exists("/opt/local/libexec/gstreamer-1.0/gst-plugin-scanner")) {
|
||||
sourcePath = "/opt/local/libexec/gstreamer-1.0/gst-plugin-scanner";
|
||||
}
|
||||
else {
|
||||
qFatal("Missing GST_PLUGIN_SCANNER.");
|
||||
}
|
||||
}
|
||||
const QString destinationPath = appBundleInfo.path + "/" + "Contents/PlugIns/gst-plugin-scanner";
|
||||
QDir dir;
|
||||
@@ -1281,11 +1297,9 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
<< "libgstrtsp.dylib"
|
||||
<< "libgstflac.dylib"
|
||||
<< "libgstwavparse.dylib"
|
||||
<< "libgstfaac.dylib"
|
||||
<< "libgstfaad.dylib"
|
||||
<< "libgstogg.dylib"
|
||||
<< "libgstopus.dylib"
|
||||
<< "libgstopusparse.dylib"
|
||||
<< "libgstasf.dylib"
|
||||
<< "libgstspeex.dylib"
|
||||
<< "libgsttaglib.dylib"
|
||||
@@ -1293,22 +1307,59 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
|
||||
<< "libgstisomp4.dylib"
|
||||
<< "libgstlibav.dylib"
|
||||
<< "libgstaiff.dylib"
|
||||
<< "libgstlame.dylib"
|
||||
<< "libgstmusepack.dylib";
|
||||
<< "libgstlame.dylib";
|
||||
|
||||
QString gstreamer_plugins_dir = qgetenv("GST_PLUGIN_PATH");
|
||||
if (gstreamer_plugins_dir.isEmpty()) {
|
||||
gstreamer_plugins_dir = "/usr/local/lib/gstreamer-1.0";
|
||||
}
|
||||
// macports does not have these.
|
||||
QStringList gstreamer_plugins_optional = QStringList() << "libgstopusparse.dylib"
|
||||
<< "libgstfaac.dylib"
|
||||
<< "libgstmusepack.dylib";
|
||||
|
||||
QString gstreamer_plugins_dir = qgetenv("GST_PLUGIN_PATH");
|
||||
if (gstreamer_plugins_dir.isEmpty()) {
|
||||
if (QDir().exists("/usr/local/lib/gstreamer-1.0")) {
|
||||
gstreamer_plugins_dir = "/usr/local/lib/gstreamer-1.0";
|
||||
}
|
||||
else if (QDir().exists("/opt/local/lib/gstreamer-1.0")) {
|
||||
gstreamer_plugins_dir = "/opt/local/lib/gstreamer-1.0";
|
||||
}
|
||||
else {
|
||||
qFatal("Missing GST_PLUGIN_PATH.");
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString &plugin : gstreamer_plugins) {
|
||||
const QString sourcePath = gstreamer_plugins_dir + "/" + plugin;
|
||||
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + plugin;
|
||||
QDir dir;
|
||||
if (dir.mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
|
||||
runStrip(destinationPath);
|
||||
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
|
||||
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
|
||||
QFileInfo info(gstreamer_plugins_dir + "/" + plugin);
|
||||
if (!info.exists()) {
|
||||
info.setFile(gstreamer_plugins_dir + "/" + info.baseName() + QString(".so"));
|
||||
if (!info.exists()) {
|
||||
LogError() << "Missing gstreamer plugin" << info.baseName();
|
||||
qFatal("Missing %s", info.baseName().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
const QString &sourcePath = info.filePath();
|
||||
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + info.fileName();
|
||||
if (QDir().mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
|
||||
runStrip(destinationPath);
|
||||
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
|
||||
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString &plugin : gstreamer_plugins_optional) {
|
||||
QFileInfo info(gstreamer_plugins_dir + "/" + plugin);
|
||||
if (!info.exists()) {
|
||||
info.setFile(gstreamer_plugins_dir + "/" + info.baseName() + QString(".so"));
|
||||
if (!info.exists()) {
|
||||
LogWarning() << "Skip missing gstreamer plugin" << info.baseName();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const QString &sourcePath = info.filePath();
|
||||
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + info.fileName();
|
||||
if (QDir().mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
|
||||
runStrip(destinationPath);
|
||||
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
|
||||
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1355,7 +1406,7 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
|
||||
deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs);
|
||||
}
|
||||
|
||||
void deployQmlImport(const QString &appBundlePath, const QSet<QString> &rpaths, const QString &importSourcePath, const QString &importName)
|
||||
void deployQmlImport(const QString &appBundlePath, const QList<QString> &rpaths, const QString &importSourcePath, const QString &importName)
|
||||
{
|
||||
QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
|
||||
|
||||
@@ -1384,11 +1435,11 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
|
||||
LogNormal() << "Application QML file path(s) is" << qmlDirs;
|
||||
LogNormal() << "QML module search path(s) is" << qmlImportPaths;
|
||||
|
||||
// Use qmlimportscanner from QLibraryInfo::BinariesPath
|
||||
// Use qmlimportscanner from QLibraryInfo::LibraryExecutablesPath
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmlimportscanner");
|
||||
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
|
||||
#else
|
||||
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlimportscanner");
|
||||
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
|
||||
#endif
|
||||
|
||||
// Fallback: Look relative to the macdeployqt binary
|
||||
@@ -1405,7 +1456,7 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
|
||||
// build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml"
|
||||
// ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
|
||||
QStringList argumentList;
|
||||
foreach (const QString &qmlDir, qmlDirs) {
|
||||
for (const QString &qmlDir : qmlDirs) {
|
||||
argumentList.append("-rootPath");
|
||||
argumentList.append(qmlDir);
|
||||
}
|
||||
@@ -1453,7 +1504,7 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
|
||||
std::sort(array.begin(), array.end(), importLessThan);
|
||||
|
||||
// deploy each import
|
||||
foreach (const QVariant &importValue, array) {
|
||||
for (const QVariant &importValue : array) {
|
||||
QVariantMap import = importValue.toMap();
|
||||
QString name = import["name"].toString();
|
||||
QString path = import["path"].toString();
|
||||
@@ -1550,7 +1601,7 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath();
|
||||
QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/";
|
||||
QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files);
|
||||
foreach (const QString &binary, foundRootBinaries) {
|
||||
for (const QString &binary : foundRootBinaries) {
|
||||
QString binaryPath = rootBinariesPath + binary;
|
||||
pendingBinaries.push(binaryPath);
|
||||
pendingBinariesSet.insert(binaryPath);
|
||||
@@ -1559,14 +1610,14 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
|
||||
bool getAbsoltuePath = true;
|
||||
QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath);
|
||||
foreach (const QString &binary, foundPluginBinaries) {
|
||||
for (const QString &binary : foundPluginBinaries) {
|
||||
pendingBinaries.push(binary);
|
||||
pendingBinariesSet.insert(binary);
|
||||
}
|
||||
|
||||
// Add frameworks for processing.
|
||||
QStringList frameworkPaths = findAppFrameworkPaths(appBundlePath);
|
||||
foreach (const QString &frameworkPath, frameworkPaths) {
|
||||
for (const QString &frameworkPath : frameworkPaths) {
|
||||
|
||||
// Prioritise first to sign any additional inner bundles found in the Helpers folder (e.g
|
||||
// used by QtWebEngine).
|
||||
@@ -1575,7 +1626,7 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
helpersIterator.next();
|
||||
QString helpersPath = helpersIterator.filePath();
|
||||
QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs);
|
||||
foreach (const QString &innerBundleName, innerBundleNames)
|
||||
for (const QString &innerBundleName : innerBundleNames)
|
||||
signedBinaries += codesignBundle(identity,
|
||||
helpersPath + "/" + innerBundleName,
|
||||
additionalBinariesContainingRpaths);
|
||||
@@ -1588,7 +1639,7 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
librariesIterator.next();
|
||||
QString librariesPath = librariesIterator.filePath();
|
||||
QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath);
|
||||
foreach (const QString &binary, bundleFiles) {
|
||||
for (const QString &binary : bundleFiles) {
|
||||
pendingBinaries.push(binary);
|
||||
pendingBinariesSet.insert(binary);
|
||||
}
|
||||
@@ -1613,7 +1664,7 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
pendingBinaries.push(binary);
|
||||
pendingBinariesSet.insert(binary);
|
||||
int dependenciesSkipped = 0;
|
||||
foreach (const QString &dependency, dependencies) {
|
||||
for (const QString &dependency : std::as_const(dependencies)) {
|
||||
// Skip dependencies that are outside the current app bundle, because this might
|
||||
// cause a codesign error if the current bundle is part of the dependency (e.g.
|
||||
// a bundle is part of a framework helper, and depends on that framework).
|
||||
@@ -1724,109 +1775,3 @@ void fixupFramework(const QString &frameworkName)
|
||||
addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary);
|
||||
}
|
||||
|
||||
bool FinalCheck(const QString &appBundlePath) {
|
||||
|
||||
bool success = true;
|
||||
|
||||
QDirIterator iter(appBundlePath, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
QString filepath = iter.fileInfo().filePath();
|
||||
|
||||
if (filepath.endsWith(".plist") ||
|
||||
filepath.endsWith(".icns") ||
|
||||
filepath.endsWith(".prl") ||
|
||||
filepath.endsWith(".conf") ||
|
||||
filepath.endsWith(".h") ||
|
||||
filepath.endsWith(".nib") ||
|
||||
filepath.endsWith(".strings") ||
|
||||
filepath.endsWith(".css") ||
|
||||
filepath.endsWith("CodeResources") ||
|
||||
filepath.endsWith("PkgInfo") ||
|
||||
filepath.endsWith(".modulemap")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//qDebug() << "Final check on" << filepath;
|
||||
|
||||
QProcess otool;
|
||||
otool.start("otool", QStringList() << "-L" << filepath);
|
||||
otool.waitForFinished();
|
||||
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
|
||||
LogError() << otool.readAllStandardError();
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
QString output = otool.readAllStandardOutput();
|
||||
QStringList output_lines = output.split("\n", Qt::SkipEmptyParts);
|
||||
if (output_lines.size() < 2) {
|
||||
LogError() << "Could not parse otool output:" << output;
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
QString first_line = output_lines.first();
|
||||
if (first_line.endsWith(':')) first_line.chop(1);
|
||||
if (first_line == filepath) {
|
||||
output_lines.removeFirst();
|
||||
}
|
||||
else {
|
||||
LogError() << "First line" << first_line << "does not match" << filepath;
|
||||
success = false;
|
||||
}
|
||||
static const QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$"));
|
||||
for (const QString &output_line : output_lines) {
|
||||
|
||||
//qDebug() << "Final check on" << filepath << output_line;
|
||||
|
||||
const auto match = regexp.match(output_line);
|
||||
if (match.hasMatch()) {
|
||||
QString library = match.captured(1);
|
||||
if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this.
|
||||
continue;
|
||||
}
|
||||
else if (library.startsWith("@executable_path")) {
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@executable_path", appBundlePath + "/Contents/MacOS");
|
||||
if (!QFile(real_path).exists()) {
|
||||
LogError() << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("@rpath")) {
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@rpath", appBundlePath + "/Contents/Frameworks");
|
||||
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
|
||||
LogError() << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("@loader_path")) {
|
||||
QString loader_path = QFileInfo(filepath).path();
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@loader_path", loader_path);
|
||||
if (!QFile(real_path).exists()) {
|
||||
LogError() << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library
|
||||
continue;
|
||||
}
|
||||
else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason.
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
LogError() << "File" << filepath << "points to" << library;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogError() << "Could not parse otool output line:" << output_line;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
12
3rdparty/macdeployqt/shared.h
vendored
@@ -62,7 +62,7 @@ public:
|
||||
|
||||
bool isDebugLibrary() const
|
||||
{
|
||||
return binaryName.contains(QLatin1String("_debug"));
|
||||
return binaryName.endsWith(QStringLiteral("_debug"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
QString qtPath;
|
||||
QString pluginPath;
|
||||
QStringList deployedFrameworks;
|
||||
QSet<QString> rpathsUsed;
|
||||
QList<QString> rpathsUsed;
|
||||
bool useLoaderPath;
|
||||
bool isFramework;
|
||||
bool isDebug;
|
||||
@@ -112,10 +112,10 @@ public:
|
||||
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
|
||||
|
||||
OtoolInfo findDependencyInfo(const QString &binaryPath);
|
||||
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
||||
QString findAppBinary(const QString &appBundlePath);
|
||||
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
||||
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
||||
QString copyFramework(const FrameworkInfo &framework, const QString path);
|
||||
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
|
||||
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
|
||||
@@ -136,6 +136,6 @@ QSet<QString> codesignBundle(const QString &identity,
|
||||
void codesign(const QString &identity, const QString &appBundlePath);
|
||||
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
|
||||
void fixupFramework(const QString &appBundlePath);
|
||||
bool FinalCheck(const QString &appBundlePath);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
12
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -10,11 +10,7 @@ endif()
|
||||
|
||||
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
||||
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
else()
|
||||
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
endif()
|
||||
qt_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
||||
target_include_directories(singleapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
@@ -28,11 +24,7 @@ target_link_libraries(singleapplication PRIVATE
|
||||
|
||||
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
||||
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
else()
|
||||
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
endif()
|
||||
qt_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||
target_include_directories(singlecoreapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
||||
@@ -134,7 +134,7 @@ SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondar
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->primary == false) {
|
||||
if (!inst->primary) {
|
||||
d->startPrimary();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
|
||||
@@ -234,9 +234,9 @@ QString SingleApplication::currentUser() {
|
||||
* Sends message to the Primary Instance.
|
||||
* @param message The message to send.
|
||||
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||
* @return true if the message was sent successfuly, false otherwise.
|
||||
* @return true if the message was sent successfully, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||
bool SingleApplication::sendMessage(const QByteArray &message, const int timeout) {
|
||||
|
||||
Q_D(SingleApplication);
|
||||
|
||||
@@ -244,8 +244,9 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||
if (isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect))
|
||||
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->socket_->write(message);
|
||||
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
class SingleApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleApplication class handles multipe instances of the same Application
|
||||
* @brief The SingleApplication class handles multiple instances of the same Application
|
||||
* @see QApplication
|
||||
*/
|
||||
class SingleApplication : public QApplication {
|
||||
class SingleApplication : public QApplication { // clazy:exclude=ctor-missing-parent-argument
|
||||
Q_OBJECT
|
||||
|
||||
typedef QApplication app_t;
|
||||
@@ -136,7 +136,7 @@ class SingleApplication : public QApplication {
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||
bool sendMessage(const QByteArray &message, const int timeout = 1000);
|
||||
|
||||
signals:
|
||||
void instanceStarted();
|
||||
|
||||
@@ -169,7 +169,7 @@ void SingleApplicationPrivate::genBlockServerName() {
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock() {
|
||||
void SingleApplicationPrivate::initializeMemoryBlock() const {
|
||||
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
inst->primary = false;
|
||||
@@ -237,8 +237,9 @@ bool SingleApplicationPrivate::connectToPrimary(const int timeout, const Connect
|
||||
forever {
|
||||
randomSleep();
|
||||
|
||||
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||
if (socket_->state() != QLocalSocket::ConnectingState) {
|
||||
socket_->connectToServer(blockServerName_);
|
||||
}
|
||||
|
||||
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
|
||||
@@ -284,7 +285,7 @@ bool SingleApplicationPrivate::connectToPrimary(const int timeout, const Connect
|
||||
|
||||
}
|
||||
|
||||
quint16 SingleApplicationPrivate::blockChecksum() {
|
||||
quint16 SingleApplicationPrivate::blockChecksum() const {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||
@@ -296,7 +297,7 @@ quint16 SingleApplicationPrivate::blockChecksum() {
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleApplicationPrivate::primaryPid() {
|
||||
qint64 SingleApplicationPrivate::primaryPid() const {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
@@ -307,7 +308,7 @@ qint64 SingleApplicationPrivate::primaryPid() {
|
||||
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::primaryUser() {
|
||||
QString SingleApplicationPrivate::primaryUser() const {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
@@ -327,7 +328,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
const ConnectionInfo info = connectionMap_[nextConnSocket];
|
||||
slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
});
|
||||
|
||||
@@ -337,7 +338,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
const ConnectionInfo info = connectionMap_[nextConnSocket];
|
||||
switch (info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
@@ -461,7 +462,7 @@ void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSo
|
||||
void SingleApplicationPrivate::randomSleep() {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
|
||||
#else
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||
|
||||
18
3rdparty/singleapplication/singleapplication_p.h
vendored
@@ -37,7 +37,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
|
||||
#include "singleapplication.h"
|
||||
|
||||
@@ -55,7 +55,7 @@ struct InstancesInfo {
|
||||
|
||||
struct ConnectionInfo {
|
||||
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
};
|
||||
@@ -80,18 +80,18 @@ class SingleApplicationPrivate : public QObject {
|
||||
explicit SingleApplicationPrivate(SingleApplication *ptr);
|
||||
~SingleApplicationPrivate() override;
|
||||
|
||||
QString getUsername();
|
||||
static QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void initializeMemoryBlock() const;
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
QString primaryUser();
|
||||
quint16 blockChecksum() const;
|
||||
qint64 primaryPid() const;
|
||||
QString primaryUser() const;
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void randomSleep();
|
||||
static void randomSleep();
|
||||
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory_;
|
||||
@@ -100,7 +100,7 @@ class SingleApplicationPrivate : public QObject {
|
||||
quint32 instanceNumber_;
|
||||
QString blockServerName_;
|
||||
SingleApplication::Options options_;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
|
||||
@@ -134,7 +134,7 @@ SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allow
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->primary == false) {
|
||||
if (!inst->primary) {
|
||||
d->startPrimary();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
|
||||
@@ -234,9 +234,9 @@ QString SingleCoreApplication::currentUser() {
|
||||
* Sends message to the Primary Instance.
|
||||
* @param message The message to send.
|
||||
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||
* @return true if the message was sent successfuly, false otherwise.
|
||||
* @return true if the message was sent successfully, false otherwise.
|
||||
*/
|
||||
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||
bool SingleCoreApplication::sendMessage(const QByteArray &message, const int timeout) {
|
||||
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
@@ -244,8 +244,9 @@ bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||
if (isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect))
|
||||
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->socket_->write(message);
|
||||
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
class SingleCoreApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleCoreApplication class handles multipe instances of the same Application
|
||||
* @brief The SingleCoreApplication class handles multiple instances of the same Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleCoreApplication : public QCoreApplication {
|
||||
@@ -135,7 +135,7 @@ class SingleCoreApplication : public QCoreApplication {
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||
bool sendMessage(const QByteArray &message, const int timeout = 1000);
|
||||
|
||||
signals:
|
||||
void instanceStarted();
|
||||
|
||||
@@ -169,7 +169,7 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
||||
void SingleCoreApplicationPrivate::initializeMemoryBlock() const {
|
||||
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
inst->primary = false;
|
||||
@@ -237,8 +237,9 @@ bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const Con
|
||||
forever {
|
||||
randomSleep();
|
||||
|
||||
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||
if (socket_->state() != QLocalSocket::ConnectingState) {
|
||||
socket_->connectToServer(blockServerName_);
|
||||
}
|
||||
|
||||
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
|
||||
@@ -284,7 +285,7 @@ bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const Con
|
||||
|
||||
}
|
||||
|
||||
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||
quint16 SingleCoreApplicationPrivate::blockChecksum() const {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||
@@ -296,7 +297,7 @@ quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||
qint64 SingleCoreApplicationPrivate::primaryPid() const {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
@@ -307,7 +308,7 @@ qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||
|
||||
}
|
||||
|
||||
QString SingleCoreApplicationPrivate::primaryUser() {
|
||||
QString SingleCoreApplicationPrivate::primaryUser() const {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
@@ -327,7 +328,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
const ConnectionInfo info = connectionMap_[nextConnSocket];
|
||||
slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
});
|
||||
|
||||
@@ -337,7 +338,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
const ConnectionInfo info = connectionMap_[nextConnSocket];
|
||||
switch (info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
@@ -461,7 +462,7 @@ void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *clos
|
||||
void SingleCoreApplicationPrivate::randomSleep() {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
|
||||
#else
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
|
||||
@@ -80,18 +80,18 @@ class SingleCoreApplicationPrivate : public QObject {
|
||||
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
|
||||
~SingleCoreApplicationPrivate() override;
|
||||
|
||||
QString getUsername();
|
||||
static QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void initializeMemoryBlock() const;
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
QString primaryUser();
|
||||
quint16 blockChecksum() const;
|
||||
qint64 primaryPid() const;
|
||||
QString primaryUser() const;
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void randomSleep();
|
||||
static void randomSleep();
|
||||
|
||||
SingleCoreApplication *q_ptr;
|
||||
QSharedMemory *memory_;
|
||||
@@ -100,7 +100,7 @@ class SingleCoreApplicationPrivate : public QObject {
|
||||
quint32 instanceNumber_;
|
||||
QString blockServerName_;
|
||||
SingleCoreApplication::Options options_;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
|
||||
359
CMakeLists.txt
@@ -12,13 +12,13 @@ include(cmake/Summary.cmake)
|
||||
include(cmake/OptionalSource.cmake)
|
||||
include(cmake/ParseArguments.cmake)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(LINUX ON)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
set(FREEBSD ON)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
set(OPENBSD ON)
|
||||
endif()
|
||||
|
||||
@@ -35,34 +35,38 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
list(APPEND COMPILE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:C>:-std=c99>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wunused
|
||||
-Wshadow
|
||||
-Wundef
|
||||
-Wuninitialized
|
||||
-Wredundant-decls
|
||||
-Wcast-align
|
||||
-Winit-self
|
||||
-Wmissing-include-dirs
|
||||
-Wmissing-declarations
|
||||
-Wstrict-overflow=2
|
||||
-Wunused-parameter
|
||||
-Wformat=2
|
||||
-Wdisabled-optimization
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
|
||||
)
|
||||
if(MSVC)
|
||||
list(APPEND COMPILE_OPTIONS /std:c++17 /MP)
|
||||
else()
|
||||
list(APPEND COMPILE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:C>:-std=c99>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wunused
|
||||
-Wshadow
|
||||
-Wundef
|
||||
-Wuninitialized
|
||||
-Wredundant-decls
|
||||
-Wcast-align
|
||||
-Winit-self
|
||||
-Wmissing-include-dirs
|
||||
-Wmissing-declarations
|
||||
-Wstrict-overflow=2
|
||||
-Wunused-parameter
|
||||
-Wformat=2
|
||||
-Wdisabled-optimization
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
|
||||
)
|
||||
endif()
|
||||
|
||||
option(BUILD_WERROR "Build with -Werror" OFF)
|
||||
if(BUILD_WERROR)
|
||||
list(APPEND COMPILE_OPTIONS -Werror)
|
||||
endif(BUILD_WERROR)
|
||||
endif()
|
||||
|
||||
add_compile_options(${COMPILE_OPTIONS})
|
||||
|
||||
@@ -77,33 +81,33 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_EXECUTABLE NAMES ccache)
|
||||
if (CCACHE_EXECUTABLE)
|
||||
if(CCACHE_EXECUTABLE)
|
||||
message(STATUS "ccache found: will be used for compilation and linkage")
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE})
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Boost REQUIRED)
|
||||
find_package(Threads)
|
||||
find_package(Backtrace QUIET)
|
||||
find_package(Backtrace)
|
||||
if(Backtrace_FOUND)
|
||||
set(HAVE_BACKTRACE ON)
|
||||
endif()
|
||||
find_package(Iconv QUIET)
|
||||
find_package(Iconv)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
if (NOT Protobuf_PROTOC_EXECUTABLE)
|
||||
if(NOT Protobuf_PROTOC_EXECUTABLE)
|
||||
message(FATAL_ERROR "Missing protobuf compiler.")
|
||||
endif()
|
||||
if(LINUX)
|
||||
find_package(ALSA REQUIRED)
|
||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||
else(LINUX)
|
||||
else()
|
||||
find_package(ALSA)
|
||||
pkg_check_modules(DBUS dbus-1)
|
||||
endif(LINUX)
|
||||
if (UNIX AND NOT APPLE)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(X11)
|
||||
pkg_check_modules(XCB xcb)
|
||||
endif()
|
||||
@@ -123,7 +127,7 @@ pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
|
||||
pkg_check_modules(LIBVLC libvlc)
|
||||
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
|
||||
pkg_check_modules(LIBPULSE libpulse)
|
||||
pkg_check_modules(CHROMAPRINT libchromaprint)
|
||||
pkg_check_modules(CHROMAPRINT libchromaprint>=1.4)
|
||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
|
||||
@@ -132,81 +136,99 @@ find_package(FFTW3)
|
||||
find_package(GTest)
|
||||
find_library(GMOCK_LIBRARY gmock)
|
||||
|
||||
if(NOT QT_DEFAULT_MAJOR_VERSION)
|
||||
set(QT_DEFAULT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
set(QT_MAJOR_VERSION ${QT_DEFAULT_MAJOR_VERSION} CACHE STRING "Qt version to use (5 or 6), defaults to ${QT_DEFAULT_MAJOR_VERSION}")
|
||||
|
||||
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
|
||||
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
||||
option(QT_VERSION_MAJOR "Qt version to use (5 or 6)")
|
||||
option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
|
||||
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
|
||||
|
||||
if(WITH_QT6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
endif()
|
||||
|
||||
if(BUILD_WITH_QT5)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
elseif(BUILD_WITH_QT6)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
if(QT_MAJOR_VERSION EQUAL 5)
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
elseif(QT_MAJOR_VERSION EQUAL 6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
else()
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
if(QT_MAJOR_VERSION)
|
||||
set(QT_VERSION_MAJOR ${QT_MAJOR_VERSION})
|
||||
endif()
|
||||
|
||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||
set(QT_OPTIONAL_COMPONENTS Test)
|
||||
|
||||
if(QT_MAJOR_VERSION EQUAL 5)
|
||||
set(QT_MIN_VERSION 5.8)
|
||||
if(QT_VERSION_MAJOR)
|
||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
||||
endif()
|
||||
|
||||
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
||||
if(DBUS_FOUND AND NOT WIN32)
|
||||
list(APPEND QT_COMPONENTS DBus)
|
||||
endif()
|
||||
if(X11_FOUND)
|
||||
set(QT_OPTIONAL_COMPONENTS Test)
|
||||
set(QT_MIN_VERSION 5.8)
|
||||
|
||||
if(BUILD_WITH_QT6 OR QT_VERSION_MAJOR EQUAL 6)
|
||||
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
|
||||
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
|
||||
elseif(BUILD_WITH_QT5 OR QT_VERSION_MAJOR EQUAL 5)
|
||||
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
|
||||
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
|
||||
else()
|
||||
# Automatically detect Qt version.
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
|
||||
if(QT_FOUND AND QT_VERSION_MAJOR EQUAL 6)
|
||||
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
|
||||
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
|
||||
elseif(QT_FOUND AND QT_VERSION_MAJOR EQUAL 5)
|
||||
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
|
||||
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
|
||||
else()
|
||||
message(FATAL_ERROR "Missing Qt.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(QT_VERSION_MAJOR)
|
||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
||||
endif()
|
||||
|
||||
if(X11_FOUND AND BUILD_WITH_QT5)
|
||||
list(APPEND QT_OPTIONAL_COMPONENTS X11Extras)
|
||||
endif()
|
||||
if(WIN32)
|
||||
list(APPEND QT_OPTIONAL_COMPONENTS WinExtras)
|
||||
endif()
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
||||
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
||||
|
||||
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
|
||||
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
|
||||
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
|
||||
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
|
||||
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
|
||||
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
|
||||
set(QT_LIBRARIES Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Concurrent Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Sql)
|
||||
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
|
||||
set(QtCore_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
|
||||
set(QtConcurrent_LIBRARIES Qt${QT_VERSION_MAJOR}::Concurrent)
|
||||
set(QtGui_LIBRARIES Qt${QT_VERSION_MAJOR}::Gui)
|
||||
set(QtWidgets_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set(QtNetwork_LIBRARIES Qt${QT_VERSION_MAJOR}::Network)
|
||||
set(QtSql_LIBRARIES Qt${QT_VERSION_MAJOR}::Sql)
|
||||
set(QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql)
|
||||
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||
if(BUILD_WITH_QT5 AND Qt5X11Extras_FOUND)
|
||||
set(HAVE_X11EXTRAS ON)
|
||||
set(QtX11Extras_LIBRARIES Qt5::X11Extras)
|
||||
list(APPEND QT_LIBRARIES Qt5::X11Extras)
|
||||
endif()
|
||||
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||
set(HAVE_WINEXTRAS ON)
|
||||
endif()
|
||||
if(Qt${QT_MAJOR_VERSION}Test_FOUND)
|
||||
set(QtTest_LIBRARIES Qt${QT_MAJOR_VERSION}::Test)
|
||||
if(Qt${QT_VERSION_MAJOR}Test_FOUND)
|
||||
set(QtTest_LIBRARIES Qt${QT_VERSION_MAJOR}::Test)
|
||||
endif()
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
|
||||
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert)
|
||||
endif()
|
||||
|
||||
if(BUILD_WITH_QT5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
|
||||
macro(qt_add_resources)
|
||||
qt5_add_resources(${ARGN})
|
||||
endmacro()
|
||||
macro(qt_wrap_cpp)
|
||||
qt5_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
macro(qt_wrap_ui)
|
||||
qt5_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt5_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
macro(qt_add_dbus_interface)
|
||||
qt5_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
@@ -223,26 +245,45 @@ if(X11_FOUND)
|
||||
message(WARNING, "Missing X11/XF86keysym.h")
|
||||
endif()
|
||||
|
||||
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_VERSION_MAJOR}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()
|
||||
endif(X11_FOUND)
|
||||
|
||||
option(USE_TAGLIB "Build with TagLib" OFF)
|
||||
option(USE_TAGPARSER "Build with TagParser" OFF)
|
||||
|
||||
if(NOT USE_TAGLIB AND NOT USE_TAGPARSER)
|
||||
set(USE_TAGLIB ON)
|
||||
endif()
|
||||
|
||||
# TAGLIB
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
||||
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
|
||||
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
|
||||
if(HAVE_TAGLIB_DSFFILE_H)
|
||||
set(HAVE_TAGLIB_DSFFILE ON)
|
||||
endif(HAVE_TAGLIB_DSFFILE_H)
|
||||
if(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
||||
endif(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||
if(USE_TAGLIB)
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
||||
if(TAGLIB_FOUND)
|
||||
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
|
||||
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
|
||||
if(HAVE_TAGLIB_DSFFILE_H)
|
||||
set(HAVE_TAGLIB_DSFFILE ON)
|
||||
endif(HAVE_TAGLIB_DSFFILE_H)
|
||||
if(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
||||
endif(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TAGPARSER
|
||||
if(USE_TAGPARSER)
|
||||
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
||||
endif()
|
||||
|
||||
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
||||
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
||||
endif()
|
||||
|
||||
# SingleApplication
|
||||
add_subdirectory(3rdparty/singleapplication)
|
||||
@@ -252,11 +293,12 @@ set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
|
||||
|
||||
if(APPLE)
|
||||
find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle")
|
||||
add_subdirectory(3rdparty/macdeployqt)
|
||||
add_subdirectory(3rdparty/SPMediaKeyTap)
|
||||
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
|
||||
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
|
||||
endif(APPLE)
|
||||
add_subdirectory(3rdparty/macdeployqt)
|
||||
add_subdirectory(ext/macdeploycheck)
|
||||
endif()
|
||||
|
||||
if(NOT SPARKLE AND (APPLE OR WIN32))
|
||||
if(BUILD_WITH_QT6)
|
||||
@@ -269,23 +311,22 @@ if(NOT SPARKLE AND (APPLE OR WIN32))
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if(WIN32 AND NOT MSVC)
|
||||
# RC compiler
|
||||
string(REPLACE "gcc" "windres" CMAKE_RC_COMPILER_INIT ${CMAKE_C_COMPILER})
|
||||
enable_language(RC)
|
||||
SET(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -o <OBJECT> <SOURCE> -I ${CMAKE_SOURCE_DIR}/dist/windows")
|
||||
endif(WIN32)
|
||||
endif()
|
||||
|
||||
# Optional bits
|
||||
if(WIN32)
|
||||
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
|
||||
endif(WIN32)
|
||||
endif()
|
||||
|
||||
optional_component(ALSA ON "ALSA integration"
|
||||
DEPENDS "alsa" ALSA_FOUND
|
||||
)
|
||||
|
||||
optional_component(LIBPULSE ON "Pulse audio integration"
|
||||
optional_component(LIBPULSE ON "PulseAudio integration"
|
||||
DEPENDS "libpulse" LIBPULSE_FOUND
|
||||
)
|
||||
|
||||
@@ -306,11 +347,17 @@ optional_component(VLC ON "Engine: VLC backend"
|
||||
DEPENDS "libvlc" LIBVLC_FOUND
|
||||
)
|
||||
|
||||
optional_component(CHROMAPRINT ON "Chromaprint (Tag fetching from Musicbrainz)"
|
||||
optional_component(SONGFINGERPRINTING ON "Song fingerprinting and tracking"
|
||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||
DEPENDS "gstreamer" GSTREAMER_FOUND
|
||||
)
|
||||
|
||||
if (X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
|
||||
optional_component(MUSICBRAINZ ON "MusicBrainz integration"
|
||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||
DEPENDS "gstreamer" GSTREAMER_FOUND
|
||||
)
|
||||
|
||||
if(X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
|
||||
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
|
||||
endif()
|
||||
|
||||
@@ -318,12 +365,21 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
|
||||
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
|
||||
)
|
||||
|
||||
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
||||
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND
|
||||
)
|
||||
if(BUILD_WITH_QT6 AND (Qt6Core_VERSION VERSION_EQUAL 6.2.0 OR Qt6Core_VERSION VERSION_GREATER 6.2.0))
|
||||
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts" DEPENDS "X11" X11_FOUND)
|
||||
else()
|
||||
if(HAVE_X11EXTRAS OR HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
|
||||
set(HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H ON)
|
||||
endif()
|
||||
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
||||
DEPENDS "X11" X11_FOUND
|
||||
DEPENDS "Qt >= 6.2, X11Extras or qpa/qplatformnativeinterface.h header" HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H
|
||||
)
|
||||
endif()
|
||||
|
||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||
DEPENDS "gstreamer" GSTREAMER_FOUND
|
||||
)
|
||||
|
||||
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
||||
@@ -372,24 +428,23 @@ optional_component(MOODBAR ON "Moodbar"
|
||||
DEPENDS "gstreamer" HAVE_GSTREAMER
|
||||
)
|
||||
|
||||
if(LINUX OR APPLE)
|
||||
option(USE_BUNDLE "Bundle dependencies" OFF)
|
||||
elseif(WIN32)
|
||||
if(APPLE OR WIN32)
|
||||
option(USE_BUNDLE "Bundle dependencies" ON)
|
||||
else()
|
||||
option(USE_BUNDLE "Bundle dependencies" OFF)
|
||||
endif()
|
||||
|
||||
if (USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
||||
if(LINUX)
|
||||
set(USE_BUNDLE_DIR "../plugins")
|
||||
endif(LINUX)
|
||||
if(APPLE)
|
||||
set(USE_BUNDLE_DIR "../PlugIns")
|
||||
endif(APPLE)
|
||||
endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
||||
|
||||
# Check that we have sqlite3 with FTS5
|
||||
if(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
||||
if(LINUX)
|
||||
set(USE_BUNDLE_DIR "../plugins")
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(USE_BUNDLE_DIR "../PlugIns")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
# Check that we have Qt with sqlite driver
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
||||
check_cxx_source_runs("
|
||||
@@ -400,12 +455,29 @@ if(NOT CMAKE_CROSSCOMPILING)
|
||||
db.setDatabaseName(\":memory:\");
|
||||
if (!db.open()) { return 1; }
|
||||
QSqlQuery q(db);
|
||||
q.prepare(\"CREATE VIRTUAL TABLE test_fts USING fts5(test, tokenize = 'unicode61 remove_diacritics 0');\");
|
||||
q.prepare(\"CREATE TABLE test (test TEXT);\");
|
||||
if (!q.exec()) return 1;
|
||||
}
|
||||
"
|
||||
SQLITE3_FTS5
|
||||
QT_SQLITE_TEST
|
||||
)
|
||||
if(QT_SQLITE_TEST)
|
||||
# Check that we have sqlite3 with FTS5
|
||||
check_cxx_source_runs("
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
int main() {
|
||||
QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");
|
||||
db.setDatabaseName(\":memory:\");
|
||||
if (!db.open()) { return 1; }
|
||||
QSqlQuery q(db);
|
||||
q.prepare(\"CREATE VIRTUAL TABLE test_fts USING fts5(test, tokenize = 'unicode61 remove_diacritics 0');\");
|
||||
if (!q.exec()) return 1;
|
||||
}
|
||||
"
|
||||
SQLITE_FTS5_TEST
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set up definitions
|
||||
@@ -417,6 +489,13 @@ add_definitions(-DQT_USE_QSTRINGBUILDER)
|
||||
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE)
|
||||
if(MSVC)
|
||||
add_definitions(-DPROTOBUF_USE_DLLS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(dist)
|
||||
@@ -432,13 +511,9 @@ if(GTest_FOUND AND GMOCK_LIBRARY AND QtTest_LIBRARIES)
|
||||
endif()
|
||||
|
||||
# Uninstall support
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
|
||||
# Show a summary of what we have enabled
|
||||
summary_show()
|
||||
@@ -448,10 +523,16 @@ elseif(NOT HAVE_GSTREAMER)
|
||||
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
||||
endif()
|
||||
|
||||
if(NOT SQLITE3_FTS5 AND NOT CMAKE_CROSSCOMPILING)
|
||||
message(WARNING "sqlite3 must be enabled with FTS5. See: https://www.sqlite.org/fts5.html")
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
if(QT_SQLITE_TEST)
|
||||
if(NOT SQLITE_FTS5_TEST)
|
||||
message(WARNING "sqlite must be enabled with FTS5. See: https://www.sqlite.org/fts5.html")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "The Qt sqlite driver test failed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
||||
message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.")
|
||||
endif()
|
||||
|
||||
66
Changelog
@@ -2,6 +2,60 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
1.0.0:
|
||||
|
||||
Bugfixes:
|
||||
* Fix updating temporary metadata when reloading songs outside of the collection.
|
||||
* Don't strip off "Live" from song title when sending scrobbles.
|
||||
* Fix incorrect use of QFutureWatcher.
|
||||
* Fix compile of Utilities::Hmac with Qt 6.2.
|
||||
* Fix a memory leak when using right click context menu in internet search.
|
||||
* Fix a gstreamer bus leak when adding streams and remote playlists.
|
||||
* Fix "Source ID x was not found when attempting to remove it" error.
|
||||
* Escape ampersands in playlist tabs.
|
||||
* Fix analyzer with S24_32LE audio format.
|
||||
* (macOS) Fix incorrect playlist alternating row colors with dark theme.
|
||||
* (Windows) Fix adding songs with Japanese characters from the files tab.
|
||||
|
||||
Enhancements:
|
||||
* Add replaygain fallback gain setting.
|
||||
* Add option to turn off playlist alternating row colors.
|
||||
* Make the default tabbbar background color lighter.
|
||||
* Remove use of deprecated WinExtras Qt module.
|
||||
* Add CMake test for Qt sqlite support.
|
||||
* Automatically detect Qt version if BUILD_WITH_QT5 or BUILD_WITH_QT6 is not specified.
|
||||
* Correct playlist tabbar favorite tooltip from "click" to "double-click".
|
||||
* Remove scroll over icon to change track option since it does not work reliable.
|
||||
* Improve resume playback on startup.
|
||||
* Re-request stream URL for Tidal and QObuz when resuming playback after pausing for more than 30 seconds.
|
||||
* Add Finnish, Ukrainian, Dutch, Japanese, Chinese, Catalan and Portuguese (Brazil).
|
||||
* Add support for TagParser (https://github.com/Martchus/tagparser) as an alternative to TagLib.
|
||||
* Add Subsonic option to turn off HTTP/2.
|
||||
* Fix minor Clang-Tidy and Clazy warnings.
|
||||
* Use higher resolution images from last.fm API.
|
||||
* Add MD5 token authentication for Subsonic.
|
||||
* Use 500 albums per request when receiving albums from Subsonic.
|
||||
* Use QX11Application with Qt >= 6.2 for X11 global shortcuts.
|
||||
* Allow fading when a ALSA PCM device is selected.
|
||||
* Store Tidal MPEG-DASH file in data uri.
|
||||
* Use XSPF image elements as manually set artwork.
|
||||
* Make error dialog larger.
|
||||
* Show error dialog for failed SQL queries.
|
||||
* Show error dialog when failing to read or write album covers.
|
||||
* Add module music formats (mod, s3m, xm, it) to detected filetypes.
|
||||
* Disable gapless playback for module music formats to workaround gstreamer bug.
|
||||
* Update directory ID and song path immediately when organizing collection songs.
|
||||
* Add right click option to star a playlist in playlist tabs.
|
||||
* Use seconds instead of minutes for scrobble submit delay.
|
||||
* (macOS) Build with libgpod.
|
||||
* (Windows) Fix compile with MSVC.
|
||||
|
||||
New features:
|
||||
* Add ALSA PCM devices.
|
||||
* Add song fingerprinting and tracking.
|
||||
* Add support for native global shortcuts on MATE.
|
||||
* Add radios view with channels from Radio Paradise and SomaFM.
|
||||
|
||||
0.9.3:
|
||||
|
||||
Bugfixes:
|
||||
@@ -108,8 +162,8 @@ ChangeLog
|
||||
* 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.
|
||||
* Change star/unstar playlist to double-click instead of singleclick.
|
||||
* Don't edit playlist name on double-click 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.
|
||||
@@ -304,7 +358,7 @@ ChangeLog
|
||||
0.6.10:
|
||||
|
||||
Bugfixes:
|
||||
* Fixed Subsonic album covers not working for albums with non ASCII charcters.
|
||||
* Fixed Subsonic album covers not working for albums with non ASCII characters.
|
||||
* Fixed reading date and genre from individual tracks in CUE sheets.
|
||||
* Fixed resume playback on startup for CUE songs.
|
||||
* Fixed album cover manager not showing complete album titles in the list of album covers.
|
||||
@@ -313,7 +367,7 @@ ChangeLog
|
||||
* Fixed engine and device in context using too large icons when icons were loaded from the system theme.
|
||||
* Fixed "Secure connection setup failed" problem on Windows when playing streams.
|
||||
* Fixed margin for song title text in context.
|
||||
* Fixed UNC paths with non ASCII charcters not working.
|
||||
* Fixed UNC paths with non ASCII characters not working.
|
||||
|
||||
Enhancements:
|
||||
* Allowing all characters except slash and backslash when organising music unless options to strip characters is checked.
|
||||
@@ -328,9 +382,9 @@ ChangeLog
|
||||
* Only showing song length in context when available.
|
||||
* Sort album cover search results by score and pick the best 3 first before trying others to improve album cover search speed.
|
||||
* Make scrobbler work for streams.
|
||||
* Added search for lyrics as a seperate option in context.
|
||||
* Added search for lyrics as a separate option in context.
|
||||
* Made font and font sizes in context configurable.
|
||||
* Splitting artist and song title to the relevant metadata when artist and song title is sent as title seperated by a dash in streams.
|
||||
* Splitting artist and song title to the relevant metadata when artist and song title is sent as title separated by a dash in streams.
|
||||
* Added label to show collection pixmap disk cache used in settings.
|
||||
* Icreased default collection pixmap disk cache to 360.
|
||||
|
||||
|
||||
26
README.md
@@ -1,7 +1,8 @@
|
||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||
[](https://paypal.me/jonaskvinge)
|
||||
[](https://patreon.com/jonaskvinge)
|
||||
=======================
|
||||
[](https://github.com/sponsors/jonaski)
|
||||
[](https://patreon.com/jonaskvinge)
|
||||
[](https://paypal.me/jonaskvinge)
|
||||
|
||||
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
|
||||
|
||||
@@ -44,14 +45,14 @@ Funding developers is a way to contribute to open source projects you appreciate
|
||||
* Playlist management
|
||||
* Smart and dynamic playlists
|
||||
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||
* Edit tags on music files
|
||||
* Edit tags on audio files
|
||||
* Fetch tags from MusicBrainz
|
||||
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
|
||||
* Song lyrics from [AudD](https://audd.io/), [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
* Transfer music to iPod, MTP or mass-storage USB player
|
||||
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
||||
* Subsonic, Tidal and Qobuz streaming support
|
||||
|
||||
@@ -64,29 +65,28 @@ It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
||||
|
||||
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
|
||||
|
||||
* [CMake and Make tools](https://cmake.org/)
|
||||
* [CMake](https://cmake.org/)
|
||||
* [GNU Make](https://www.gnu.org/software/make/)
|
||||
* [GCC](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) compiler
|
||||
* [Boost](https://www.boost.org/)
|
||||
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
|
||||
* [GLib](https://developer.gnome.org/glib/)
|
||||
* [Protobuf](https://developers.google.com/protocol-buffers/)
|
||||
* [Qt 5.8 or higher (or Qt 6) with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
||||
* [Qt components X11Extras and D-Bus for Linux/BSD and WinExtras for Windows](https://www.qt.io/)
|
||||
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
|
||||
* [Chromaprint](https://acoustid.org/chromaprint)
|
||||
* [ALSA (linux)](https://www.alsa-project.org/)
|
||||
* [D-Bus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||
* [ALSA (Linux required)](https://www.alsa-project.org/)
|
||||
* [D-Bus (Linux required)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
||||
* [GnuTLS](https://www.gnutls.org/)
|
||||
* [TagLib](https://www.taglib.org/)
|
||||
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
* Song fingerprinting and MusicBrainz tagging: [Chromaprint](https://acoustid.org/chromaprint)
|
||||
* Moodbar: [fftw3](http://www.fftw.org/)
|
||||
* PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
||||
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||
* Moodbar: [fftw3](http://www.fftw.org/)
|
||||
|
||||
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented, and works best, it is therefore recommended to use GStreamer.
|
||||
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
|
||||
#if(MACDEPLOYQT_EXECUTABLE)
|
||||
# message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
||||
#else()
|
||||
# message(WARNING "Missing macdeployqt executable.")
|
||||
#endif()
|
||||
|
||||
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
|
||||
if(MACDEPLOYQT_EXECUTABLE)
|
||||
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
||||
else()
|
||||
message(WARNING "Missing macdeployqt executable.")
|
||||
endif()
|
||||
|
||||
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
|
||||
if(CREATEDMG_EXECUTABLE)
|
||||
@@ -14,24 +13,30 @@ else()
|
||||
message(WARNING "Missing create-dmg executable.")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macversion.sh OUTPUT_VARIABLE MACOS_VERSION_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT MACOS_VERSION_PACKAGE)
|
||||
message(WARNING "Could not set macOS version.")
|
||||
endif()
|
||||
|
||||
if(MACDEPLOYQT_EXECUTABLE AND CREATEDMG_EXECUTABLE AND MACOS_VERSION_PACKAGE)
|
||||
add_custom_target(dmg
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
|
||||
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
|
||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
|
||||
COMMAND ${CREATEDMG_EXECUTABLE} --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}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
if(MACDEPLOYQT_EXECUTABLE)
|
||||
add_custom_target(copy_gstreamer_plugins
|
||||
#COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app
|
||||
)
|
||||
add_custom_target(dmg2
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
|
||||
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
|
||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
|
||||
COMMAND ${CREATEDMG_EXECUTABLE} --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}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||
add_custom_target(deploy
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3
|
||||
-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
|
||||
-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gio-modules/libgiognutls.so
|
||||
#-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt
|
||||
)
|
||||
add_custom_target(deploycheck
|
||||
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
|
||||
DEPENDS macdeploycheck
|
||||
)
|
||||
if(CREATEDMG_EXECUTABLE)
|
||||
add_custom_target(dmg
|
||||
COMMAND ${CREATEDMG_EXECUTABLE} --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}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS deploy deploycheck
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -15,11 +15,7 @@ macro(optional_source TOGGLE)
|
||||
list(APPEND OTHER_SOURCES ${OPTIONAL_SOURCE_HEADERS})
|
||||
|
||||
set(_uic_sources)
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
else()
|
||||
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
endif()
|
||||
qt_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
list(APPEND OTHER_SOURCES ${_uic_sources})
|
||||
list(APPEND OTHER_UIC_SOURCES ${_uic_sources})
|
||||
endif(${TOGGLE})
|
||||
|
||||
@@ -75,10 +75,6 @@ macro(add_po outfiles po_prefix)
|
||||
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
||||
endforeach(_lang)
|
||||
file(APPEND ${_qrc} "</qresource></RCC>")
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_add_resources(${outfiles} ${_qrc})
|
||||
else()
|
||||
qt5_add_resources(${outfiles} ${_qrc})
|
||||
endif()
|
||||
qt_add_resources(${outfiles} ${_qrc})
|
||||
endif()
|
||||
endmacro(add_po)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 9)
|
||||
set(STRAWBERRY_VERSION_PATCH 3)
|
||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||
set(STRAWBERRY_VERSION_MINOR 0)
|
||||
set(STRAWBERRY_VERSION_PATCH 0)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<file>schema/schema-11.sql</file>
|
||||
<file>schema/schema-12.sql</file>
|
||||
<file>schema/schema-13.sql</file>
|
||||
<file>schema/schema-14.sql</file>
|
||||
<file>schema/schema-15.sql</file>
|
||||
<file>schema/device-schema.sql</file>
|
||||
<file>style/strawberry.css</file>
|
||||
<file>style/smartplaylistsearchterm.css</file>
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
<file>icons/128x128/tidal.png</file>
|
||||
<file>icons/128x128/qobuz.png</file>
|
||||
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/128x128/radio.png</file>
|
||||
<file>icons/128x128/somafm.png</file>
|
||||
<file>icons/128x128/radioparadise.png</file>
|
||||
<file>icons/64x64/albums.png</file>
|
||||
<file>icons/64x64/alsa.png</file>
|
||||
<file>icons/64x64/application-exit.png</file>
|
||||
@@ -185,6 +188,9 @@
|
||||
<file>icons/64x64/tidal.png</file>
|
||||
<file>icons/64x64/qobuz.png</file>
|
||||
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/64x64/radio.png</file>
|
||||
<file>icons/64x64/somafm.png</file>
|
||||
<file>icons/64x64/radioparadise.png</file>
|
||||
<file>icons/48x48/albums.png</file>
|
||||
<file>icons/48x48/alsa.png</file>
|
||||
<file>icons/48x48/application-exit.png</file>
|
||||
@@ -282,6 +288,9 @@
|
||||
<file>icons/48x48/tidal.png</file>
|
||||
<file>icons/48x48/qobuz.png</file>
|
||||
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/48x48/radio.png</file>
|
||||
<file>icons/48x48/somafm.png</file>
|
||||
<file>icons/48x48/radioparadise.png</file>
|
||||
<file>icons/32x32/albums.png</file>
|
||||
<file>icons/32x32/alsa.png</file>
|
||||
<file>icons/32x32/application-exit.png</file>
|
||||
@@ -379,6 +388,9 @@
|
||||
<file>icons/32x32/tidal.png</file>
|
||||
<file>icons/32x32/qobuz.png</file>
|
||||
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/32x32/radio.png</file>
|
||||
<file>icons/32x32/somafm.png</file>
|
||||
<file>icons/32x32/radioparadise.png</file>
|
||||
<file>icons/22x22/albums.png</file>
|
||||
<file>icons/22x22/alsa.png</file>
|
||||
<file>icons/22x22/application-exit.png</file>
|
||||
@@ -476,5 +488,8 @@
|
||||
<file>icons/22x22/tidal.png</file>
|
||||
<file>icons/22x22/qobuz.png</file>
|
||||
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/22x22/radio.png</file>
|
||||
<file>icons/22x22/somafm.png</file>
|
||||
<file>icons/22x22/radioparadise.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
BIN
data/icons/128x128/radio.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
data/icons/128x128/radioparadise.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
data/icons/128x128/somafm.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
data/icons/22x22/radio.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/icons/22x22/radioparadise.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/icons/22x22/somafm.png
Normal file
|
After Width: | Height: | Size: 639 B |
BIN
data/icons/32x32/radio.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
data/icons/32x32/radioparadise.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
data/icons/32x32/somafm.png
Normal file
|
After Width: | Height: | Size: 626 B |
BIN
data/icons/48x48/radio.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
data/icons/48x48/radioparadise.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
data/icons/48x48/somafm.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/icons/64x64/radio.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
data/icons/64x64/radioparadise.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
data/icons/64x64/somafm.png
Normal file
|
After Width: | Height: | Size: 996 B |
BIN
data/icons/full/radio.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
data/icons/full/radioparadise.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
data/icons/full/somafm.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,35 +1,35 @@
|
||||
CREATE TABLE device_%deviceid_directories (
|
||||
path TEXT NOT NULL,
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
subdirs INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE device_%deviceid_subdirectories (
|
||||
directory_id INTEGER NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
mtime INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE device_%deviceid_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -40,29 +40,32 @@ CREATE TABLE device_%deviceid_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -77,4 +80,4 @@ CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
);
|
||||
|
||||
UPDATE devices SET schema_version=2 WHERE ROWID=%deviceid;
|
||||
UPDATE devices SET schema_version=3 WHERE ROWID=%deviceid;
|
||||
|
||||
5
data/schema/schema-14.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE %allsongstables ADD COLUMN fingerprint TEXT DEFAULT '';
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN lastseen INTEGER NOT NULL DEFAULT -1;
|
||||
|
||||
UPDATE schema_version SET version=14;
|
||||
8
data/schema/schema-15.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS radio_channels (
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
name TEXT DEFAULT '',
|
||||
url TEXT DEFAULT '',
|
||||
thumbnail_url TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
UPDATE schema_version SET version=15;
|
||||
@@ -4,40 +4,40 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
||||
|
||||
DELETE FROM schema_version;
|
||||
|
||||
INSERT INTO schema_version (version) VALUES (13);
|
||||
INSERT INTO schema_version (version) VALUES (15);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS directories (
|
||||
path TEXT NOT NULL,
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
subdirs INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subdirectories (
|
||||
directory_id INTEGER NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
mtime INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -48,29 +48,32 @@ CREATE TABLE IF NOT EXISTS songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -78,25 +81,25 @@ CREATE TABLE IF NOT EXISTS songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -107,29 +110,32 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -137,25 +143,25 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -166,29 +172,32 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -196,25 +205,25 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -225,29 +234,32 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -255,25 +267,25 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -284,29 +296,32 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -314,25 +329,25 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -343,29 +358,32 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -373,25 +391,25 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -402,29 +420,32 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -432,25 +453,25 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -461,29 +482,32 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
filesize INTEGER NOT NULL DEFAULT -1,
|
||||
mtime INTEGER NOT NULL DEFAULT -1,
|
||||
ctime INTEGER NOT NULL DEFAULT -1,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
@@ -491,15 +515,15 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS playlists (
|
||||
|
||||
name TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
last_played INTEGER NOT NULL DEFAULT -1,
|
||||
ui_order INTEGER NOT NULL DEFAULT 0,
|
||||
special_type TEXT,
|
||||
ui_path TEXT,
|
||||
special_type TEXT DEFAULT '',
|
||||
ui_path TEXT DEFAULT '',
|
||||
is_favorite INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
dynamic_playlist_type INTEGER,
|
||||
dynamic_playlist_backend TEXT,
|
||||
dynamic_playlist_backend TEXT DEFAULT '',
|
||||
dynamic_playlist_data BLOB
|
||||
|
||||
);
|
||||
@@ -509,27 +533,27 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
||||
playlist INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL DEFAULT 0,
|
||||
collection_id INTEGER,
|
||||
playlist_url TEXT,
|
||||
playlist_url TEXT DEFAULT '',
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
title TEXT DEFAULT '',
|
||||
album TEXT DEFAULT '',
|
||||
artist TEXT DEFAULT '',
|
||||
albumartist TEXT DEFAULT '',
|
||||
track INTEGER,
|
||||
disc INTEGER,
|
||||
year INTEGER,
|
||||
originalyear INTEGER,
|
||||
genre TEXT,
|
||||
genre TEXT DEFAULT '',
|
||||
compilation INTEGER DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
composer TEXT DEFAULT '',
|
||||
performer TEXT DEFAULT '',
|
||||
grouping TEXT DEFAULT '',
|
||||
comment TEXT DEFAULT '',
|
||||
lyrics TEXT DEFAULT '',
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
artist_id TEXT DEFAULT '',
|
||||
album_id TEXT DEFAULT '',
|
||||
song_id TEXT DEFAULT '',
|
||||
|
||||
beginning INTEGER,
|
||||
length INTEGER,
|
||||
@@ -540,44 +564,54 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
||||
|
||||
source INTEGER,
|
||||
directory_id INTEGER,
|
||||
url TEXT,
|
||||
url TEXT DEFAULT '',
|
||||
filetype INTEGER,
|
||||
filesize INTEGER,
|
||||
mtime INTEGER,
|
||||
ctime INTEGER,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
fingerprint TEXT DEFAULT '',
|
||||
|
||||
playcount INTEGER DEFAULT 0,
|
||||
skipcount INTEGER DEFAULT 0,
|
||||
lastplayed INTEGER DEFAULT 0,
|
||||
lastplayed INTEGER DEFAULT -1,
|
||||
lastseen INTEGER DEFAULT -1,
|
||||
|
||||
compilation_detected INTEGER DEFAULT 0,
|
||||
compilation_on INTEGER DEFAULT 0,
|
||||
compilation_off INTEGER DEFAULT 0,
|
||||
compilation_effective INTEGER DEFAULT 0,
|
||||
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
art_automatic TEXT DEFAULT '',
|
||||
art_manual TEXT DEFAULT '',
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_albumartist TEXT DEFAULT '',
|
||||
effective_originalyear INTEGER,
|
||||
|
||||
cue_path TEXT,
|
||||
cue_path TEXT DEFAULT '',
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
unique_id TEXT NOT NULL,
|
||||
friendly_name TEXT,
|
||||
unique_id TEXT NOT NULL DEFAULT '',
|
||||
friendly_name TEXT DEFAULT '',
|
||||
size INTEGER,
|
||||
icon TEXT,
|
||||
icon TEXT DEFAULT '',
|
||||
schema_version INTEGER NOT NULL DEFAULT 0,
|
||||
transcode_mode NOT NULL DEFAULT 3,
|
||||
transcode_format NOT NULL DEFAULT 5
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS radio_channels (
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
name TEXT DEFAULT '',
|
||||
url TEXT DEFAULT '',
|
||||
thumbnail_url TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_url ON songs (url);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, artist);
|
||||
|
||||
13
debian/control
vendored
@@ -17,6 +17,7 @@ Build-Depends: debhelper (>= 11),
|
||||
libpulse-dev,
|
||||
libtag1-dev,
|
||||
qtbase5-dev,
|
||||
qtbase5-private-dev,
|
||||
qtbase5-dev-tools,
|
||||
qttools5-dev,
|
||||
libqt5x11extras5-dev,
|
||||
@@ -41,24 +42,26 @@ Depends: ${shlibs:Depends},
|
||||
gstreamer1.0-pulseaudio
|
||||
Homepage: http://www.strawberrymusicplayer.org/
|
||||
Description: Audio player and music collection organizer
|
||||
Strawberry is a music player aimed at music collectors, audio enthusiasts and audiophiles.
|
||||
Strawberry is a music player aimed at music collectors and audiophiles.
|
||||
.
|
||||
Features:
|
||||
- Play and organize music
|
||||
- Supports WAV, FLAC, WavPack, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF
|
||||
- Audio CD playback
|
||||
- Native desktop notifications
|
||||
- Playlist management
|
||||
- Playlists in multiple formats
|
||||
- Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||
- Edit tags on music files
|
||||
- Fetch tags from MusicBrainz
|
||||
- Edit tags on audio files
|
||||
- Automatically retrieve tags from MusicBrainz
|
||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
- Streaming support for Subsonic
|
||||
- Streaming support for Subsonic-compatible servers
|
||||
- Unofficial streaming support for Tidal and Qobuz
|
||||
.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
|
||||
20
dist/CMakeLists.txt
vendored
@@ -7,24 +7,24 @@ if(DEB_CODENAME AND DEB_DATE)
|
||||
endif(DEB_CODENAME AND DEB_DATE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
|
||||
|
||||
if (APPLE)
|
||||
if(APPLE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
||||
endif (APPLE)
|
||||
endif(APPLE)
|
||||
|
||||
if (WIN32)
|
||||
if(WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
|
||||
endif (WIN32)
|
||||
endif(WIN32)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)
|
||||
install(FILES ../data/icons/64x64/strawberry.png DESTINATION share/icons/hicolor/64x64/apps/)
|
||||
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/apps/)
|
||||
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
|
||||
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
|
||||
install(FILES unix/strawberry.1 unix/strawberry-tagreader.1 DESTINATION share/man/man1)
|
||||
endif (UNIX AND NOT APPLE)
|
||||
endif(UNIX AND NOT APPLE)
|
||||
|
||||
if (APPLE)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
|
||||
endif (APPLE)
|
||||
if(APPLE)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
|
||||
endif()
|
||||
|
||||
107
dist/macos/macgstcopy.sh
vendored
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Script to copy gstreamer plugins before macdeployqt is run.
|
||||
|
||||
if [ "$1" = "" ]; then
|
||||
echo "Usage: $0 <bundledir>"
|
||||
exit 1
|
||||
fi
|
||||
bundledir=$1
|
||||
|
||||
if [ "$GIO_EXTRA_MODULES" = "" ]; then
|
||||
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing libgiognutls.so."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$GST_PLUGIN_SCANNER" = "" ]; then
|
||||
echo "Error: Set the GST_PLUGIN_SCANNER environment variable to the gst-plugin-scanner."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$GST_PLUGIN_PATH" = "" ]; then
|
||||
echo "Error: Set the GST_PLUGIN_PATH environment variable to the path containing gstreamer plugins."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${bundledir}/Contents/PlugIns/gio-modules" || exit 1
|
||||
mkdir -p "${bundledir}/Contents/PlugIns/gstreamer" || exit 1
|
||||
|
||||
if ! [ -f "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
|
||||
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
|
||||
exit 1
|
||||
fi
|
||||
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
|
||||
|
||||
if ! [ -f "${GST_PLUGIN_SCANNER}" ]; then
|
||||
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
|
||||
exit 1
|
||||
fi
|
||||
cp -v -f "${GST_PLUGIN_SCANNER}" "${bundledir}/Contents/PlugIns/" || exit 1
|
||||
|
||||
gst_plugins="
|
||||
libgstapetag.dylib
|
||||
libgstapp.dylib
|
||||
libgstaudioconvert.dylib
|
||||
libgstaudiofx.dylib
|
||||
libgstaudiomixer.dylib
|
||||
libgstaudioparsers.dylib
|
||||
libgstaudiorate.dylib
|
||||
libgstaudioresample.dylib
|
||||
libgstaudiotestsrc.dylib
|
||||
libgstaudiovisualizers.dylib
|
||||
libgstauparse.dylib
|
||||
libgstautoconvert.dylib
|
||||
libgstautodetect.dylib
|
||||
libgstcoreelements.dylib
|
||||
libgstequalizer.dylib
|
||||
libgstgio.dylib
|
||||
libgsticydemux.dylib
|
||||
libgstid3demux.dylib
|
||||
libgstlevel.dylib
|
||||
libgstosxaudio.dylib
|
||||
libgstplayback.dylib
|
||||
libgstrawparse.dylib
|
||||
libgstreplaygain.dylib
|
||||
libgstsoup.dylib
|
||||
libgstspectrum.dylib
|
||||
libgsttypefindfunctions.dylib
|
||||
libgstvolume.dylib
|
||||
libgstxingmux.dylib
|
||||
libgsttcp.dylib
|
||||
libgstudp.dylib
|
||||
libgstpbtypes.dylib
|
||||
libgstrtp.dylib
|
||||
libgstrtsp.dylib
|
||||
libgstflac.dylib
|
||||
libgstwavparse.dylib
|
||||
libgstfaad.dylib
|
||||
libgstogg.dylib
|
||||
libgstopus.dylib
|
||||
libgstasf.dylib
|
||||
libgstspeex.dylib
|
||||
libgsttaglib.dylib
|
||||
libgstvorbis.dylib
|
||||
libgstisomp4.dylib
|
||||
libgstlibav.dylib
|
||||
libgstaiff.dylib
|
||||
libgstlame.dylib
|
||||
libgstopusparse.dylib
|
||||
libgstfaac.dylib
|
||||
libgstmusepack.dylib
|
||||
"
|
||||
|
||||
gst_plugins=$(echo "$gst_plugins" | tr '\n' ' ' | sed -e 's/^ //g' | sed -e 's/ / /g')
|
||||
|
||||
for gst_plugin in $gst_plugins
|
||||
do
|
||||
if [ -f "${GST_PLUGIN_PATH}/${gst_plugin}" ]; then
|
||||
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
|
||||
else
|
||||
echo "Warning: Missing gstreamer plugin ${GST_PLUGIN_PATH}/${gst_plugin}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -f "/usr/local/lib/libbrotlicommon.1.dylib" ]; then
|
||||
mkdir -p ${bundledir}/Contents/Frameworks
|
||||
cp -v -f "/usr/local/lib/libbrotlicommon.1.dylib" "${bundledir}/Contents/Frameworks/"
|
||||
fi
|
||||
@@ -15,34 +15,36 @@
|
||||
<translation type="qt">strawberry</translation>
|
||||
<description>
|
||||
<p>
|
||||
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles. The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.
|
||||
Strawberry is a music player and music collection organizer. It is aimed at music collectors and audiophiles. With Strawberry you can play and manage your digital music collection, or stream your favorite radios. It also has unofficial streaming support for Tidal and Qobuz. Strawberry is free software released under GPL. The source code is available on GitHub. It's written in C++ using the Qt toolkit and GStreamer. Strawberry is compatible with both Qt version 5 and 6.
|
||||
</p>
|
||||
<p>Features:</p>
|
||||
<ul>
|
||||
<li>Play and organize music</li>
|
||||
<li>Supports most popular audio formats and CD playback</li>
|
||||
<li>Native desktop notifications</li>
|
||||
<li>Playlists in multiple formats</li>
|
||||
<li>Playlist management and playlists in multiple formats</li>
|
||||
<li>Smart and dynamic playlists</li>
|
||||
<li>Advanced audio output and device configuration for bit-perfect playback on Linux</li>
|
||||
<li>Edit tags on music files</li>
|
||||
<li>Fetch tags from MusicBrainz</li>
|
||||
<li>Edit tags on audio files</li>
|
||||
<li>Automatically retrieve tags from MusicBrainz</li>
|
||||
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
|
||||
<li>Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com</li>
|
||||
<li>Support for multiple backends</li>
|
||||
<li>Audio analyzer and equalizer</li>
|
||||
<li>Transfer music to iPod, iPhone, MTP or mass-storage USB player</li>
|
||||
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
|
||||
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
|
||||
<li>Streaming support for Subsonic</li>
|
||||
<li>Streaming support for Subsonic-compatible servers</li>
|
||||
<li>Unofficial streaming support for Tidal and Qobuz</li>
|
||||
</ul>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Song playing showing context</caption>
|
||||
<image width="1600" height="874">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-001.png</image>
|
||||
<image width="1432" height="834">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-003.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Collection overview</caption>
|
||||
<image width="1600" height="874">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-002.png</image>
|
||||
<image width="1432" height="834">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-004.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||
|
||||
18
dist/unix/strawberry.1
vendored
@@ -5,7 +5,7 @@ Strawberry \- music player and music collection organizer
|
||||
.B strawberry
|
||||
[\fI\,options\/\fR] [\fI\,URL(s)\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Strawberry is a music player especially aimed at audiophiles.
|
||||
Strawberry is a music player aimed at music collectors and audiophiles.
|
||||
.TP
|
||||
Features:
|
||||
.br
|
||||
@@ -17,13 +17,15 @@ Features:
|
||||
.br
|
||||
- Native desktop notifications
|
||||
.br
|
||||
- Playlist management
|
||||
.br
|
||||
- Playlists in multiple formats
|
||||
.br
|
||||
- Advanced output and device options with support for bit perfect playback on Linux
|
||||
.br
|
||||
- Edit tags on music files
|
||||
- Edit tags on audio files
|
||||
.br
|
||||
- Fetch tags from MusicBrainz
|
||||
- Automatically retrieve tags from MusicBrainz
|
||||
.br
|
||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||
.br
|
||||
@@ -33,11 +35,15 @@ Features:
|
||||
.br
|
||||
- Audio analyzer
|
||||
.br
|
||||
- Equalizer
|
||||
- Audio Equalizer
|
||||
.br
|
||||
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||
.br
|
||||
- Streaming from Subsonic
|
||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
.br
|
||||
- Streaming support for Subsonic-compatible servers
|
||||
.br
|
||||
- Unofficial streaming support for Tidal and Qobuz
|
||||
.TP
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
.SH OPTIONS
|
||||
|
||||
58
dist/unix/strawberry.spec.in
vendored
@@ -49,28 +49,31 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
|
||||
BuildRequires: pkgconfig(taglib)
|
||||
%endif
|
||||
BuildRequires: pkgconfig(fftw3)
|
||||
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@DBus)
|
||||
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Test)
|
||||
%if "@QT_VERSION_MAJOR@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Core)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Gui)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Widgets)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Concurrent)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Network)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Sql)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@DBus)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Test)
|
||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@X11Extras)
|
||||
%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)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Sql)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@DBus)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Test)
|
||||
%if "@QT_VERSION_MAJOR@" == "5"
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@X11Extras)
|
||||
%endif
|
||||
%endif
|
||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
||||
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@LinguistTools)
|
||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
|
||||
%endif
|
||||
BuildRequires: pkgconfig(gstreamer-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||
@@ -89,10 +92,11 @@ BuildRequires: pkgconfig(libvlc)
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%if "@QT_MAJOR_VERSION@" == "6"
|
||||
%if "@QT_VERSION_MAJOR@" == "6"
|
||||
Requires: qt6-sql-sqlite
|
||||
Requires: qt6-network-tls
|
||||
%endif
|
||||
%if "@QT_MAJOR_VERSION@" == "5"
|
||||
%if "@QT_VERSION_MAJOR@" == "5"
|
||||
Requires: libQt5Sql5-sqlite
|
||||
%endif
|
||||
%endif
|
||||
@@ -107,18 +111,20 @@ Features:
|
||||
MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
||||
- Audio CD playback
|
||||
- Native desktop notifications
|
||||
- Playlist management
|
||||
- Playlists in multiple formats
|
||||
- Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||
- Edit tags on music files
|
||||
- Fetch tags from MusicBrainz
|
||||
- Edit tags on audio files
|
||||
- Automatically retrieve tags from MusicBrainz
|
||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
- Transfer music to iPod, MTP or mass-storage USB player
|
||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
- Streaming support for Subsonic
|
||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||
- Streaming support for Subsonic-compatible servers
|
||||
- Unofficial streaming support for Tidal and Qobuz
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%debug_package
|
||||
@@ -131,7 +137,7 @@ Features:
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
||||
%endif
|
||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_MAJOR_VERSION=@QT_MAJOR_VERSION@
|
||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
|
||||
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7)
|
||||
%make_build
|
||||
%else
|
||||
|
||||
61
dist/windows/strawberry.nsi.in
vendored
@@ -195,14 +195,14 @@ Section "Strawberry" Strawberry
|
||||
|
||||
!ifdef arch_x86
|
||||
File "libgcc_s_sjlj-1.dll"
|
||||
File "libcrypto-1_1.dll"
|
||||
File "libssl-1_1.dll"
|
||||
File "libcrypto-3.dll"
|
||||
File "libssl-3.dll"
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
File "libgcc_s_seh-1.dll"
|
||||
File "libcrypto-1_1-x64.dll"
|
||||
File "libssl-1_1-x64.dll"
|
||||
File "libcrypto-3-x64.dll"
|
||||
File "libssl-3-x64.dll"
|
||||
!endif
|
||||
|
||||
File "avcodec-58.dll"
|
||||
@@ -229,6 +229,7 @@ Section "Strawberry" Strawberry
|
||||
File "libgobject-2.0-0.dll"
|
||||
File "libgstapp-1.0-0.dll"
|
||||
File "libgstaudio-1.0-0.dll"
|
||||
File "libgstbadaudio-1.0-0.dll"
|
||||
File "libgstbase-1.0-0.dll"
|
||||
File "libgstfft-1.0-0.dll"
|
||||
File "libgstnet-1.0-0.dll"
|
||||
@@ -250,12 +251,13 @@ Section "Strawberry" Strawberry
|
||||
File "libmp3lame-0.dll"
|
||||
File "libnettle-8.dll"
|
||||
File "libogg-0.dll"
|
||||
File "libopenmpt-0.dll"
|
||||
File "libopus-0.dll"
|
||||
File "liborc-0.4-0.dll"
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-26.dll"
|
||||
File "libprotobuf-29.dll"
|
||||
File "libpsl-5.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "libspeex-1.dll"
|
||||
@@ -267,6 +269,7 @@ Section "Strawberry" Strawberry
|
||||
File "libunistring-2.dll"
|
||||
File "libvorbis-0.dll"
|
||||
File "libvorbisenc-2.dll"
|
||||
File "libvorbisfile-3.dll"
|
||||
File "libwavpack-1.dll"
|
||||
File "libwinpthread-1.dll"
|
||||
File "libxml2-2.dll"
|
||||
@@ -283,7 +286,6 @@ Section "Strawberry" Strawberry
|
||||
File "Qt6Network.dll"
|
||||
File "Qt6Sql.dll"
|
||||
File "Qt6Widgets.dll"
|
||||
File "Qt6WinExtras.dll"
|
||||
File "libqtsparkle-qt6.dll"
|
||||
!else
|
||||
File "Qt5Concurrent.dll"
|
||||
@@ -292,7 +294,6 @@ Section "Strawberry" Strawberry
|
||||
File "Qt5Network.dll"
|
||||
File "Qt5Sql.dll"
|
||||
File "Qt5Widgets.dll"
|
||||
File "Qt5WinExtras.dll"
|
||||
File "libqtsparkle-qt5.dll"
|
||||
!endif
|
||||
|
||||
@@ -342,26 +343,33 @@ Section "GIO modules" gio-modules
|
||||
File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Qt Platforms" platforms
|
||||
Section "Qt Platform plugins" platforms
|
||||
SetOutPath "$INSTDIR\platforms"
|
||||
File "/oname=qwindows.dll" "platforms\qwindows.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Qt SQL Drivers" sqldrivers
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
|
||||
Section "Qt styles" styles
|
||||
SetOutPath "$INSTDIR\styles"
|
||||
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Qt image format plugins" imageformats
|
||||
Section "Qt imageformats" imageformats
|
||||
SetOutPath "$INSTDIR\imageformats"
|
||||
File "/oname=qgif.dll" "imageformats\qgif.dll"
|
||||
File "/oname=qico.dll" "imageformats\qico.dll"
|
||||
File "/oname=qjpeg.dll" "imageformats\qjpeg.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Qt style plugins" styles
|
||||
SetOutPath "$INSTDIR\styles"
|
||||
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
|
||||
!ifdef with_qt6
|
||||
Section "Qt TLS plugins" tls
|
||||
SetOutPath "$INSTDIR\tls"
|
||||
File "/oname=qopensslbackend.dll" "tls\qopensslbackend.dll"
|
||||
SectionEnd
|
||||
!endif
|
||||
|
||||
Section "Qt SQL Drivers" sqldrivers
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Gstreamer plugins" gstreamer-plugins
|
||||
@@ -413,6 +421,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||
File "/oname=libgstpbtypes.dll" "gstreamer-plugins\libgstpbtypes.dll"
|
||||
File "/oname=libgstrtp.dll" "gstreamer-plugins\libgstrtp.dll"
|
||||
File "/oname=libgstrtsp.dll" "gstreamer-plugins\libgstrtsp.dll"
|
||||
File "/oname=libgstopenmpt.dll" "gstreamer-plugins\libgstopenmpt.dll"
|
||||
|
||||
SectionEnd
|
||||
|
||||
@@ -460,14 +469,14 @@ Section "Uninstall"
|
||||
|
||||
!ifdef arch_x86
|
||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||
Delete "$INSTDIR\libcrypto-1_1.dll"
|
||||
Delete "$INSTDIR\libssl-1_1.dll"
|
||||
Delete "$INSTDIR\libcrypto-3.dll"
|
||||
Delete "$INSTDIR\libssl-3.dll"
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
Delete "$INSTDIR\libgcc_s_seh-1.dll"
|
||||
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
|
||||
Delete "$INSTDIR\libssl-1_1-x64.dll"
|
||||
Delete "$INSTDIR\libcrypto-3-x64.dll"
|
||||
Delete "$INSTDIR\libssl-3-x64.dll"
|
||||
!endif
|
||||
|
||||
Delete "$INSTDIR\avcodec-58.dll"
|
||||
@@ -494,6 +503,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libgobject-2.0-0.dll"
|
||||
Delete "$INSTDIR\libgstapp-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstbadaudio-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstbase-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstfft-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstnet-1.0-0.dll"
|
||||
@@ -515,12 +525,13 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libmp3lame-0.dll"
|
||||
Delete "$INSTDIR\libnettle-8.dll"
|
||||
Delete "$INSTDIR\libogg-0.dll"
|
||||
Delete "$INSTDIR\libopenmpt-0.dll"
|
||||
Delete "$INSTDIR\libopus-0.dll"
|
||||
Delete "$INSTDIR\liborc-0.4-0.dll"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-26.dll"
|
||||
Delete "$INSTDIR\libprotobuf-29.dll"
|
||||
Delete "$INSTDIR\libpsl-5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||
@@ -534,6 +545,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libunistring-2.dll"
|
||||
Delete "$INSTDIR\libvorbis-0.dll"
|
||||
Delete "$INSTDIR\libvorbisenc-2.dll"
|
||||
Delete "$INSTDIR\libvorbisfile-3.dll"
|
||||
Delete "$INSTDIR\libwavpack-1.dll"
|
||||
Delete "$INSTDIR\libwinpthread-1.dll"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
@@ -545,14 +557,12 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\Qt5Network.dll"
|
||||
Delete "$INSTDIR\Qt5Sql.dll"
|
||||
Delete "$INSTDIR\Qt5Widgets.dll"
|
||||
Delete "$INSTDIR\Qt5WinExtras.dll"
|
||||
Delete "$INSTDIR\Qt6Concurrent.dll"
|
||||
Delete "$INSTDIR\Qt6Core.dll"
|
||||
Delete "$INSTDIR\Qt6Gui.dll"
|
||||
Delete "$INSTDIR\Qt6Network.dll"
|
||||
Delete "$INSTDIR\Qt6Sql.dll"
|
||||
Delete "$INSTDIR\Qt6Widgets.dll"
|
||||
Delete "$INSTDIR\Qt6WinExtras.dll"
|
||||
Delete "$INSTDIR\swresample-3.dll"
|
||||
Delete "$INSTDIR\swscale-5.dll"
|
||||
Delete "$INSTDIR\zlib1.dll"
|
||||
@@ -568,8 +578,11 @@ Section "Uninstall"
|
||||
|
||||
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
|
||||
!ifdef with_qt6
|
||||
Delete "$INSTDIR\tls\qopensslbackend.dll"
|
||||
!endif
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
||||
Delete "$INSTDIR\imageformats\qgif.dll"
|
||||
Delete "$INSTDIR\imageformats\qico.dll"
|
||||
@@ -621,12 +634,14 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstpbtypes.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstrtp.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstrtsp.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstopenmpt.dll"
|
||||
|
||||
Delete "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Remove the installation folders.
|
||||
RMDir "$INSTDIR\platforms"
|
||||
RMDir "$INSTDIR\styles"
|
||||
RMDir "$INSTDIR\tls"
|
||||
RMDir "$INSTDIR\sqldrivers"
|
||||
RMDir "$INSTDIR\imageformats"
|
||||
RMDir "$INSTDIR\gio-modules"
|
||||
|
||||
@@ -311,8 +311,9 @@ static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint l
|
||||
#else
|
||||
gint32 value = GST_READ_UINT24_LE (_in);
|
||||
#endif
|
||||
if (value & 0x00800000)
|
||||
if (value & 0x00800000) {
|
||||
value |= 0xff000000;
|
||||
}
|
||||
|
||||
out[op] = value / max_value;
|
||||
op = (op + 1) % nfft;
|
||||
@@ -374,8 +375,9 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
|
||||
guint bands = spectrum->bands;
|
||||
guint nfft = 2 * bands - 2;
|
||||
|
||||
for (i = 0; i < nfft; i++)
|
||||
for (i = 0; i < nfft; i++) {
|
||||
spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft];
|
||||
}
|
||||
|
||||
// Should be safe to execute the same plan multiple times in parallel.
|
||||
fftw_execute(spectrum->plan);
|
||||
@@ -435,8 +437,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
|
||||
/* rounding error for frames_per_interval in ns,
|
||||
* aggregated it in accumulated_error */
|
||||
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
|
||||
if (spectrum->frames_per_interval == 0)
|
||||
if (spectrum->frames_per_interval == 0) {
|
||||
spectrum->frames_per_interval = 1;
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
|
||||
G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
|
||||
@@ -448,8 +451,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
|
||||
gst_fastspectrum_flush (spectrum);
|
||||
}
|
||||
|
||||
if (spectrum->num_frames == 0)
|
||||
if (spectrum->num_frames == 0) {
|
||||
spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
|
||||
}
|
||||
|
||||
input_pos = spectrum->input_pos;
|
||||
input_data = spectrum->input_data;
|
||||
@@ -462,10 +466,12 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
|
||||
"message frames todo: %u, fft frames todo: %u, input frames %"
|
||||
G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
|
||||
block_size = msg_todo;
|
||||
if (block_size > (size / bpf))
|
||||
if (block_size > (size / bpf)) {
|
||||
block_size = (size / bpf);
|
||||
if (block_size > fft_todo)
|
||||
}
|
||||
if (block_size > fft_todo) {
|
||||
block_size = fft_todo;
|
||||
}
|
||||
|
||||
/* Move the current frames into our ringbuffers */
|
||||
input_data(data, spectrum->input_ring_buffer, block_size, max_value, input_pos, nfft);
|
||||
@@ -514,8 +520,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
|
||||
memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double));
|
||||
}
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
|
||||
if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts)) {
|
||||
spectrum->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
|
||||
}
|
||||
|
||||
spectrum->num_frames = 0;
|
||||
spectrum->num_fft = 0;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
static gboolean gst_moodbar_plugin_init(GstPlugin* plugin) {
|
||||
static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
|
||||
|
||||
if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
|
||||
return FALSE;
|
||||
|
||||
@@ -8,38 +8,29 @@ set(SOURCES
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
core/logging.h
|
||||
core/messagehandler.h
|
||||
core/messagereply.h
|
||||
core/workerpool.h
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
|
||||
endif(APPLE)
|
||||
qt_wrap_cpp(MOC ${HEADERS})
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(MOC ${HEADERS})
|
||||
else()
|
||||
qt5_wrap_cpp(MOC ${HEADERS})
|
||||
endif()
|
||||
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
)
|
||||
link_directories(${GLIB_LIBRARY_DIRS})
|
||||
|
||||
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
||||
|
||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||
target_include_directories(libstrawberry-common PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
if(Backtrace_FOUND)
|
||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${Backtrace_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
target_link_libraries(libstrawberry-common PRIVATE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${GLIB_LIBRARIES}
|
||||
@@ -48,6 +39,5 @@ target_link_libraries(libstrawberry-common PRIVATE
|
||||
)
|
||||
|
||||
if(Backtrace_FOUND)
|
||||
target_include_directories(libstrawberry-common PRIVATE ${Backtrace_INCLUDE_DIRS})
|
||||
target_link_libraries(libstrawberry-common PRIVATE ${Backtrace_LIBRARIES})
|
||||
endif(Backtrace_FOUND)
|
||||
endif()
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <cxxabi.h>
|
||||
#ifndef _MSC_VER
|
||||
# include <cxxabi.h>
|
||||
#endif
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef HAVE_BACKTRACE
|
||||
@@ -54,10 +56,10 @@ static QMap<QString, Level>* sClassLevels = nullptr;
|
||||
static QIODevice *sNullDevice = nullptr;
|
||||
|
||||
//const char* kDefaultLogLevels = "*:3";
|
||||
const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3";
|
||||
const char *kDefaultLogLevels = "GstEnginePipeline:2,*:3";
|
||||
|
||||
static const char *kMessageHandlerMagic = "__logging_message__";
|
||||
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||
static const size_t kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||
static QtMessageHandler sOriginalMessageHandler = nullptr;
|
||||
|
||||
template <class T>
|
||||
@@ -99,7 +101,7 @@ class DebugBase : public QDebug {
|
||||
// Debug message will be stored in a buffer.
|
||||
class BufferedDebug : public DebugBase<BufferedDebug> {
|
||||
public:
|
||||
BufferedDebug() {}
|
||||
BufferedDebug() = default;
|
||||
explicit BufferedDebug(QtMsgType) : buf_(new QBuffer, later_deleter) {
|
||||
buf_->open(QIODevice::WriteOnly);
|
||||
|
||||
@@ -118,7 +120,7 @@ class BufferedDebug : public DebugBase<BufferedDebug> {
|
||||
// Debug message will be logged immediately.
|
||||
class LoggedDebug : public DebugBase<LoggedDebug> {
|
||||
public:
|
||||
LoggedDebug() {}
|
||||
LoggedDebug() = default;
|
||||
explicit LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; }
|
||||
};
|
||||
|
||||
@@ -178,7 +180,7 @@ void SetLevels(const QString &levels) {
|
||||
|
||||
if (!sClassLevels) return;
|
||||
|
||||
for (const QString& item : levels.split(',')) {
|
||||
for (const QString &item : levels.split(',')) {
|
||||
const QStringList class_level = item.split(':');
|
||||
|
||||
QString class_name;
|
||||
@@ -224,7 +226,7 @@ static QString ParsePrettyFunction(const char *pretty_function) {
|
||||
|
||||
const int space = class_name.lastIndexOf(' ');
|
||||
if (space != -1) {
|
||||
class_name = class_name.mid(space+1);
|
||||
class_name = class_name.mid(space + 1);
|
||||
}
|
||||
|
||||
return class_name;
|
||||
@@ -275,12 +277,12 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
|
||||
return ret.space();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
QString CXXDemangle(const QString &mangled_function);
|
||||
|
||||
QString CXXDemangle(const QString &mangled_function) {
|
||||
|
||||
int status = 0;
|
||||
char* demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
|
||||
char *demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
|
||||
if (status == 0) {
|
||||
QString ret = QString::fromLatin1(demangled_function);
|
||||
free(demangled_function);
|
||||
@@ -289,9 +291,24 @@ QString CXXDemangle(const QString &mangled_function) {
|
||||
return mangled_function; // Probably not a C++ function.
|
||||
|
||||
}
|
||||
#endif // Q_OS_UNIX
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString LinuxDemangle(const QString &symbol);
|
||||
QString LinuxDemangle(const QString &symbol) {
|
||||
|
||||
QRegularExpression regex("\\(([^+]+)");
|
||||
QRegularExpressionMatch match = regex.match(symbol);
|
||||
if (!match.hasMatch()) {
|
||||
return symbol;
|
||||
}
|
||||
QString mangled_function = match.captured(1);
|
||||
return CXXDemangle(mangled_function);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
QString DarwinDemangle(const QString &symbol);
|
||||
|
||||
QString DarwinDemangle(const QString &symbol) {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
@@ -303,23 +320,9 @@ QString DarwinDemangle(const QString &symbol) {
|
||||
return CXXDemangle(mangled_function);
|
||||
|
||||
}
|
||||
|
||||
QString LinuxDemangle(const QString &symbol);
|
||||
|
||||
QString LinuxDemangle(const QString &symbol) {
|
||||
|
||||
QRegularExpression regex("\\(([^+]+)");
|
||||
QRegularExpressionMatch match = regex.match(symbol);
|
||||
if (!match.hasMatch()) {
|
||||
return symbol;
|
||||
}
|
||||
QString mangled_function = match.captured(1);
|
||||
return CXXDemangle(mangled_function);
|
||||
|
||||
}
|
||||
#endif // Q_OS_MACOS
|
||||
|
||||
QString DemangleSymbol(const QString &symbol);
|
||||
|
||||
QString DemangleSymbol(const QString &symbol) {
|
||||
#ifdef Q_OS_MACOS
|
||||
return DarwinDemangle(symbol);
|
||||
@@ -332,9 +335,9 @@ QString DemangleSymbol(const QString &symbol) {
|
||||
|
||||
void DumpStackTrace() {
|
||||
#ifdef HAVE_BACKTRACE
|
||||
void* callstack[128];
|
||||
void *callstack[128];
|
||||
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
|
||||
char** symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
|
||||
char **symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
|
||||
// Start from 1 to skip ourself.
|
||||
for (int i = 1; i < callstack_size; ++i) {
|
||||
std::cerr << DemangleSymbol(QString::fromLatin1(symbols[i])).toStdString() << std::endl;
|
||||
@@ -350,9 +353,9 @@ void DumpStackTrace() {
|
||||
// doesn't override any behavior that should be needed after return.
|
||||
#define qCreateLogger(line, pretty_function, category, level) logging::CreateLogger<LoggedDebug>(logging::Level_##level, logging::ParsePrettyFunction(pretty_function), line, category)
|
||||
|
||||
QDebug CreateLoggerInfo(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Info); }
|
||||
QDebug CreateLoggerFatal(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Fatal); }
|
||||
QDebug CreateLoggerError(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Error); }
|
||||
QDebug CreateLoggerInfo(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Info); }
|
||||
QDebug CreateLoggerFatal(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Fatal); }
|
||||
QDebug CreateLoggerError(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Error); }
|
||||
|
||||
#ifdef QT_NO_WARNING_OUTPUT
|
||||
QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); }
|
||||
@@ -371,7 +374,7 @@ QDebug CreateLoggerError(int line, const char *pretty_function, const char* cate
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
QString print_duration(T duration, const std::string& unit) {
|
||||
QString print_duration(T duration, const std::string &unit) {
|
||||
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,31 @@
|
||||
# define qLog(level) while (false) QNoDebug()
|
||||
# define qLogCat(level, category) while (false) QNoDebug()
|
||||
#else
|
||||
# define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, nullptr)
|
||||
# ifdef _MSC_VER
|
||||
# define qLog(level) logging::CreateLogger##level(__LINE__, __FUNCSIG__, nullptr)
|
||||
# else
|
||||
# define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, nullptr)
|
||||
# endif // _MSC_VER
|
||||
|
||||
// This macro specifies a separate category for message filtering.
|
||||
// The default qLog will use the class name extracted from the function name for this purpose.
|
||||
// The category is also printed in the message along with the class name.
|
||||
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, category)
|
||||
# ifdef _MSC_VER
|
||||
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __FUNCSIG__, category)
|
||||
# else
|
||||
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, category)
|
||||
# endif // _MSC_VER
|
||||
|
||||
#endif // QT_NO_DEBUG_STREAM
|
||||
|
||||
namespace logging {
|
||||
|
||||
class NullDevice : public QIODevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NullDevice(QObject *parent = nullptr) : QIODevice(parent) {}
|
||||
|
||||
protected:
|
||||
qint64 readData(char*, qint64) override { return -1; }
|
||||
qint64 writeData(const char*, qint64 len) override { return len; }
|
||||
|
||||
@@ -64,7 +64,7 @@ void _MessageHandlerBase::SetDevice(QIODevice *device) {
|
||||
|
||||
void _MessageHandlerBase::DeviceReadyRead() {
|
||||
|
||||
while (device_->bytesAvailable()) {
|
||||
while (device_->bytesAvailable() > 0) {
|
||||
if (!reading_protobuf_) {
|
||||
// Read the length of the next message
|
||||
QDataStream s(device_);
|
||||
|
||||
@@ -130,7 +130,7 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
|
||||
template <typename MT>
|
||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
|
||||
std::string data = message.SerializeAsString();
|
||||
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
|
||||
@@ -164,8 +164,9 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
|
||||
local_server_name_ = qApp->applicationName().toLower();
|
||||
|
||||
if (local_server_name_.isEmpty())
|
||||
if (local_server_name_.isEmpty()) {
|
||||
local_server_name_ = "workerpool";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -218,7 +219,7 @@ void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name)
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::Start() {
|
||||
metaObject()->invokeMethod(this, "DoStart");
|
||||
QMetaObject::invokeMethod(this, "DoStart");
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
@@ -232,9 +233,9 @@ void WorkerPool<HandlerType>::DoStart() {
|
||||
executable_path_ = executable_name_;
|
||||
|
||||
QStringList search_path;
|
||||
search_path << qApp->applicationDirPath();
|
||||
search_path << QCoreApplication::applicationDirPath();
|
||||
#if defined(Q_OS_MACOS) && defined(USE_BUNDLE)
|
||||
search_path << qApp->applicationDirPath() + "/" + USE_BUNDLE_DIR;
|
||||
search_path << QCoreApplication::applicationDirPath() + "/" + USE_BUNDLE_DIR;
|
||||
#endif
|
||||
|
||||
for (const QString &path_prefix : search_path) {
|
||||
@@ -381,7 +382,7 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||
}
|
||||
|
||||
// Wake up the main thread
|
||||
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
||||
|
||||
return reply;
|
||||
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(MESSAGES tagreadermessages.proto)
|
||||
set(SOURCES tagreader.cpp)
|
||||
set(SOURCES tagreaderbase.cpp)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
list(APPEND SOURCES tagreadertaglib.cpp)
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
list(APPEND SOURCES tagreadertagparser.cpp)
|
||||
endif()
|
||||
|
||||
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${PROTOBUF_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${TAGLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||
@@ -30,8 +44,17 @@ target_include_directories(libstrawberry-tagreader PRIVATE
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||
${GLIB_LIBRARIES}
|
||||
${PROTOBUF_LIBRARY}
|
||||
${TAGLIB_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
libstrawberry-common
|
||||
)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||
endif()
|
||||
|
||||
27
ext/libstrawberry-tagreader/tagreaderbase.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2018-2021, 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 <string>
|
||||
|
||||
#include "tagreaderbase.h"
|
||||
|
||||
const std::string TagReaderBase::kEmbeddedCover = "(embedded)";
|
||||
|
||||
TagReaderBase::TagReaderBase() = default;
|
||||
TagReaderBase::~TagReaderBase() = default;
|
||||
53
ext/libstrawberry-tagreader/tagreaderbase.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2018-2021, 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 TAGREADERBASE_H
|
||||
#define TAGREADERBASE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
/*
|
||||
* This class holds all useful methods to read and write tags from/to files.
|
||||
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||
*/
|
||||
class TagReaderBase {
|
||||
public:
|
||||
explicit TagReaderBase();
|
||||
~TagReaderBase();
|
||||
|
||||
virtual bool IsMediaFile(const QString &filename) const = 0;
|
||||
|
||||
virtual void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
||||
virtual bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
||||
|
||||
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
|
||||
virtual bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) = 0;
|
||||
|
||||
protected:
|
||||
static const std::string kEmbeddedCover;
|
||||
|
||||
Q_DISABLE_COPY(TagReaderBase)
|
||||
};
|
||||
|
||||
#endif // TAGREADERBASE_H
|
||||
@@ -23,6 +23,10 @@ message SongMetadata {
|
||||
DSDIFF = 15;
|
||||
PCM = 16;
|
||||
APE = 17;
|
||||
MOD = 18;
|
||||
S3M = 19;
|
||||
XM = 20;
|
||||
IT = 21;
|
||||
CDDA = 90;
|
||||
STREAM = 91;
|
||||
}
|
||||
@@ -60,10 +64,11 @@ message SongMetadata {
|
||||
|
||||
optional int32 playcount = 27;
|
||||
optional int32 skipcount = 28;
|
||||
optional int32 lastplayed = 29;
|
||||
optional int64 lastplayed = 29;
|
||||
optional int64 lastseen = 30;
|
||||
|
||||
optional bool suspicious_tags = 30;
|
||||
optional string art_automatic = 31;
|
||||
optional bool suspicious_tags = 31;
|
||||
optional string art_automatic = 32;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "tagreader.h"
|
||||
#include "tagreadertaglib.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <taglib/taglib.h>
|
||||
@@ -68,6 +67,10 @@
|
||||
#include <taglib/opusfile.h>
|
||||
#include <taglib/trueaudiofile.h>
|
||||
#include <taglib/apefile.h>
|
||||
#include <taglib/modfile.h>
|
||||
#include <taglib/s3mfile.h>
|
||||
#include <taglib/xmfile.h>
|
||||
#include <taglib/itfile.h>
|
||||
#ifdef HAVE_TAGLIB_DSFFILE
|
||||
# include <taglib/dsffile.h>
|
||||
#endif
|
||||
@@ -78,13 +81,11 @@
|
||||
#include <QtGlobal>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QDateTime>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/logging.h"
|
||||
@@ -93,12 +94,17 @@
|
||||
|
||||
class FileRefFactory {
|
||||
public:
|
||||
virtual ~FileRefFactory() {}
|
||||
FileRefFactory() = default;
|
||||
virtual ~FileRefFactory() = default;
|
||||
virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(FileRefFactory)
|
||||
};
|
||||
|
||||
class TagLibFileRefFactory : public FileRefFactory {
|
||||
public:
|
||||
TagLibFileRefFactory() = default;
|
||||
TagLib::FileRef *GetFileRef(const QString &filename) override {
|
||||
#ifdef Q_OS_WIN32
|
||||
return new TagLib::FileRef(filename.toStdWString().c_str());
|
||||
@@ -106,6 +112,8 @@ class TagLibFileRefFactory : public FileRefFactory {
|
||||
return new TagLib::FileRef(QFile::encodeName(filename).constData());
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
Q_DISABLE_COPY(TagLibFileRefFactory)
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -128,25 +136,22 @@ const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
|
||||
} // namespace
|
||||
|
||||
|
||||
TagReader::TagReader() :
|
||||
factory_(new TagLibFileRefFactory),
|
||||
kEmbeddedCover("(embedded)") {
|
||||
}
|
||||
TagReaderTagLib::TagReaderTagLib() : factory_(new TagLibFileRefFactory) {}
|
||||
|
||||
TagReader::~TagReader() {
|
||||
TagReaderTagLib::~TagReaderTagLib() {
|
||||
delete factory_;
|
||||
}
|
||||
|
||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
||||
bool TagReaderTagLib::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();
|
||||
return fileref && !fileref->isNull() && fileref->file() && fileref->tag();
|
||||
|
||||
}
|
||||
|
||||
spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
spb::tagreader::SongMetadata_FileType TagReaderTagLib::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_WAV;
|
||||
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_FLAC;
|
||||
@@ -162,6 +167,10 @@ spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *
|
||||
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_MPC;
|
||||
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_TRUEAUDIO;
|
||||
if (dynamic_cast<TagLib::APE::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_APE;
|
||||
if (dynamic_cast<TagLib::Mod::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_MOD;
|
||||
if (dynamic_cast<TagLib::S3M::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_S3M;
|
||||
if (dynamic_cast<TagLib::XM::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_XM;
|
||||
if (dynamic_cast<TagLib::IT::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_IT;
|
||||
#ifdef HAVE_TAGLIB_DSFFILE
|
||||
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_DSF;
|
||||
#endif
|
||||
@@ -173,7 +182,7 @@ spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||
const QFileInfo info(filename);
|
||||
@@ -189,6 +198,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
|
||||
#else
|
||||
song->set_ctime(info.created().toSecsSinceEpoch());
|
||||
#endif
|
||||
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
if (fileref->isNull()) {
|
||||
@@ -399,7 +409,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
|
||||
|
||||
if (compilation.isEmpty()) {
|
||||
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
||||
if (QStringFromStdString(song->artist()).toLower() == "various artists" || QStringFromStdString(song->albumartist()).toLower() == "various artists") {
|
||||
if (QStringFromStdString(song->artist()).compare("various artists") == 0 || QStringFromStdString(song->albumartist()).compare("various artists") == 0) {
|
||||
song->set_compilation(true);
|
||||
}
|
||||
}
|
||||
@@ -414,6 +424,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
|
||||
SetDefault(track);
|
||||
SetDefault(disc);
|
||||
SetDefault(year);
|
||||
SetDefault(originalyear);
|
||||
SetDefault(bitrate);
|
||||
SetDefault(samplerate);
|
||||
SetDefault(bitdepth);
|
||||
@@ -422,20 +433,20 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
|
||||
|
||||
}
|
||||
|
||||
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
|
||||
void TagReaderTagLib::Decode(const TagLib::String &tag, std::string *output) {
|
||||
|
||||
QString tmp = TStringToQString(tag).trimmed();
|
||||
output->assign(DataCommaSizeFromQString(tmp));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::Decode(const QString &tag, std::string *output) {
|
||||
void TagReaderTagLib::Decode(const QString &tag, std::string *output) {
|
||||
|
||||
output->assign(DataCommaSizeFromQString(tag));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||
void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
||||
@@ -460,7 +471,7 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc,
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||
void TagReaderTagLib::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||
if (it != map.end()) {
|
||||
@@ -504,7 +515,7 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc,
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||
@@ -522,7 +533,7 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||
|
||||
}
|
||||
|
||||
bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
bool TagReaderTagLib::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
|
||||
@@ -603,7 +614,7 @@ bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMeta
|
||||
return result;
|
||||
}
|
||||
|
||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||
@@ -615,14 +626,14 @@ void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMeta
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
||||
void TagReaderTagLib::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
const QByteArray utf8(value.toUtf8());
|
||||
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
void TagReaderTagLib::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
TagLib::ByteVector id_vector(id);
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
@@ -653,7 +664,7 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
void TagReaderTagLib::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
TagLib::ByteVector id_vector("USLT");
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
@@ -685,7 +696,7 @@ void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Ta
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
if (filename.isEmpty()) return QByteArray();
|
||||
|
||||
@@ -786,7 +797,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
||||
QByteArray TagReaderTagLib::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
||||
|
||||
QByteArray ret;
|
||||
|
||||
@@ -804,7 +815,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
||||
|
||||
}
|
||||
|
||||
bool TagReader::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
||||
bool TagReaderTagLib::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TAGREADER_H
|
||||
#define TAGREADER_H
|
||||
#ifndef TAGREADERTAGLIB_H
|
||||
#define TAGREADERTAGLIB_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -33,29 +33,31 @@
|
||||
#include <taglib/apefile.h>
|
||||
#include <taglib/id3v2tag.h>
|
||||
|
||||
#include "tagreaderbase.h"
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
class FileRefFactory;
|
||||
|
||||
/**
|
||||
/*
|
||||
* This class holds all useful methods to read and write tags from/to files.
|
||||
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||
*/
|
||||
class TagReader {
|
||||
class TagReaderTagLib : public TagReaderBase {
|
||||
public:
|
||||
explicit TagReader();
|
||||
~TagReader();
|
||||
explicit TagReaderTagLib();
|
||||
~TagReaderTagLib();
|
||||
|
||||
bool IsMediaFile(const QString &filename) const;
|
||||
bool IsMediaFile(const QString &filename) const override;
|
||||
|
||||
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
|
||||
|
||||
private:
|
||||
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||
|
||||
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const;
|
||||
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data);
|
||||
|
||||
static void Decode(const TagLib::String &tag, std::string *output);
|
||||
static void Decode(const QString &tag, std::string *output);
|
||||
|
||||
@@ -69,10 +71,12 @@ class TagReader {
|
||||
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;
|
||||
|
||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||
|
||||
private:
|
||||
FileRefFactory *factory_;
|
||||
|
||||
const std::string kEmbeddedCover;
|
||||
Q_DISABLE_COPY(TagReaderTagLib)
|
||||
};
|
||||
|
||||
#endif // TAGREADER_H
|
||||
#endif // TAGREADERTAGLIB_H
|
||||
424
ext/libstrawberry-tagreader/tagreadertagparser.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2021, 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 "tagreadertagparser.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <tagparser/mediafileinfo.h>
|
||||
#include <tagparser/diagnostics.h>
|
||||
#include <tagparser/progressfeedback.h>
|
||||
#include <tagparser/tag.h>
|
||||
#include <tagparser/abstracttrack.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QDateTime>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/messagehandler.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
TagReaderTagParser::TagReaderTagParser() = default;
|
||||
|
||||
TagReaderTagParser::~TagReaderTagParser() = default;
|
||||
|
||||
bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
||||
|
||||
qLog(Debug) << "Checking for valid file" << filename;
|
||||
|
||||
QFileInfo fileinfo(filename);
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
|
||||
|
||||
try {
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
TagParser::Diagnostics diag;
|
||||
TagParser::AbortableProgressFeedback progress;
|
||||
|
||||
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||
taginfo.open(true);
|
||||
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
const auto tracks = taginfo.tracks();
|
||||
for (const auto track : tracks) {
|
||||
if (track->mediaType() == TagParser::MediaType::Audio) {
|
||||
taginfo.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
taginfo.close();
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
qLog(Debug) << "Reading tags from" << filename;
|
||||
|
||||
const QFileInfo fileinfo(filename);
|
||||
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return;
|
||||
|
||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||
|
||||
song->set_basefilename(DataCommaSizeFromQString(fileinfo.fileName()));
|
||||
song->set_url(url.constData(), url.size());
|
||||
song->set_filesize(fileinfo.size());
|
||||
song->set_mtime(fileinfo.lastModified().toSecsSinceEpoch());
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
song->set_ctime(fileinfo.birthTime().isValid() ? fileinfo.birthTime().toSecsSinceEpoch() : fileinfo.lastModified().toSecsSinceEpoch());
|
||||
#else
|
||||
song->set_ctime(fileinfo.created().toSecsSinceEpoch());
|
||||
#endif
|
||||
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
|
||||
try {
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
TagParser::Diagnostics diag;
|
||||
TagParser::AbortableProgressFeedback progress;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
taginfo.setPath(filename.toStdWString().toStdString());
|
||||
#else
|
||||
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||
#endif
|
||||
|
||||
taginfo.open(true);
|
||||
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
const auto tracks = taginfo.tracks();
|
||||
for (const auto track : tracks) {
|
||||
switch(track->format().general) {
|
||||
case TagParser::GeneralMediaFormat::Flac:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_FLAC);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::WavPack:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_WAVPACK);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::MonkeysAudio:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_APE);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::WindowsMediaAudio:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_ASF);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Vorbis:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGVORBIS);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Opus:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGOPUS);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Speex:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGSPEEX);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Mpeg1Audio:
|
||||
switch(track->format().sub) {
|
||||
case TagParser::SubFormats::Mpeg1Layer3:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPEG);
|
||||
break;
|
||||
case TagParser::SubFormats::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Mpc:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPC);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Pcm:
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_PCM);
|
||||
break;
|
||||
case TagParser::GeneralMediaFormat::Unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
song->set_length_nanosec(track->duration().totalMilliseconds() * kNsecPerMsec);
|
||||
song->set_samplerate(track->samplingFrequency());
|
||||
song->set_bitdepth(track->bitsPerSample());
|
||||
song->set_bitrate(std::max(track->bitrate(), track->maxBitrate()));
|
||||
}
|
||||
|
||||
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
|
||||
taginfo.close();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto tag : taginfo.tags()) {
|
||||
song->set_albumartist(tag->value(TagParser::KnownField::AlbumArtist).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_artist(tag->value(TagParser::KnownField::Artist).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_album(tag->value(TagParser::KnownField::Album).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_title(tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_genre(tag->value(TagParser::KnownField::Genre).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_composer(tag->value(TagParser::KnownField::Composer).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_performer(tag->value(TagParser::KnownField::Performers).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_grouping(tag->value(TagParser::KnownField::Grouping).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_comment(tag->value(TagParser::KnownField::Comment).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_lyrics(tag->value(TagParser::KnownField::Lyrics).toString(TagParser::TagTextEncoding::Utf8));
|
||||
song->set_year(tag->value(TagParser::KnownField::RecordDate).toInteger());
|
||||
song->set_originalyear(tag->value(TagParser::KnownField::ReleaseDate).toInteger());
|
||||
song->set_track(tag->value(TagParser::KnownField::TrackPosition).toInteger());
|
||||
song->set_disc(tag->value(TagParser::KnownField::DiskPosition).toInteger());
|
||||
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
}
|
||||
|
||||
// Set integer fields to -1 if they're not valid
|
||||
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
|
||||
SetDefault(track);
|
||||
SetDefault(disc);
|
||||
SetDefault(year);
|
||||
SetDefault(originalyear);
|
||||
SetDefault(bitrate);
|
||||
SetDefault(samplerate);
|
||||
SetDefault(bitdepth);
|
||||
SetDefault(lastplayed);
|
||||
#undef SetDefault
|
||||
|
||||
song->set_valid(true);
|
||||
|
||||
taginfo.close();
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
|
||||
qLog(Debug) << "Saving tags to" << filename;
|
||||
|
||||
try {
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
TagParser::Diagnostics diag;
|
||||
TagParser::AbortableProgressFeedback progress;
|
||||
#ifdef Q_OS_WIN32
|
||||
taginfo.setPath(filename.toStdWString().toStdString());
|
||||
#else
|
||||
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||
#endif
|
||||
taginfo.open(false);
|
||||
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (taginfo.tags().size() <= 0) {
|
||||
taginfo.createAppropriateTags();
|
||||
}
|
||||
|
||||
for (const auto tag : taginfo.tags()) {
|
||||
tag->setValue(TagParser::KnownField::AlbumArtist, TagParser::TagValue(song.albumartist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Artist, TagParser::TagValue(song.artist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Album, TagParser::TagValue(song.album(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Title, TagParser::TagValue(song.title(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Genre, TagParser::TagValue(song.genre(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Composer, TagParser::TagValue(song.composer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Performers, TagParser::TagValue(song.performer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Grouping, TagParser::TagValue(song.grouping(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Comment, TagParser::TagValue(song.comment(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::Lyrics, TagParser::TagValue(song.lyrics(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||
tag->setValue(TagParser::KnownField::TrackPosition, TagParser::TagValue(song.track()));
|
||||
tag->setValue(TagParser::KnownField::DiskPosition, TagParser::TagValue(song.disc()));
|
||||
tag->setValue(TagParser::KnownField::RecordDate, TagParser::TagValue(song.year()));
|
||||
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
|
||||
}
|
||||
taginfo.applyChanges(diag, progress);
|
||||
taginfo.close();
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
if (filename.isEmpty()) return QByteArray();
|
||||
|
||||
qLog(Debug) << "Loading art from" << filename;
|
||||
|
||||
try {
|
||||
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
TagParser::Diagnostics diag;
|
||||
TagParser::AbortableProgressFeedback progress;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
taginfo.setPath(filename.toStdWString().toStdString());
|
||||
#else
|
||||
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||
#endif
|
||||
|
||||
taginfo.open();
|
||||
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
for (const auto tag : taginfo.tags()) {
|
||||
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
||||
QByteArray data(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
|
||||
taginfo.close();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
taginfo.close();
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return QByteArray();
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
|
||||
qLog(Debug) << "Saving art to" << filename;
|
||||
|
||||
try {
|
||||
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
TagParser::Diagnostics diag;
|
||||
TagParser::AbortableProgressFeedback progress;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
taginfo.setPath(filename.toStdWString().toStdString());
|
||||
#else
|
||||
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||
#endif
|
||||
|
||||
taginfo.open();
|
||||
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (taginfo.tags().size() <= 0) {
|
||||
taginfo.createAppropriateTags();
|
||||
}
|
||||
|
||||
for (const auto tag : taginfo.tags()) {
|
||||
tag->setValue(TagParser::KnownField::Cover, TagParser::TagValue(data.toStdString()));
|
||||
}
|
||||
|
||||
taginfo.applyChanges(diag, progress);
|
||||
taginfo.close();
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
51
ext/libstrawberry-tagreader/tagreadertagparser.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2021, 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 TAGREADERTAGPARSER_H
|
||||
#define TAGREADERTAGPARSER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "tagreadermessages.pb.h"
|
||||
#include "tagreaderbase.h"
|
||||
|
||||
/*
|
||||
* This class holds all useful methods to read and write tags from/to files.
|
||||
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||
*/
|
||||
class TagReaderTagParser : public TagReaderBase {
|
||||
public:
|
||||
explicit TagReaderTagParser();
|
||||
~TagReaderTagParser();
|
||||
|
||||
bool IsMediaFile(const QString &filename) const override;
|
||||
|
||||
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
|
||||
|
||||
Q_DISABLE_COPY(TagReaderTagParser)
|
||||
};
|
||||
|
||||
#endif // TAGREADERTAGPARSER_H
|
||||
15
ext/macdeploycheck/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
qt_wrap_cpp(MACDEPLOYCHECK_MOC ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.h)
|
||||
link_directories(${GLIB_LIBRARY_DIRS})
|
||||
add_executable(macdeploycheck macdeploycheck.cpp ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.cpp ${MACDEPLOYCHECK_MOC})
|
||||
target_include_directories(macdeploycheck PUBLIC SYSTEM
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(macdeploycheck PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
target_link_libraries(macdeploycheck PUBLIC
|
||||
"-framework AppKit"
|
||||
${GLIB_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
)
|
||||
150
ext/macdeploycheck/macdeploycheck.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2021, 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 <QCoreApplication>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
int main(int argc, char **argv);
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
logging::Init();
|
||||
|
||||
qLog(Info) << "Running macdeploycheck";
|
||||
|
||||
if (argc < 1) {
|
||||
qLog(Error) << "Usage: macdeploycheck <bundledir>";
|
||||
return 1;
|
||||
}
|
||||
QString bundle_path = QString::fromLocal8Bit(argv[1]);
|
||||
|
||||
bool success = true;
|
||||
|
||||
QDirIterator iter(bundle_path, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
|
||||
while (iter.hasNext()) {
|
||||
|
||||
iter.next();
|
||||
|
||||
QString filepath = iter.fileInfo().filePath();
|
||||
|
||||
// Ignore these files.
|
||||
if (filepath.endsWith(".plist") ||
|
||||
filepath.endsWith(".icns") ||
|
||||
filepath.endsWith(".prl") ||
|
||||
filepath.endsWith(".conf") ||
|
||||
filepath.endsWith(".h") ||
|
||||
filepath.endsWith(".nib") ||
|
||||
filepath.endsWith(".strings") ||
|
||||
filepath.endsWith(".css") ||
|
||||
filepath.endsWith("CodeResources") ||
|
||||
filepath.endsWith("PkgInfo") ||
|
||||
filepath.endsWith(".modulemap")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QProcess otool;
|
||||
otool.start("otool", QStringList() << "-L" << filepath);
|
||||
otool.waitForFinished();
|
||||
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
|
||||
qLog(Error) << "otool failed for" << filepath << ":" << otool.readAllStandardError();
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
QString output = otool.readAllStandardOutput();
|
||||
QStringList output_lines = output.split("\n", Qt::SkipEmptyParts);
|
||||
if (output_lines.size() < 2) {
|
||||
qLog(Error) << "Could not parse otool output:" << output;
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
QString first_line = output_lines.first();
|
||||
if (first_line.endsWith(':')) first_line.chop(1);
|
||||
if (first_line == filepath) {
|
||||
output_lines.removeFirst();
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "First line" << first_line << "does not match" << filepath;
|
||||
success = false;
|
||||
}
|
||||
QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$"));
|
||||
for (const QString &output_line : output_lines) {
|
||||
|
||||
//qDebug() << "Final check on" << filepath << output_line;
|
||||
|
||||
QRegularExpressionMatch match = regexp.match(output_line);
|
||||
if (match.hasMatch()) {
|
||||
QString library = match.captured(1);
|
||||
if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this.
|
||||
continue;
|
||||
}
|
||||
else if (library.startsWith("@executable_path")) {
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS");
|
||||
if (!QFile(real_path).exists()) {
|
||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("@rpath")) {
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks");
|
||||
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
|
||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("@loader_path")) {
|
||||
QString loader_path = QFileInfo(filepath).path();
|
||||
QString real_path = library;
|
||||
real_path = real_path.replace("@loader_path", loader_path);
|
||||
if (!QFile(real_path).exists()) {
|
||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library
|
||||
continue;
|
||||
}
|
||||
else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason.
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "File" << filepath << "points to" << library;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "Could not parse otool output line:" << output_line;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
|
||||
}
|
||||
@@ -3,26 +3,25 @@ cmake_minimum_required(VERSION 3.0)
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
|
||||
|
||||
set(SOURCES main.cpp tagreaderworker.cpp)
|
||||
set(HEADERS tagreaderworker.h)
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(MOC ${HEADERS})
|
||||
qt6_add_resources(QRC data/data.qrc)
|
||||
else()
|
||||
qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_add_resources(QRC data/data.qrc)
|
||||
qt_wrap_cpp(MOC ${HEADERS})
|
||||
|
||||
link_directories(${GLIB_LIBRARY_DIRS})
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
)
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${TAGLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(strawberry-tagreader PRIVATE
|
||||
@@ -34,20 +33,29 @@ target_include_directories(strawberry-tagreader PRIVATE
|
||||
|
||||
target_link_libraries(strawberry-tagreader PRIVATE
|
||||
${GLIB_LIBRARIES}
|
||||
${TAGLIB_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
libstrawberry-common
|
||||
libstrawberry-tagreader
|
||||
)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(FREEBSD)
|
||||
target_link_libraries(strawberry-tagreader PRIVATE execinfo)
|
||||
endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(strawberry-tagreader PRIVATE /System/Library/Frameworks/Foundation.framework)
|
||||
endif(APPLE)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
install(TARGETS strawberry-tagreader DESTINATION ${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/certs">
|
||||
<file>godaddy-root.pem</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,24 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
|
||||
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
|
||||
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
|
||||
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
|
||||
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
|
||||
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
|
||||
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
|
||||
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
|
||||
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
|
||||
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
|
||||
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
|
||||
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
|
||||
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
|
||||
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
|
||||
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
|
||||
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
|
||||
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
|
||||
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
|
||||
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
|
||||
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
|
||||
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
|
||||
ReYNnyicsbkqWletNw+vHX/bvZ8=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -18,18 +18,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QList>
|
||||
#include <QLocalSocket>
|
||||
#include <QSsl>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslSocket>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "tagreaderworker.h"
|
||||
@@ -64,10 +64,6 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
QSslSocket::addDefaultCaCertificates(QSslCertificate::fromPath(":/certs/godaddy-root.pem", QSsl::Pem));
|
||||
#endif
|
||||
|
||||
TagReaderWorker worker(&socket);
|
||||
|
||||
return a.exec();
|
||||
|
||||
@@ -59,5 +59,5 @@ void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
|
||||
void TagReaderWorker::DeviceClosed() {
|
||||
AbstractMessageHandler<spb::tagreader::Message>::DeviceClosed();
|
||||
|
||||
qApp->exit();
|
||||
QCoreApplication::exit();
|
||||
}
|
||||
|
||||
@@ -24,12 +24,18 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "core/messagehandler.h"
|
||||
#include "tagreader.h"
|
||||
#if defined(USE_TAGLIB)
|
||||
# include "tagreadertaglib.h"
|
||||
#elif defined(USE_TAGPARSER)
|
||||
# include "tagreadertagparser.h"
|
||||
#endif
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
|
||||
|
||||
@@ -38,7 +44,11 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||
void DeviceClosed() override;
|
||||
|
||||
private:
|
||||
TagReader tag_reader_;
|
||||
#if defined(USE_TAGLIB)
|
||||
TagReaderTagLib tag_reader_;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // TAGREADERWORKER_H
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
include(../cmake/Translations.cmake)
|
||||
endif(HAVE_TRANSLATIONS)
|
||||
endif()
|
||||
|
||||
set(SOURCES
|
||||
core/mainwindow.cpp
|
||||
@@ -11,6 +11,7 @@ set(SOURCES
|
||||
core/player.cpp
|
||||
core/commandlineoptions.cpp
|
||||
core/database.cpp
|
||||
core/sqlquery.cpp
|
||||
core/metatypes.cpp
|
||||
core/deletefiles.cpp
|
||||
core/filesystemmusicstorage.cpp
|
||||
@@ -36,11 +37,10 @@ set(SOURCES
|
||||
core/utilities.cpp
|
||||
core/imageutils.cpp
|
||||
core/iconloader.cpp
|
||||
core/qtsystemtrayicon.cpp
|
||||
core/standarditemiconloader.cpp
|
||||
core/systemtrayicon.cpp
|
||||
core/scopedtransaction.cpp
|
||||
core/translations.cpp
|
||||
core/systemtrayicon.cpp
|
||||
|
||||
engine/enginetype.cpp
|
||||
engine/enginebase.cpp
|
||||
@@ -76,6 +76,7 @@ set(SOURCES
|
||||
collection/sqlrow.cpp
|
||||
collection/savedgroupingmanager.cpp
|
||||
collection/groupbydialog.cpp
|
||||
collection/collectiontask.cpp
|
||||
|
||||
playlist/playlist.cpp
|
||||
playlist/playlistbackend.cpp
|
||||
@@ -215,9 +216,6 @@ set(SOURCES
|
||||
osd/osdbase.cpp
|
||||
osd/osdpretty.cpp
|
||||
|
||||
musicbrainz/acoustidclient.cpp
|
||||
musicbrainz/musicbrainzclient.cpp
|
||||
|
||||
internet/internetservices.cpp
|
||||
internet/internetservice.cpp
|
||||
internet/internetplaylistitem.cpp
|
||||
@@ -232,6 +230,17 @@ set(SOURCES
|
||||
internet/internetcollectionviewcontainer.cpp
|
||||
internet/internetsearchview.cpp
|
||||
|
||||
radios/radioservices.cpp
|
||||
radios/radiobackend.cpp
|
||||
radios/radiomodel.cpp
|
||||
radios/radioview.cpp
|
||||
radios/radioviewcontainer.cpp
|
||||
radios/radioservice.cpp
|
||||
radios/radioplaylistitem.cpp
|
||||
radios/radiochannel.cpp
|
||||
radios/somafmservice.cpp
|
||||
radios/radioparadiseservice.cpp
|
||||
|
||||
scrobbler/audioscrobbler.cpp
|
||||
scrobbler/scrobblerservices.cpp
|
||||
scrobbler/scrobblerservice.cpp
|
||||
@@ -253,11 +262,13 @@ set(SOURCES
|
||||
set(HEADERS
|
||||
core/mainwindow.h
|
||||
core/application.h
|
||||
core/appearance.h
|
||||
core/player.h
|
||||
core/database.h
|
||||
core/deletefiles.h
|
||||
core/filesystemwatcherinterface.h
|
||||
core/mergedproxymodel.h
|
||||
core/multisortfilterproxy.h
|
||||
core/networkaccessmanager.h
|
||||
core/threadsafenetworkdiskcache.h
|
||||
core/networktimeouts.h
|
||||
@@ -265,10 +276,11 @@ set(HEADERS
|
||||
core/songloader.h
|
||||
core/tagreaderclient.h
|
||||
core/taskmanager.h
|
||||
core/thread.h
|
||||
core/urlhandler.h
|
||||
core/qtsystemtrayicon.h
|
||||
core/standarditemiconloader.h
|
||||
core/systemtrayicon.h
|
||||
core/translations.h
|
||||
core/potranslator.h
|
||||
core/mimedata.h
|
||||
core/stylesheetloader.h
|
||||
|
||||
@@ -310,6 +322,7 @@ set(HEADERS
|
||||
playlist/playlistlistcontainer.h
|
||||
playlist/playlistlistmodel.h
|
||||
playlist/playlistlistview.h
|
||||
playlist/playlistlistsortfiltermodel.h
|
||||
playlist/playlistmanager.h
|
||||
playlist/playlistsaveoptionsdialog.h
|
||||
playlist/playlistsequence.h
|
||||
@@ -330,10 +343,13 @@ set(HEADERS
|
||||
playlistparsers/parserbase.h
|
||||
playlistparsers/playlistparser.h
|
||||
playlistparsers/plsparser.h
|
||||
playlistparsers/wplparser.h
|
||||
playlistparsers/xmlparser.h
|
||||
playlistparsers/xspfparser.h
|
||||
|
||||
smartplaylists/playlistgenerator.h
|
||||
smartplaylists/playlistgeneratorinserter.h
|
||||
smartplaylists/playlistquerygenerator.h
|
||||
smartplaylists/playlistgeneratormimedata.h
|
||||
smartplaylists/smartplaylistquerywizardplugin.h
|
||||
smartplaylists/smartplaylistsearchpreview.h
|
||||
@@ -428,24 +444,33 @@ set(HEADERS
|
||||
widgets/loginstatewidget.h
|
||||
widgets/qsearchfield.h
|
||||
widgets/ratingwidget.h
|
||||
widgets/forcescrollperpixel.h
|
||||
|
||||
osd/osdbase.h
|
||||
osd/osdpretty.h
|
||||
|
||||
musicbrainz/acoustidclient.h
|
||||
musicbrainz/musicbrainzclient.h
|
||||
|
||||
internet/internetservices.h
|
||||
internet/internetservice.h
|
||||
internet/internetsongmimedata.h
|
||||
internet/internetsearchview.h
|
||||
internet/internetsearchmodel.h
|
||||
internet/internetsearchsortmodel.h
|
||||
internet/internetsearchitemdelegate.h
|
||||
internet/internetsearchview.h
|
||||
internet/localredirectserver.h
|
||||
internet/internetsongsview.h
|
||||
internet/internettabsview.h
|
||||
internet/internetcollectionview.h
|
||||
internet/internetcollectionviewcontainer.h
|
||||
internet/internetsearchview.h
|
||||
|
||||
radios/radioservices.h
|
||||
radios/radiobackend.h
|
||||
radios/radiomodel.h
|
||||
radios/radioview.h
|
||||
radios/radioviewcontainer.h
|
||||
radios/radioservice.h
|
||||
radios/radiomimedata.h
|
||||
radios/somafmservice.h
|
||||
radios/radioparadiseservice.h
|
||||
|
||||
scrobbler/audioscrobbler.h
|
||||
scrobbler/scrobblerservices.h
|
||||
@@ -530,6 +555,8 @@ set(UI
|
||||
internet/internetcollectionviewcontainer.ui
|
||||
internet/internetsearchview.ui
|
||||
|
||||
radios/radioviewcontainer.ui
|
||||
|
||||
organize/organizedialog.ui
|
||||
organize/organizeerrordialog.ui
|
||||
|
||||
@@ -542,7 +569,7 @@ option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
|
||||
|
||||
if(NOT APPLE)
|
||||
set(NOT_APPLE ON)
|
||||
optional_source(NOT_APPLE SOURCES widgets/qsearchfield_nonmac.cpp)
|
||||
optional_source(NOT_APPLE SOURCES widgets/qsearchfield_nonmac.cpp core/qtsystemtrayicon.cpp HEADERS core/qtsystemtrayicon.h)
|
||||
endif()
|
||||
|
||||
if(HAVE_GLOBALSHORTCUTS)
|
||||
@@ -551,137 +578,62 @@ if(HAVE_GLOBALSHORTCUTS)
|
||||
HEADERS globalshortcuts/globalshortcutsmanager.h globalshortcuts/globalshortcutsbackend.h globalshortcuts/globalshortcutgrabber.h settings/globalshortcutssettingspage.h
|
||||
UI globalshortcuts/globalshortcutgrabber.ui settings/globalshortcutssettingspage.ui
|
||||
)
|
||||
if(HAVE_X11EXTRAS OR WIN32)
|
||||
set(X11_OR_WIN ON)
|
||||
endif()
|
||||
optional_source(X11_OR_WIN
|
||||
SOURCES globalshortcuts/globalshortcutsbackend-system.cpp globalshortcuts/globalshortcut.cpp
|
||||
HEADERS globalshortcuts/globalshortcutsbackend-system.h globalshortcuts/globalshortcut.h
|
||||
)
|
||||
optional_source(HAVE_X11EXTRAS
|
||||
SOURCES globalshortcuts/globalshortcut-x11.cpp
|
||||
)
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES globalshortcuts/globalshortcutsbackend-gsd.cpp globalshortcuts/globalshortcutsbackend-kde.cpp
|
||||
HEADERS globalshortcuts/globalshortcutsbackend-gsd.h globalshortcuts/globalshortcutsbackend-kde.h
|
||||
SOURCES globalshortcuts/globalshortcutsbackend-kde.cpp globalshortcuts/globalshortcutsbackend-gnome.cpp globalshortcuts/globalshortcutsbackend-mate.cpp
|
||||
HEADERS globalshortcuts/globalshortcutsbackend-kde.h globalshortcuts/globalshortcutsbackend-gnome.h globalshortcuts/globalshortcutsbackend-mate.h
|
||||
)
|
||||
optional_source(HAVE_X11_GLOBALSHORTCUTS
|
||||
SOURCES globalshortcuts/globalshortcutsbackend-x11.cpp globalshortcuts/globalshortcut.cpp globalshortcuts/globalshortcut-x11.cpp
|
||||
HEADERS globalshortcuts/globalshortcutsbackend-x11.h globalshortcuts/globalshortcut.h
|
||||
)
|
||||
optional_source(WIN32
|
||||
SOURCES globalshortcuts/globalshortcut-win.cpp
|
||||
SOURCES globalshortcuts/globalshortcutsbackend-win.cpp globalshortcuts/globalshortcut.cpp globalshortcuts/globalshortcut-win.cpp
|
||||
HEADERS globalshortcuts/globalshortcutsbackend-win.h globalshortcuts/globalshortcut.h
|
||||
)
|
||||
endif(HAVE_GLOBALSHORTCUTS)
|
||||
|
||||
# ALSA
|
||||
optional_source(HAVE_ALSA
|
||||
SOURCES
|
||||
engine/alsadevicefinder.cpp
|
||||
)
|
||||
optional_source(HAVE_ALSA SOURCES engine/alsadevicefinder.cpp engine/alsapcmdevicefinder.cpp)
|
||||
|
||||
# DBUS
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES
|
||||
osd/osddbus.cpp
|
||||
HEADERS
|
||||
osd/osddbus.h
|
||||
)
|
||||
optional_source(HAVE_DBUS SOURCES osd/osddbus.cpp HEADERS osd/osddbus.h)
|
||||
|
||||
# GStreamer
|
||||
optional_source(HAVE_GSTREAMER
|
||||
SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp engine/gstelementdeleter.cpp
|
||||
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h
|
||||
SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp
|
||||
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h
|
||||
)
|
||||
|
||||
# VLC
|
||||
optional_source(HAVE_VLC
|
||||
SOURCES engine/vlcengine.cpp
|
||||
HEADERS engine/vlcengine.h
|
||||
)
|
||||
optional_source(HAVE_VLC SOURCES engine/vlcengine.cpp HEADERS engine/vlcengine.h)
|
||||
|
||||
# DBUS and MPRIS - Unix specific
|
||||
if(UNIX AND HAVE_DBUS)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
|
||||
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES core/mpris.cpp core/mpris2.cpp
|
||||
HEADERS core/mpris.h core/mpris2.h
|
||||
)
|
||||
optional_source(HAVE_UDISKS2
|
||||
SOURCES device/udisks2lister.cpp
|
||||
HEADERS device/udisks2lister.h
|
||||
)
|
||||
optional_source(HAVE_DBUS SOURCES core/mpris2.cpp HEADERS core/mpris2.h)
|
||||
optional_source(HAVE_UDISKS2 SOURCES device/udisks2lister.cpp HEADERS device/udisks2lister.h)
|
||||
|
||||
if (BUILD_WITH_QT6)
|
||||
# MPRIS 2.0 DBUS interfaces
|
||||
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.Player.xml core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
|
||||
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.xml core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
|
||||
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.TrackList.xml core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
|
||||
|
||||
# MPRIS 2.0 DBUS interfaces
|
||||
qt6_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.Player.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
|
||||
qt6_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
|
||||
qt6_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.TrackList.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
|
||||
# MPRIS 2.1 DBUS interfaces
|
||||
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.Playlists.xml core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
|
||||
|
||||
# MPRIS 2.1 DBUS interfaces
|
||||
qt6_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.Playlists.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
|
||||
# org.freedesktop.Notifications DBUS interface
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.Notifications.xml dbus/notification)
|
||||
|
||||
# org.freedesktop.Notifications DBUS interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.Notifications.xml
|
||||
dbus/notification)
|
||||
# org.gnome.SettingsDaemon interface
|
||||
qt_add_dbus_interface(SOURCES dbus/org.gnome.SettingsDaemon.MediaKeys.xml dbus/gnomesettingsdaemon)
|
||||
|
||||
# org.gnome.SettingsDaemon interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
# org.mate.SettingsDaemon interface
|
||||
qt_add_dbus_interface(SOURCES dbus/org.mate.SettingsDaemon.MediaKeys.xml dbus/matesettingsdaemon)
|
||||
|
||||
# org.kde.KGlobalAccel interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.xml
|
||||
dbus/kglobalaccel)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.Component.xml
|
||||
dbus/kglobalaccelcomponent)
|
||||
|
||||
else()
|
||||
|
||||
# MPRIS 2.0 DBUS interfaces
|
||||
qt5_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.Player.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
|
||||
qt5_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
|
||||
qt5_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.TrackList.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
|
||||
|
||||
# MPRIS 2.1 DBUS interfaces
|
||||
qt5_add_dbus_adaptor(SOURCES
|
||||
dbus/org.mpris.MediaPlayer2.Playlists.xml
|
||||
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
|
||||
|
||||
# org.freedesktop.Notifications DBUS interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.Notifications.xml
|
||||
dbus/notification)
|
||||
|
||||
# org.gnome.SettingsDaemon interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
|
||||
# org.kde.KGlobalAccel interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.xml
|
||||
dbus/kglobalaccel)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.Component.xml
|
||||
dbus/kglobalaccelcomponent)
|
||||
|
||||
endif()
|
||||
# org.kde.KGlobalAccel interface
|
||||
qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.xml dbus/kglobalaccel)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.Component.xml dbus/kglobalaccelcomponent)
|
||||
|
||||
# org.freedesktop.Avahi.Server interface
|
||||
add_custom_command(
|
||||
@@ -712,50 +664,17 @@ if(UNIX AND HAVE_DBUS)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp)
|
||||
|
||||
if(HAVE_UDISKS2)
|
||||
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml
|
||||
PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Filesystem.xml
|
||||
PROPERTIES NO_NAMESPACE dbus/udisks2filesystem INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml
|
||||
PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml
|
||||
PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml
|
||||
PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.DBus.ObjectManager.xml
|
||||
dbus/objectmanager)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Filesystem.xml
|
||||
dbus/udisks2filesystem)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Block.xml
|
||||
dbus/udisks2block)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Drive.xml
|
||||
dbus/udisks2drive)
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Job.xml
|
||||
dbus/udisks2job)
|
||||
else()
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.DBus.ObjectManager.xml
|
||||
dbus/objectmanager)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Filesystem.xml
|
||||
dbus/udisks2filesystem)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Block.xml
|
||||
dbus/udisks2block)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Drive.xml
|
||||
dbus/udisks2drive)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.UDisks2.Job.xml
|
||||
dbus/udisks2job)
|
||||
endif()
|
||||
endif(HAVE_UDISKS2)
|
||||
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE dbus/udisks2filesystem INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
|
||||
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.DBus.ObjectManager.xml dbus/objectmanager)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Filesystem.xml dbus/udisks2filesystem)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Block.xml dbus/udisks2block)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Drive.xml dbus/udisks2drive)
|
||||
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Job.xml dbus/udisks2job)
|
||||
endif(HAVE_UDISKS2)
|
||||
|
||||
endif(UNIX AND HAVE_DBUS)
|
||||
|
||||
@@ -797,29 +716,24 @@ optional_source(HAVE_LIBGPOD
|
||||
)
|
||||
|
||||
# GIO device backend
|
||||
optional_source(HAVE_GIO
|
||||
SOURCES device/giolister.cpp
|
||||
HEADERS device/giolister.h
|
||||
)
|
||||
optional_source(HAVE_GIO SOURCES device/giolister.cpp HEADERS device/giolister.h)
|
||||
|
||||
# mtp device
|
||||
# MTP device
|
||||
optional_source(HAVE_LIBMTP
|
||||
SOURCES
|
||||
device/mtpconnection.cpp
|
||||
device/mtpdevice.cpp
|
||||
device/mtploader.cpp
|
||||
HEADERS
|
||||
device/mtpconnection.h
|
||||
device/mtpdevice.h
|
||||
device/mtploader.h
|
||||
)
|
||||
|
||||
# Pulse audio integration
|
||||
optional_source(HAVE_LIBPULSE
|
||||
SOURCES
|
||||
engine/pulsedevicefinder.cpp
|
||||
)
|
||||
optional_source(HAVE_LIBPULSE SOURCES engine/pulsedevicefinder.cpp)
|
||||
|
||||
# MusicBrainz and transcoder require GStreamer
|
||||
# Transcoder require GStreamer
|
||||
optional_source(HAVE_GSTREAMER
|
||||
SOURCES
|
||||
transcoder/transcoder.cpp
|
||||
@@ -838,6 +752,14 @@ HEADERS
|
||||
transcoder/transcoder.h
|
||||
transcoder/transcodedialog.h
|
||||
transcoder/transcoderoptionsdialog.h
|
||||
transcoder/transcoderoptionsinterface.h
|
||||
transcoder/transcoderoptionsflac.h
|
||||
transcoder/transcoderoptionswavpack.h
|
||||
transcoder/transcoderoptionsvorbis.h
|
||||
transcoder/transcoderoptionsopus.h
|
||||
transcoder/transcoderoptionsspeex.h
|
||||
transcoder/transcoderoptionsaac.h
|
||||
transcoder/transcoderoptionsasf.h
|
||||
transcoder/transcoderoptionsmp3.h
|
||||
settings/transcodersettingspage.h
|
||||
UI
|
||||
@@ -855,30 +777,39 @@ UI
|
||||
settings/transcodersettingspage.ui
|
||||
)
|
||||
|
||||
# CDIO backend and device
|
||||
if(HAVE_GSTREAMER)
|
||||
optional_source(HAVE_CHROMAPRINT
|
||||
SOURCES
|
||||
musicbrainz/chromaprinter.cpp
|
||||
musicbrainz/tagfetcher.cpp
|
||||
HEADERS
|
||||
musicbrainz/tagfetcher.h
|
||||
)
|
||||
optional_source(HAVE_AUDIOCD
|
||||
SOURCES
|
||||
device/cddadevice.cpp
|
||||
device/cddalister.cpp
|
||||
device/cddasongloader.cpp
|
||||
HEADERS
|
||||
device/cddadevice.h
|
||||
device/cddalister.h
|
||||
device/cddasongloader.h
|
||||
)
|
||||
# CHROMAPRINT
|
||||
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||
optional_source(CHROMAPRINT_FOUND SOURCES engine/chromaprinter.cpp)
|
||||
endif()
|
||||
|
||||
# MusicBrainz
|
||||
optional_source(HAVE_MUSICBRAINZ
|
||||
SOURCES
|
||||
musicbrainz/acoustidclient.cpp
|
||||
musicbrainz/musicbrainzclient.cpp
|
||||
musicbrainz/tagfetcher.cpp
|
||||
HEADERS
|
||||
musicbrainz/acoustidclient.h
|
||||
musicbrainz/musicbrainzclient.h
|
||||
musicbrainz/tagfetcher.h
|
||||
)
|
||||
|
||||
# CDIO backend and device
|
||||
optional_source(HAVE_AUDIOCD
|
||||
SOURCES
|
||||
device/cddadevice.cpp
|
||||
device/cddalister.cpp
|
||||
device/cddasongloader.cpp
|
||||
HEADERS
|
||||
device/cddadevice.h
|
||||
device/cddalister.h
|
||||
device/cddasongloader.h
|
||||
)
|
||||
|
||||
# Platform specific - macOS
|
||||
optional_source(APPLE
|
||||
SOURCES
|
||||
core/scoped_nsautorelease_pool.mm
|
||||
core/mac_utilities.mm
|
||||
core/mac_startup.mm
|
||||
core/macsystemtrayicon.mm
|
||||
@@ -888,22 +819,15 @@ optional_source(APPLE
|
||||
engine/macosdevicefinder.cpp
|
||||
globalshortcuts/globalshortcutsbackend-macos.mm
|
||||
globalshortcuts/globalshortcutgrabber.mm
|
||||
device/macosdevicelister.mm
|
||||
HEADERS
|
||||
core/macsystemtrayicon.h
|
||||
core/macfslistener.h
|
||||
osd/osdmac.h
|
||||
globalshortcuts/globalshortcutsbackend-macos.h
|
||||
device/macosdevicelister.h
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
optional_source(HAVE_LIBMTP
|
||||
SOURCES
|
||||
device/macosdevicelister.mm
|
||||
HEADERS
|
||||
device/macosdevicelister.h
|
||||
)
|
||||
endif()
|
||||
|
||||
# Platform specific - Windows
|
||||
optional_source(WIN32
|
||||
SOURCES
|
||||
@@ -1004,33 +928,27 @@ optional_source(HAVE_MOODBAR
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(MOC ${HEADERS})
|
||||
qt6_wrap_ui(UIC ${UI})
|
||||
qt6_add_resources(QRC ${RESOURCES})
|
||||
else()
|
||||
qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_wrap_ui(UIC ${UI})
|
||||
qt5_add_resources(QRC ${RESOURCES})
|
||||
endif()
|
||||
qt_wrap_cpp(MOC ${HEADERS})
|
||||
qt_wrap_ui(UIC ${UI})
|
||||
qt_add_resources(QRC ${RESOURCES})
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
|
||||
set(LINGUAS "All" CACHE STRING "A space-seperated list of translations to compile in to Strawberry, or \"None\".")
|
||||
if (LINGUAS STREQUAL "All")
|
||||
if(LINGUAS STREQUAL "All")
|
||||
# build LANGUAGES from all existing .po files
|
||||
file(GLOB pofiles translations/*.po)
|
||||
foreach(pofile ${pofiles})
|
||||
get_filename_component(lang ${pofile} NAME_WE)
|
||||
list(APPEND LANGUAGES ${lang})
|
||||
endforeach(pofile)
|
||||
else (LINGUAS STREQUAL "All")
|
||||
if (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
set (LANGUAGES "")
|
||||
else (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
else(LINGUAS STREQUAL "All")
|
||||
if(NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
set(LANGUAGES "")
|
||||
else(NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS})
|
||||
endif (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
endif (LINGUAS STREQUAL "All")
|
||||
endif(NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
endif(LINGUAS STREQUAL "All")
|
||||
|
||||
add_pot(POT
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
|
||||
@@ -1051,7 +969,6 @@ link_directories(
|
||||
${GOBJECT_LIBRARY_DIRS}
|
||||
${GNUTLS_LIBRARY_DIRS}
|
||||
${SQLITE_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
||||
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
|
||||
${QTSPARKLE_LIBRARY_DIRS}
|
||||
@@ -1060,7 +977,7 @@ link_directories(
|
||||
|
||||
if(HAVE_ALSA)
|
||||
link_directories(${ALSA_LIBRARY_DIRS})
|
||||
endif(HAVE_ALSA)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBPULSE)
|
||||
link_directories(${LIBPULSE_LIBRARY_DIRS})
|
||||
@@ -1075,40 +992,47 @@ if(HAVE_GSTREAMER)
|
||||
${GSTREAMER_TAG_LIBRARY_DIRS}
|
||||
${GSTREAMER_PBUTILS_LIBRARY_DIRS}
|
||||
)
|
||||
endif(HAVE_GSTREAMER)
|
||||
endif()
|
||||
|
||||
if(HAVE_VLC)
|
||||
link_directories(${LIBVLC_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(HAVE_CHROMAPRINT)
|
||||
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||
link_directories(${CHROMAPRINT_LIBRARY_DIRS})
|
||||
endif(HAVE_CHROMAPRINT)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
link_directories(${X11_LIBRARY_DIRS})
|
||||
endif(X11_FOUND)
|
||||
endif()
|
||||
|
||||
if(XCB_FOUND)
|
||||
link_directories(${XCB_LIBRARY_DIRS})
|
||||
endif(XCB_FOUND)
|
||||
endif()
|
||||
|
||||
if(HAVE_GIO)
|
||||
link_directories(${GIO_LIBRARY_DIRS})
|
||||
endif(HAVE_GIO)
|
||||
endif()
|
||||
|
||||
if(HAVE_AUDIOCD)
|
||||
link_directories(${LIBCDIO_LIBRARY_DIRS})
|
||||
endif(HAVE_AUDIOCD)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBGPOD)
|
||||
link_directories(${LIBGPOD_LIBRARY_DIRS})
|
||||
link_directories(${GDK_PIXBUF_LIBRARY_DIRS})
|
||||
endif(HAVE_LIBGPOD)
|
||||
link_directories(${LIBGPOD_LIBRARY_DIRS} ${GDK_PIXBUF_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBMTP)
|
||||
link_directories(${LIBMTP_LIBRARY_DIRS})
|
||||
endif(HAVE_LIBMTP)
|
||||
endif()
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
add_library(strawberry_lib STATIC
|
||||
${SOURCES}
|
||||
@@ -1126,9 +1050,12 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
|
||||
${GOBJECT_INCLUDE_DIRS}
|
||||
${GNUTLS_INCLUDE_DIRS}
|
||||
${SQLITE_INCLUDE_DIRS}
|
||||
${TAGLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
|
||||
target_include_directories(strawberry_lib SYSTEM PUBLIC ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
target_include_directories(strawberry_lib PUBLIC
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}
|
||||
@@ -1147,12 +1074,11 @@ target_link_libraries(strawberry_lib PUBLIC
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
${SQLITE_LIBRARIES}
|
||||
${TAGLIB_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
${SINGLEAPPLICATION_LIBRARIES}
|
||||
${SINGLECOREAPPLICATION_LIBRARIES}
|
||||
${QTSPARKLE_LIBRARIES}
|
||||
${Iconv_LIBRARY}
|
||||
${Iconv_LIBRARIES}
|
||||
libstrawberry-common
|
||||
libstrawberry-tagreader
|
||||
)
|
||||
@@ -1160,7 +1086,7 @@ target_link_libraries(strawberry_lib PUBLIC
|
||||
if(HAVE_ALSA)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES})
|
||||
endif(HAVE_ALSA)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBPULSE)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBPULSE_INCLUDE_DIRS})
|
||||
@@ -1184,7 +1110,7 @@ if(HAVE_GSTREAMER)
|
||||
${GSTREAMER_TAG_LIBRARIES}
|
||||
${GSTREAMER_PBUTILS_LIBRARIES}
|
||||
)
|
||||
endif(HAVE_GSTREAMER)
|
||||
endif()
|
||||
|
||||
if(HAVE_MOODBAR)
|
||||
target_link_libraries(strawberry_lib PRIVATE gstmoodbar)
|
||||
@@ -1195,41 +1121,44 @@ if(HAVE_VLC)
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_CHROMAPRINT)
|
||||
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${CHROMAPRINT_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES})
|
||||
endif(HAVE_CHROMAPRINT)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES})
|
||||
endif(X11_FOUND)
|
||||
endif()
|
||||
|
||||
if(XCB_FOUND)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${XCB_INCLUDE_DIR})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${XCB_LIBRARIES})
|
||||
endif(XCB_FOUND)
|
||||
endif()
|
||||
|
||||
if(HAVE_GIO)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${GIO_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${GIO_LIBRARIES})
|
||||
endif(HAVE_GIO)
|
||||
endif()
|
||||
|
||||
if(HAVE_AUDIOCD)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBCDIO_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBCDIO_LIBRARIES})
|
||||
endif(HAVE_AUDIOCD)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBGPOD)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBGPOD_INCLUDE_DIRS} ${GDK_PIXBUF_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBGPOD_LIBRARIES} ${GDK_PIXBUF_LIBRARIES})
|
||||
endif(HAVE_LIBGPOD)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBMTP)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBMTP_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES})
|
||||
endif(HAVE_LIBMTP)
|
||||
endif()
|
||||
|
||||
if(FREEBSD)
|
||||
target_link_libraries(strawberry_lib PRIVATE iconv)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(strawberry_lib PRIVATE
|
||||
@@ -1245,24 +1174,17 @@ if(APPLE)
|
||||
if(HAVE_SPARKLE)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${SPARKLE}/Headers)
|
||||
target_link_libraries(strawberry_lib PRIVATE ${SPARKLE})
|
||||
endif(HAVE_SPARKLE)
|
||||
endif(APPLE)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(strawberry_lib PRIVATE dsound)
|
||||
endif(WIN32)
|
||||
|
||||
if(X11_FOUND)
|
||||
# Hack: the Gold linker pays attention to the order that libraries are specified on the link line.
|
||||
# -lX11 and -ldl are provided earlier in the link command but they're actually used by libraries that appear after them, so they end up getting ignored.
|
||||
# This appends them to the very end of the link line, ensuring they're always used.
|
||||
if(FREEBSD)
|
||||
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB})
|
||||
else()
|
||||
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB} ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(strawberry_lib PRIVATE dsound dwmapi)
|
||||
if(MSVC)
|
||||
target_link_libraries(strawberry_lib PRIVATE sqlite3)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ..)
|
||||
@@ -1270,13 +1192,13 @@ set(EXECUTABLE_OUTPUT_PATH ..)
|
||||
# Show the console window in debug mode on Windows
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE)
|
||||
set(STRAWBERRY-WIN32-FLAG WIN32)
|
||||
endif(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE)
|
||||
endif()
|
||||
|
||||
# Resource file for windows
|
||||
if(WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../dist/windows/windres.rc.in ${CMAKE_CURRENT_BINARY_DIR}/windres.rc)
|
||||
set(STRAWBERRY-WIN32-RESOURCES windres.rc)
|
||||
endif(WIN32)
|
||||
endif()
|
||||
|
||||
add_executable(strawberry
|
||||
MACOSX_BUNDLE
|
||||
@@ -1304,4 +1226,4 @@ endif()
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist")
|
||||
endif (APPLE)
|
||||
endif()
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
template class Analyzer::Base<QWidget>;
|
||||
#endif
|
||||
|
||||
Analyzer::Base::Base(QWidget *parent, uint scopeSize)
|
||||
Analyzer::Base::Base(QWidget *parent, const uint scopeSize)
|
||||
: QWidget(parent),
|
||||
timeout_(40),
|
||||
fht_(new FHT(scopeSize)),
|
||||
@@ -69,10 +69,10 @@ void Analyzer::Base::hideEvent(QHideEvent*) { timer_.stop(); }
|
||||
|
||||
void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
|
||||
|
||||
void Analyzer::Base::transform(Scope& scope) {
|
||||
void Analyzer::Base::transform(Scope &scope) {
|
||||
|
||||
QVector<float> aux(fht_->size());
|
||||
if (static_cast<long unsigned int>(aux.size()) >= scope.size()) {
|
||||
if (static_cast<unsigned long int>(aux.size()) >= scope.size()) {
|
||||
std::copy(scope.begin(), scope.end(), aux.begin());
|
||||
}
|
||||
else {
|
||||
@@ -93,12 +93,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
|
||||
|
||||
switch (engine_->state()) {
|
||||
case Engine::Playing: {
|
||||
const Engine::Scope& thescope = engine_->scope(timeout_);
|
||||
const Engine::Scope &thescope = engine_->scope(timeout_);
|
||||
int i = 0;
|
||||
|
||||
// convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm
|
||||
for (uint x = 0; static_cast<int>(x) < fht_->size(); ++x) {
|
||||
lastscope_[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
|
||||
lastscope_[x] = static_cast<float>(thescope[i] + thescope[i + 1]) / (2 * (1U << 15U));
|
||||
i += 2;
|
||||
}
|
||||
|
||||
@@ -126,10 +126,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
|
||||
|
||||
int Analyzer::Base::resizeExponent(int exp) {
|
||||
|
||||
if (exp < 3)
|
||||
if (exp < 3) {
|
||||
exp = 3;
|
||||
else if (exp > 9)
|
||||
}
|
||||
else if (exp > 9) {
|
||||
exp = 9;
|
||||
}
|
||||
|
||||
if (exp != fht_->sizeExp()) {
|
||||
delete fht_;
|
||||
@@ -139,43 +141,53 @@ int Analyzer::Base::resizeExponent(int exp) {
|
||||
|
||||
}
|
||||
|
||||
int Analyzer::Base::resizeForBands(int bands) {
|
||||
int Analyzer::Base::resizeForBands(const int bands) {
|
||||
|
||||
int exp = 0;
|
||||
if (bands <= 8)
|
||||
if (bands <= 8) {
|
||||
exp = 4;
|
||||
else if (bands <= 16)
|
||||
}
|
||||
else if (bands <= 16) {
|
||||
exp = 5;
|
||||
else if (bands <= 32)
|
||||
}
|
||||
else if (bands <= 32) {
|
||||
exp = 6;
|
||||
else if (bands <= 64)
|
||||
}
|
||||
else if (bands <= 64) {
|
||||
exp = 7;
|
||||
else if (bands <= 128)
|
||||
}
|
||||
else if (bands <= 128) {
|
||||
exp = 8;
|
||||
else
|
||||
}
|
||||
else {
|
||||
exp = 9;
|
||||
}
|
||||
|
||||
resizeExponent(exp);
|
||||
return fht_->size() / 2;
|
||||
|
||||
}
|
||||
|
||||
void Analyzer::Base::demo(QPainter& p) {
|
||||
void Analyzer::Base::demo(QPainter &p) {
|
||||
|
||||
static int t = 201; // FIXME make static to namespace perhaps
|
||||
|
||||
if (t > 999) t = 1; // 0 = wasted calculations
|
||||
if (t > 999) {
|
||||
t = 1; // 0 = wasted calculations
|
||||
}
|
||||
if (t < 201) {
|
||||
Scope s(32);
|
||||
|
||||
const double dt = double(t) / 200;
|
||||
for (uint i = 0; i < s.size(); ++i)
|
||||
const double dt = static_cast<double>(t) / 200;
|
||||
for (uint i = 0; i < s.size(); ++i) {
|
||||
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
|
||||
}
|
||||
|
||||
analyze(p, s, new_frame_);
|
||||
}
|
||||
else
|
||||
else {
|
||||
analyze(p, Scope(32, 0), new_frame_);
|
||||
}
|
||||
|
||||
++t;
|
||||
|
||||
@@ -185,7 +197,7 @@ void Analyzer::Base::polishEvent() {
|
||||
init();
|
||||
}
|
||||
|
||||
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
|
||||
void Analyzer::interpolate(const Scope &inVec, Scope &outVec) {
|
||||
|
||||
double pos = 0.0;
|
||||
const double step = static_cast<double>(inVec.size()) / outVec.size();
|
||||
@@ -196,18 +208,22 @@ void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
|
||||
|
||||
uint64_t indexLeft = offset + 0;
|
||||
|
||||
if (indexLeft >= inVec.size()) indexLeft = inVec.size() - 1;
|
||||
if (indexLeft >= inVec.size()) {
|
||||
indexLeft = inVec.size() - 1;
|
||||
}
|
||||
|
||||
uint64_t indexRight = offset + 1;
|
||||
|
||||
if (indexRight >= inVec.size()) indexRight = inVec.size() - 1;
|
||||
if (indexRight >= inVec.size()) {
|
||||
indexRight = inVec.size() - 1;
|
||||
}
|
||||
|
||||
outVec[i] = inVec[indexLeft] * (1.0 - error) + inVec[indexRight] * error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Analyzer::initSin(Scope& v, const uint size) {
|
||||
void Analyzer::initSin(Scope &v, const uint size) {
|
||||
|
||||
double step = (M_PI * 2) / size;
|
||||
double radian = 0;
|
||||
@@ -222,7 +238,9 @@ void Analyzer::initSin(Scope& v, const uint size) {
|
||||
void Analyzer::Base::timerEvent(QTimerEvent *e) {
|
||||
|
||||
QWidget::timerEvent(e);
|
||||
if (e->timerId() != timer_.timerId()) return;
|
||||
if (e->timerId() != timer_.timerId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
new_frame_ = true;
|
||||
update();
|
||||
|
||||
@@ -57,11 +57,11 @@ class Base : public QWidget {
|
||||
public:
|
||||
~Base() override { delete fht_; }
|
||||
|
||||
uint timeout() const { return timeout_; }
|
||||
int timeout() const { return timeout_; }
|
||||
|
||||
void set_engine(EngineBase *engine) { engine_ = engine; }
|
||||
|
||||
void changeTimeout(uint newTimeout) {
|
||||
void changeTimeout(int newTimeout) {
|
||||
timeout_ = newTimeout;
|
||||
if (timer_.isActive()) {
|
||||
timer_.stop();
|
||||
@@ -72,7 +72,7 @@ class Base : public QWidget {
|
||||
virtual void framerateChanged() {}
|
||||
|
||||
protected:
|
||||
explicit Base(QWidget*, uint scopeSize = 7);
|
||||
explicit Base(QWidget*, const uint scopeSize = 7);
|
||||
|
||||
void hideEvent(QHideEvent*) override;
|
||||
void showEvent(QShowEvent*) override;
|
||||
@@ -82,15 +82,15 @@ class Base : public QWidget {
|
||||
void polishEvent();
|
||||
|
||||
int resizeExponent(int);
|
||||
int resizeForBands(int);
|
||||
int resizeForBands(const int);
|
||||
virtual void init() {}
|
||||
virtual void transform(Scope&);
|
||||
virtual void analyze(QPainter& p, const Scope&, bool new_frame) = 0;
|
||||
virtual void demo(QPainter& p);
|
||||
virtual void analyze(QPainter &p, const Scope&, const bool new_frame) = 0;
|
||||
virtual void demo(QPainter &p);
|
||||
|
||||
protected:
|
||||
QBasicTimer timer_;
|
||||
uint timeout_;
|
||||
int timeout_;
|
||||
FHT *fht_;
|
||||
EngineBase *engine_;
|
||||
Scope lastscope_;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QVariant>
|
||||
@@ -44,6 +46,8 @@
|
||||
#include "engine/enginebase.h"
|
||||
#include "engine/enginetype.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
|
||||
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
|
||||
|
||||
@@ -90,7 +94,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
context_menu_->addSeparator();
|
||||
|
||||
double_click_timer_->setSingleShot(true);
|
||||
double_click_timer_->setInterval(250);
|
||||
double_click_timer_->setInterval(250ms);
|
||||
QObject::connect(double_click_timer_, &QTimer::timeout, this, &AnalyzerContainer::ShowPopupMenu);
|
||||
|
||||
Load();
|
||||
@@ -99,7 +103,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
if (engine_->type() != Engine::EngineType::GStreamer) return;
|
||||
if (engine_->type() != Engine::EngineType::GStreamer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->button() == Qt::RightButton) {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
@@ -120,8 +126,10 @@ void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SetEngine(EngineBase *engine) {
|
||||
|
||||
if (current_analyzer_) current_analyzer_->set_engine(engine);
|
||||
engine_ = engine;
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::DisableAnalyzer() {
|
||||
@@ -222,12 +230,12 @@ void AnalyzerContainer::Save() {
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::AddFramerate(const QString& name, const int framerate) {
|
||||
void AnalyzerContainer::AddFramerate(const QString &name, const int framerate) {
|
||||
|
||||
QAction *action = context_menu_framerate_->addAction(name);
|
||||
group_framerate_->addAction(action);
|
||||
framerate_list_ << framerate;
|
||||
action->setCheckable(true);
|
||||
QObject::connect(action, &QAction::triggered, [this, framerate]() { ChangeFramerate(framerate); } );
|
||||
QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } );
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class AnalyzerContainer : public QWidget {
|
||||
void SaveFramerate(const int framerate);
|
||||
template <typename T>
|
||||
void AddAnalyzerType();
|
||||
void AddFramerate(const QString& name, const int framerate);
|
||||
void AddFramerate(const QString &name, const int framerate);
|
||||
|
||||
private:
|
||||
int current_framerate_; // fps
|
||||
@@ -94,7 +94,7 @@ class AnalyzerContainer : public QWidget {
|
||||
QPoint last_click_pos_;
|
||||
bool ignore_next_click_;
|
||||
|
||||
Analyzer::Base* current_analyzer_;
|
||||
Analyzer::Base *current_analyzer_;
|
||||
EngineBase *engine_;
|
||||
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "blockanalyzer.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QWidget>
|
||||
@@ -37,8 +38,8 @@
|
||||
|
||||
const int BlockAnalyzer::kHeight = 2;
|
||||
const int BlockAnalyzer::kWidth = 4;
|
||||
const int BlockAnalyzer::kMinRows = 3; // arbituary
|
||||
const int BlockAnalyzer::kMinColumns = 32; // arbituary
|
||||
const int BlockAnalyzer::kMinRows = 3; // arbitrary
|
||||
const int BlockAnalyzer::kMinColumns = 32; // arbitrary
|
||||
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
|
||||
const int BlockAnalyzer::kFadeSize = 90;
|
||||
|
||||
@@ -56,14 +57,13 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
|
||||
fade_bars_(kFadeSize),
|
||||
fade_pos_(1 << 8, 50),
|
||||
fade_intensity_(1 << 8, 32),
|
||||
step_(0)
|
||||
{
|
||||
step_(0) {
|
||||
|
||||
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
|
||||
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
|
||||
|
||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
|
||||
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1));
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
// all is explained in analyze()..
|
||||
// +1 to counter -1 in maxSizes, trust me we need this!
|
||||
columns_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
|
||||
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||
rows_ = static_cast<int>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||
|
||||
// this is the y-offset for drawing from the top of the widget
|
||||
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
|
||||
@@ -89,15 +89,15 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
if (rows_ != oldRows) {
|
||||
barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
|
||||
for (uint i = 0; i < kFadeSize; ++i)
|
||||
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(kWidth, rows_ * (kHeight + 1)));
|
||||
|
||||
yscale_.resize(rows_ + 1);
|
||||
|
||||
const int PRE = 1, PRO = 1; // PRE and PRO allow us to restrict the range somewhat
|
||||
|
||||
for (int z = 0; z < rows_; ++z)
|
||||
for (int z = 0; z < rows_; ++z) {
|
||||
yscale_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
|
||||
}
|
||||
|
||||
yscale_[rows_] = 0;
|
||||
|
||||
@@ -117,7 +117,7 @@ void BlockAnalyzer::determineStep() {
|
||||
// the fall time of 30 is too slow on framerates above 50fps
|
||||
const double fallTime = static_cast<double>(timeout() < 20 ? 20 * rows_ : 30 * rows_);
|
||||
|
||||
step_ = double(rows_ * timeout()) / fallTime;
|
||||
step_ = static_cast<double>(rows_ * timeout()) / fallTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -169,10 +169,12 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
|
||||
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
||||
|
||||
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||
if (static_cast<double>(y) > store_[x])
|
||||
if (static_cast<double>(y) > store_[x]) {
|
||||
y = static_cast<int>(store_[x] += step_);
|
||||
else
|
||||
}
|
||||
else {
|
||||
store_[x] = y;
|
||||
}
|
||||
|
||||
// If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
|
||||
// if the fadeout is quite faded now, then display the new one
|
||||
@@ -193,14 +195,15 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
|
||||
}
|
||||
|
||||
for (int x = 0; x < store_.size(); ++x)
|
||||
for (int x = 0; x < store_.size(); ++x) {
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
|
||||
}
|
||||
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
|
||||
}
|
||||
|
||||
static inline void adjustToLimits(int &b, int &f, int &amount) {
|
||||
static inline void adjustToLimits(const int b, int &f, int &amount) {
|
||||
|
||||
// with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
|
||||
|
||||
@@ -248,6 +251,8 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||
|
||||
private:
|
||||
const QColor &c;
|
||||
|
||||
Q_DISABLE_COPY(OutputOnExit)
|
||||
};
|
||||
|
||||
OutputOnExit allocateOnTheStack(fg);
|
||||
@@ -278,20 +283,24 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||
|
||||
// check the saturation for the two colours is sufficient that hue alone can
|
||||
// provide sufficient contrast
|
||||
if (ds > amount / 2 && (bs > 125 && fs > 125))
|
||||
if (ds > amount / 2 && (bs > 125 && fs > 125)) {
|
||||
return fg;
|
||||
else if (dv > amount / 2 && (bv > 125 && fv > 125))
|
||||
}
|
||||
else if (dv > amount / 2 && (bv > 125 && fv > 125)) {
|
||||
return fg;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs < 50 && ds < 40) {
|
||||
// low saturation on a low saturation is sad
|
||||
const int tmp = 50 - fs;
|
||||
fs = 50;
|
||||
if (static_cast<int>(amount) > tmp)
|
||||
if (static_cast<int>(amount) > tmp) {
|
||||
amount -= tmp;
|
||||
else
|
||||
}
|
||||
else {
|
||||
amount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// test that there is available value to honor our contrast requirement
|
||||
@@ -308,19 +317,24 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||
return QColor::fromHsv(fh, fs, fv);
|
||||
}
|
||||
|
||||
if (fv > bv && bv > static_cast<int>(amount))
|
||||
if (fv > bv && bv > static_cast<int>(amount)) {
|
||||
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
|
||||
}
|
||||
|
||||
if (fv < bv && fv > static_cast<int>(amount))
|
||||
if (fv < bv && fv > static_cast<int>(amount)) {
|
||||
return QColor::fromHsv(fh, fs, fv - amount);
|
||||
}
|
||||
|
||||
if (fv > bv && (255 - fv > static_cast<int>(amount)))
|
||||
if (fv > bv && (255 - fv > static_cast<int>(amount))) {
|
||||
return QColor::fromHsv(fh, fs, fv + amount);
|
||||
}
|
||||
|
||||
if (fv < bv && (255 - bv > static_cast<int>(amount)))
|
||||
if (fv < bv && (255 - bv > static_cast<int>(amount))) {
|
||||
return QColor::fromHsv(fh, fs, bv + amount);
|
||||
}
|
||||
|
||||
return Qt::blue;
|
||||
|
||||
}
|
||||
|
||||
void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
@@ -338,9 +352,10 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
bar()->fill(bg);
|
||||
|
||||
QPainter p(bar());
|
||||
for (int y = 0; y < rows_; ++y)
|
||||
for (int y = 0; y < rows_; ++y) {
|
||||
// graduate the fg color
|
||||
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
|
||||
}
|
||||
|
||||
{
|
||||
const QColor bg2 = palette().color(QPalette::Window).darker(112);
|
||||
@@ -357,7 +372,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
const int r2 = bg2.red(), g2 = bg2.green(), b2 = bg2.blue();
|
||||
|
||||
// Precalculate all fade-bar pixmaps
|
||||
for (uint y = 0; y < kFadeSize; ++y) {
|
||||
for (int y = 0; y < kFadeSize; ++y) {
|
||||
fade_bars_[y].fill(palette().color(QPalette::Window));
|
||||
QPainter f(&fade_bars_[y]);
|
||||
for (int z = 0; z < rows_; ++z) {
|
||||
@@ -386,8 +401,10 @@ void BlockAnalyzer::drawBackground() {
|
||||
|
||||
if (!p.paintEngine()) return;
|
||||
|
||||
for (int x = 0; x < columns_; ++x)
|
||||
for (int y = 0; y < rows_; ++y)
|
||||
for (int x = 0; x < columns_; ++x) {
|
||||
for (int y = 0; y < rows_; ++y) {
|
||||
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,17 +43,15 @@ const int BoomAnalyzer::kColumnWidth = 4;
|
||||
const int BoomAnalyzer::kMaxBandCount = 256;
|
||||
const int BoomAnalyzer::kMinBandCount = 32;
|
||||
|
||||
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
||||
const char *BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
||||
|
||||
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
||||
BoomAnalyzer::BoomAnalyzer(QWidget *parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
bands_(0),
|
||||
scope_(kMinBandCount),
|
||||
fg_(palette().color(QPalette::Highlight)),
|
||||
K_barHeight_(1.271) // 1.471
|
||||
,
|
||||
F_peakSpeed_(1.103) // 1.122
|
||||
,
|
||||
K_barHeight_(1.271), // 1.471
|
||||
F_peakSpeed_(1.103), // 1.122
|
||||
F_(1.0),
|
||||
bar_height_(kMaxBandCount, 0),
|
||||
peak_height_(kMaxBandCount, 0),
|
||||
@@ -65,15 +63,15 @@ BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeK_barHeight(int newValue) {
|
||||
void BoomAnalyzer::changeK_barHeight(const int newValue) {
|
||||
K_barHeight_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
|
||||
void BoomAnalyzer::changeF_peakSpeed(const int newValue) {
|
||||
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
void BoomAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
QWidget::resizeEvent(e);
|
||||
|
||||
@@ -83,7 +81,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
|
||||
scope_.resize(bands_);
|
||||
|
||||
F_ = double(HEIGHT) / (log10(256) * double(1.1) /*<- max. amplitude*/);
|
||||
F_ = static_cast<double>(HEIGHT) / (log10(256) * static_cast<double>(1.1) /*<- max. amplitude*/);
|
||||
|
||||
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
||||
canvas_ = QPixmap(size());
|
||||
@@ -101,7 +99,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::transform(Scope& s) {
|
||||
void BoomAnalyzer::transform(Scope &s) {
|
||||
|
||||
fht_->spectrum(s.data());
|
||||
fht_->scale(s.data(), 1.0 / 50);
|
||||
@@ -110,7 +108,7 @@ void BoomAnalyzer::transform(Scope& s) {
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
void BoomAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame) {
|
||||
|
||||
if (!new_frame || engine_->state() == Engine::Paused) {
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
@@ -160,8 +158,9 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
y = height() - static_cast<int>(bar_height_[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
|
||||
canvas_painter.setPen(fg_);
|
||||
if (bar_height_[i] > 0)
|
||||
if (bar_height_[i] > 0) {
|
||||
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
|
||||
}
|
||||
|
||||
y = height() - static_cast<int>(peak_height_[i]);
|
||||
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||
|
||||
@@ -42,10 +42,10 @@ class BoomAnalyzer : public Analyzer::Base {
|
||||
public:
|
||||
Q_INVOKABLE explicit BoomAnalyzer(QWidget*);
|
||||
|
||||
static const char* kName;
|
||||
static const char *kName;
|
||||
|
||||
void transform(Analyzer::Scope &s) override;
|
||||
void analyze(QPainter &p, const Analyzer::Scope&, bool new_frame) override;
|
||||
void analyze(QPainter &p, const Analyzer::Scope&, const bool new_frame) override;
|
||||
|
||||
public slots:
|
||||
void changeK_barHeight(int);
|
||||
|
||||
@@ -26,47 +26,52 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <QVector>
|
||||
#include <QtMath>
|
||||
|
||||
FHT::FHT(uint n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? static_cast<int>(-1) : static_cast<int>(n)) {
|
||||
|
||||
FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
|
||||
if (n > 3) {
|
||||
buf_vector_.resize(num_);
|
||||
tab_vector_.resize(num_ * 2);
|
||||
makeCasTable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FHT::~FHT() {}
|
||||
FHT::~FHT() = default;
|
||||
|
||||
int FHT::sizeExp() const { return exp2_; }
|
||||
int FHT::size() const { return num_; }
|
||||
|
||||
float* FHT::buf_() { return buf_vector_.data(); }
|
||||
float* FHT::tab_() { return tab_vector_.data(); }
|
||||
int* FHT::log_() { return log_vector_.data(); }
|
||||
float *FHT::buf_() { return buf_vector_.data(); }
|
||||
float *FHT::tab_() { return tab_vector_.data(); }
|
||||
int *FHT::log_() { return log_vector_.data(); }
|
||||
|
||||
void FHT::makeCasTable(void) {
|
||||
float* costab = tab_();
|
||||
float* sintab = tab_() + num_ / 2 + 1;
|
||||
|
||||
float *costab = tab_();
|
||||
float *sintab = tab_() + num_ / 2 + 1;
|
||||
|
||||
for (int ul = 0; ul < num_; ul++) {
|
||||
float d = M_PI * ul / (num_ / 2);
|
||||
float d = M_PI * ul / (num_ / 2); // NOLINT(bugprone-integer-division)
|
||||
*costab = *sintab = cos(d);
|
||||
|
||||
costab += 2;
|
||||
sintab += 2;
|
||||
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::scale(float* p, float d) {
|
||||
void FHT::scale(float *p, float d) const {
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
|
||||
}
|
||||
|
||||
void FHT::ewma(float* d, float* s, float w) {
|
||||
void FHT::ewma(float *d, float *s, float w) const {
|
||||
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||
}
|
||||
|
||||
void FHT::logSpectrum(float* out, float* p) {
|
||||
void FHT::logSpectrum(float *out, float *p) {
|
||||
|
||||
int n = num_ / 2, i = 0, j = 0, k = 0, *r = nullptr;
|
||||
if (log_vector_.size() < n) {
|
||||
@@ -93,47 +98,60 @@ void FHT::logSpectrum(float* out, float* p) {
|
||||
|
||||
}
|
||||
|
||||
void FHT::semiLogSpectrum(float* p) {
|
||||
void FHT::semiLogSpectrum(float *p) {
|
||||
|
||||
power2(p);
|
||||
for (int i = 0; i < (num_ / 2); i++, p++) {
|
||||
float e = 10.0 * log10(sqrt(*p / 2));
|
||||
*p = e < 0 ? 0 : e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::spectrum(float* p) {
|
||||
void FHT::spectrum(float *p) {
|
||||
|
||||
power2(p);
|
||||
for (int i = 0; i < (num_ / 2); i++, p++)
|
||||
for (int i = 0; i < (num_ / 2); i++, p++) {
|
||||
*p = static_cast<float>(sqrt(*p / 2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::power(float* p) {
|
||||
void FHT::power(float *p) {
|
||||
|
||||
power2(p);
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
|
||||
|
||||
}
|
||||
|
||||
void FHT::power2(float* p) {
|
||||
void FHT::power2(float *p) {
|
||||
|
||||
_transform(p, num_, 0);
|
||||
|
||||
*p = static_cast<float>(2 * pow(*p, 2));
|
||||
p++;
|
||||
|
||||
float* q = p + num_ - 2;
|
||||
float *q = p + num_ - 2;
|
||||
for (int i = 1; i < (num_ / 2); i++) {
|
||||
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
|
||||
p++;
|
||||
q--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::transform(float* p) {
|
||||
if (num_ == 8)
|
||||
void FHT::transform(float *p) {
|
||||
|
||||
if (num_ == 8) {
|
||||
transform8(p);
|
||||
else
|
||||
}
|
||||
else {
|
||||
_transform(p, num_, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::transform8(float* p) {
|
||||
void FHT::transform8(float *p) {
|
||||
|
||||
float a = 0.0, b = 0.0, c = 0.0, d = 0.0, e = 0.0, f = 0.0, g = 0.0, h = 0.0, b_f2 = 0.0, d_h2 = 0.0;
|
||||
float a_c_eg = 0.0, a_ce_g = 0.0, ac_e_g = 0.0, aceg = 0.0, b_df_h = 0.0, bdfh = 0.0;
|
||||
@@ -162,7 +180,7 @@ void FHT::transform8(float* p) {
|
||||
|
||||
}
|
||||
|
||||
void FHT::_transform(float* p, int n, int k) {
|
||||
void FHT::_transform(float *p, int n, int k) {
|
||||
|
||||
if (n == 8) {
|
||||
transform8(p + k);
|
||||
|
||||
@@ -41,9 +41,9 @@ class FHT {
|
||||
QVector<float> tab_vector_;
|
||||
QVector<int> log_vector_;
|
||||
|
||||
float* buf_();
|
||||
float* tab_();
|
||||
int* log_();
|
||||
float *buf_();
|
||||
float *tab_();
|
||||
int *log_();
|
||||
|
||||
/**
|
||||
* Create a table of "cas" (cosine and sine) values.
|
||||
@@ -63,12 +63,12 @@ class FHT {
|
||||
* should be at least 3. Values of more than 3 need a trigonometry table.
|
||||
* @see makeCasTable()
|
||||
*/
|
||||
explicit FHT(int);
|
||||
explicit FHT(uint);
|
||||
|
||||
~FHT();
|
||||
int sizeExp() const;
|
||||
int size() const;
|
||||
void scale(float*, float);
|
||||
void scale(float*, float) const;
|
||||
|
||||
/**
|
||||
* Exponentially Weighted Moving Average (EWMA) filter.
|
||||
@@ -76,7 +76,7 @@ class FHT {
|
||||
* @param s is fresh input.
|
||||
* @param w is the weighting factor.
|
||||
*/
|
||||
void ewma(float* d, float* s, float w);
|
||||
void ewma(float *d, float *s, float w) const;
|
||||
|
||||
/**
|
||||
* Logarithmic audio spectrum. Maps semi-logarithmic spectrum
|
||||
@@ -85,7 +85,7 @@ class FHT {
|
||||
* @param p is the input array.
|
||||
* @param out is the spectrum.
|
||||
*/
|
||||
void logSpectrum(float* out, float* p);
|
||||
void logSpectrum(float *out, float *p);
|
||||
|
||||
/**
|
||||
* Semi-logarithmic audio spectrum.
|
||||
@@ -117,7 +117,7 @@ class FHT {
|
||||
/**
|
||||
* Discrete Hartley transform of data sets with 8 values.
|
||||
*/
|
||||
void transform8(float*);
|
||||
static void transform8(float*);
|
||||
|
||||
void transform(float*);
|
||||
};
|
||||
|
||||
@@ -50,13 +50,13 @@ const int Rainbow::RainbowAnalyzer::kRainbowHeight[] = {21, 16};
|
||||
const int Rainbow::RainbowAnalyzer::kRainbowOverlap[] = {13, 15};
|
||||
const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33};
|
||||
|
||||
const char* Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
||||
const char* Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
|
||||
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02f;
|
||||
const char *Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
||||
const char *Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
|
||||
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02F;
|
||||
|
||||
Rainbow::RainbowAnalyzer::RainbowType Rainbow::RainbowAnalyzer::rainbowtype;
|
||||
|
||||
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent)
|
||||
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
timer_id_(startTimer(kFrameIntervalMs)),
|
||||
frame_(0),
|
||||
@@ -64,8 +64,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
|
||||
available_rainbow_width_(0),
|
||||
px_per_frame_(0),
|
||||
x_offset_(0),
|
||||
background_brush_(QColor(0x0f, 0x43, 0x73))
|
||||
{
|
||||
background_brush_(QColor(0x0f, 0x43, 0x73)) {
|
||||
|
||||
rainbowtype = rbtype;
|
||||
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
|
||||
@@ -73,7 +72,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
|
||||
memset(history_, 0, sizeof(history_));
|
||||
|
||||
for (int i = 0; i < kRainbowBands; ++i) {
|
||||
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
|
||||
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin); // NOLINT(bugprone-integer-division)
|
||||
|
||||
// pow constants computed so that
|
||||
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
|
||||
@@ -82,9 +81,9 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::transform(Scope& s) { fht_->spectrum(s.data()); }
|
||||
void Rainbow::RainbowAnalyzer::transform(Scope &s) { fht_->spectrum(s.data()); }
|
||||
|
||||
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
|
||||
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent *e) {
|
||||
|
||||
if (e->timerId() == timer_id_) {
|
||||
frame_ = (frame_ + 1) % kFrameCount[rainbowtype];
|
||||
@@ -95,7 +94,7 @@ void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
Q_UNUSED(e);
|
||||
|
||||
@@ -109,7 +108,7 @@ void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
|
||||
void Rainbow::RainbowAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_frame) {
|
||||
|
||||
// Discard the second half of the transform
|
||||
const int scope_size = static_cast<int>(s.size() / 2);
|
||||
@@ -117,7 +116,7 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
|
||||
if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
|
||||
// Transform the music into rainbows!
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
float* band_start = history_ + band * kHistorySize;
|
||||
float *band_start = history_ + band * kHistorySize;
|
||||
|
||||
// Move the history of each band across by 1 frame.
|
||||
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
|
||||
@@ -139,8 +138,8 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
|
||||
|
||||
// Create polylines for the rainbows.
|
||||
QPointF polyline[kRainbowBands * kHistorySize];
|
||||
QPointF* dest = polyline;
|
||||
float* source = history_;
|
||||
QPointF *dest = polyline;
|
||||
float *source = history_;
|
||||
|
||||
const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2;
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
@@ -204,8 +203,8 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
|
||||
|
||||
}
|
||||
|
||||
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
||||
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget *parent)
|
||||
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Nyancat, parent) {}
|
||||
|
||||
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
|
||||
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget *parent)
|
||||
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {}
|
||||
|
||||
@@ -47,10 +47,10 @@ class RainbowAnalyzer : public Analyzer::Base {
|
||||
public:
|
||||
enum RainbowType {
|
||||
Nyancat = 0,
|
||||
Dash = 1
|
||||
Dash = 1
|
||||
};
|
||||
|
||||
RainbowAnalyzer(const RainbowType &rbtype, QWidget *parent);
|
||||
RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
|
||||
|
||||
protected:
|
||||
void transform(Analyzer::Scope&) override;
|
||||
@@ -93,7 +93,7 @@ class RainbowAnalyzer : public Analyzer::Base {
|
||||
|
||||
private:
|
||||
// "constants" that get initialized in the constructor
|
||||
float band_scale_[kRainbowBands];
|
||||
float band_scale_[kRainbowBands]{};
|
||||
QPen colors_[kRainbowBands];
|
||||
|
||||
// Rainbow Nyancat & Dash
|
||||
@@ -104,9 +104,9 @@ class RainbowAnalyzer : public Analyzer::Base {
|
||||
int frame_;
|
||||
|
||||
// The y positions of each point on the rainbow.
|
||||
float history_[kHistorySize * kRainbowBands];
|
||||
float history_[kHistorySize * kRainbowBands]{};
|
||||
|
||||
// A cache of the last frame's rainbow,
|
||||
// A cache of the last frame's rainbow,
|
||||
// so it can be used in the next frame.
|
||||
QPixmap buffer_[2];
|
||||
int current_buffer_;
|
||||
@@ -118,7 +118,7 @@ class RainbowAnalyzer : public Analyzer::Base {
|
||||
// X spacing between each point in the polyline.
|
||||
int px_per_frame_;
|
||||
|
||||
// Amount the buffer_ is shifted to the left (off the edge of the widget)
|
||||
// Amount the buffer_ is shifted to the left (off the edge of the widget)
|
||||
// to make the rainbow extend from 0 to available_rainbow_width_.
|
||||
int x_offset_;
|
||||
|
||||
@@ -129,18 +129,18 @@ class NyanCatAnalyzer : public RainbowAnalyzer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE explicit NyanCatAnalyzer(QWidget* parent);
|
||||
|
||||
static const char* kName;
|
||||
Q_INVOKABLE explicit NyanCatAnalyzer(QWidget *parent);
|
||||
|
||||
static const char *kName;
|
||||
};
|
||||
|
||||
class RainbowDashAnalyzer : public RainbowAnalyzer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE explicit RainbowDashAnalyzer(QWidget* parent);
|
||||
Q_INVOKABLE explicit RainbowDashAnalyzer(QWidget *parent);
|
||||
|
||||
static const char* kName;
|
||||
static const char *kName;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,9 +43,9 @@
|
||||
#include "scrobbler/lastfmimport.h"
|
||||
|
||||
const char *SCollection::kSongsTable = "songs";
|
||||
const char *SCollection::kFtsTable = "songs_fts";
|
||||
const char *SCollection::kDirsTable = "directories";
|
||||
const char *SCollection::kSubdirsTable = "subdirectories";
|
||||
const char *SCollection::kFtsTable = "songs_fts";
|
||||
|
||||
SCollection::SCollection(Application *app, QObject *parent)
|
||||
: QObject(parent),
|
||||
@@ -62,7 +62,7 @@ SCollection::SCollection(Application *app, QObject *parent)
|
||||
backend()->moveToThread(app->database()->thread());
|
||||
qLog(Debug) << backend_ << "moved to thread" << app->database()->thread();
|
||||
|
||||
backend_->Init(app->database(), Song::Source_Collection, kSongsTable, kDirsTable, kSubdirsTable, kFtsTable);
|
||||
backend_->Init(app->database(), app->task_manager(), Song::Source_Collection, kSongsTable, kFtsTable, kDirsTable, kSubdirsTable);
|
||||
|
||||
model_ = new CollectionModel(backend_, app_, this);
|
||||
|
||||
@@ -78,7 +78,7 @@ SCollection::~SCollection() {
|
||||
}
|
||||
if (watcher_thread_) {
|
||||
watcher_thread_->exit();
|
||||
watcher_thread_->wait(5000 /* five seconds */);
|
||||
watcher_thread_->wait(5000);
|
||||
}
|
||||
backend_->deleteLater();
|
||||
|
||||
@@ -97,6 +97,7 @@ void SCollection::Init() {
|
||||
watcher_->set_backend(backend_);
|
||||
watcher_->set_task_manager(app_->task_manager());
|
||||
|
||||
QObject::connect(backend_, &CollectionBackend::Error, this, &SCollection::Error);
|
||||
QObject::connect(backend_, &CollectionBackend::DirectoryDiscovered, watcher_, &CollectionWatcher::AddDirectory);
|
||||
QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
|
||||
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||
@@ -107,6 +108,7 @@ void SCollection::Init() {
|
||||
QObject::connect(watcher_, &CollectionWatcher::SubdirsDiscovered, backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
||||
QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
||||
QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, backend_, &CollectionBackend::CompilationsNeedUpdating);
|
||||
QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, backend_, &CollectionBackend::UpdateLastSeen);
|
||||
|
||||
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, backend_, &CollectionBackend::UpdateLastPlayed);
|
||||
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdatePlayCount, backend_, &CollectionBackend::UpdatePlayCount);
|
||||
@@ -132,7 +134,7 @@ void SCollection::Exit() {
|
||||
|
||||
void SCollection::ExitReceived() {
|
||||
|
||||
QObject *obj = qobject_cast<QObject*>(sender());
|
||||
QObject *obj = sender();
|
||||
QObject::disconnect(obj, nullptr, this, nullptr);
|
||||
qLog(Debug) << obj << "successfully exited.";
|
||||
wait_for_exit_.removeAll(obj);
|
||||
@@ -148,8 +150,8 @@ void SCollection::AbortScan() { watcher_->Stop(); }
|
||||
|
||||
void SCollection::Rescan(const SongList &songs) {
|
||||
|
||||
qLog(Debug) << "Rescan" << songs.size() << "songs";
|
||||
if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs);
|
||||
qLog(Debug) << "Rescan" << songs.size() << "songs";
|
||||
if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ class SCollection : public QObject {
|
||||
~SCollection() override;
|
||||
|
||||
static const char *kSongsTable;
|
||||
static const char *kFtsTable;
|
||||
static const char *kDirsTable;
|
||||
static const char *kSubdirsTable;
|
||||
static const char *kFtsTable;
|
||||
|
||||
void Init();
|
||||
void Exit();
|
||||
@@ -79,6 +79,7 @@ class SCollection : public QObject {
|
||||
void ExitReceived();
|
||||
|
||||
signals:
|
||||
void Error(QString);
|
||||
void ExitFinished();
|
||||
|
||||
private:
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QFileInfo>
|
||||
@@ -35,10 +37,12 @@
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "core/sqlquery.h"
|
||||
#include "collectionquery.h"
|
||||
#include "directory.h"
|
||||
|
||||
class QThread;
|
||||
class TaskManager;
|
||||
class Database;
|
||||
class SmartPlaylistSearch;
|
||||
|
||||
@@ -50,14 +54,14 @@ class CollectionBackendInterface : public QObject {
|
||||
|
||||
struct Album {
|
||||
Album() {}
|
||||
Album(const QString &_album_artist, const QString &_album, const QUrl &_art_automatic, const QUrl &_art_manual, const QList<QUrl> &_urls, const Song::FileType _filetype, const QString &_cue_path) :
|
||||
album_artist(_album_artist),
|
||||
album(_album),
|
||||
art_automatic(_art_automatic),
|
||||
art_manual(_art_manual),
|
||||
urls(_urls),
|
||||
filetype(_filetype),
|
||||
cue_path(_cue_path) {}
|
||||
Album(const QString &_album_artist, const QString &_album, const QUrl &_art_automatic, const QUrl &_art_manual, const QList<QUrl> &_urls, const Song::FileType _filetype, const QString &_cue_path)
|
||||
: album_artist(_album_artist),
|
||||
album(_album),
|
||||
art_automatic(_art_automatic),
|
||||
art_manual(_art_manual),
|
||||
urls(_urls),
|
||||
filetype(_filetype),
|
||||
cue_path(_cue_path) {}
|
||||
|
||||
QString album_artist;
|
||||
QString album;
|
||||
@@ -83,6 +87,7 @@ class CollectionBackendInterface : public QObject {
|
||||
virtual void UpdateTotalAlbumCountAsync() = 0;
|
||||
|
||||
virtual SongList FindSongsInDirectory(const int id) = 0;
|
||||
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
|
||||
virtual SubdirectoryList SubdirsInDirectory(const int id) = 0;
|
||||
virtual DirectoryList GetAllDirectories() = 0;
|
||||
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
|
||||
@@ -106,8 +111,10 @@ class CollectionBackendInterface : public QObject {
|
||||
|
||||
virtual Song GetSongById(const int id) = 0;
|
||||
|
||||
virtual SongList GetSongsByFingerprint(const QString &fingerprint) = 0;
|
||||
|
||||
// Returns all sections of a song with the given filename. If there's just one section the resulting list will have it's size equal to 1.
|
||||
virtual SongList GetSongsByUrl(const QUrl &url) = 0;
|
||||
virtual SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) = 0;
|
||||
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
|
||||
// Using default beginning value is suitable when searching for single-section songs.
|
||||
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
||||
@@ -123,11 +130,13 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
||||
|
||||
void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table);
|
||||
void Init(Database *db, TaskManager *task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
||||
void Close();
|
||||
|
||||
void ExitAsync();
|
||||
|
||||
void ReportErrors(const CollectionQuery &query);
|
||||
|
||||
Database *db() const override { return db_; }
|
||||
|
||||
QString songs_table() const override { return songs_table_; }
|
||||
@@ -143,6 +152,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void UpdateTotalAlbumCountAsync() override;
|
||||
|
||||
SongList FindSongsInDirectory(const int id) override;
|
||||
SongList SongsWithMissingFingerprint(const int id) override;
|
||||
SubdirectoryList SubdirsInDirectory(const int id) override;
|
||||
DirectoryList GetAllDirectories() override;
|
||||
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
|
||||
@@ -160,23 +170,24 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) override;
|
||||
AlbumList GetAlbumsByArtist(const QString &artist, const QueryOptions &opt = QueryOptions()) override;
|
||||
|
||||
void UpdateManualAlbumArtAsync(const QString &album_artist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
|
||||
void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
|
||||
void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url) override;
|
||||
|
||||
Album GetAlbumArt(const QString &album_artist, const QString &album) override;
|
||||
Album GetAlbumArt(const QString &effective_albumartist, const QString &album) override;
|
||||
|
||||
Song GetSongById(const int id) override;
|
||||
SongList GetSongsById(const QList<int> &ids);
|
||||
SongList GetSongsById(const QStringList &ids);
|
||||
SongList GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column);
|
||||
|
||||
SongList GetSongsByUrl(const QUrl &url) override;
|
||||
SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) override;
|
||||
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
|
||||
|
||||
void AddDirectory(const QString &path) override;
|
||||
void RemoveDirectory(const Directory &dir) override;
|
||||
|
||||
SongList ExecCollectionQuery(CollectionQuery *query);
|
||||
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
||||
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
||||
|
||||
void IncrementPlayCountAsync(const int id);
|
||||
void IncrementSkipCountAsync(const int id, const float progress);
|
||||
@@ -187,12 +198,15 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
Song GetSongBySongId(const QString &song_id);
|
||||
SongList GetSongsBySongId(const QStringList &song_ids);
|
||||
|
||||
SongList GetAllSongs();
|
||||
SongList FindSongs(const SmartPlaylistSearch &search);
|
||||
SongList GetSongsByFingerprint(const QString &fingerprint) override;
|
||||
|
||||
SongList SmartPlaylistsGetAllSongs();
|
||||
SongList SmartPlaylistsFindSongs(const SmartPlaylistSearch &search);
|
||||
|
||||
Song::Source Source() const;
|
||||
|
||||
void AddOrUpdateSongsAsync(const SongList &songs);
|
||||
void UpdateSongsBySongIDAsync(const SongMap &new_songs);
|
||||
|
||||
void UpdateSongRatingAsync(const int id, const double rating);
|
||||
void UpdateSongsRatingAsync(const QList<int> &ids, const double rating);
|
||||
@@ -204,6 +218,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void UpdateTotalArtistCount();
|
||||
void UpdateTotalAlbumCount();
|
||||
void AddOrUpdateSongs(const SongList &songs);
|
||||
void UpdateSongsBySongID(const SongMap &new_songs);
|
||||
void UpdateMTimesOnly(const SongList &songs);
|
||||
void DeleteSongs(const SongList &songs);
|
||||
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
|
||||
@@ -215,7 +230,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void IncrementPlayCount(const int id);
|
||||
void IncrementSkipCount(const int id, const float progress);
|
||||
void ResetStatistics(const int id);
|
||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||
void SongPathChanged(const Song &song, const QFileInfo &new_file, const std::optional<int> new_collection_directory_id);
|
||||
|
||||
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
|
||||
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
|
||||
@@ -224,6 +239,9 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void UpdateSongRating(const int id, const double rating);
|
||||
void UpdateSongsRating(const QList<int> &id_list, const double rating);
|
||||
|
||||
void UpdateLastSeen(const int directory_id, const int expire_unavailable_songs_days);
|
||||
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
|
||||
|
||||
signals:
|
||||
void DirectoryDiscovered(Directory, SubdirectoryList);
|
||||
void DirectoryDeleted(Directory);
|
||||
@@ -241,6 +259,8 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
void ExitFinished();
|
||||
|
||||
void Error(QString);
|
||||
|
||||
private:
|
||||
struct CompilationInfo {
|
||||
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
|
||||
@@ -252,7 +272,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
int has_not_compilation_detected;
|
||||
};
|
||||
|
||||
void UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const QueryOptions &opt = QueryOptions());
|
||||
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt = QueryOptions());
|
||||
SubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
|
||||
@@ -265,6 +285,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
private:
|
||||
Database *db_;
|
||||
TaskManager *task_manager_;
|
||||
Song::Source source_;
|
||||
QString songs_table_;
|
||||
QString dirs_table_;
|
||||
|
||||
@@ -51,7 +51,7 @@ void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
|
||||
QStandardItem *item = new QStandardItem(dir.path);
|
||||
item->setData(dir.id, kIdRole);
|
||||
item->setIcon(dir_icon_);
|
||||
storage_ << std::make_shared<FilesystemMusicStorage>(dir.path);
|
||||
storage_ << std::make_shared<FilesystemMusicStorage>(dir.path, dir.id);
|
||||
appendRow(item);
|
||||
|
||||
}
|
||||
@@ -94,13 +94,10 @@ QVariant CollectionDirectoryModel::data(const QModelIndex &idx, int role) const
|
||||
case MusicStorage::Role_Storage:
|
||||
case MusicStorage::Role_StorageForceConnect:
|
||||
return QVariant::fromValue(storage_[idx.row()]);
|
||||
|
||||
case MusicStorage::Role_FreeSpace:
|
||||
return Utilities::FileSystemFreeSpace(data(idx, Qt::DisplayRole).toString());
|
||||
|
||||
return Utilities::FileSystemFreeSpace(data(idx, Qt::DisplayRole).toString());
|
||||
case MusicStorage::Role_Capacity:
|
||||
return Utilities::FileSystemCapacity(data(idx, Qt::DisplayRole).toString());
|
||||
|
||||
return Utilities::FileSystemCapacity(data(idx, Qt::DisplayRole).toString());
|
||||
default:
|
||||
return QStandardItemModel::data(idx, role);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class CollectionDirectoryModel : public QStandardItemModel {
|
||||
static const int kIdRole = Qt::UserRole + 1;
|
||||
|
||||
QIcon dir_icon_;
|
||||
CollectionBackend* backend_;
|
||||
CollectionBackend *backend_;
|
||||
QList<std::shared_ptr<MusicStorage>> storage_;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
@@ -65,7 +67,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
|
||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
|
||||
|
||||
ui_->filter->setToolTip(
|
||||
ui_->search_field->setToolTip(
|
||||
QString("<html><head/><body><p>") +
|
||||
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
|
||||
QString(" ") +
|
||||
@@ -85,7 +87,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
QString("</p></body></html>")
|
||||
);
|
||||
|
||||
QObject::connect(ui_->filter, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
|
||||
QObject::connect(ui_->search_field, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
|
||||
QObject::connect(filter_delay_, &QTimer::timeout, this, &CollectionFilterWidget::FilterDelayTimeout);
|
||||
|
||||
filter_delay_->setInterval(kFilterDelay);
|
||||
@@ -133,7 +135,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
collection_menu_->addSeparator();
|
||||
ui_->options->setMenu(collection_menu_);
|
||||
|
||||
QObject::connect(ui_->filter, &QSearchField::textChanged, this, &CollectionFilterWidget::FilterTextChanged);
|
||||
QObject::connect(ui_->search_field, &QSearchField::textChanged, this, &CollectionFilterWidget::FilterTextChanged);
|
||||
|
||||
ReloadSettings();
|
||||
|
||||
@@ -141,6 +143,51 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
|
||||
CollectionFilterWidget::~CollectionFilterWidget() { delete ui_; }
|
||||
|
||||
void CollectionFilterWidget::Init(CollectionModel *model) {
|
||||
|
||||
if (model_) {
|
||||
QObject::disconnect(model_, nullptr, this, nullptr);
|
||||
QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr);
|
||||
QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr);
|
||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
||||
for (QAction *action : filter_ages) {
|
||||
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
model_ = model;
|
||||
|
||||
// Connect signals
|
||||
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_.get(), &GroupByDialog::CollectionGroupingChanged);
|
||||
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
|
||||
QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
|
||||
|
||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
||||
for (QAction *action : filter_ages) {
|
||||
int age = filter_ages_[action];
|
||||
QObject::connect(action, &QAction::triggered, this, [this, age]() { model_->SetFilterAge(age); } );
|
||||
}
|
||||
|
||||
// Load settings
|
||||
if (!settings_group_.isEmpty()) {
|
||||
QSettings s;
|
||||
s.beginGroup(settings_group_);
|
||||
int version = 0;
|
||||
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
|
||||
if (version == 1) {
|
||||
model_->SetGroupBy(CollectionModel::Grouping(
|
||||
CollectionModel::GroupBy(s.value(group_by(1), static_cast<int>(CollectionModel::GroupBy_AlbumArtist)).toInt()),
|
||||
CollectionModel::GroupBy(s.value(group_by(2), static_cast<int>(CollectionModel::GroupBy_AlbumDisc)).toInt()),
|
||||
CollectionModel::GroupBy(s.value(group_by(3), static_cast<int>(CollectionModel::GroupBy_None)).toInt())));
|
||||
}
|
||||
else {
|
||||
model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None));
|
||||
}
|
||||
s.endGroup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::ReloadSettings() {
|
||||
|
||||
QSettings s;
|
||||
@@ -148,7 +195,7 @@ void CollectionFilterWidget::ReloadSettings() {
|
||||
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt();
|
||||
s.endGroup();
|
||||
ui_->options->setIconSize(QSize(iconsize, iconsize));
|
||||
ui_->filter->setIconSize(iconsize);
|
||||
ui_->search_field->setIconSize(iconsize);
|
||||
|
||||
}
|
||||
|
||||
@@ -255,7 +302,7 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) {
|
||||
|
||||
}
|
||||
|
||||
QAction *CollectionFilterWidget::CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping &grouping) {
|
||||
QAction *CollectionFilterWidget::CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping grouping) {
|
||||
|
||||
QAction *ret = new QAction(text, parent);
|
||||
ret->setCheckable(true);
|
||||
@@ -281,7 +328,7 @@ void CollectionFilterWidget::SaveGroupBy() {
|
||||
void CollectionFilterWidget::ShowGroupingManager() {
|
||||
|
||||
if (!groupings_manager_) {
|
||||
groupings_manager_.reset(new SavedGroupingManager);
|
||||
groupings_manager_ = std::make_unique<SavedGroupingManager>();
|
||||
}
|
||||
groupings_manager_->SetFilter(this);
|
||||
groupings_manager_->UpdateModel();
|
||||
@@ -289,56 +336,22 @@ void CollectionFilterWidget::ShowGroupingManager() {
|
||||
|
||||
}
|
||||
|
||||
bool CollectionFilterWidget::SearchFieldHasFocus() const {
|
||||
|
||||
void CollectionFilterWidget::FocusOnFilter(QKeyEvent *event) {
|
||||
|
||||
ui_->filter->setFocus();
|
||||
QApplication::sendEvent(ui_->filter, event);
|
||||
return ui_->search_field->hasFocus();
|
||||
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::SetCollectionModel(CollectionModel *model) {
|
||||
void CollectionFilterWidget::FocusSearchField() {
|
||||
|
||||
if (model_) {
|
||||
QObject::disconnect(model_, nullptr, this, nullptr);
|
||||
QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr);
|
||||
QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr);
|
||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
||||
for (QAction *action : filter_ages) {
|
||||
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
|
||||
}
|
||||
}
|
||||
ui_->search_field->setFocus();
|
||||
|
||||
model_ = model;
|
||||
}
|
||||
|
||||
// Connect signals
|
||||
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_.get(), &GroupByDialog::CollectionGroupingChanged);
|
||||
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
|
||||
QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
|
||||
void CollectionFilterWidget::FocusOnFilter(QKeyEvent *event) {
|
||||
|
||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
||||
for (QAction *action : filter_ages) {
|
||||
int age = filter_ages_[action];
|
||||
QObject::connect(action, &QAction::triggered, [this, age]() { model_->SetFilterAge(age); } );
|
||||
}
|
||||
|
||||
// Load settings
|
||||
if (!settings_group_.isEmpty()) {
|
||||
QSettings s;
|
||||
s.beginGroup(settings_group_);
|
||||
int version = 0;
|
||||
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
|
||||
if (version == 1) {
|
||||
model_->SetGroupBy(CollectionModel::Grouping(
|
||||
CollectionModel::GroupBy(s.value(group_by(1), int(CollectionModel::GroupBy_AlbumArtist)).toInt()),
|
||||
CollectionModel::GroupBy(s.value(group_by(2), int(CollectionModel::GroupBy_AlbumDisc)).toInt()),
|
||||
CollectionModel::GroupBy(s.value(group_by(3), int(CollectionModel::GroupBy_None)).toInt())));
|
||||
}
|
||||
else {
|
||||
model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None));
|
||||
}
|
||||
s.endGroup();
|
||||
}
|
||||
ui_->search_field->setFocus();
|
||||
QApplication::sendEvent(ui_->search_field, event);
|
||||
|
||||
}
|
||||
|
||||
@@ -354,16 +367,16 @@ void CollectionFilterWidget::GroupByClicked(QAction *action) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping &g) {
|
||||
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g) {
|
||||
|
||||
if (!settings_group_.isEmpty()) {
|
||||
// Save the settings
|
||||
QSettings s;
|
||||
s.beginGroup(settings_group_);
|
||||
s.setValue(group_by_version(), 1);
|
||||
s.setValue(group_by(1), int(g[0]));
|
||||
s.setValue(group_by(2), int(g[1]));
|
||||
s.setValue(group_by(3), int(g[2]));
|
||||
s.setValue(group_by(1), static_cast<int>(g[0]));
|
||||
s.setValue(group_by(2), static_cast<int>(g[1]));
|
||||
s.setValue(group_by(3), static_cast<int>(g[2]));
|
||||
s.endGroup();
|
||||
}
|
||||
|
||||
@@ -372,7 +385,7 @@ void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping &g)
|
||||
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Grouping &g) {
|
||||
void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Grouping g) {
|
||||
|
||||
for (QAction *action : group_by_group_->actions()) {
|
||||
if (action->property("group_by").isNull()) continue;
|
||||
@@ -391,20 +404,20 @@ void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Groupin
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::SetFilterHint(const QString &hint) {
|
||||
ui_->filter->setPlaceholderText(hint);
|
||||
ui_->search_field->setPlaceholderText(hint);
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::SetQueryMode(QueryOptions::QueryMode query_mode) {
|
||||
|
||||
ui_->filter->clear();
|
||||
ui_->filter->setEnabled(query_mode == QueryOptions::QueryMode_All);
|
||||
ui_->search_field->clear();
|
||||
ui_->search_field->setEnabled(query_mode == QueryOptions::QueryMode_All);
|
||||
|
||||
model_->SetFilterQueryMode(query_mode);
|
||||
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::ShowInCollection(const QString &search) {
|
||||
ui_->filter->setText(search);
|
||||
ui_->search_field->setText(search);
|
||||
}
|
||||
|
||||
void CollectionFilterWidget::SetAgeFilterEnabled(bool enabled) {
|
||||
@@ -433,7 +446,7 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
|
||||
break;
|
||||
|
||||
case Qt::Key_Escape:
|
||||
ui_->filter->clear();
|
||||
ui_->search_field->clear();
|
||||
e->accept();
|
||||
break;
|
||||
}
|
||||
@@ -461,9 +474,9 @@ void CollectionFilterWidget::FilterTextChanged(const QString &text) {
|
||||
|
||||
void CollectionFilterWidget::FilterDelayTimeout() {
|
||||
|
||||
emit Filter(ui_->filter->text());
|
||||
emit Filter(ui_->search_field->text());
|
||||
if (filter_applies_to_model_) {
|
||||
model_->SetFilterText(ui_->filter->text());
|
||||
model_->SetFilterText(ui_->search_field->text());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include "collectionquery.h"
|
||||
@@ -41,7 +41,6 @@ class QKeyEvent;
|
||||
|
||||
class GroupByDialog;
|
||||
class SavedGroupingManager;
|
||||
class SettingsDialog;
|
||||
class Ui_CollectionFilterWidget;
|
||||
|
||||
class CollectionFilterWidget : public QWidget {
|
||||
@@ -59,6 +58,8 @@ class CollectionFilterWidget : public QWidget {
|
||||
AlwaysDelayed,
|
||||
};
|
||||
|
||||
void Init(CollectionModel *model);
|
||||
|
||||
static QActionGroup *CreateGroupByActions(QObject *parent);
|
||||
|
||||
void UpdateGroupByActions();
|
||||
@@ -74,7 +75,6 @@ class CollectionFilterWidget : public QWidget {
|
||||
|
||||
void SetSettingsGroup(const QString &group) { settings_group_ = group; }
|
||||
void SetSettingsPrefix(const QString &prefix) { settings_prefix_ = prefix; }
|
||||
void SetCollectionModel(CollectionModel *model);
|
||||
|
||||
QString group_by();
|
||||
QString group_by_version();
|
||||
@@ -82,6 +82,9 @@ class CollectionFilterWidget : public QWidget {
|
||||
|
||||
void ReloadSettings();
|
||||
|
||||
bool SearchFieldHasFocus() const;
|
||||
void FocusSearchField();
|
||||
|
||||
public slots:
|
||||
void SetQueryMode(QueryOptions::QueryMode query_mode);
|
||||
void FocusOnFilter(QKeyEvent *e);
|
||||
@@ -96,7 +99,7 @@ class CollectionFilterWidget : public QWidget {
|
||||
void keyReleaseEvent(QKeyEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void GroupingChanged(const CollectionModel::Grouping &g);
|
||||
void GroupingChanged(const CollectionModel::Grouping g);
|
||||
void GroupByClicked(QAction *action);
|
||||
void SaveGroupBy();
|
||||
void ShowGroupingManager();
|
||||
@@ -105,8 +108,8 @@ class CollectionFilterWidget : public QWidget {
|
||||
void FilterDelayTimeout();
|
||||
|
||||
private:
|
||||
static QAction *CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping &grouping);
|
||||
void CheckCurrentGrouping(const CollectionModel::Grouping &g);
|
||||
static QAction *CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping grouping);
|
||||
void CheckCurrentGrouping(const CollectionModel::Grouping g);
|
||||
|
||||
private:
|
||||
Ui_CollectionFilterWidget *ui_;
|
||||
@@ -114,13 +117,12 @@ class CollectionFilterWidget : public QWidget {
|
||||
|
||||
std::unique_ptr<GroupByDialog> group_by_dialog_;
|
||||
std::unique_ptr<SavedGroupingManager> groupings_manager_;
|
||||
SettingsDialog *settings_dialog_;
|
||||
|
||||
QMenu *filter_age_menu_;
|
||||
QMenu *group_by_menu_;
|
||||
QMenu *collection_menu_;
|
||||
QActionGroup *group_by_group_;
|
||||
QMap<QAction*, int> filter_ages_;
|
||||
QHash<QAction*, int> filter_ages_;
|
||||
|
||||
QTimer *filter_delay_;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSearchField" name="filter" native="true">
|
||||
<widget class="QSearchField" name="search_field" native="true">
|
||||
<property name="placeholderText" stdset="0">
|
||||
<string>Enter search terms here</string>
|
||||
</property>
|
||||
|
||||
@@ -50,6 +50,9 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
|
||||
int container_level;
|
||||
Song metadata;
|
||||
CollectionItem *compilation_artist_node_;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(CollectionItem)
|
||||
};
|
||||
|
||||
#endif // COLLECTIONITEM_H
|
||||
|
||||
@@ -125,10 +125,9 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
|
||||
|
||||
if (!event || !view) return false;
|
||||
|
||||
QHelpEvent *he = static_cast<QHelpEvent*>(event);
|
||||
QString text = displayText(idx.data(), QLocale::system());
|
||||
|
||||
if (text.isEmpty() || !he) return false;
|
||||
if (text.isEmpty() || !event) return false;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::ToolTip: {
|
||||
@@ -138,12 +137,12 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
|
||||
bool is_elided = displayed_text.width() < real_text.width();
|
||||
|
||||
if (is_elided) {
|
||||
QToolTip::showText(he->globalPos(), text, view);
|
||||
QToolTip::showText(event->globalPos(), text, view);
|
||||
}
|
||||
else if (idx.data(Qt::ToolTipRole).isValid()) {
|
||||
// If the item has a tooltip text, display it
|
||||
QString tooltip_text = idx.data(Qt::ToolTipRole).toString();
|
||||
QToolTip::showText(he->globalPos(), tooltip_text, view);
|
||||
QToolTip::showText(event->globalPos(), tooltip_text, view);
|
||||
}
|
||||
else {
|
||||
// in case that another text was previously displayed
|
||||
@@ -156,7 +155,7 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
|
||||
return true;
|
||||
|
||||
case QEvent::WhatsThis:
|
||||
QWhatsThis::showText(he->globalPos(), text, view);
|
||||
QWhatsThis::showText(event->globalPos(), text, view);
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||