Compare commits
407 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
896da46422 | ||
|
|
7c07f5eb2a | ||
|
|
298cff37de | ||
|
|
8add802fe9 | ||
|
|
6d080a0d59 | ||
|
|
f0ae1051ee | ||
|
|
1ad0ffeaa6 | ||
|
|
45b44d012d | ||
|
|
cb6cbb9ee5 | ||
|
|
013a0d9cb0 | ||
|
|
8abb3ea225 | ||
|
|
74a5233b5d | ||
|
|
cd695a4522 | ||
|
|
eec4b2cc0e | ||
|
|
616875d0d2 | ||
|
|
eb749bd76f | ||
|
|
7d1e404efd | ||
|
|
4a01a578d1 | ||
|
|
393a2e0ea0 | ||
|
|
8e21decb8d | ||
|
|
22bfbab832 | ||
|
|
28d0cc8795 | ||
|
|
51f2383a07 | ||
|
|
a6426c6eba | ||
|
|
0631da6c8e | ||
|
|
3960239cfe | ||
|
|
b16e48af6f | ||
|
|
4d3950565a | ||
|
|
8d24bc50c9 | ||
|
|
458efe9168 | ||
|
|
ae940a8681 | ||
|
|
d76b223b7c | ||
|
|
7243d5f7cb | ||
|
|
387f74f66a | ||
|
|
61ffb7d97a | ||
|
|
a553693f34 | ||
|
|
cdcfd64ec4 | ||
|
|
7f4302ba20 | ||
|
|
0d94f2d376 | ||
|
|
c1eb20a20b | ||
|
|
634db67685 | ||
|
|
86648258a3 | ||
|
|
287f0a3739 | ||
|
|
8f42df209a | ||
|
|
390fd64a74 | ||
|
|
48ee471def | ||
|
|
e862afcb78 | ||
|
|
872da05ff6 | ||
|
|
d09e2daf00 | ||
|
|
e2d5b44b0a | ||
|
|
52d42ef2a8 | ||
|
|
b6abc34461 | ||
|
|
ae6a50626d | ||
|
|
8f9dbfee2c | ||
|
|
c71ccda967 | ||
|
|
0f284f2e1d | ||
|
|
45ac80dd8c | ||
|
|
09cdba4b3d | ||
|
|
d94ee8863c | ||
|
|
cc8ced6430 | ||
|
|
cdb144e6a6 | ||
|
|
58fb8643c6 | ||
|
|
b81cfa8acb | ||
|
|
938ee20f1f | ||
|
|
d02dc54c1b | ||
|
|
8680a54ae4 | ||
|
|
30001be0ee | ||
|
|
4614cb5ec1 | ||
|
|
e390f3a399 | ||
|
|
e22d463d11 | ||
|
|
5877aa822c | ||
|
|
a2915913bb | ||
|
|
a8b40747b2 | ||
|
|
b63030d302 | ||
|
|
4457e416db | ||
|
|
c7c1a8ede1 | ||
|
|
fd1e9d7fb0 | ||
|
|
59a6d2317b | ||
|
|
531c171542 | ||
|
|
0c743452b0 | ||
|
|
4893d3da1f | ||
|
|
5cdc24bfb0 | ||
|
|
523cdca449 | ||
|
|
09537d73da | ||
|
|
99c0c0b3b1 | ||
|
|
45bc353341 | ||
|
|
b2fc41a911 | ||
|
|
ebefe8b6d2 | ||
|
|
9e3508134b | ||
|
|
3166ca2127 | ||
|
|
204508478f | ||
|
|
51ce674c97 | ||
|
|
6fcde9fe5f | ||
|
|
c688e3431d | ||
|
|
230376c7f3 | ||
|
|
91d0a2cd0c | ||
|
|
1ffd010e4a | ||
|
|
ae366e141f | ||
|
|
eb869c6e97 | ||
|
|
b559f7cd16 | ||
|
|
375d32f08e | ||
|
|
89d6b7cec0 | ||
|
|
fdf96e8342 | ||
|
|
c5d73d7b09 | ||
|
|
34b4cc2d9e | ||
|
|
40ed17e609 | ||
|
|
673d76af45 | ||
|
|
c13fb6f9d5 | ||
|
|
424b1e7d1f | ||
|
|
e80c9c4101 | ||
|
|
f75c3633e9 | ||
|
|
d535cd8276 | ||
|
|
c13f6ab2af | ||
|
|
d2a30bfb78 | ||
|
|
e5b17092b4 | ||
|
|
466cb4c78b | ||
|
|
9722e1ddc4 | ||
|
|
e3a9b0b943 | ||
|
|
d668a8aee2 | ||
|
|
75dc0aafcf | ||
|
|
41b94233c6 | ||
|
|
52cff01b9c | ||
|
|
372fc6603d | ||
|
|
9221797c9d | ||
|
|
7501131558 | ||
|
|
bba7be99a3 | ||
|
|
abb95534d0 | ||
|
|
0b7b4c12e4 | ||
|
|
4056f00169 | ||
|
|
10303cb9c0 | ||
|
|
e3587d369e | ||
|
|
2a048502cc | ||
|
|
cac01fbde9 | ||
|
|
2d2ce191ec | ||
|
|
6380cb8458 | ||
|
|
48e0e6af71 | ||
|
|
b756bccc7a | ||
|
|
ae8eed7a67 | ||
|
|
ae4882bec3 | ||
|
|
1379741859 | ||
|
|
b45c7ace78 | ||
|
|
3843d9f55b | ||
|
|
f3422cb2fe | ||
|
|
73692797dc | ||
|
|
31dd910289 | ||
|
|
da51580299 | ||
|
|
9db148b1ec | ||
|
|
091b1b8209 | ||
|
|
900a4071ed | ||
|
|
df4b2f7938 | ||
|
|
803f44d9bc | ||
|
|
4b67aee020 | ||
|
|
71dc47d6c9 | ||
|
|
9cb305fb0d | ||
|
|
1672130486 | ||
|
|
8e135e3e79 | ||
|
|
aa1a4f6ea5 | ||
|
|
ba34cf5258 | ||
|
|
647089d2a8 | ||
|
|
5211508eb4 | ||
|
|
e6f05ae465 | ||
|
|
a9193f9b76 | ||
|
|
e373a17cd3 | ||
|
|
ebab9b7e4a | ||
|
|
6de0399807 | ||
|
|
5cc7bb80f6 | ||
|
|
6e0bd9b3f8 | ||
|
|
d1943f72d3 | ||
|
|
81709873bd | ||
|
|
f6bb7cd8ed | ||
|
|
d1c19e431c | ||
|
|
9ab2dde8ab | ||
|
|
71559bb125 | ||
|
|
ae4c95262c | ||
|
|
dbbf07c9c1 | ||
|
|
ab8cd619d5 | ||
|
|
c30ad2819d | ||
|
|
311e91797a | ||
|
|
52be4df355 | ||
|
|
0364e81050 | ||
|
|
2d49b71bc9 | ||
|
|
a18a4bdf31 | ||
|
|
d3acbe8288 | ||
|
|
22afcbcbb6 | ||
|
|
495c6bc21c | ||
|
|
cfd1fe59f3 | ||
|
|
c46cf5bc84 | ||
|
|
a8742557bd | ||
|
|
3bea58cf56 | ||
|
|
5aaa5231b8 | ||
|
|
82d10dd7cb | ||
|
|
841065fb91 | ||
|
|
c7146ef138 | ||
|
|
08f32d1de6 | ||
|
|
4c3f86aa4d | ||
|
|
445cf22333 | ||
|
|
4919de647a | ||
|
|
77fae99528 | ||
|
|
10bd4b40b9 | ||
|
|
9d8e6bb253 | ||
|
|
49c71ecfad | ||
|
|
d18834b415 | ||
|
|
e52cda193e | ||
|
|
0d5edd35ea | ||
|
|
f3f51c3d9d | ||
|
|
1431916183 | ||
|
|
ae48008803 | ||
|
|
5664813931 | ||
|
|
3948af80b8 | ||
|
|
343d6f9aff | ||
|
|
51b379502f | ||
|
|
82142751de | ||
|
|
4e5755f218 | ||
|
|
2f4f29517e | ||
|
|
e8a073651f | ||
|
|
d23da7a612 | ||
|
|
1ae4938da3 | ||
|
|
b5e27d4d69 | ||
|
|
d74fc8d1ce | ||
|
|
c5d9a41967 | ||
|
|
7e3042c4f4 | ||
|
|
1291dadbd6 | ||
|
|
f645099a39 | ||
|
|
8f32038891 | ||
|
|
a6fb1dcdf9 | ||
|
|
f01b469f3f | ||
|
|
47884919c8 | ||
|
|
653a35496d | ||
|
|
9b14df6b27 | ||
|
|
09813f3c5a | ||
|
|
7d76b7e4c2 | ||
|
|
47b5dea95c | ||
|
|
b0df63f1e8 | ||
|
|
3c4209b676 | ||
|
|
d51b9a8e0e | ||
|
|
3b56125bd2 | ||
|
|
6e69e39007 | ||
|
|
97208cb329 | ||
|
|
414a4a97fb | ||
|
|
2a809f96c4 | ||
|
|
17799b03f3 | ||
|
|
efc55fc648 | ||
|
|
22bd41211a | ||
|
|
4cb0171bd0 | ||
|
|
ce6c5af72c | ||
|
|
171575256c | ||
|
|
d3664dcf78 | ||
|
|
0788981783 | ||
|
|
aeee7c02d5 | ||
|
|
6e49a50461 | ||
|
|
fbc99827ab | ||
|
|
3b134320c4 | ||
|
|
c315e5016d | ||
|
|
7aebd6ed57 | ||
|
|
1a8ca06495 | ||
|
|
a27ae7e4a6 | ||
|
|
dd0ab897aa | ||
|
|
00ad92fb6d | ||
|
|
f84128ecbd | ||
|
|
ba89e0f4e3 | ||
|
|
832d36a4d4 | ||
|
|
0b437b3bfb | ||
|
|
7b50ec4630 | ||
|
|
4ddb13abac | ||
|
|
d2ac081177 | ||
|
|
9692fbf15b | ||
|
|
be966488e8 | ||
|
|
0ce613264f | ||
|
|
34634d776e | ||
|
|
673ded3819 | ||
|
|
b9a94ad3ae | ||
|
|
3f80b330cc | ||
|
|
01632d538c | ||
|
|
b0a9b1cd09 | ||
|
|
1f772081fd | ||
|
|
4ae54dbaad | ||
|
|
e47f4ff731 | ||
|
|
465369d79e | ||
|
|
15ddf6ff20 | ||
|
|
16a753bd95 | ||
|
|
c15103636c | ||
|
|
8a5f82ee7d | ||
|
|
5ec33ec821 | ||
|
|
ab7d383cf1 | ||
|
|
184e9a5c93 | ||
|
|
c4f5363cde | ||
|
|
002882cebf | ||
|
|
1cb3ec0c7b | ||
|
|
6bf325c6f6 | ||
|
|
09c7ff9e8b | ||
|
|
c2a94b61bf | ||
|
|
1db16232de | ||
|
|
3da681a6b1 | ||
|
|
a79b3e7852 | ||
|
|
b1099e6974 | ||
|
|
4f6e06131c | ||
|
|
19f69e9e6c | ||
|
|
01481da773 | ||
|
|
3e8f7e1cf1 | ||
|
|
5da69646f2 | ||
|
|
3cac01583b | ||
|
|
d16a26605e | ||
|
|
a4f692c788 | ||
|
|
9f01206c57 | ||
|
|
d34fc551ed | ||
|
|
7aa5f0d258 | ||
|
|
276a34bb66 | ||
|
|
0d820eda12 | ||
|
|
1991c1b677 | ||
|
|
459404e3f0 | ||
|
|
badc623a3c | ||
|
|
8e39f92cb7 | ||
|
|
3ff4885973 | ||
|
|
f9d45f7657 | ||
|
|
789ff9df5c | ||
|
|
a2064ed16b | ||
|
|
b3d06c0868 | ||
|
|
db1a6b3e38 | ||
|
|
8390237cc4 | ||
|
|
9967eae7bb | ||
|
|
b3be7d1c6f | ||
|
|
33ccb5dbb2 | ||
|
|
5b90c0d695 | ||
|
|
472a660239 | ||
|
|
ab67536d9a | ||
|
|
ee85fb3aec | ||
|
|
214b6f4358 | ||
|
|
af0d092054 | ||
|
|
b07903c3e9 | ||
|
|
ffa4c6bf09 | ||
|
|
b4125fa56c | ||
|
|
2c72302087 | ||
|
|
0fa52bc64f | ||
|
|
f55a80b15a | ||
|
|
0735483321 | ||
|
|
53fc2c7c21 | ||
|
|
f22133c3c5 | ||
|
|
2d5a6d6583 | ||
|
|
dc4adf2836 | ||
|
|
dd6e254e4f | ||
|
|
4c028c1659 | ||
|
|
d332a6777a | ||
|
|
378251f229 | ||
|
|
b6b9b903ed | ||
|
|
143d68cfd5 | ||
|
|
a31eac1426 | ||
|
|
797196f7fc | ||
|
|
c9d0bc81dd | ||
|
|
b5448ff607 | ||
|
|
5ebd363d5d | ||
|
|
1d439e673e | ||
|
|
0b7b7656b2 | ||
|
|
eb270df835 | ||
|
|
ff73dd2183 | ||
|
|
e043a03eb6 | ||
|
|
3cb4e8e373 | ||
|
|
7e6de528b4 | ||
|
|
df901c30ef | ||
|
|
13856b33ec | ||
|
|
a3a1c6f4c8 | ||
|
|
638998a861 | ||
|
|
6e2ec89a05 | ||
|
|
af67de8aa6 | ||
|
|
425dac478e | ||
|
|
b15c4ecd10 | ||
|
|
d7f88cf3a4 | ||
|
|
5b7fbcd9b8 | ||
|
|
7af64b0782 | ||
|
|
b84c70e811 | ||
|
|
f5b245c72d | ||
|
|
4307183817 | ||
|
|
aeb32783d6 | ||
|
|
3927b3bf27 | ||
|
|
da9d2f9417 | ||
|
|
1cec48e8f8 | ||
|
|
f1105393da | ||
|
|
dc7047e3c2 | ||
|
|
08b2945623 | ||
|
|
cbcc223150 | ||
|
|
9830f21e4a | ||
|
|
5f49567bf7 | ||
|
|
6154ae7342 | ||
|
|
978f3a3682 | ||
|
|
1f0961d574 | ||
|
|
a101252701 | ||
|
|
c96c29b1e3 | ||
|
|
3b0fc180ff | ||
|
|
9b8bfdf33c | ||
|
|
4328831fcd | ||
|
|
e5b3df41e9 | ||
|
|
cf5259e218 | ||
|
|
f24b6a520c | ||
|
|
4140163ab2 | ||
|
|
7afde0e93f | ||
|
|
d27a571882 | ||
|
|
1c70e3be25 | ||
|
|
1819f64467 | ||
|
|
9852e588c1 | ||
|
|
71a1ea481b | ||
|
|
9e32f0d778 | ||
|
|
4478174dc2 | ||
|
|
07553476d4 | ||
|
|
1773283456 | ||
|
|
221ab51d90 | ||
|
|
43e0dd922b | ||
|
|
b3262652c3 | ||
|
|
0cfa2b8c20 |
@@ -80,7 +80,6 @@ commands:
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
libxine-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
@@ -96,9 +95,7 @@ commands:
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libmtp-devel
|
||||
libusbmuxd-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
@@ -150,8 +147,6 @@ commands:
|
||||
taglib-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libusbmuxd-devel
|
||||
libmtp-devel
|
||||
libchromaprint-devel
|
||||
fftw-devel
|
||||
@@ -214,8 +209,6 @@ commands:
|
||||
libchromaprint-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libusbmuxd-devel
|
||||
libmtp-devel
|
||||
libjpeg-devel
|
||||
cairo-devel
|
||||
@@ -233,68 +226,6 @@ commands:
|
||||
gstreamer1-plugins-base-devel
|
||||
|
||||
|
||||
install_mageia_dependencies:
|
||||
description: Install Mageia dependencies
|
||||
steps:
|
||||
- run:
|
||||
name: Update packages
|
||||
command: urpmi.update -a
|
||||
- run:
|
||||
name: Configure auto update
|
||||
command: urpmi --auto-update
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: >
|
||||
urpmi --force
|
||||
urpmi-debuginfo-install
|
||||
git
|
||||
tar
|
||||
rpmdevtools
|
||||
make
|
||||
cmake
|
||||
glibc
|
||||
binutils
|
||||
gcc-c++
|
||||
man
|
||||
gettext
|
||||
notification-daemon
|
||||
dbus-devel
|
||||
libgnutls-devel
|
||||
lib64boost-devel
|
||||
lib64protobuf-devel
|
||||
protobuf-compiler
|
||||
lib64sqlite3-devel
|
||||
lib64alsa2-devel
|
||||
lib64pulseaudio-devel
|
||||
lib64notify-devel
|
||||
lib64qt5core-devel
|
||||
lib64qt5gui-devel
|
||||
lib64qt5widgets-devel
|
||||
lib64qt5network-devel
|
||||
lib64qt5concurrent-devel
|
||||
lib64qt5sql-devel
|
||||
lib64qt5dbus-devel
|
||||
lib64qt5x11extras-devel
|
||||
lib64qt5help-devel
|
||||
libqt5test-devel
|
||||
lib64gstreamer1.0-devel
|
||||
lib64gstreamer-plugins-base1.0-devel
|
||||
lib64cdio-devel
|
||||
lib64gpod-devel
|
||||
lib64plist-devel
|
||||
lib64usbmuxd-devel
|
||||
lib64mtp-devel
|
||||
lib64raw1394-devel
|
||||
lib64chromaprint-devel
|
||||
libfftw-devel
|
||||
desktop-file-utils
|
||||
appstream-util
|
||||
libappstream-glib8
|
||||
hicolor-icon-theme
|
||||
qt5ct
|
||||
lib64mesaegl1
|
||||
|
||||
|
||||
install_debian_dependencies:
|
||||
description: Install Debian dependencies
|
||||
steps:
|
||||
@@ -303,6 +234,7 @@ commands:
|
||||
command: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -336,9 +268,6 @@ commands:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
|
||||
|
||||
install_ubuntu_dependencies:
|
||||
@@ -354,6 +283,7 @@ commands:
|
||||
command: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -390,9 +320,6 @@ commands:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
|
||||
|
||||
jobs:
|
||||
@@ -407,24 +334,6 @@ jobs:
|
||||
- build_source
|
||||
|
||||
|
||||
build_opensuse_tumbleweed:
|
||||
docker:
|
||||
- image: opensuse/tumbleweed
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- run:
|
||||
name: Update packages
|
||||
command: zypper --non-interactive --gpg-auto-import-keys ref
|
||||
- run:
|
||||
name: Upgrade packages
|
||||
command: zypper --non-interactive --gpg-auto-import-keys dup
|
||||
- install_opensuse_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
build_opensuse_lp151:
|
||||
docker:
|
||||
- image: opensuse/leap:15.1
|
||||
@@ -488,28 +397,6 @@ jobs:
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_mageia_7:
|
||||
docker:
|
||||
- image: mageia:7
|
||||
environment:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- install_mageia_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_source
|
||||
- build_rpm
|
||||
|
||||
|
||||
build_debian_stretch:
|
||||
docker:
|
||||
- image: debian:stretch
|
||||
steps:
|
||||
- install_debian_dependencies
|
||||
- checkout
|
||||
- cmake
|
||||
- build_deb
|
||||
|
||||
build_debian_buster:
|
||||
docker:
|
||||
- image: debian:buster
|
||||
@@ -567,10 +454,6 @@ workflows:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_opensuse_tumbleweed:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_opensuse_lp151:
|
||||
filters:
|
||||
tags:
|
||||
@@ -591,22 +474,12 @@ workflows:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_mageia_7:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_centos_8:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
|
||||
- build_debian_stretch:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_debian_buster:
|
||||
filters:
|
||||
tags:
|
||||
|
||||
105
.clang-format
Normal file
@@ -0,0 +1,105 @@
|
||||
BasedOnStyle: Chromium
|
||||
Language: Cpp
|
||||
Standard: Cpp11
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignConsecutiveMacros: true
|
||||
AlignEscapedNewlines: true
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLambdasOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: No
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeBraces: Stroustrup
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 2
|
||||
Cpp11BracedListStyle: false
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: true
|
||||
FixNamespaceComments: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentCaseLabels: true
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 100
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakAssignment: 0
|
||||
PenaltyBreakBeforeFirstCallParameter: 0
|
||||
PenaltyBreakComment: 0
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 0
|
||||
PenaltyBreakTemplateDeclaration: 0
|
||||
PenaltyExcessCharacter: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 0
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: false
|
||||
AfterCaseLabel: false
|
||||
AfterStruct: false
|
||||
AfterClass: false
|
||||
AfterEnum: false
|
||||
AfterUnion: false
|
||||
AfterControlStatement: Never
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: true
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
@@ -7,7 +7,7 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
For general technical questions and help with technical issues please use the forum on https://forum.strawberrymusicplayer.org/
|
||||
For technical issues, questions and feature requests please use the forum on https://forum.strawberrymusicplayer.org/
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
489
.github/workflows/ccpp.yml
vendored
@@ -38,7 +38,6 @@ jobs:
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
libxine-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
@@ -54,9 +53,7 @@ jobs:
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libmtp-devel
|
||||
libusbmuxd-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
@@ -109,7 +106,6 @@ jobs:
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
libxine-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
@@ -125,9 +121,7 @@ jobs:
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libmtp-devel
|
||||
libusbmuxd-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
@@ -188,7 +182,6 @@ jobs:
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
libxine-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
@@ -204,9 +197,7 @@ jobs:
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libmtp-devel
|
||||
libusbmuxd-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
@@ -269,7 +260,6 @@ jobs:
|
||||
libpulse-devel
|
||||
gstreamer-devel
|
||||
gstreamer-plugins-base-devel
|
||||
libxine-devel
|
||||
vlc-devel
|
||||
libQt5Core-devel
|
||||
libQt5Gui-devel
|
||||
@@ -285,9 +275,7 @@ jobs:
|
||||
libqt5-linguist-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libmtp-devel
|
||||
libusbmuxd-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
@@ -313,6 +301,78 @@ jobs:
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_opensuse_qt6:
|
||||
name: Build openSUSE Qt 6
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: opensuse/tumbleweed
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- name: Add Qt 6 repository
|
||||
run: zypper -n ar -c -f -n 'repo-qt6' https://download.opensuse.org/repositories/home:/jonaski:/qt6/openSUSE_Tumbleweed/ repo-qt6
|
||||
- name: Update packages
|
||||
run: zypper --non-interactive --gpg-auto-import-keys ref
|
||||
- name: Upgrade packages
|
||||
run: zypper --non-interactive --gpg-auto-import-keys dup
|
||||
- name: Install openSUSE dependencies
|
||||
run: >
|
||||
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
|
||||
vlc-devel
|
||||
qt6-core-devel
|
||||
qt6-gui-devel
|
||||
qt6-widgets-devel
|
||||
qt6-concurrent-devel
|
||||
qt6-network-devel
|
||||
qt6-sql-devel
|
||||
qt6-dbus-devel
|
||||
qt6-test-devel
|
||||
qt6-x11extras-devel
|
||||
qt6-base-common-devel
|
||||
qt6-sql-sqlite
|
||||
qt6-qt5compat-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libmtp-devel
|
||||
libchromaprint-devel
|
||||
desktop-file-utils
|
||||
update-desktop-files
|
||||
appstream-glib
|
||||
hicolor-icon-theme
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
|
||||
- name: Build
|
||||
working-directory: build
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
|
||||
build_fedora_32:
|
||||
name: Build Fedora 32
|
||||
runs-on: ubuntu-latest
|
||||
@@ -364,8 +424,160 @@ jobs:
|
||||
taglib-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libusbmuxd-devel
|
||||
libmtp-devel
|
||||
libchromaprint-devel
|
||||
fftw-devel
|
||||
desktop-file-utils
|
||||
libappstream-glib
|
||||
hicolor-icon-theme
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
- name: Create RPM build sources directories
|
||||
working-directory: build
|
||||
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- name: Copy source tarball
|
||||
working-directory: build
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_fedora_33:
|
||||
name: Build Fedora 33
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: fedora:33
|
||||
env:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
|
||||
- name: Update packages
|
||||
run: yum update --assumeyes
|
||||
|
||||
- name: Upgrade packages
|
||||
run: yum upgrade --assumeyes
|
||||
|
||||
- name: Install Fedora dependencies
|
||||
run: >
|
||||
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
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
- name: Create RPM build sources directories
|
||||
working-directory: build
|
||||
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- name: Copy source tarball
|
||||
working-directory: build
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_fedora_34:
|
||||
name: Build Fedora 34
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: fedora:34
|
||||
env:
|
||||
RPM_BUILD_NCPUS: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
|
||||
- name: Update packages
|
||||
run: yum update --assumeyes
|
||||
|
||||
- name: Upgrade packages
|
||||
run: yum upgrade --assumeyes
|
||||
|
||||
- name: Install Fedora dependencies
|
||||
run: >
|
||||
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
|
||||
@@ -448,8 +660,6 @@ jobs:
|
||||
libchromaprint-devel
|
||||
libcdio-devel
|
||||
libgpod-devel
|
||||
libplist-devel
|
||||
libusbmuxd-devel
|
||||
libmtp-devel
|
||||
libjpeg-devel
|
||||
cairo-devel
|
||||
@@ -485,150 +695,6 @@ jobs:
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_mageia_7:
|
||||
name: Build Mageia 7
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mageia:7
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
|
||||
- name: Update packages
|
||||
run: urpmi.update -a
|
||||
|
||||
- name: Configure auto update
|
||||
run: urpmi --auto-update
|
||||
|
||||
- name: Install Mageia dependencies
|
||||
run: >
|
||||
urpmi --force
|
||||
urpmi-debuginfo-install
|
||||
git
|
||||
tar
|
||||
rpmdevtools
|
||||
make
|
||||
cmake
|
||||
glibc
|
||||
binutils
|
||||
gcc-c++
|
||||
man
|
||||
gettext
|
||||
notification-daemon
|
||||
dbus-devel
|
||||
libgnutls-devel
|
||||
lib64boost-devel
|
||||
lib64protobuf-devel
|
||||
protobuf-compiler
|
||||
lib64sqlite3-devel
|
||||
lib64alsa2-devel
|
||||
lib64pulseaudio-devel
|
||||
lib64notify-devel
|
||||
lib64qt5core-devel
|
||||
lib64qt5gui-devel
|
||||
lib64qt5widgets-devel
|
||||
lib64qt5network-devel
|
||||
lib64qt5concurrent-devel
|
||||
lib64qt5sql-devel
|
||||
lib64qt5dbus-devel
|
||||
lib64qt5x11extras-devel
|
||||
lib64qt5help-devel
|
||||
libqt5test-devel
|
||||
lib64gstreamer1.0-devel
|
||||
lib64gstreamer-plugins-base1.0-devel
|
||||
lib64cdio-devel
|
||||
lib64gpod-devel
|
||||
lib64plist-devel
|
||||
lib64usbmuxd-devel
|
||||
lib64mtp-devel
|
||||
lib64raw1394-devel
|
||||
lib64chromaprint-devel
|
||||
libfftw-devel
|
||||
desktop-file-utils
|
||||
appstream-util
|
||||
libappstream-glib8
|
||||
hicolor-icon-theme
|
||||
qt5ct
|
||||
lib64mesaegl1
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: Create source tarball
|
||||
working-directory: build
|
||||
run: ../dist/scripts/maketarball.sh
|
||||
- name: Create RPM build sources directories
|
||||
working-directory: build
|
||||
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||
- name: Copy source tarball
|
||||
working-directory: build
|
||||
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||
- name: Build RPM
|
||||
working-directory: build
|
||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||
|
||||
|
||||
build_debian_stretch:
|
||||
name: Build Debian Stretch
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:stretch
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- name: Install Debian dependencies
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
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
|
||||
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
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: make deb
|
||||
shell: bash
|
||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||
|
||||
|
||||
build_debian_buster:
|
||||
name: Build Debian Buster
|
||||
runs-on: ubuntu-latest
|
||||
@@ -640,6 +706,7 @@ jobs:
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -673,9 +740,6 @@ jobs:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -699,6 +763,7 @@ jobs:
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -732,9 +797,6 @@ jobs:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -760,6 +822,7 @@ jobs:
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -796,9 +859,6 @@ jobs:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -824,6 +884,7 @@ jobs:
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -860,9 +921,6 @@ jobs:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -888,6 +946,7 @@ jobs:
|
||||
run: >
|
||||
apt-get update && apt-get install -y
|
||||
build-essential
|
||||
dh-make
|
||||
ssh
|
||||
git
|
||||
make
|
||||
@@ -924,9 +983,6 @@ jobs:
|
||||
libcdio-dev
|
||||
libmtp-dev
|
||||
libgpod-dev
|
||||
libimobiledevice-dev
|
||||
libplist-dev
|
||||
libusbmuxd-dev
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory build
|
||||
@@ -944,8 +1000,10 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- name: Unlink python
|
||||
run: brew unlink python@2
|
||||
- name: Update
|
||||
run: brew update
|
||||
- name: Upgrade
|
||||
run: brew upgrade
|
||||
- name: Install packages
|
||||
run: >
|
||||
brew install
|
||||
@@ -969,8 +1027,6 @@ jobs:
|
||||
gst-libav
|
||||
libcdio
|
||||
libmtp
|
||||
libimobiledevice
|
||||
libplist
|
||||
create-dmg
|
||||
taglib
|
||||
|
||||
@@ -999,10 +1055,14 @@ jobs:
|
||||
working-directory: build
|
||||
shell: bash
|
||||
run: make install
|
||||
#- name: Create DMG
|
||||
# working-directory: build
|
||||
# shell: bash
|
||||
# run: make dmg
|
||||
- name: Create DMG
|
||||
working-directory: build
|
||||
shell: bash
|
||||
run: make dmg
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: upload-macos
|
||||
path: build/strawberry-*.dmg
|
||||
|
||||
|
||||
build-windows:
|
||||
@@ -1034,22 +1094,16 @@ jobs:
|
||||
-DENABLE_WIN32_CONSOLE=OFF
|
||||
-DENABLE_DBUS=OFF
|
||||
-DENABLE_LIBGPOD=OFF
|
||||
-DENABLE_IMOBILEDEVICE=OFF
|
||||
-DENABLE_LIBMTP=OFF
|
||||
-DENABLE_XINE=OFF
|
||||
-DProtobuf_PROTOC_EXECUTABLE=/usr/src/strawberry-mxe/usr/x86_64-pc-linux-gnu/bin/protoc
|
||||
|
||||
- name: Run Make
|
||||
working-directory: build
|
||||
run: make -j2
|
||||
|
||||
- name: Strip executables
|
||||
working-directory: build
|
||||
run: /usr/src/strawberry-mxe/usr/bin/x86_64-w64-mingw32.shared-strip *.exe
|
||||
|
||||
- name: Create directories
|
||||
working-directory: build
|
||||
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins xine-plugins nsisplugins
|
||||
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins nsisplugins
|
||||
|
||||
- name: Copy GIO modules
|
||||
working-directory: build
|
||||
@@ -1123,13 +1177,9 @@ jobs:
|
||||
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstlibav.dll
|
||||
${GITHUB_WORKSPACE}/build/gstreamer-plugins/
|
||||
|
||||
- name: Copy killproc.exe
|
||||
- name: Copy extra binaries
|
||||
working-directory: build
|
||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/killproc.exe .
|
||||
|
||||
- name: Copy liborc-0.4-0.dll
|
||||
working-directory: build
|
||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/liborc-0.4-0.dll .
|
||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,liborc-0.4-0.dll} .
|
||||
|
||||
- name: Copy dependencies
|
||||
working-directory: build
|
||||
@@ -1145,6 +1195,10 @@ jobs:
|
||||
-F ./gstreamer-plugins
|
||||
-R /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared
|
||||
|
||||
- name: Strip binaries
|
||||
working-directory: build
|
||||
run: find . -type f \( -iname \*.dll -o -iname \*.exe \) -exec /usr/src/strawberry-mxe/usr/bin/x86_64-w64-mingw32.shared-strip {} \;
|
||||
|
||||
- name: Copy nsis files
|
||||
working-directory: build
|
||||
run: cp ${GITHUB_WORKSPACE}/dist/windows/*.nsi ${GITHUB_WORKSPACE}/dist/windows/*.nsh ${GITHUB_WORKSPACE}/dist/windows/*.ico .
|
||||
@@ -1152,3 +1206,46 @@ jobs:
|
||||
- name: Build Windows installer
|
||||
working-directory: build
|
||||
run: makensis strawberry.nsi
|
||||
|
||||
|
||||
upload-macos:
|
||||
name: Upload macOS DMG
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
needs:
|
||||
- build-macos
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: uploads
|
||||
- name: Install SSH keys
|
||||
uses: shimataro/ssh-key-action@v2
|
||||
with:
|
||||
known_hosts: ${{ secrets.KNOWN_HOSTS2 }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
- name: rsync
|
||||
run: |
|
||||
set -x
|
||||
for i in $(find uploads -type f -name '*.dmg'); do
|
||||
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/catalina/
|
||||
done
|
||||
|
||||
|
||||
build_snap:
|
||||
name: Build Snap
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1.2.0
|
||||
- uses: snapcore/action-build@v1
|
||||
id: snapcraft
|
||||
- uses: snapcore/action-publish@v1
|
||||
if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
store_login: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||
snap: ${{ steps.snapcraft.outputs.snap }}
|
||||
release: beta
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: release_snap
|
||||
path: ${{ steps.snapcraft.outputs.snap }}
|
||||
|
||||
1
.gitignore
vendored
@@ -104,7 +104,6 @@ Thumbs.db
|
||||
|
||||
# Stuff in dist
|
||||
maketarball.sh
|
||||
create-dmg.sh
|
||||
changelog
|
||||
PKGBUILD
|
||||
|
||||
|
||||
56
.travis.yml
@@ -2,8 +2,6 @@ sudo: required
|
||||
language: C++
|
||||
os:
|
||||
- osx
|
||||
services:
|
||||
- docker
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
@@ -12,42 +10,36 @@ before_install:
|
||||
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
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
docker build -f Dockerfile -t strawberry-build . || travis_terminate 1;
|
||||
docker run --name build -itd strawberry-build /bin/bash || travis_terminate 1;
|
||||
docker exec build git clone https://github.com/strawberrymusicplayer/strawberry;
|
||||
fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
git fetch --unshallow || travis_terminate 1;
|
||||
git pull || travis_terminate 1;
|
||||
brew unlink python@2 || travis_terminate 1;
|
||||
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 libimobiledevice libplist;
|
||||
brew install create-dmg;
|
||||
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
|
||||
export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools;
|
||||
export PATH="/usr/local/opt/gettext/bin:$PATH";
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig/:/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH";
|
||||
ls /usr/local/lib/gstreamer-1.0;
|
||||
fi
|
||||
- git fetch --unshallow
|
||||
- git pull
|
||||
- brew update
|
||||
- travis_wait 120 brew upgrade || echo "Failed"
|
||||
- travis_wait 120 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 cask install sparkle
|
||||
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework
|
||||
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM
|
||||
- export Qt5_DIR=/usr/local/opt/qt5/lib/cmake
|
||||
- export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
|
||||
- ls /usr/local/lib/gstreamer-1.0
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON ; fi
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DUSE_BUNDLE=ON
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
make -j8 || travis_terminate 1;
|
||||
make install || travis_terminate 1;
|
||||
make dmg;
|
||||
fi
|
||||
- make -j8
|
||||
- make install
|
||||
- make dmg
|
||||
after_success:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry*.dmg; fi
|
||||
- ls -lh strawberry*.dmg
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||
if [[ "$TRAVIS_BRANCH" == "master" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos;
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
|
||||
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos;
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
32
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -10,38 +10,46 @@ endif()
|
||||
|
||||
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
||||
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
||||
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
else()
|
||||
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||
endif()
|
||||
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
||||
target_include_directories(singleapplication SYSTEM PRIVATE
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Widgets_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtWidgets_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(singleapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(singleapplication PRIVATE
|
||||
${Qt5Core_LIBRARIES}
|
||||
${Qt5Widgets_LIBRARIES}
|
||||
${Qt5Network_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtWidgets_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
)
|
||||
|
||||
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
||||
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
||||
QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
else()
|
||||
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
endif()
|
||||
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||
target_include_directories(singlecoreapplication SYSTEM PRIVATE
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
target_include_directories(singlecoreapplication PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(singlecoreapplication PRIVATE
|
||||
${Qt5Core_LIBRARIES}
|
||||
${Qt5Network_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
)
|
||||
|
||||
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
186
3rdparty/singleapplication/singleapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -35,113 +35,131 @@
|
||||
#include <limits>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <QThread>
|
||||
#include <QSharedMemory>
|
||||
#include <QLocalSocket>
|
||||
#include <QByteArray>
|
||||
#include <QElapsedTimer>
|
||||
#include <QtDebug>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#else
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* Checks and fires up LocalServer or closes the program if another instance already exists
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param {bool} allowSecondaryInstances
|
||||
* @param allowSecondary Whether to enable secondary instance support
|
||||
* @param options Optional flags to toggle specific behaviour
|
||||
* @param timeout Maximum time blocking functions are allowed during app load
|
||||
*/
|
||||
SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||
: app_t(argc, argv), d_ptr(new SingleApplicationPrivate(this)) {
|
||||
: app_t(argc, argv),
|
||||
d_ptr(new SingleApplicationPrivate(this)) {
|
||||
|
||||
Q_D(SingleApplication);
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
d->options_ = options;
|
||||
|
||||
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||
d->randomSleep();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||
d->memory_->attach();
|
||||
delete d->memory_;
|
||||
#endif
|
||||
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||
|
||||
// Create a shared memory block
|
||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
||||
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleApplication: Unable to lock memory block after create.";
|
||||
abortSafely();
|
||||
}
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
}
|
||||
else {
|
||||
// Attempt to attach to the memory segment
|
||||
if (! d->memory->attach()) {
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||
// Attempt to attach to the memory segment
|
||||
if (!d->memory_->attach()) {
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
abortSafely();
|
||||
}
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
else {
|
||||
qCritical() << "SingleApplication: Unable to create block.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while (true) {
|
||||
d->memory->lock();
|
||||
|
||||
forever {
|
||||
// If the shared memory block's checksum is valid continue
|
||||
if (d->blockChecksum() == inst->checksum) break;
|
||||
|
||||
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||
if (time.elapsed() > 5000) {
|
||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
QThread::sleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||
#else
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::sleep(8 + static_cast<unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
||||
#endif
|
||||
// Otherwise wait for a random period and try again.
|
||||
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
d->randomSleep();
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if (allowSecondary) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if (d->options & Mode::SecondaryNotification) {
|
||||
if (d->options_ & Mode::SecondaryNotification) {
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
|
||||
}
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
|
||||
|
||||
@@ -151,34 +169,73 @@ SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondar
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
SingleApplication::~SingleApplication() {
|
||||
Q_D(SingleApplication);
|
||||
delete d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is primary.
|
||||
* @return Returns true if the instance is primary, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::isPrimary() {
|
||||
Q_D(SingleApplication);
|
||||
return d->server != nullptr;
|
||||
return d->server_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is secondary.
|
||||
* @return Returns true if the instance is secondary, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::isSecondary() {
|
||||
Q_D(SingleApplication);
|
||||
return d->server == nullptr;
|
||||
return d->server_ == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||
* @return Returns a unique instance id.
|
||||
*/
|
||||
quint32 SingleApplication::instanceId() {
|
||||
Q_D(SingleApplication);
|
||||
return d->instanceNumber;
|
||||
return d->instanceNumber_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||
* Especially useful when SingleApplication is coupled with OS. specific APIs.
|
||||
* @return Returns the primary instance PID.
|
||||
*/
|
||||
qint64 SingleApplication::primaryPid() {
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the primary instance is running as.
|
||||
* @return Returns the username the primary instance is running as.
|
||||
*/
|
||||
QString SingleApplication::primaryUser() {
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the current instance is running as.
|
||||
* @return Returns the username the current instance is running as.
|
||||
*/
|
||||
QString SingleApplication::currentUser() {
|
||||
Q_D(SingleApplication);
|
||||
return d->getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||
|
||||
Q_D(SingleApplication);
|
||||
@@ -187,11 +244,26 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||
if (isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect);
|
||||
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect))
|
||||
return false;
|
||||
|
||||
d->socket->write(message);
|
||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
||||
d->socket->flush();
|
||||
d->socket_->write(message);
|
||||
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||
d->socket_->flush();
|
||||
return dataWritten;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the shared memory block and exits with a failure.
|
||||
* This function halts program execution.
|
||||
*/
|
||||
void SingleApplication::abortSafely() {
|
||||
|
||||
Q_D(SingleApplication);
|
||||
|
||||
qCritical() << "SingleApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
|
||||
}
|
||||
|
||||
41
3rdparty/singleapplication/singleapplication.h
vendored
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,16 +42,15 @@
|
||||
class SingleApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleApplication class handles multipe instances of the same
|
||||
* Application
|
||||
* @see QCoreApplication
|
||||
* @brief The SingleApplication class handles multipe instances of the same Application
|
||||
* @see QApplication
|
||||
*/
|
||||
class SingleApplication : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
typedef QApplication app_t;
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
@@ -63,11 +62,11 @@ public:
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
@@ -91,7 +90,7 @@ public:
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
explicit SingleApplication(int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000);
|
||||
~SingleApplication() override;
|
||||
|
||||
/**
|
||||
@@ -118,6 +117,18 @@ public:
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the user running the primary instance
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString primaryUser();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the current user
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString currentUser();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
@@ -125,18 +136,18 @@ public:
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
||||
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||
|
||||
signals:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||
|
||||
private:
|
||||
SingleApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleApplication)
|
||||
|
||||
void abortSafely();
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||
|
||||
#endif // SINGLEAPPLICATION_H
|
||||
#endif // SINGLEAPPLICATION_H
|
||||
|
||||
355
3rdparty/singleapplication/singleapplication_p.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -44,6 +44,8 @@
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QIODevice>
|
||||
#include <QSharedMemory>
|
||||
#include <QByteArray>
|
||||
@@ -52,6 +54,12 @@
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#else
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
@@ -61,33 +69,73 @@
|
||||
# include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *_q_ptr)
|
||||
: q_ptr(_q_ptr),
|
||||
memory(nullptr),
|
||||
socket(nullptr),
|
||||
server(nullptr),
|
||||
instanceNumber(-1)
|
||||
{}
|
||||
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *ptr)
|
||||
: q_ptr(ptr),
|
||||
memory_(nullptr),
|
||||
socket_(nullptr),
|
||||
server_(nullptr),
|
||||
instanceNumber_(-1) {}
|
||||
|
||||
SingleApplicationPrivate::~SingleApplicationPrivate() {
|
||||
|
||||
if (socket != nullptr) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
if (socket_ != nullptr) {
|
||||
socket_->close();
|
||||
delete socket_;
|
||||
socket_ = nullptr;
|
||||
}
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if (server != nullptr) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
if (memory_ != nullptr) {
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
if (server_ != nullptr) {
|
||||
server_->close();
|
||||
delete server_;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory_->unlock();
|
||||
|
||||
delete memory;
|
||||
delete memory_;
|
||||
memory_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::getUsername() {
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
QString username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = QString::fromLocal8Bit(pw->pw_name);
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
username = qEnvironmentVariable("USER");
|
||||
#else
|
||||
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||
#endif
|
||||
}
|
||||
return username;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username[UNLEN + 1];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
return QString::fromWCharArray(username);
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
return qEnvironmentVariable("USERNAME");
|
||||
#else
|
||||
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -99,11 +147,11 @@ void SingleApplicationPrivate::genBlockServerName() {
|
||||
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
|
||||
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
|
||||
|
||||
if (!(options & SingleApplication::Mode::ExcludeAppVersion)) {
|
||||
if (!(options_ & SingleApplication::Mode::ExcludeAppVersion)) {
|
||||
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
|
||||
}
|
||||
|
||||
if (! (options & SingleApplication::Mode::ExcludeAppPath)) {
|
||||
if (!(options_ & SingleApplication::Mode::ExcludeAppPath)) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||
#else
|
||||
@@ -112,42 +160,22 @@ void SingleApplicationPrivate::genBlockServerName() {
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if (options & SingleApplication::Mode::User) {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
appData.addData(username);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
||||
}
|
||||
else {
|
||||
appData.addData(qgetenv("USERNAME"));
|
||||
}
|
||||
#endif
|
||||
if (options_ & SingleApplication::Mode::User) {
|
||||
appData.addData(getUsername().toUtf8());
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock() {
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
}
|
||||
@@ -156,133 +184,161 @@ void SingleApplicationPrivate::startPrimary() {
|
||||
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName);
|
||||
server = new QLocalServer();
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if (options & SingleApplication::Mode::User) {
|
||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
server->listen(blockServerName);
|
||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber_ = 0;
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName_);
|
||||
server_ = new QLocalServer();
|
||||
|
||||
instanceNumber = 0;
|
||||
// Restrict access to the socket according to the SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if (options_ & SingleApplication::Mode::User) {
|
||||
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
server_->listen(blockServerName_);
|
||||
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startSecondary() {}
|
||||
void SingleApplicationPrivate::startSecondary() {
|
||||
|
||||
void SingleApplicationPrivate::connectToPrimary(const int msecs, const ConnectionType connectionType) {
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
|
||||
inst->secondary += 1;
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber_ = inst->secondary;
|
||||
|
||||
}
|
||||
|
||||
bool SingleApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
|
||||
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||
if (socket == nullptr) {
|
||||
socket = new QLocalSocket();
|
||||
if (socket_ == nullptr) {
|
||||
socket_ = new QLocalSocket();
|
||||
}
|
||||
|
||||
// If already connected - we are done;
|
||||
if (socket->state() == QLocalSocket::ConnectedState)
|
||||
return;
|
||||
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||
|
||||
// If not connect
|
||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState) {
|
||||
socket->connectToServer(blockServerName);
|
||||
}
|
||||
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||
|
||||
// Wait for being connected
|
||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
||||
socket->waitForConnected(msecs);
|
||||
forever {
|
||||
randomSleep();
|
||||
|
||||
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||
socket_->connectToServer(blockServerName_);
|
||||
|
||||
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||
socket_->waitForConnected(timeout - time.elapsed());
|
||||
}
|
||||
|
||||
// If connected break out of the loop
|
||||
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||
|
||||
// If elapsed time since start is longer than the method timeout return
|
||||
if (time.elapsed() >= timeout) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleApplication protocol
|
||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
writeStream << blockServerName_.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber_;
|
||||
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||
#else
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
#endif
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
writeStream << checksum;
|
||||
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||
headerStream << static_cast<quint64>(initMsg.length());
|
||||
|
||||
headerStream << static_cast <quint64>(initMsg.length());
|
||||
socket_->write(header);
|
||||
socket_->write(initMsg);
|
||||
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
|
||||
socket_->flush();
|
||||
|
||||
socket->write(header);
|
||||
socket->write(initMsg);
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten(msecs);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
quint16 SingleApplicationPrivate::blockChecksum() {
|
||||
|
||||
return qChecksum(static_cast <const char *>(memory->data()), offsetof(InstancesInfo, checksum));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||
#else
|
||||
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||
#endif
|
||||
|
||||
return checksum;
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleApplicationPrivate::primaryPid() {
|
||||
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
qint64 pid = inst->primaryPid;
|
||||
memory_->unlock();
|
||||
|
||||
return pid;
|
||||
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::primaryUser() {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
QByteArray username = inst->primaryUser;
|
||||
memory_->unlock();
|
||||
|
||||
return QString::fromUtf8(username);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
|
||||
connectionMap_.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
switch (info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
@@ -294,15 +350,14 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
|
||||
if (!connectionMap.contains(sock)) {
|
||||
if (!connectionMap_.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,13 +366,12 @@ void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
}
|
||||
|
||||
QDataStream headerStream(sock);
|
||||
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
ConnectionInfo &info = connectionMap_[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
|
||||
@@ -331,11 +385,11 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
if (!connectionMap.contains(sock)) {
|
||||
if (!connectionMap_.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
ConnectionInfo &info = connectionMap_[sock];
|
||||
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||
return;
|
||||
}
|
||||
@@ -343,8 +397,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
|
||||
readStream.setVersion(QDataStream::Qt_5_6);
|
||||
readStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
@@ -354,7 +407,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
||||
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
@@ -364,9 +417,13 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||
#else
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
#endif
|
||||
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||
|
||||
if (!isValid) {
|
||||
sock->close();
|
||||
@@ -376,7 +433,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification)) {
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleApplication::Mode::SecondaryNotification)) {
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
@@ -395,7 +452,19 @@ void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const
|
||||
|
||||
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||
|
||||
if (closedSocket->bytesAvailable() > 0)
|
||||
if (closedSocket->bytesAvailable() > 0) {
|
||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::randomSleep() {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
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));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
26
3rdparty/singleapplication/singleapplication_p.h
vendored
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2016
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include "singleapplication.h"
|
||||
@@ -48,6 +49,7 @@ struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
char primaryUser[128];
|
||||
quint16 checksum;
|
||||
};
|
||||
|
||||
@@ -60,6 +62,7 @@ struct ConnectionInfo {
|
||||
|
||||
class SingleApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
@@ -74,27 +77,30 @@ class SingleApplicationPrivate : public QObject {
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
|
||||
explicit SingleApplicationPrivate(SingleApplication *_q_ptr);
|
||||
explicit SingleApplicationPrivate(SingleApplication *ptr);
|
||||
~SingleApplicationPrivate() override;
|
||||
|
||||
QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
QString primaryUser();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void randomSleep();
|
||||
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
QSharedMemory *memory_;
|
||||
QLocalSocket *socket_;
|
||||
QLocalServer *server_;
|
||||
quint32 instanceNumber_;
|
||||
QString blockServerName_;
|
||||
SingleApplication::Options options_;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
|
||||
188
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,106 +42,124 @@
|
||||
#include <QByteArray>
|
||||
#include <QElapsedTimer>
|
||||
#include <QtDebug>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#else
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
#include "singlecoreapplication_p.h"
|
||||
|
||||
/**
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
||||
* if another instance already exists
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param {bool} allowSecondaryInstances
|
||||
* @param allowSecondary Whether to enable secondary instance support
|
||||
* @param options Optional flags to toggle specific behaviour
|
||||
* @param timeout Maximum time blocking functions are allowed during app load
|
||||
*/
|
||||
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||
: app_t(argc, argv), d_ptr(new SingleCoreApplicationPrivate(this)) {
|
||||
: app_t(argc, argv),
|
||||
d_ptr(new SingleCoreApplicationPrivate(this)) {
|
||||
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
d->options_ = options;
|
||||
|
||||
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||
d->randomSleep();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||
d->memory_->attach();
|
||||
delete d->memory_;
|
||||
#endif
|
||||
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||
|
||||
// Create a shared memory block
|
||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
||||
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to lock memory block after create.";
|
||||
abortSafely();
|
||||
}
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
}
|
||||
else {
|
||||
// Attempt to attach to the memory segment
|
||||
if (!d->memory->attach()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||
// Attempt to attach to the memory segment
|
||||
if (!d->memory_->attach()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||
abortSafely();
|
||||
}
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to lock memory block after attach.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
else {
|
||||
qCritical() << "SingleCoreApplication: Unable to create block.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while (true) {
|
||||
d->memory->lock();
|
||||
|
||||
if(d->blockChecksum() == inst->checksum) break;
|
||||
forever {
|
||||
// If the shared memory block's checksum is valid continue
|
||||
if (d->blockChecksum() == inst->checksum) break;
|
||||
|
||||
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||
if (time.elapsed() > 5000) {
|
||||
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
QThread::sleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||
#else
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::sleep(8 + static_cast<unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
||||
#endif
|
||||
// Otherwise wait for a random period and try again.
|
||||
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleCoreApplication: Unable to unlock memory for random wait.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
d->randomSleep();
|
||||
if (!d->memory_->lock()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to lock memory after random wait.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if (allowSecondary) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if(d->options & Mode::SecondaryNotification) {
|
||||
if (d->options_ & Mode::SecondaryNotification) {
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
|
||||
}
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleCoreApplication: Unable to unlock memory after secondary start.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
if (!d->memory_->unlock()) {
|
||||
qDebug() << "SingleCoreApplication: Unable to unlock memory at end of execution.";
|
||||
qDebug() << d->memory_->errorString();
|
||||
}
|
||||
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
|
||||
|
||||
@@ -151,47 +169,101 @@ SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allow
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
SingleCoreApplication::~SingleCoreApplication() {
|
||||
Q_D(SingleCoreApplication);
|
||||
delete d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is primary.
|
||||
* @return Returns true if the instance is primary, false otherwise.
|
||||
*/
|
||||
bool SingleCoreApplication::isPrimary() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server != nullptr;
|
||||
return d->server_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is secondary.
|
||||
* @return Returns true if the instance is secondary, false otherwise.
|
||||
*/
|
||||
bool SingleCoreApplication::isSecondary() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server == nullptr;
|
||||
return d->server_ == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||
* @return Returns a unique instance id.
|
||||
*/
|
||||
quint32 SingleCoreApplication::instanceId() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->instanceNumber;
|
||||
return d->instanceNumber_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||
* Especially useful when SingleCoreApplication is coupled with OS. specific APIs.
|
||||
* @return Returns the primary instance PID.
|
||||
*/
|
||||
qint64 SingleCoreApplication::primaryPid() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the primary instance is running as.
|
||||
* @return Returns the username the primary instance is running as.
|
||||
*/
|
||||
QString SingleCoreApplication::primaryUser() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->primaryUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the current instance is running as.
|
||||
* @return Returns the username the current instance is running as.
|
||||
*/
|
||||
QString SingleCoreApplication::currentUser() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
// Nobody to connect to
|
||||
if(isPrimary()) return false;
|
||||
if (isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect);
|
||||
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect))
|
||||
return false;
|
||||
|
||||
d->socket->write(message);
|
||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
||||
d->socket->flush();
|
||||
d->socket_->write(message);
|
||||
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||
d->socket_->flush();
|
||||
return dataWritten;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the shared memory block and exits with a failure.
|
||||
* This function halts program execution.
|
||||
*/
|
||||
void SingleCoreApplication::abortSafely() {
|
||||
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
qCritical() << "SingleCoreApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,8 +42,7 @@
|
||||
class SingleCoreApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleCoreApplication class handles multiple instances of the same
|
||||
* Application
|
||||
* @brief The SingleCoreApplication class handles multipe instances of the same Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleCoreApplication : public QCoreApplication {
|
||||
@@ -51,7 +50,7 @@ class SingleCoreApplication : public QCoreApplication {
|
||||
|
||||
typedef QCoreApplication app_t;
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleCoreApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
@@ -63,11 +62,11 @@ public:
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
@@ -89,9 +88,8 @@ public:
|
||||
* operations. It does not guarantee that the SingleCoreApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
explicit SingleCoreApplication(int &argc, char *argv[], const bool allowSecondary = false, const Options options = Mode::User, const int timeout = 1000);
|
||||
~SingleCoreApplication() override;
|
||||
|
||||
/**
|
||||
@@ -118,6 +116,18 @@ public:
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the user running the primary instance
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString primaryUser();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the current user
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString currentUser();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
@@ -125,17 +135,18 @@ public:
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
||||
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||
|
||||
signals:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||
|
||||
private:
|
||||
private:
|
||||
SingleCoreApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
||||
void abortSafely();
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
||||
|
||||
#endif // SINGLECOREAPPLICATION_H
|
||||
#endif // SINGLECOREAPPLICATION_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -44,6 +44,8 @@
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QIODevice>
|
||||
#include <QSharedMemory>
|
||||
#include <QByteArray>
|
||||
@@ -52,6 +54,12 @@
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#else
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
#include "singlecoreapplication_p.h"
|
||||
@@ -61,33 +69,73 @@
|
||||
# include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *_q_ptr)
|
||||
: q_ptr(_q_ptr),
|
||||
memory(nullptr),
|
||||
socket(nullptr),
|
||||
server(nullptr),
|
||||
instanceNumber(-1)
|
||||
{}
|
||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *ptr)
|
||||
: q_ptr(ptr),
|
||||
memory_(nullptr),
|
||||
socket_(nullptr),
|
||||
server_(nullptr),
|
||||
instanceNumber_(-1) {}
|
||||
|
||||
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
|
||||
|
||||
if (socket != nullptr) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
if (socket_ != nullptr) {
|
||||
socket_->close();
|
||||
delete socket_;
|
||||
socket_ = nullptr;
|
||||
}
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if (server != nullptr) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
if (memory_ != nullptr) {
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
if (server_ != nullptr) {
|
||||
server_->close();
|
||||
delete server_;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory_->unlock();
|
||||
|
||||
delete memory;
|
||||
delete memory_;
|
||||
memory_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString SingleCoreApplicationPrivate::getUsername() {
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
QString username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = QString::fromLocal8Bit(pw->pw_name);
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
username = qEnvironmentVariable("USER");
|
||||
#else
|
||||
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||
#endif
|
||||
}
|
||||
return username;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username[UNLEN + 1];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
return QString::fromWCharArray(username);
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
return qEnvironmentVariable("USERNAME");
|
||||
#else
|
||||
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -99,11 +147,11 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
|
||||
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
|
||||
|
||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
||||
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
||||
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
|
||||
}
|
||||
|
||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
||||
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||
#else
|
||||
@@ -112,42 +160,22 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if (options & SingleCoreApplication::Mode::User) {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
appData.addData(username);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
||||
}
|
||||
else {
|
||||
appData.addData(qgetenv("USERNAME"));
|
||||
}
|
||||
#endif
|
||||
if (options_ & SingleCoreApplication::Mode::User) {
|
||||
appData.addData(getUsername().toUtf8());
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
}
|
||||
@@ -156,133 +184,161 @@ void SingleCoreApplicationPrivate::startPrimary() {
|
||||
|
||||
Q_Q(SingleCoreApplication);
|
||||
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName);
|
||||
server = new QLocalServer();
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||
if (options & SingleCoreApplication::Mode::User) {
|
||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
server->listen(blockServerName);
|
||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber_ = 0;
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName_);
|
||||
server_ = new QLocalServer();
|
||||
|
||||
instanceNumber = 0;
|
||||
// Restrict access to the socket according to the SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||
if (options_ & SingleCoreApplication::Mode::User) {
|
||||
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
server_->listen(blockServerName_);
|
||||
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::startSecondary() {}
|
||||
void SingleCoreApplicationPrivate::startSecondary() {
|
||||
|
||||
void SingleCoreApplicationPrivate::connectToPrimary(const int msecs, const ConnectionType connectionType) {
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
|
||||
inst->secondary += 1;
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber_ = inst->secondary;
|
||||
|
||||
}
|
||||
|
||||
bool SingleCoreApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
|
||||
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||
if (socket == nullptr) {
|
||||
socket = new QLocalSocket();
|
||||
if (socket_ == nullptr) {
|
||||
socket_ = new QLocalSocket();
|
||||
}
|
||||
|
||||
// If already connected - we are done;
|
||||
if (socket->state() == QLocalSocket::ConnectedState)
|
||||
return;
|
||||
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||
|
||||
// If not connect
|
||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState) {
|
||||
socket->connectToServer(blockServerName);
|
||||
}
|
||||
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||
|
||||
// Wait for being connected
|
||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
||||
socket->waitForConnected(msecs);
|
||||
forever {
|
||||
randomSleep();
|
||||
|
||||
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||
socket_->connectToServer(blockServerName_);
|
||||
|
||||
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||
socket_->waitForConnected(timeout - time.elapsed());
|
||||
}
|
||||
|
||||
// If connected break out of the loop
|
||||
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||
|
||||
// If elapsed time since start is longer than the method timeout return
|
||||
if (time.elapsed() >= timeout) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleCoreApplication protocol
|
||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
writeStream << blockServerName_.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber_;
|
||||
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||
#else
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
#endif
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
writeStream << checksum;
|
||||
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||
headerStream << static_cast<quint64>(initMsg.length());
|
||||
|
||||
headerStream << static_cast <quint64>(initMsg.length());
|
||||
socket_->write(header);
|
||||
socket_->write(initMsg);
|
||||
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
|
||||
socket_->flush();
|
||||
|
||||
socket->write(header);
|
||||
socket->write(initMsg);
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten(msecs);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||
|
||||
return qChecksum(static_cast <const char*> (memory->data()), offsetof(InstancesInfo, checksum));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||
#else
|
||||
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||
#endif
|
||||
|
||||
return checksum;
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
qint64 pid = inst->primaryPid;
|
||||
memory_->unlock();
|
||||
|
||||
return pid;
|
||||
|
||||
}
|
||||
|
||||
QString SingleCoreApplicationPrivate::primaryUser() {
|
||||
|
||||
memory_->lock();
|
||||
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||
QByteArray username = inst->primaryUser;
|
||||
memory_->unlock();
|
||||
|
||||
return QString::fromUtf8(username);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
|
||||
connectionMap_.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
});
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
|
||||
auto &info = connectionMap_[nextConnSocket];
|
||||
switch (info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
@@ -294,15 +350,14 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
|
||||
if (!connectionMap.contains(sock)) {
|
||||
if (!connectionMap_.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,13 +366,12 @@ void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
}
|
||||
|
||||
QDataStream headerStream(sock);
|
||||
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
ConnectionInfo &info = connectionMap_[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
|
||||
@@ -331,11 +385,11 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
|
||||
Q_Q(SingleCoreApplication);
|
||||
|
||||
if (!connectionMap.contains(sock)) {
|
||||
if (!connectionMap_.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
ConnectionInfo &info = connectionMap_[sock];
|
||||
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||
return;
|
||||
}
|
||||
@@ -343,8 +397,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
|
||||
readStream.setVersion(QDataStream::Qt_5_6);
|
||||
readStream.setVersion(QDataStream::Qt_5_8);
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
@@ -354,7 +407,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
||||
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
@@ -364,9 +417,13 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||
#else
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
#endif
|
||||
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||
|
||||
if (!isValid) {
|
||||
sock->close();
|
||||
@@ -376,7 +433,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleCoreApplication::Mode::SecondaryNotification)) {
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleCoreApplication::Mode::SecondaryNotification)) {
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
@@ -395,7 +452,19 @@ void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, c
|
||||
|
||||
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||
|
||||
if (closedSocket->bytesAvailable() > 0)
|
||||
if (closedSocket->bytesAvailable() > 0) {
|
||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::randomSleep() {
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
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));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2016
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
@@ -48,6 +49,7 @@ struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
char primaryUser[128];
|
||||
quint16 checksum;
|
||||
};
|
||||
|
||||
@@ -60,6 +62,7 @@ struct ConnectionInfo {
|
||||
|
||||
class SingleCoreApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
@@ -74,27 +77,30 @@ class SingleCoreApplicationPrivate : public QObject {
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
||||
|
||||
explicit SingleCoreApplicationPrivate(SingleCoreApplication *_q_ptr);
|
||||
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
|
||||
~SingleCoreApplicationPrivate() override;
|
||||
|
||||
QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
QString primaryUser();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void randomSleep();
|
||||
|
||||
SingleCoreApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleCoreApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
QSharedMemory *memory_;
|
||||
QLocalSocket *socket_;
|
||||
QLocalServer *server_;
|
||||
quint32 instanceNumber_;
|
||||
QString blockServerName_;
|
||||
SingleCoreApplication::Options options_;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
|
||||
14
3rdparty/taglib/ConfigureChecks.cmake
vendored
@@ -6,32 +6,32 @@ include(CheckCXXSourceCompiles)
|
||||
# Check if the size of numeric types are suitable.
|
||||
|
||||
check_type_size("short" SIZEOF_SHORT)
|
||||
if(NOT ${SIZEOF_SHORT} EQUAL 2)
|
||||
if(NOT SIZEOF_SHORT EQUAL 2)
|
||||
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("int" SIZEOF_INT)
|
||||
if(NOT ${SIZEOF_INT} EQUAL 4)
|
||||
if(NOT SIZEOF_INT EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("long long" SIZEOF_LONGLONG)
|
||||
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
||||
if(NOT SIZEOF_LONGLONG EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||
if(${SIZEOF_WCHAR_T} LESS 2)
|
||||
if(SIZEOF_WCHAR_T LESS 2)
|
||||
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||
endif()
|
||||
|
||||
check_type_size("float" SIZEOF_FLOAT)
|
||||
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
|
||||
if(NOT SIZEOF_FLOAT EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("double" SIZEOF_DOUBLE)
|
||||
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
if(NOT SIZEOF_DOUBLE EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
@@ -212,5 +212,5 @@ endif()
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM WINRT 1)
|
||||
set(PLATFORM WINRT 1)
|
||||
endif()
|
||||
|
||||
5
3rdparty/taglib/toolkit/tutils.h
vendored
@@ -160,6 +160,9 @@ inline String formatString(const char *format, ...) {
|
||||
char buf[BufferSize];
|
||||
int length;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
|
||||
# if defined(HAVE_VSNPRINTF)
|
||||
|
||||
length = vsnprintf(buf, BufferSize, format, args);
|
||||
@@ -180,6 +183,8 @@ inline String formatString(const char *format, ...) {
|
||||
|
||||
# endif
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (length > 0)
|
||||
|
||||
198
CMakeLists.txt
@@ -13,7 +13,9 @@ include(cmake/OptionalSource.cmake)
|
||||
include(cmake/ParseArguments.cmake)
|
||||
include(cmake/Rpm.cmake)
|
||||
include(cmake/Deb.cmake)
|
||||
include(cmake/Dmg.cmake)
|
||||
if(APPLE)
|
||||
include(cmake/Dmg.cmake)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
@@ -50,16 +52,11 @@ list(APPEND COMPILE_OPTIONS
|
||||
-Wunused-parameter
|
||||
-Wformat=2
|
||||
-Wdisabled-optimization
|
||||
-Wno-sign-conversion
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND COMPILE_OPTIONS -Wno-unused-parameter)
|
||||
endif()
|
||||
|
||||
option(BUILD_WERROR "Build with -Werror" OFF)
|
||||
if(BUILD_WERROR)
|
||||
list(APPEND COMPILE_OPTIONS -Werror)
|
||||
@@ -77,6 +74,11 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
||||
set(DEBUG ON)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_EXECUTABLE NAMES ccache)
|
||||
if (CCACHE_EXECUTABLE)
|
||||
message(STATUS "ccache found: will be used for compilation and linkage")
|
||||
@@ -91,9 +93,12 @@ find_package(Backtrace QUIET)
|
||||
if(Backtrace_FOUND)
|
||||
set(HAVE_BACKTRACE ON)
|
||||
endif()
|
||||
find_package(Iconv QUIET)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
|
||||
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)
|
||||
@@ -118,23 +123,28 @@ pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
|
||||
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
|
||||
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
|
||||
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
|
||||
pkg_check_modules(LIBXINE libxine)
|
||||
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(LIBGPOD libgpod-1.0>=0.7.92)
|
||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
|
||||
pkg_search_module(LIBUSBMUXD libusbmuxd-2.0 libusbmuxd)
|
||||
pkg_search_module(LIBPLIST libplist-2.0 libplist)
|
||||
pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
|
||||
find_package(Gettext)
|
||||
find_package(FFTW3)
|
||||
|
||||
# QT
|
||||
set(QT_MIN_VERSION 5.6)
|
||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
|
||||
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
||||
|
||||
if(WITH_QT6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_WITH_QT5 AND NOT BUILD_WITH_QT6)
|
||||
set(BUILD_WITH_QT5 ON)
|
||||
endif()
|
||||
|
||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||
if(X11_FOUND)
|
||||
list(APPEND QT_COMPONENTS X11Extras)
|
||||
endif()
|
||||
@@ -148,32 +158,73 @@ if(WIN32)
|
||||
list(APPEND QT_COMPONENTS WinExtras)
|
||||
endif()
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
|
||||
set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Sql_LIBRARIES})
|
||||
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
||||
|
||||
if(Qt5DBus_FOUND)
|
||||
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt5X11Extras_FOUND)
|
||||
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Qt5MacExtras_FOUND)
|
||||
list(APPEND QT_LIBRARIES ${Qt5MacExtras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5MacExtras_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Qt5WinExtras_FOUND)
|
||||
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if (Qt5LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
||||
if(BUILD_WITH_QT6)
|
||||
list(APPEND QT_COMPONENTS Core5Compat)
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
set(QtCore_LIBRARIES Qt6::Core)
|
||||
set(QtConcurrent_LIBRARIES Qt6::Concurrent)
|
||||
set(QtWidgets_LIBRARIES Qt6::Widgets)
|
||||
set(QtNetwork_LIBRARIES Qt6::Network)
|
||||
set(QtSql_LIBRARIES Qt6::Sql)
|
||||
set(QT_LIBRARIES Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Core5Compat)
|
||||
if(Qt6DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES Qt6::DBus)
|
||||
list(APPEND QT_LIBRARIES Qt6::DBus)
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt6::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt6X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES Qt6::X11Extras)
|
||||
list(APPEND QT_LIBRARIES Qt6::X11Extras)
|
||||
endif()
|
||||
if(Qt6MacExtras_FOUND)
|
||||
set(QtMacExtras_LIBRARIES Qt6::MacExtras)
|
||||
list(APPEND QT_LIBRARIES Qt6::MacExtras)
|
||||
endif()
|
||||
if(Qt6WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES Qt6::WinExtras)
|
||||
list(APPEND QT_LIBRARIES Qt6::WinExtras)
|
||||
endif()
|
||||
find_package(Qt6 QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if (Qt6LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt6::lconvert)
|
||||
endif()
|
||||
elseif(BUILD_WITH_QT5)
|
||||
set(QT_MIN_VERSION 5.8)
|
||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
set(QtCore_LIBRARIES ${Qt5Core_LIBRARIES})
|
||||
set(QtConcurrent_LIBRARIES ${Qt5Concurrent_LIBRARIES})
|
||||
set(QtWidgets_LIBRARIES ${Qt5Widgets_LIBRARIES})
|
||||
set(QtNetwork_LIBRARIES ${Qt5Network_LIBRARIES})
|
||||
set(QtSql_LIBRARIES ${Qt5Sql_LIBRARIES})
|
||||
set(QT_LIBRARIES ${QtCore_LIBRARIES} ${QtConcurrent_LIBRARIES} ${QtWidgets_LIBRARIES} ${QtNetwork_LIBRARIES} ${QtSql_LIBRARIES})
|
||||
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
||||
if(Qt5DBus_FOUND)
|
||||
set(QtDBus_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
|
||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
|
||||
endif()
|
||||
if(Qt5X11Extras_FOUND)
|
||||
set(QtX11Extras_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Qt5MacExtras_FOUND)
|
||||
set(QtMacExtras_LIBRARIES ${Qt5MacExtras_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5MacExtras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5MacExtras_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Qt5WinExtras_FOUND)
|
||||
set(QtWinExtras_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
||||
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
||||
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
|
||||
endif()
|
||||
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||
if (Qt5LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Set BUILD_WITH_QT5 or BUILD_WITH_QT6")
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
@@ -221,9 +272,20 @@ set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/singleap
|
||||
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
|
||||
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
|
||||
|
||||
if (APPLE)
|
||||
if(APPLE)
|
||||
find_library(SPARKLE Sparkle)
|
||||
endif (APPLE)
|
||||
endif(APPLE)
|
||||
|
||||
if(NOT SPARKLE AND (APPLE OR WIN32))
|
||||
if(BUILD_WITH_QT6)
|
||||
pkg_check_modules(QTSPARKLE qtsparkle-qt6)
|
||||
else()
|
||||
pkg_check_modules(QTSPARKLE qtsparkle-qt5)
|
||||
endif()
|
||||
if(QTSPARKLE_FOUND)
|
||||
set(HAVE_QTSPARKLE ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# RC compiler
|
||||
@@ -258,10 +320,6 @@ optional_component(GSTREAMER ON "Engine: GStreamer backend"
|
||||
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
|
||||
)
|
||||
|
||||
optional_component(XINE ON "Engine: Xine backend"
|
||||
DEPENDS "libxine" LIBXINE_FOUND
|
||||
)
|
||||
|
||||
optional_component(VLC ON "Engine: VLC backend"
|
||||
DEPENDS "libvlc" LIBVLC_FOUND
|
||||
)
|
||||
@@ -293,31 +351,35 @@ optional_component(GIO ON "Devices: GIO device backend"
|
||||
|
||||
optional_component(LIBGPOD ON "Devices: iPod classic support"
|
||||
DEPENDS "libgpod" LIBGPOD_FOUND
|
||||
DEPENDS "gdk-pixbuf" GDK_PIXBUF_FOUND
|
||||
)
|
||||
|
||||
optional_component(LIBMTP ON "Devices: MTP support"
|
||||
DEPENDS "libmtp" LIBMTP_FOUND
|
||||
)
|
||||
|
||||
optional_component(IMOBILEDEVICE ON "Devices: iPhone, iPod Touch, iPad and Apple TV support"
|
||||
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
|
||||
DEPENDS "libplist" LIBPLIST_FOUND
|
||||
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
|
||||
DEPENDS "libgpod" HAVE_LIBGPOD
|
||||
)
|
||||
|
||||
optional_component(SPARKLE ON "Sparkle integration"
|
||||
DEPENDS "macOS" APPLE
|
||||
DEPENDS "Sparkle" SPARKLE
|
||||
)
|
||||
|
||||
optional_component(TRANSLATIONS ON "Translations"
|
||||
DEPENDS "gettext" GETTEXT_FOUND
|
||||
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
||||
)
|
||||
if(BUILD_WITH_QT6)
|
||||
optional_component(TRANSLATIONS ON "Translations"
|
||||
DEPENDS "gettext" GETTEXT_FOUND
|
||||
DEPENDS "Qt6LinguistTools" Qt6LinguistTools_FOUND
|
||||
)
|
||||
else()
|
||||
optional_component(TRANSLATIONS ON "Translations"
|
||||
DEPENDS "gettext" GETTEXT_FOUND
|
||||
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
||||
)
|
||||
endif()
|
||||
|
||||
option(INSTALL_TRANSLATIONS "Install translations" OFF)
|
||||
|
||||
optional_component(SUBSONIC ON "Subsonic support")
|
||||
optional_component(TIDAL ON "Tidal support")
|
||||
optional_component(QOBUZ ON "Qobuz support")
|
||||
|
||||
optional_component(MOODBAR ON "Moodbar"
|
||||
DEPENDS "fftw3" FFTW3_FOUND
|
||||
@@ -343,8 +405,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_REQUIRED_FLAGS "--std=c++11")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Sql_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_INCLUDES ${QtCore_INCLUDE_DIRS} ${QtSql_INCLUDE_DIRS})
|
||||
check_cxx_source_runs("
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
@@ -361,20 +423,6 @@ if(NOT CMAKE_CROSSCOMPILING)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_XINE)
|
||||
check_cxx_source_compiles("
|
||||
#define METRONOM_INTERNAL
|
||||
#include <iostream>
|
||||
#include <xine/metronom.h>
|
||||
int main() {
|
||||
metronom_t metronom;
|
||||
std::cout << metronom.pts_per_smpls;
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
XINE_ANALYZER)
|
||||
endif()
|
||||
|
||||
# Set up definitions
|
||||
|
||||
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
|
||||
@@ -415,8 +463,8 @@ add_custom_target(uninstall
|
||||
|
||||
# Show a summary of what we have enabled
|
||||
summary_show()
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC)
|
||||
message(FATAL_ERROR "You need to have either GStreamer, Xine or VLC to compile!")
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_VLC)
|
||||
message(FATAL_ERROR "You need to have either GStreamer or VLC to compile!")
|
||||
elseif(NOT HAVE_GSTREAMER)
|
||||
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
||||
endif()
|
||||
|
||||
93
Changelog
@@ -2,6 +2,99 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
0.8.1:
|
||||
|
||||
Bugfixes:
|
||||
* Fixed engine selection in backend settings with Qt 6.
|
||||
* Fixed pixelated playlist source icon for currently playing song.
|
||||
* Fixed crash when deleting queued songs from playlist.
|
||||
* Fixed situations where songs could disappear or be shown multiple times with certain collection groupings.
|
||||
* Fixed initial sizes of playlist header columns.
|
||||
* Fixed Strawberry preventing logout.
|
||||
* Fixed incorrectly splitting of basename for moodbar and transcoding for filenames with several dots.
|
||||
* Fixed certain cases where "playing now" for scrobbler were sent twice.
|
||||
* Fixed album cover loaded twice for certain songs causing slugglish playing widget.
|
||||
* Fixed playing widget to draw text after album cover is fully shown.
|
||||
* Fixed crash when trying to copy a closed playlist to a device.
|
||||
* Fixed incorrect song source for CUE songs when added through the collection watcher.
|
||||
* Disable use of HTML in system tray tooltip on Cinnamon too.
|
||||
* Remove problematic '&' character from OSD messages.
|
||||
* (macOS) Fixed crash on exit when cover manager is open.
|
||||
* (macOS) Fixed graphical corruption.
|
||||
* (Windows) Fixed GStreamer registry problems.
|
||||
* (Windows) Register Tidal URL Scheme in Windows installer.
|
||||
|
||||
Enhancements:
|
||||
* Improved playlist autoscrolling.
|
||||
* Only allow playlist right click tag editing for editable songs.
|
||||
* Read song creation time from subsonic API.
|
||||
* Remember manually set compilation status for albums when songs are rescanned.
|
||||
* Added icons for edit tag playlist right click menu actions.
|
||||
* Maximize dialogs if they are already open when clicked again in the menu.
|
||||
* Added support for compilation tag to edit tag dialog.
|
||||
* Show song info and album cover in OSD on stop and pause.
|
||||
* Reshow OSD on song restart.
|
||||
* Always save initial settings.
|
||||
* Removed use of deprecated gstreamer "low-percent" (Minimum buffer fill setting).
|
||||
* Added buffer low and high watermark settings to backend settings.
|
||||
* Make use of newer version of the desktop notifications service when available.
|
||||
|
||||
New features:
|
||||
* Added setting for enabling scrobbling based on song source.
|
||||
* Added optional delete from disk in collection and playlist.
|
||||
* Added Last.fm import data wizard.
|
||||
* Added smart and dynamic playlists.
|
||||
* Added song ratings.
|
||||
* Added Qobuz streaming support.
|
||||
* Added Subsonic server side scrobbling support.
|
||||
* Load thumbnails from iPods to show under device collection.
|
||||
|
||||
0.7.2:
|
||||
|
||||
Bugfixes:
|
||||
* Fixed installation directory for translations.
|
||||
* Fixed collection sorting for non-ASCII characters.
|
||||
* Fixed closing connected devices on exit.
|
||||
|
||||
0.7.1:
|
||||
|
||||
Bugfixes:
|
||||
* Fixed incorrectly mapped global shortcuts keys "2" and "3".
|
||||
* Fixed Last.fm scrobbling to correctly start array notation for parameters at 0 and not 1.
|
||||
* Fixed sending trackNumber correctly for Last.fm and Libre.fm scrobbling.
|
||||
* Fixed collection search when using special characters in the search query.
|
||||
* Fixed reading and saving MP4 lyrics tag.
|
||||
* Fixed reading ASF comment tag.
|
||||
* Fixed adding playlist songs outside the collection when there are multiple files with the same URL.
|
||||
* Fixed the rescan songs option to work with local songs outside of the collection.
|
||||
* Fixed problems with editing song metadata in the playlists.
|
||||
* Fixed saving and restoring playlist scrollbar position when switching between playlists.
|
||||
* Fixed minor issue in cue parser with date and genre.
|
||||
* (macOS) Fixed gst-libav plugin issue resulting in MP3 not working.
|
||||
|
||||
Enhancements:
|
||||
* Simplified and improved startup behaviour code.
|
||||
* Adapted all source code to be compatible with Qt 6, and increased required Qt version to 5.8.
|
||||
* Added option to compile with Qt 6 (-DWITH_QT6=ON).
|
||||
* Base warning for show in file browser on unique directories to avoid unneeded warning about opening many files.
|
||||
* Use album artist instead of artist for album repeat mode when available.
|
||||
* Added extra safety for overwriting files for filesystem storages when organizing files.
|
||||
* Remove diacritics in FTS search.
|
||||
* Improved playlist context menu.
|
||||
* Added fatal CMake error for missing protobuf compiler.
|
||||
* Added support for parsing radio streams metadata with tilde in title.
|
||||
* Added CMake option to install translation files.
|
||||
* Increased maximum time step for seeking to 60.
|
||||
* (Unix) Added playback actions to desktop file.
|
||||
* (macOS) Hide behaviour settings that are unavailable on macOS.
|
||||
* (macOS) Fixed compile warnings.
|
||||
* (macOS) Added Sparkle integration to notify on new versions.
|
||||
* (Windows) Added QtSparkle support to notify on new versions.
|
||||
|
||||
Removed features:
|
||||
* Removed Xine engine support.
|
||||
* Removed broken imobiledevice (iPhone) support.
|
||||
|
||||
0.6.13:
|
||||
|
||||
Bugfixes:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
from jonaski/opensuse:lp151
|
||||
|
||||
run mkdir -p /usr/src/app
|
||||
workdir /usr/src/app
|
||||
copy . /usr/src/app
|
||||
26
README.md
@@ -2,7 +2,7 @@
|
||||
[](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 5 framework.
|
||||
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.
|
||||
|
||||

|
||||
|
||||
@@ -35,7 +35,8 @@ You can also make a one-time payment through [paypal.me/jonaskvinge](https://pay
|
||||
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
||||
* Audio CD playback
|
||||
* Native desktop notifications
|
||||
* Playlists in multiple formats
|
||||
* Playlist management
|
||||
* Smart and dynamic playlists
|
||||
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||
* Edit tags on music files
|
||||
* Fetch tags from MusicBrainz
|
||||
@@ -44,9 +45,9 @@ You can also make a one-time payment through [paypal.me/jonaskvinge](https://pay
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Transfer music to iPod, MTP or mass-storage USB player
|
||||
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
||||
* Subsonic and Tidal streaming support
|
||||
* Subsonic, Tidal and Qobuz streaming support
|
||||
|
||||
|
||||
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
||||
@@ -63,14 +64,14 @@ To build Strawberry from source you need the following installed on your system
|
||||
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
|
||||
* [GLib](https://developer.gnome.org/glib/)
|
||||
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
|
||||
* [Qt 5.6 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
||||
* [Qt 5 components X11Extras and DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
|
||||
* [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 DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
|
||||
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
|
||||
* [Chromaprint library](https://acoustid.org/chromaprint)
|
||||
* [ALSA library (linux)](https://www.alsa-project.org/)
|
||||
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org) or [VLC](https://www.videolan.org)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
||||
* [GnuTLS](https://www.gnutls.org/)
|
||||
|
||||
Optional dependencies:
|
||||
@@ -78,12 +79,13 @@ Optional dependencies:
|
||||
* 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/)
|
||||
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
|
||||
* Moodbar: [fftw3](http://www.fftw.org/)
|
||||
|
||||
Either GStreamer, Xine or VLC engine is required, but only GStreamer is fully implemented so far.
|
||||
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented so far.
|
||||
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
||||
|
||||
With Qt 6 we also depend on the Core5Compat module for QTextCodec.
|
||||
|
||||
### :wrench: Compiling from source
|
||||
|
||||
### Get the code:
|
||||
@@ -95,8 +97,12 @@ You should also install the gstreamer plugins base and good, and optionally bad
|
||||
cd strawberry
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j4
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
|
||||
To compile with Qt 6 use:
|
||||
|
||||
cmake .. -DBUILD_WITH_QT6=ON
|
||||
|
||||
### :penguin: Packaging status
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macversion.sh OUTPUT_VARIABLE MACOS_VERSION_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
add_custom_target(dmg
|
||||
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
|
||||
COMMAND create-dmg --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
@@ -15,7 +15,11 @@ macro(optional_source TOGGLE)
|
||||
list(APPEND OTHER_SOURCES ${OPTIONAL_SOURCE_HEADERS})
|
||||
|
||||
set(_uic_sources)
|
||||
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
if(BUILD_WITH_QT6)
|
||||
qt6_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
else()
|
||||
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||
endif()
|
||||
list(APPEND OTHER_SOURCES ${_uic_sources})
|
||||
list(APPEND OTHER_UIC_SOURCES ${_uic_sources})
|
||||
endif(${TOGGLE})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
@@ -19,6 +19,8 @@ set (XGETTEXT_OPTIONS
|
||||
--from-code=utf-8
|
||||
)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
|
||||
|
||||
macro(add_pot outfiles header pot)
|
||||
# Make relative filenames for all source files
|
||||
set(add_pot_sources)
|
||||
@@ -64,14 +66,21 @@ macro(add_po outfiles po_prefix)
|
||||
)
|
||||
|
||||
list(APPEND ${outfiles} ${_qm_filepath})
|
||||
list(APPEND INSTALL_TRANSLATIONS_FILES ${_qm_filepath})
|
||||
endforeach (_lang)
|
||||
|
||||
# Generate a qrc file for the translations
|
||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
||||
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
||||
foreach(_lang ${ADD_PO_LANGUAGES})
|
||||
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
||||
endforeach(_lang)
|
||||
file(APPEND ${_qrc} "</qresource></RCC>")
|
||||
qt5_add_resources(${outfiles} ${_qrc})
|
||||
if(NOT INSTALL_TRANSLATIONS)
|
||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
||||
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
||||
foreach(_lang ${ADD_PO_LANGUAGES})
|
||||
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()
|
||||
endif()
|
||||
endmacro(add_po)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 6)
|
||||
set(STRAWBERRY_VERSION_PATCH 13)
|
||||
set(STRAWBERRY_VERSION_MINOR 8)
|
||||
set(STRAWBERRY_VERSION_PATCH 1)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
<file>schema/schema-10.sql</file>
|
||||
<file>schema/schema-11.sql</file>
|
||||
<file>schema/schema-12.sql</file>
|
||||
<file>schema/schema-13.sql</file>
|
||||
<file>schema/device-schema.sql</file>
|
||||
<file>style/strawberry.css</file>
|
||||
<file>style/smartplaylistsearchterm.css</file>
|
||||
<file>html/playing-tooltip.html</file>
|
||||
<file>html/oauthsuccess.html</file>
|
||||
<file>pictures/strawberry.png</file>
|
||||
@@ -40,6 +42,8 @@
|
||||
<file>pictures/osd_shadow_edge.png</file>
|
||||
<file>pictures/nyancat.png</file>
|
||||
<file>pictures/rainbowdash.png</file>
|
||||
<file>pictures/star-on.png</file>
|
||||
<file>pictures/star-off.png</file>
|
||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||
<file>mood/sample.mood</file>
|
||||
<file>text/ghosts.txt</file>
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
<file>icons/128x128/view-refresh.png</file>
|
||||
<file>icons/128x128/library-music.png</file>
|
||||
<file>icons/128x128/vlc.png</file>
|
||||
<file>icons/128x128/xine.png</file>
|
||||
<file>icons/128x128/zoom-in.png</file>
|
||||
<file>icons/128x128/zoom-out.png</file>
|
||||
<file>icons/128x128/scrobble.png</file>
|
||||
@@ -90,6 +89,8 @@
|
||||
<file>icons/128x128/love.png</file>
|
||||
<file>icons/128x128/subsonic.png</file>
|
||||
<file>icons/128x128/tidal.png</file>
|
||||
<file>icons/128x128/qobuz.png</file>
|
||||
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/64x64/albums.png</file>
|
||||
<file>icons/64x64/alsa.png</file>
|
||||
<file>icons/64x64/application-exit.png</file>
|
||||
@@ -172,7 +173,6 @@
|
||||
<file>icons/64x64/view-refresh.png</file>
|
||||
<file>icons/64x64/library-music.png</file>
|
||||
<file>icons/64x64/vlc.png</file>
|
||||
<file>icons/64x64/xine.png</file>
|
||||
<file>icons/64x64/zoom-in.png</file>
|
||||
<file>icons/64x64/zoom-out.png</file>
|
||||
<file>icons/64x64/scrobble.png</file>
|
||||
@@ -181,6 +181,8 @@
|
||||
<file>icons/64x64/love.png</file>
|
||||
<file>icons/64x64/subsonic.png</file>
|
||||
<file>icons/64x64/tidal.png</file>
|
||||
<file>icons/64x64/qobuz.png</file>
|
||||
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/48x48/albums.png</file>
|
||||
<file>icons/48x48/alsa.png</file>
|
||||
<file>icons/48x48/application-exit.png</file>
|
||||
@@ -267,7 +269,6 @@
|
||||
<file>icons/48x48/view-refresh.png</file>
|
||||
<file>icons/48x48/library-music.png</file>
|
||||
<file>icons/48x48/vlc.png</file>
|
||||
<file>icons/48x48/xine.png</file>
|
||||
<file>icons/48x48/zoom-in.png</file>
|
||||
<file>icons/48x48/zoom-out.png</file>
|
||||
<file>icons/48x48/scrobble.png</file>
|
||||
@@ -276,6 +277,8 @@
|
||||
<file>icons/48x48/love.png</file>
|
||||
<file>icons/48x48/subsonic.png</file>
|
||||
<file>icons/48x48/tidal.png</file>
|
||||
<file>icons/48x48/qobuz.png</file>
|
||||
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/32x32/albums.png</file>
|
||||
<file>icons/32x32/alsa.png</file>
|
||||
<file>icons/32x32/application-exit.png</file>
|
||||
@@ -362,7 +365,6 @@
|
||||
<file>icons/32x32/view-refresh.png</file>
|
||||
<file>icons/32x32/library-music.png</file>
|
||||
<file>icons/32x32/vlc.png</file>
|
||||
<file>icons/32x32/xine.png</file>
|
||||
<file>icons/32x32/zoom-in.png</file>
|
||||
<file>icons/32x32/zoom-out.png</file>
|
||||
<file>icons/32x32/scrobble.png</file>
|
||||
@@ -371,6 +373,8 @@
|
||||
<file>icons/32x32/love.png</file>
|
||||
<file>icons/32x32/subsonic.png</file>
|
||||
<file>icons/32x32/tidal.png</file>
|
||||
<file>icons/32x32/qobuz.png</file>
|
||||
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
|
||||
<file>icons/22x22/albums.png</file>
|
||||
<file>icons/22x22/alsa.png</file>
|
||||
<file>icons/22x22/application-exit.png</file>
|
||||
@@ -457,7 +461,6 @@
|
||||
<file>icons/22x22/view-refresh.png</file>
|
||||
<file>icons/22x22/library-music.png</file>
|
||||
<file>icons/22x22/vlc.png</file>
|
||||
<file>icons/22x22/xine.png</file>
|
||||
<file>icons/22x22/zoom-in.png</file>
|
||||
<file>icons/22x22/zoom-out.png</file>
|
||||
<file>icons/22x22/scrobble.png</file>
|
||||
@@ -466,5 +469,7 @@
|
||||
<file>icons/22x22/love.png</file>
|
||||
<file>icons/22x22/subsonic.png</file>
|
||||
<file>icons/22x22/tidal.png</file>
|
||||
<file>icons/22x22/qobuz.png</file>
|
||||
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
BIN
data/icons/128x128/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
data/icons/128x128/qobuz.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
BIN
data/icons/22x22/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 918 B |
BIN
data/icons/22x22/qobuz.png
Normal file
|
After Width: | Height: | Size: 964 B |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
data/icons/32x32/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
data/icons/32x32/qobuz.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
BIN
data/icons/48x48/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
data/icons/48x48/qobuz.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
data/icons/64x64/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
data/icons/64x64/qobuz.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
BIN
data/icons/full/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
data/icons/full/qobuz.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
BIN
data/pictures/star-off.png
Normal file
|
After Width: | Height: | Size: 351 B |
BIN
data/pictures/star-on.png
Normal file
|
After Width: | Height: | Size: 284 B |
@@ -62,7 +62,9 @@ CREATE TABLE device_%deviceid_songs (
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
@@ -72,7 +74,7 @@ CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (c
|
||||
|
||||
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
);
|
||||
|
||||
UPDATE devices SET schema_version=1 WHERE ROWID=%deviceid;
|
||||
UPDATE devices SET schema_version=2 WHERE ROWID=%deviceid;
|
||||
|
||||
@@ -180,7 +180,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
@@ -195,7 +195,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
@@ -210,7 +210,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
|
||||
231
data/schema/schema-13.sql
Normal file
@@ -0,0 +1,231 @@
|
||||
ALTER TABLE %allsongstables ADD COLUMN rating INTEGER DEFAULT -1;
|
||||
|
||||
ALTER TABLE playlists ADD COLUMN dynamic_playlist_type INTEGER;
|
||||
|
||||
ALTER TABLE playlists ADD COLUMN dynamic_playlist_backend TEXT;
|
||||
|
||||
ALTER TABLE playlists ADD COLUMN dynamic_playlist_data BLOB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
UPDATE schema_version SET version=13;
|
||||
@@ -13,7 +13,7 @@ CREATE VIRTUAL TABLE %allsongstables_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ CREATE VIRTUAL TABLE playlist_items_fts_ USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
||||
|
||||
DELETE FROM schema_version;
|
||||
|
||||
INSERT INTO schema_version (version) VALUES (12);
|
||||
INSERT INTO schema_version (version) VALUES (13);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS directories (
|
||||
path TEXT NOT NULL,
|
||||
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS songs (
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
@@ -70,178 +70,9 @@ CREATE TABLE IF NOT EXISTS songs (
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
cue_path TEXT,
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
@@ -254,7 +85,7 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||
track INTEGER NOT NULL DEFAULT -1,
|
||||
disc INTEGER NOT NULL DEFAULT -1,
|
||||
year INTEGER NOT NULL DEFAULT -1,
|
||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||
genre TEXT,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
@@ -298,7 +129,363 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
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,
|
||||
compilation INTEGER NOT NULL DEFAULT 0,
|
||||
composer TEXT,
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
comment TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
artist_id TEXT,
|
||||
album_id TEXT,
|
||||
song_id TEXT,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||
|
||||
source INTEGER NOT NULL DEFAULT 0,
|
||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||
url TEXT NOT NULL,
|
||||
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,
|
||||
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed 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,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
@@ -309,7 +496,11 @@ CREATE TABLE IF NOT EXISTS playlists (
|
||||
ui_order INTEGER NOT NULL DEFAULT 0,
|
||||
special_type TEXT,
|
||||
ui_path TEXT,
|
||||
is_favorite INTEGER NOT NULL DEFAULT 0
|
||||
is_favorite INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
dynamic_playlist_type INTEGER,
|
||||
dynamic_playlist_backend TEXT,
|
||||
dynamic_playlist_data BLOB
|
||||
|
||||
);
|
||||
|
||||
@@ -371,7 +562,9 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
||||
effective_albumartist TEXT,
|
||||
effective_originalyear INTEGER,
|
||||
|
||||
cue_path TEXT
|
||||
cue_path TEXT,
|
||||
|
||||
rating INTEGER DEFAULT -1
|
||||
|
||||
);
|
||||
|
||||
@@ -410,52 +603,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
@@ -470,11 +618,11 @@ CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_songs_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts5(
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
@@ -485,7 +633,82 @@ CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts5(
|
||||
|
||||
ftstitle,
|
||||
ftsalbum,
|
||||
ftsartist,
|
||||
ftsalbumartist,
|
||||
ftscomposer,
|
||||
ftsperformer,
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
@@ -500,6 +723,6 @@ CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts5(
|
||||
ftsgrouping,
|
||||
ftsgenre,
|
||||
ftscomment,
|
||||
tokenize = "unicode61 remove_diacritics 0"
|
||||
tokenize = "unicode61 remove_diacritics 1"
|
||||
|
||||
);
|
||||
|
||||
42
data/style/smartplaylistsearchterm.css
Normal file
@@ -0,0 +1,42 @@
|
||||
#frame {
|
||||
border: 1px solid palette(mid);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
#remove {
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-bottom-right-radius: 5px;
|
||||
border: 0px solid transparent;
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 %light,
|
||||
stop:0.4 %light,
|
||||
stop:0.6 %dark,
|
||||
stop:1 %dark);
|
||||
|
||||
margin-left: 5px;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
#remove:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 %light2,
|
||||
stop:0.4 %light2,
|
||||
stop:0.6 %base,
|
||||
stop:1 %base);
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
|
||||
#remove:pressed {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 %base,
|
||||
stop:0.4 %base,
|
||||
stop:0.6 %light2,
|
||||
stop:1 %light2);
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
3
debian/control
vendored
@@ -24,10 +24,7 @@ Build-Depends: debhelper (>= 11),
|
||||
libgstreamer-plugins-base1.0-dev,
|
||||
libcdio-dev,
|
||||
libgpod-dev,
|
||||
libimobiledevice-dev,
|
||||
libmtp-dev,
|
||||
libplist-dev,
|
||||
libusbmuxd-dev,
|
||||
libchromaprint-dev,
|
||||
libfftw3-dev
|
||||
Standards-Version: 4.2.1
|
||||
|
||||
38
debian/copyright
vendored
@@ -12,7 +12,6 @@ Files: src/core/timeconstants.h
|
||||
ext/libstrawberry-common/core/logging.h
|
||||
ext/libstrawberry-common/core/messagehandler.cpp
|
||||
ext/libstrawberry-common/core/messagehandler.h
|
||||
ext/libstrawberry-common/core/override.h
|
||||
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
|
||||
License: Apache-2.0
|
||||
|
||||
@@ -122,8 +121,14 @@ Files: src/core/main.cpp
|
||||
src/context/contextalbumsview.h
|
||||
src/widgets/playingwidget.cpp
|
||||
src/widgets/playingwidget.h
|
||||
src/widgets/osdpretty.cpp
|
||||
src/widgets/osdpretty.h
|
||||
src/osd/osdbase.cpp
|
||||
src/osd/osdbase.h
|
||||
src/osd/osdpretty.cpp
|
||||
src/osd/osdpretty.h
|
||||
src/osd/osddbus.cpp
|
||||
src/osd/osddbus.h
|
||||
src/osd/osdmac.cpp
|
||||
src/osd/osdmac.h
|
||||
src/dialogs/about.cpp
|
||||
src/dialogs/about.h
|
||||
src/playlist/playlist.cpp
|
||||
@@ -171,12 +176,12 @@ Files: src/core/main.cpp
|
||||
src/settings/shortcutssettingspage.h
|
||||
src/settings/appearancesettingspage.cpp
|
||||
src/settings/appearancesettingspage.h
|
||||
src/organise/organise.cpp
|
||||
src/organise/organise.h
|
||||
src/organise/organisedialog.cpp
|
||||
src/organise/organisedialog.h
|
||||
src/organise/organiseerrordialog.cpp
|
||||
src/organise/organiseerrordialog.h
|
||||
src/organize/organize.cpp
|
||||
src/organize/organize.h
|
||||
src/organize/organizedialog.cpp
|
||||
src/organize/organizedialog.h
|
||||
src/organize/organizeerrordialog.cpp
|
||||
src/organize/organizeerrordialog.h
|
||||
src/transcoder/transcoder.cpp
|
||||
src/transcoder/transcoder.h
|
||||
src/musicbrainz/musicbrainzclient.cpp
|
||||
@@ -213,21 +218,6 @@ Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2003-2005, Mark Kretschmann <markey@web.de>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/engine/xineengine.cpp
|
||||
src/engine/xineengine.h
|
||||
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2005, Ian Monroe <ian@monroe.nu>
|
||||
2005, Christophe Thommeret <hftom@free.fr>
|
||||
2005, 2006, Mark Kretschmann <markey@web.de>
|
||||
2004, 2005, Max Howell <max.howell@methylblue.com>
|
||||
2003, 2004, J. Kofler <kaffeine@gmx.net>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/engine/xinescope.c
|
||||
src/engine/xinescope.h
|
||||
Copyright: 2004, Max Howell <max.howell@methylblue.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/widgets/fancytabwidget.cpp
|
||||
src/widgets/fancytabwidget.h
|
||||
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>
|
||||
|
||||
1
debian/rules
vendored
@@ -5,7 +5,6 @@
|
||||
|
||||
override_dh_auto_clean:
|
||||
rm -f dist/macos/Info.plist
|
||||
rm -f dist/macos/create-dmg.sh
|
||||
rm -f dist/unix/PKGBUILD
|
||||
rm -f dist/unix/strawberry.spec
|
||||
rm -f dist/scripts/maketarball.sh
|
||||
|
||||
2
dist/CMakeLists.txt
vendored
@@ -3,11 +3,11 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOUR
|
||||
|
||||
if (APPLE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh)
|
||||
endif (APPLE)
|
||||
|
||||
if (WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-wasapi.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-wasapi.nsi @ONLY)
|
||||
endif (WIN32)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
|
||||
4
dist/macos/Info.plist.in
vendored
@@ -34,6 +34,10 @@
|
||||
<string>public.app-category.music</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13.4</string>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
24
dist/macos/create-dmg.sh.in
vendored
@@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
version="@STRAWBERRY_VERSION_PACKAGE@"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <bundle.app> (append)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
name=$(basename "$1" | perl -pe 's/(.*).app/\1/')
|
||||
bundle_dir="$1"
|
||||
temp_dir="dmg/$name"
|
||||
if [ -z "$2" ]; then
|
||||
output_file="$name-$version.dmg"
|
||||
else
|
||||
output_file="$name-$2-$version.dmg"
|
||||
fi
|
||||
|
||||
rm -rf "$temp_dir"
|
||||
rm -f "$output_file"
|
||||
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
/usr/local/bin/create-dmg --volname "$name" --background "@CMAKE_SOURCE_DIR@/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon $bundle_dir 150 218 --window-size 600 450 $output_file $bundle_dir
|
||||
184
dist/macos/macdeploy.py
vendored
@@ -21,17 +21,17 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import commands
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
LOGGER = logging.getLogger('macdeploy')
|
||||
|
||||
LIBRARY_SEARCH_PATH = ['/usr/local/lib']
|
||||
LIBRARY_SEARCH_PATH = ['/usr/local/lib', '/usr/local/opt/icu4c/lib']
|
||||
|
||||
FRAMEWORK_SEARCH_PATH = [
|
||||
'/Library/Frameworks',
|
||||
os.path.join(os.environ['HOME'], 'Library/Frameworks')
|
||||
os.path.join(os.environ['HOME'], 'Library/Frameworks'),
|
||||
'/Library/Frameworks/Sparkle.framework/Versions'
|
||||
]
|
||||
|
||||
QT_PLUGINS = [
|
||||
@@ -66,56 +66,57 @@ GSTREAMER_SEARCH_PATH = [
|
||||
|
||||
GSTREAMER_PLUGINS = [
|
||||
|
||||
'libgstapetag.so',
|
||||
'libgstapp.so',
|
||||
'libgstaudioconvert.so',
|
||||
'libgstaudiofx.so',
|
||||
'libgstaudiomixer.so',
|
||||
'libgstaudioparsers.so',
|
||||
'libgstaudiorate.so',
|
||||
'libgstaudioresample.so',
|
||||
'libgstaudiotestsrc.so',
|
||||
'libgstaudiovisualizers.so',
|
||||
'libgstauparse.so',
|
||||
'libgstautoconvert.so',
|
||||
'libgstautodetect.so',
|
||||
'libgstcoreelements.so',
|
||||
'libgstequalizer.so',
|
||||
'libgstgio.so',
|
||||
'libgsticydemux.so',
|
||||
'libgstid3demux.so',
|
||||
'libgstlevel.so',
|
||||
'libgstosxaudio.so',
|
||||
'libgstplayback.so',
|
||||
'libgstrawparse.so',
|
||||
'libgstrealmedia.so',
|
||||
'libgstreplaygain.so',
|
||||
'libgstsoup.so',
|
||||
'libgstspectrum.so',
|
||||
'libgsttypefindfunctions.so',
|
||||
'libgstvolume.so',
|
||||
'libgstxingmux.so',
|
||||
'libgsttcp.so',
|
||||
'libgstudp.so',
|
||||
'libgstpbtypes.so',
|
||||
'libgstrtp.so',
|
||||
'libgstrtsp.so',
|
||||
'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',
|
||||
'libgstrealmedia.dylib',
|
||||
'libgstreplaygain.dylib',
|
||||
'libgstsoup.dylib',
|
||||
'libgstspectrum.dylib',
|
||||
'libgsttypefindfunctions.dylib',
|
||||
'libgstvolume.dylib',
|
||||
'libgstxingmux.dylib',
|
||||
'libgsttcp.dylib',
|
||||
'libgstudp.dylib',
|
||||
'libgstpbtypes.dylib',
|
||||
'libgstrtp.dylib',
|
||||
'libgstrtsp.dylib',
|
||||
|
||||
'libgstflac.so',
|
||||
'libgstwavparse.so',
|
||||
'libgstfaac.so',
|
||||
'libgstfaad.so',
|
||||
'libgstogg.so',
|
||||
'libgstopus.so',
|
||||
'libgstopusparse.so',
|
||||
'libgstasf.so',
|
||||
'libgstspeex.so',
|
||||
'libgsttaglib.so',
|
||||
'libgstvorbis.so',
|
||||
'libgstisomp4.so',
|
||||
'libgstlibav.so',
|
||||
'libgstaiff.so',
|
||||
'libgstlame.so',
|
||||
'libgstflac.dylib',
|
||||
'libgstwavparse.dylib',
|
||||
'libgstfaac.dylib',
|
||||
'libgstfaad.dylib',
|
||||
'libgstogg.dylib',
|
||||
'libgstopus.dylib',
|
||||
'libgstopusparse.dylib',
|
||||
'libgstasf.dylib',
|
||||
'libgstspeex.dylib',
|
||||
'libgsttaglib.dylib',
|
||||
'libgstvorbis.dylib',
|
||||
'libgstisomp4.dylib',
|
||||
'libgstlibav.dylib',
|
||||
'libgstaiff.dylib',
|
||||
'libgstlame.dylib',
|
||||
'libgstmusepack.dylib',
|
||||
|
||||
]
|
||||
|
||||
@@ -154,14 +155,8 @@ class InstallNameToolError(Error):
|
||||
class CouldNotFindGstreamerPluginError(Error):
|
||||
pass
|
||||
|
||||
class CouldNotFindXinePluginError(Error):
|
||||
pass
|
||||
|
||||
class CouldNotFindVLCPluginError(Error):
|
||||
pass
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print 'Usage: %s <bundle.app>' % sys.argv[0]
|
||||
print('Usage: %s <bundle.app>' % sys.argv[0])
|
||||
|
||||
bundle_dir = sys.argv[1]
|
||||
|
||||
@@ -181,27 +176,40 @@ fixed_frameworks = set()
|
||||
|
||||
|
||||
def GetBrokenLibraries(binary):
|
||||
#print "Checking libs for binary: %s" % binary
|
||||
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0]
|
||||
#print("Checking libs for binary: %s" % binary)
|
||||
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
|
||||
broken_libs = {'frameworks': [], 'libs': []}
|
||||
for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
|
||||
#print "Checking line: %s" % line
|
||||
#print("Checking line: %s" % line)
|
||||
if not line: # skip empty lines
|
||||
continue
|
||||
if os.path.basename(binary) == os.path.basename(line):
|
||||
#print "mnope %s-%s" % (os.path.basename(binary), os.path.basename(line))
|
||||
#print("mnope %s-%s" % (os.path.basename(binary), os.path.basename(line)))
|
||||
continue
|
||||
if re.match(r'^\s*/System/', line):
|
||||
#print("system framework: %s" % line)
|
||||
continue # System framework
|
||||
elif re.match(r'^\s*/usr/lib/', line):
|
||||
#print "unix style system lib"
|
||||
#print("unix style system lib: %s" % line)
|
||||
continue # unix style system library
|
||||
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
|
||||
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@rpath', line) or re.match(r'^\s*@loader_path', line):
|
||||
# Potentially already fixed library
|
||||
relative_path = os.path.join(*line.split('/')[3:])
|
||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||
broken_libs['frameworks'].append(relative_path)
|
||||
if line.count('/') == 1:
|
||||
relative_path = os.path.join(*line.split('/')[1:])
|
||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||
broken_libs['libs'].append(relative_path)
|
||||
elif line.count('/') == 2:
|
||||
relative_path = os.path.join(*line.split('/')[2:])
|
||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||
broken_libs['libs'].append(relative_path)
|
||||
elif line.count('/') >= 3:
|
||||
relative_path = os.path.join(*line.split('/')[3:])
|
||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||
broken_libs['frameworks'].append(relative_path)
|
||||
else:
|
||||
print("GetBrokenLibraries Error: %s" % line)
|
||||
elif re.search(r'\w+\.framework', line):
|
||||
#print("framework: %s" % line)
|
||||
broken_libs['frameworks'].append(line)
|
||||
else:
|
||||
broken_libs['libs'].append(line)
|
||||
@@ -261,14 +269,21 @@ def FixFramework(path):
|
||||
|
||||
|
||||
def FixLibrary(path):
|
||||
if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
|
||||
|
||||
if path in fixed_libraries:
|
||||
return
|
||||
else:
|
||||
fixed_libraries.add(path)
|
||||
|
||||
# Always bundle libraries provided by homebrew (/usr/local).
|
||||
if not re.match(r'^\s*/usr/local', path) and FindSystemLibrary(os.path.basename(path)) is not None:
|
||||
return
|
||||
|
||||
fixed_libraries.add(path)
|
||||
|
||||
abs_path = FindLibrary(path)
|
||||
if abs_path == "":
|
||||
print "Could not resolve %s, not fixing!" % path
|
||||
print("Could not resolve %s, not fixing!" % path)
|
||||
return
|
||||
|
||||
broken_libs = GetBrokenLibraries(abs_path)
|
||||
FixAllLibraries(broken_libs)
|
||||
|
||||
@@ -416,7 +431,7 @@ def FindSystemLibrary(library_name):
|
||||
|
||||
def FixLibraryInstallPath(library_path, library):
|
||||
system_library = FindSystemLibrary(os.path.basename(library_path))
|
||||
if system_library is None:
|
||||
if system_library is None or re.match(r'^\s*/usr/local', library_path):
|
||||
new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path)
|
||||
FixInstallPath(library_path, library, new_path)
|
||||
else:
|
||||
@@ -425,21 +440,14 @@ def FixLibraryInstallPath(library_path, library):
|
||||
|
||||
def FixFrameworkInstallPath(library_path, library):
|
||||
parts = library_path.split(os.sep)
|
||||
full_path = ""
|
||||
for i, part in enumerate(parts):
|
||||
if re.match(r'\w+\.framework', part):
|
||||
full_path = os.path.join(*parts[i:])
|
||||
break
|
||||
new_path = '@executable_path/../Frameworks/%s' % full_path
|
||||
FixInstallPath(library_path, library, new_path)
|
||||
|
||||
|
||||
def FindXinePlugin(name):
|
||||
for path in XINEPLUGIN_SEARCH_PATH:
|
||||
if os.path.exists(path):
|
||||
for dir, dirs, files in os.walk(path):
|
||||
if name in files:
|
||||
return os.path.join(dir, name)
|
||||
raise CouldNotFindXinePluginError(name)
|
||||
if full_path:
|
||||
new_path = '@executable_path/../Frameworks/%s' % full_path
|
||||
FixInstallPath(library_path, library, new_path)
|
||||
|
||||
|
||||
def FindQtPlugin(name):
|
||||
@@ -476,7 +484,7 @@ def main():
|
||||
try:
|
||||
FixPlugin('strawberry-tagreader', '.')
|
||||
except:
|
||||
print 'Failed to find blob: %s' % traceback.format_exc()
|
||||
print('Failed to find blob: %s' % traceback.format_exc())
|
||||
|
||||
for plugin in GSTREAMER_PLUGINS:
|
||||
FixPlugin(FindGstreamerPlugin(plugin), 'gstreamer')
|
||||
@@ -489,11 +497,11 @@ def main():
|
||||
#FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
||||
|
||||
if len(sys.argv) <= 2:
|
||||
print 'Would run %d commands:' % len(commands)
|
||||
print('Would run %d commands:' % len(commands))
|
||||
for command in commands:
|
||||
print ' '.join(command)
|
||||
print(' '.join(command))
|
||||
|
||||
#print 'OK?'
|
||||
#print('OK?')
|
||||
#raw_input()
|
||||
|
||||
for command in commands:
|
||||
|
||||
14
dist/macos/macversion.sh
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
macos_version=$(sw_vers -productVersion| awk -F '[.]' '{print $2}')
|
||||
macos_codenames=(
|
||||
["13"]="highsierra"
|
||||
["14"]="mojave"
|
||||
["15"]="catalina"
|
||||
)
|
||||
|
||||
if [[ -n "${macos_codenames[$macos_version]}" ]]; then
|
||||
echo "${macos_codenames[$macos_version]}"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
1
dist/scripts/maketarball.sh.in
vendored
@@ -38,7 +38,6 @@ tar -cJf $name-$version.tar.xz \
|
||||
--exclude="$root/debian/changelog" \
|
||||
--exclude="$root/dist/scripts/maketarball.sh" \
|
||||
--exclude="$root/dist/unix/PKGBUILD" \
|
||||
--exclude="$root/dist/macos/create-dmg.sh" \
|
||||
--exclude="$root/dist/macos/Info.plist" \
|
||||
--exclude="$root/dist/windows/windres.rc" \
|
||||
--exclude="$root/src/translations/translations.pot" \
|
||||
|
||||
112
dist/scripts/verify-icons.sh
vendored
@@ -16,85 +16,61 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sizes="128x128 64x64 48x48 32x32 22x22"
|
||||
sizes="full 128 64 48 32 22"
|
||||
|
||||
#
|
||||
|
||||
#for i in full/*
|
||||
#do
|
||||
# source=$i
|
||||
# file=`basename $i`
|
||||
|
||||
# id=`identify "$i"` || exit 1
|
||||
# if [ "$id" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine format and geometry for image: \"$i\"."
|
||||
# continue
|
||||
# fi
|
||||
# g=`echo $id | awk '{print $3}'` || exit 1
|
||||
# if [ "$g" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine geometry for image: \"$i\"."
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# Geometry can be 563x144+0+0 or 75x98
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
# w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
# if [ "$w" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
# continue
|
||||
# fi
|
||||
# h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
# if [ "$h" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# for x in $sizes
|
||||
# do
|
||||
|
||||
# dest="$x/$file"
|
||||
# if [ -f $dest ]; then
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# x_w=$(echo $x | cut -d 'x' -f1)
|
||||
# x_h=$(echo $x | cut -d 'x' -f2)
|
||||
|
||||
# if [ "$w" -lt "$x_w" ] || [ "$h" -lt "$x_h" ]; then
|
||||
# continue
|
||||
# fi
|
||||
|
||||
#echo "convert -verbose -resize $x $source $dest"
|
||||
#convert -verbose -resize $x $source $dest
|
||||
|
||||
# done
|
||||
#done
|
||||
|
||||
|
||||
for i in $sizes
|
||||
for s in $sizes
|
||||
do
|
||||
for x in $i/*
|
||||
if [ "$s" = "full" ]; then
|
||||
dir=$s
|
||||
else
|
||||
dir=${s}x${s}
|
||||
fi
|
||||
if ! [ -d "$dir" ]; then
|
||||
echo "Missing $dir directory."
|
||||
continue
|
||||
fi
|
||||
for f in ${dir}/*
|
||||
do
|
||||
file=`basename $x`
|
||||
|
||||
file=`basename $f`
|
||||
|
||||
for y in $sizes
|
||||
do
|
||||
if [ "$y" = "$i" ]; then
|
||||
|
||||
if [ "$s" = "$y" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! [ -f "$y/$file" ]; then
|
||||
echo "Warning: $y/$file does not exist, but $x exists."
|
||||
|
||||
if [ "$y" = "full" ]; then
|
||||
dir2=$y
|
||||
else
|
||||
dir2=${y}x${y}
|
||||
fi
|
||||
|
||||
if [ "$dir2" = "full" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! [ "$s" = "full" ] && [ $y -gt $s ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! [ -f "${dir2}/$file" ]; then
|
||||
echo "Warning: ${dir2}/$file does not exist, but ${dir}/${file} exists."
|
||||
fi
|
||||
done
|
||||
|
||||
id=`identify "$x"` || exit 1
|
||||
if [ "$dir" = "full" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
id=`identify "$f"` || exit 1
|
||||
if [ "$id" = "" ] ; then
|
||||
echo "ERROR: Cannot determine format and geometry for image: \"$x\"."
|
||||
echo "ERROR: Cannot determine format and geometry for image: \"$f\"."
|
||||
continue
|
||||
fi
|
||||
g=`echo $id | awk '{print $3}'` || exit 1
|
||||
if [ "$g" = "" ] ; then
|
||||
echo "ERROR: Cannot determine geometry for image: \"$x\"."
|
||||
echo "ERROR: Cannot determine geometry for image: \"$f\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -102,17 +78,17 @@ do
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
if [ "$w" = "" ] ; then
|
||||
echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
echo "ERROR: Cannot determine width for image: \"$f\"."
|
||||
continue
|
||||
fi
|
||||
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
if [ "$h" = "" ] ; then
|
||||
echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
echo "ERROR: Cannot determine height for image: \"$f\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! [ "${h}x${w}" = "$i" ]; then
|
||||
echo "Warning: $x is not $i, but ${h}x${w}!"
|
||||
if ! [ "${h}x${w}" = "$dir" ]; then
|
||||
echo "Warning: $f is not $dir, but ${h}x${w}!"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
4
dist/unix/PKGBUILD.in
vendored
@@ -23,15 +23,11 @@ depends=(
|
||||
gstreamer
|
||||
gst-plugins-base
|
||||
gst-plugins-good
|
||||
xine-lib
|
||||
vlc
|
||||
chromaprint
|
||||
libgpod
|
||||
libcdio
|
||||
libmtp
|
||||
libusbmuxd
|
||||
libplist
|
||||
libimobiledevice
|
||||
fftw
|
||||
)
|
||||
optdepends=(
|
||||
|
||||
@@ -12,3 +12,28 @@ Categories=AudioVideo;Player;Qt;Audio;
|
||||
StartupNotify=false
|
||||
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
|
||||
StartupWMClass=strawberry
|
||||
Actions=Play;Pause;Stop;StopAfterCurrent;Previous;Next;
|
||||
|
||||
[Desktop Action Play]
|
||||
Name=Play
|
||||
Exec=strawberry --play
|
||||
|
||||
[Desktop Action Pause]
|
||||
Name=Pause
|
||||
Exec=strawberry --pause
|
||||
|
||||
[Desktop Action Stop]
|
||||
Name=Stop
|
||||
Exec=strawberry --stop
|
||||
|
||||
[Desktop Action StopAfterCurrent]
|
||||
Name=Stop after this track
|
||||
Exec=strawberry --stop-after-current
|
||||
|
||||
[Desktop Action Previous]
|
||||
Name=Previous
|
||||
Exec=strawberry --previous
|
||||
|
||||
[Desktop Action Next]
|
||||
Name=Next
|
||||
Exec=strawberry --next
|
||||
|
||||
66
dist/unix/strawberry.spec.in
vendored
@@ -1,7 +1,7 @@
|
||||
Name: strawberry
|
||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||
Summary: A music player and music collection organiser
|
||||
Summary: A music player and music collection organizer
|
||||
Group: Applications/Multimedia
|
||||
License: GPL-3.0+
|
||||
URL: https://www.strawberrymusicplayer.org/
|
||||
@@ -73,7 +73,6 @@ BuildRequires: pkgconfig(libmtp)
|
||||
BuildRequires: pkgconfig(libnotify)
|
||||
BuildRequires: pkgconfig(libudf)
|
||||
%if 0%{?suse_version} || 0%{?fedora_version}
|
||||
BuildRequires: pkgconfig(libxine)
|
||||
BuildRequires: pkgconfig(libvlc)
|
||||
%endif
|
||||
|
||||
@@ -81,13 +80,6 @@ BuildRequires: pkgconfig(libvlc)
|
||||
Requires: libQt5Sql5-sqlite
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
||||
Requires(post): update-desktop-files
|
||||
Requires(post): gtk3-tools
|
||||
Requires(postun): update-desktop-files
|
||||
Requires(postun): gtk3-tools
|
||||
%endif
|
||||
|
||||
%description
|
||||
Strawberry is a music player and music collection organizer.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
@@ -107,7 +99,7 @@ Features:
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
- Transfer music to iPod, MTP or mass-storage USB player
|
||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
- Streaming support for Subsonic
|
||||
|
||||
@@ -115,62 +107,34 @@ Features:
|
||||
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
||||
|
||||
%build
|
||||
%if 0%{?suse_version} || 0%{?mageia}
|
||||
%{cmake} ..
|
||||
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release
|
||||
%if 0%{?centos} || 0%{?mageia}
|
||||
%make_build
|
||||
%else
|
||||
mkdir -p %{_target_platform}
|
||||
pushd %{_target_platform}
|
||||
%{cmake} ..
|
||||
popd
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version} || 0%{?mageia}
|
||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
||||
make %{?_smp_mflags}
|
||||
%else
|
||||
%make_build
|
||||
%endif
|
||||
%else
|
||||
%make_build -C %{_target_platform}
|
||||
%cmake_build
|
||||
%endif
|
||||
|
||||
%install
|
||||
%if 0%{?suse_version}
|
||||
%cmake_install
|
||||
%if 0%{?centos}
|
||||
%make_install
|
||||
%else
|
||||
%if 0%{?mageia}
|
||||
%make_install -C build
|
||||
%else
|
||||
%make_install -C %{_target_platform}
|
||||
%endif
|
||||
%if 0%{?mageia}
|
||||
%make_install -C build
|
||||
%else
|
||||
%cmake_install
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
||||
rm -f %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%suse_update_desktop_file org.strawberrymusicplayer.strawberry Qt AudioVideo Audio Player
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
||||
%post
|
||||
%desktop_database_post
|
||||
%icon_theme_cache_post
|
||||
|
||||
%postun
|
||||
%desktop_database_postun
|
||||
%icon_theme_cache_postun
|
||||
%endif
|
||||
|
||||
%check
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
||||
%if 0%{?fedora_version}
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||
%else
|
||||
%if ! 0%{?suse_version} || ( 0%{?suse_version} && 0%{?suse_version} >= 1500 )
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||
%endif
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||
%endif
|
||||
|
||||
%files
|
||||
@@ -184,9 +148,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicpl
|
||||
%if 0%{?fedora_version}
|
||||
%{_metainfodir}/*.appdata.xml
|
||||
%else
|
||||
%if ! 0%{?suse_version} || ( 0%{?suse_version} && 0%{?suse_version} >= 1500 )
|
||||
%{_datadir}/metainfo/*.appdata.xml
|
||||
%endif
|
||||
%{_datadir}/metainfo/*.appdata.xml
|
||||
%endif
|
||||
%{_mandir}/man1/%{name}.1.*
|
||||
%{_mandir}/man1/%{name}-tagreader.1.*
|
||||
|
||||
182
dist/windows/strawberry-wasapi.nsi.in
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
!if "@ARCH@" == x86
|
||||
!define arch_x86
|
||||
!endif
|
||||
|
||||
!if "@ARCH@" == i686-w64-mingw32.shared
|
||||
!define arch_x86
|
||||
!endif
|
||||
|
||||
!if "@ARCH@" == x86_64
|
||||
!define arch_x64
|
||||
!endif
|
||||
|
||||
!if "@ARCH@" == x86_64-w64-mingw32.shared
|
||||
!define arch_x64
|
||||
!endif
|
||||
|
||||
!if "@CMAKE_BUILD_TYPE@" == Debug
|
||||
!define debug
|
||||
!endif
|
||||
|
||||
!if "@BUILD_WITH_QT6@" == "ON"
|
||||
!define with_qt6
|
||||
!endif
|
||||
|
||||
!ifdef debug
|
||||
!define PRODUCT_NAME "Strawberry Music Player Debug WASAPI plugin"
|
||||
!define PRODUCT_NAME_SHORT "StrawberryWASAPI"
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME_SHORT}Debug"
|
||||
!ifdef arch_x86
|
||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player Debug"
|
||||
!endif
|
||||
!ifdef arch_x64
|
||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player Debug"
|
||||
!endif
|
||||
!else
|
||||
!define PRODUCT_NAME "Strawberry Music Player WASAPI plugin"
|
||||
!define PRODUCT_NAME_SHORT "StrawberryWASAPI"
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME_SHORT}"
|
||||
!ifdef arch_x86
|
||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Strawberry Music Player"
|
||||
!endif
|
||||
!ifdef arch_x64
|
||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES64\Strawberry Music Player"
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!define PRODUCT_VERSION_MAJOR @STRAWBERRY_VERSION_MAJOR@
|
||||
!define PRODUCT_VERSION_MINOR @STRAWBERRY_VERSION_MINOR@
|
||||
!define PRODUCT_VERSION_PATCH @STRAWBERRY_VERSION_PATCH@
|
||||
!define PRODUCT_DISPLAY_VERSION "@STRAWBERRY_VERSION_PACKAGE@"
|
||||
!define PRODUCT_DISPLAY_VERSION_SHORT "@STRAWBERRY_VERSION_PACKAGE@"
|
||||
|
||||
!define PRODUCT_PUBLISHER "Jonas Kvinge"
|
||||
!define PRODUCT_WEB_SITE "https://www.strawberrymusicplayer.org/"
|
||||
|
||||
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
|
||||
|
||||
SetCompressor /SOLID lzma
|
||||
|
||||
!addplugindir nsisplugins
|
||||
!include "MUI2.nsh"
|
||||
!include "FileAssociation.nsh"
|
||||
!include "Capabilities.nsh"
|
||||
!include LogicLib.nsh
|
||||
!include x64.nsh
|
||||
|
||||
!define MUI_ICON "strawberry.ico"
|
||||
|
||||
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||
|
||||
; Installer pages
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Uninstaller pages
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
!insertmacro MUI_UNPAGE_FINISH
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English" ;first language is the default language
|
||||
|
||||
Name "${PRODUCT_NAME}"
|
||||
!ifdef arch_x86
|
||||
!ifdef debug
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
|
||||
!endif
|
||||
!else
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
!ifdef debug
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
|
||||
!endif
|
||||
!else
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
|
||||
InstallDir "${PRODUCT_INSTALL_DIR}"
|
||||
|
||||
; Get the path where Strawberry was installed previously and set it as default path
|
||||
InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||
|
||||
ShowInstDetails show
|
||||
ShowUnInstDetails show
|
||||
RequestExecutionLevel admin
|
||||
|
||||
; Check for previous installation, and call the uninstaller if any
|
||||
Function CheckPreviousInstall
|
||||
|
||||
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||
StrCmp $R0 "" done
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
|
||||
previous version or `Cancel` to cancel this upgrade." \
|
||||
IDOK uninst
|
||||
Abort
|
||||
; Run the uninstaller
|
||||
uninst:
|
||||
ClearErrors
|
||||
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
|
||||
|
||||
done:
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function .onInit
|
||||
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
Call CheckPreviousInstall
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Section "Gstreamer wasapi plugin" gstreamer-wasapi-plugin
|
||||
SetOutPath "$INSTDIR\gstreamer-plugins"
|
||||
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstaller"
|
||||
; Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall-WASAPI.exe"
|
||||
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall-WASAPI.exe"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\strawberry.ico"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
|
||||
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
|
||||
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
||||
Delete "$INSTDIR\Uninstall-WASAPI.exe"
|
||||
|
||||
; Remove the entry from 'installed programs list'
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
|
||||
|
||||
SectionEnd
|
||||
152
dist/windows/strawberry.nsi.in
vendored
@@ -18,6 +18,10 @@
|
||||
!define debug
|
||||
!endif
|
||||
|
||||
!if "@BUILD_WITH_QT6@" == "ON"
|
||||
!define with_qt6
|
||||
!endif
|
||||
|
||||
!ifdef debug
|
||||
!define PRODUCT_NAME "Strawberry Music Player Debug"
|
||||
!define PRODUCT_NAME_SHORT "Strawberry"
|
||||
@@ -91,17 +95,33 @@ SetCompressor /SOLID lzma
|
||||
Name "${PRODUCT_NAME}"
|
||||
!ifdef arch_x86
|
||||
!ifdef debug
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
|
||||
!endif
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
!ifdef debug
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
|
||||
!endif
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
|
||||
!ifdef with_qt6
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
||||
!else
|
||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
|
||||
!endif
|
||||
!endif
|
||||
!endif
|
||||
|
||||
@@ -154,7 +174,27 @@ Section "Strawberry" Strawberry
|
||||
File "strawberry.exe"
|
||||
File "strawberry-tagreader.exe"
|
||||
File "strawberry.ico"
|
||||
File "sqlite3.exe"
|
||||
|
||||
!ifdef arch_x86
|
||||
File "libgcc_s_sjlj-1.dll"
|
||||
File "libcrypto-1_1.dll"
|
||||
File "libssl-1_1.dll"
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
File "libgcc_s_seh-1.dll"
|
||||
File "libcrypto-1_1-x64.dll"
|
||||
File "libssl-1_1-x64.dll"
|
||||
!endif
|
||||
|
||||
File "avcodec-58.dll"
|
||||
File "avfilter-7.dll"
|
||||
File "avformat-58.dll"
|
||||
File "avresample-4.dll"
|
||||
File "avutil-56.dll"
|
||||
File "libbrotlicommon.dll"
|
||||
File "libbrotlidec.dll"
|
||||
File "libbz2.dll"
|
||||
File "libcdio-19.dll"
|
||||
File "libchromaprint.dll"
|
||||
@@ -194,21 +234,41 @@ Section "Strawberry" Strawberry
|
||||
File "libnettle-8.dll"
|
||||
File "libogg-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-23.dll"
|
||||
File "libprotobuf-24.dll"
|
||||
File "libpsl-5.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libssp-0.dll"
|
||||
File "libstdc++-6.dll"
|
||||
File "libtag.dll"
|
||||
File "libtasn1-6.dll"
|
||||
File "libunistring-2.dll"
|
||||
File "libvorbis-0.dll"
|
||||
File "libvorbisenc-2.dll"
|
||||
File "libwavpack-1.dll"
|
||||
File "libwinpthread-1.dll"
|
||||
File "libxml2-2.dll"
|
||||
File "libzstd.dll"
|
||||
File "postproc-55.dll"
|
||||
File "swresample-3.dll"
|
||||
File "swscale-5.dll"
|
||||
File "zlib1.dll"
|
||||
|
||||
!ifdef with_qt6
|
||||
File "Qt6Concurrent.dll"
|
||||
File "Qt6Core.dll"
|
||||
File "Qt6Gui.dll"
|
||||
File "Qt6Network.dll"
|
||||
File "Qt6Sql.dll"
|
||||
File "Qt6Widgets.dll"
|
||||
File "Qt6WinExtras.dll"
|
||||
File "libqtsparkle-qt6.dll"
|
||||
!else
|
||||
File "Qt5Concurrent.dll"
|
||||
File "Qt5Core.dll"
|
||||
File "Qt5Gui.dll"
|
||||
@@ -216,24 +276,7 @@ Section "Strawberry" Strawberry
|
||||
File "Qt5Sql.dll"
|
||||
File "Qt5Widgets.dll"
|
||||
File "Qt5WinExtras.dll"
|
||||
File "zlib1.dll"
|
||||
File "libzstd.dll"
|
||||
File "libtasn1-6.dll"
|
||||
File "libbrotlicommon.dll"
|
||||
File "libbrotlidec.dll"
|
||||
File "libpsl-5.dll"
|
||||
File "liborc-0.4-0.dll"
|
||||
|
||||
!ifdef arch_x86
|
||||
File "libgcc_s_sjlj-1.dll"
|
||||
File "libcrypto-1_1.dll"
|
||||
File "libssl-1_1.dll"
|
||||
!endif
|
||||
|
||||
!ifdef arch_x64
|
||||
File "libgcc_s_seh-1.dll"
|
||||
File "libcrypto-1_1-x64.dll"
|
||||
File "libssl-1_1-x64.dll"
|
||||
File "libqtsparkle-qt5.dll"
|
||||
!endif
|
||||
|
||||
File "killproc.exe"
|
||||
@@ -359,6 +402,12 @@ Section "Start menu items" startmenu
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Tidal URL Scheme"
|
||||
WriteRegStr HKCR "tidal" "URL Protocol" ""
|
||||
WriteRegStr HKCR "tidal" "" "URL:tidal"
|
||||
WriteRegStr HKCR 'tidal\shell\open\command' '' '"${PRODUCT_INSTALL_DIR}\strawberry.exe" "%1"'
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstaller"
|
||||
; Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
@@ -383,7 +432,27 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\strawberry.ico"
|
||||
Delete "$INSTDIR\strawberry.exe"
|
||||
Delete "$INSTDIR\strawberry-tagreader.exe"
|
||||
Delete "$INSTDIR\sqlite3.exe"
|
||||
|
||||
!ifdef arch_x86
|
||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||
Delete "$INSTDIR\libcrypto-1_1.dll"
|
||||
Delete "$INSTDIR\libssl-1_1.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"
|
||||
!endif
|
||||
|
||||
Delete "$INSTDIR\avcodec-58.dll"
|
||||
Delete "$INSTDIR\avfilter-7.dll"
|
||||
Delete "$INSTDIR\avformat-58.dll"
|
||||
Delete "$INSTDIR\avresample-4.dll"
|
||||
Delete "$INSTDIR\avutil-56.dll"
|
||||
Delete "$INSTDIR\libbrotlicommon.dll"
|
||||
Delete "$INSTDIR\libbrotlidec.dll"
|
||||
Delete "$INSTDIR\libbz2.dll"
|
||||
Delete "$INSTDIR\libcdio-19.dll"
|
||||
Delete "$INSTDIR\libchromaprint.dll"
|
||||
@@ -423,21 +492,29 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libnettle-8.dll"
|
||||
Delete "$INSTDIR\libogg-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-23.dll"
|
||||
Delete "$INSTDIR\libprotobuf-24.dll"
|
||||
Delete "$INSTDIR\libpsl-5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
||||
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
Delete "$INSTDIR\libssp-0.dll"
|
||||
Delete "$INSTDIR\libstdc++-6.dll"
|
||||
Delete "$INSTDIR\libtag.dll"
|
||||
Delete "$INSTDIR\libtasn1-6.dll"
|
||||
Delete "$INSTDIR\libunistring-2.dll"
|
||||
Delete "$INSTDIR\libvorbis-0.dll"
|
||||
Delete "$INSTDIR\libvorbisenc-2.dll"
|
||||
Delete "$INSTDIR\libwavpack-1.dll"
|
||||
Delete "$INSTDIR\libwinpthread-1.dll"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libzstd.dll"
|
||||
Delete "$INSTDIR\postproc-55.dll"
|
||||
Delete "$INSTDIR\Qt5Concurrent.dll"
|
||||
Delete "$INSTDIR\Qt5Core.dll"
|
||||
Delete "$INSTDIR\Qt5Gui.dll"
|
||||
@@ -445,25 +522,16 @@ Section "Uninstall"
|
||||
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"
|
||||
Delete "$INSTDIR\libzstd.dll"
|
||||
Delete "$INSTDIR\libtasn1-6.dll"
|
||||
Delete "$INSTDIR\libbrotlicommon.dll"
|
||||
Delete "$INSTDIR\libbrotlidec.dll"
|
||||
Delete "$INSTDIR\libpsl-5.dll"
|
||||
Delete "$INSTDIR\liborc-0.4-0.dll"
|
||||
|
||||
!ifdef arch_x86
|
||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||
Delete "$INSTDIR\libcrypto-1_1.dll"
|
||||
Delete "$INSTDIR\libssl-1_1.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"
|
||||
!endif
|
||||
|
||||
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
|
||||
@@ -9,7 +9,7 @@ link_directories(
|
||||
${GSTREAMER_BASE_LIBRARY_DIRS}
|
||||
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
||||
${FFTW3_LIBRARY_DIRS}
|
||||
${Qt5Core_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_library(gstmoodbar STATIC ${SOURCES})
|
||||
@@ -21,7 +21,7 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
|
||||
${GSTREAMER_BASE_INCLUDE_DIRS}
|
||||
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
||||
${FFTW3_INCLUDE_DIR}
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -33,5 +33,5 @@ target_link_libraries(gstmoodbar PRIVATE
|
||||
${GSTREAMER_BASE_LIBRARIES}
|
||||
${GSTREAMER_AUDIO_LIBRARIES}
|
||||
${FFTW3_FFTW_LIBRARY}
|
||||
${Qt5Core_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -179,7 +179,7 @@ static void gst_fastspectrum_reset_state (GstFastSpectrum * spectrum) {
|
||||
|
||||
static void gst_fastspectrum_finalize (GObject * object) {
|
||||
|
||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (object);
|
||||
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(object);
|
||||
|
||||
gst_fastspectrum_reset_state (spectrum);
|
||||
g_mutex_clear (&spectrum->lock);
|
||||
@@ -190,7 +190,7 @@ static void gst_fastspectrum_finalize (GObject * object) {
|
||||
|
||||
static void gst_fastspectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) {
|
||||
|
||||
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
|
||||
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_INTERVAL:{
|
||||
@@ -222,7 +222,7 @@ static void gst_fastspectrum_set_property (GObject * object, guint prop_id, cons
|
||||
|
||||
static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) {
|
||||
|
||||
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
|
||||
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_INTERVAL:
|
||||
@@ -240,7 +240,7 @@ static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GVal
|
||||
|
||||
static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
|
||||
|
||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
||||
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||
|
||||
gst_fastspectrum_reset_state (spectrum);
|
||||
|
||||
@@ -250,7 +250,7 @@ static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
|
||||
|
||||
static gboolean gst_fastspectrum_stop (GstBaseTransform * trans) {
|
||||
|
||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
||||
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||
|
||||
gst_fastspectrum_reset_state (spectrum);
|
||||
|
||||
@@ -334,7 +334,7 @@ static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint
|
||||
|
||||
static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInfo * info) {
|
||||
|
||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (base);
|
||||
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(base);
|
||||
GstFastSpectrumInputData input_data = nullptr;
|
||||
|
||||
g_mutex_lock (&spectrum->lock);
|
||||
@@ -392,7 +392,7 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
|
||||
|
||||
static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, GstBuffer *buffer) {
|
||||
|
||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
||||
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||
guint rate = GST_AUDIO_FILTER_RATE (spectrum);
|
||||
guint bps = GST_AUDIO_FILTER_BPS (spectrum);
|
||||
guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
|
||||
|
||||
@@ -20,20 +20,24 @@ if(APPLE)
|
||||
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
|
||||
endif(APPLE)
|
||||
|
||||
qt5_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}
|
||||
${Qt5Core_LIBRARY_DIRS}
|
||||
${Qt5Network_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
${QtNetwork_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
||||
|
||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-common PRIVATE
|
||||
@@ -46,8 +50,8 @@ target_include_directories(libstrawberry-common PRIVATE
|
||||
target_link_libraries(libstrawberry-common PRIVATE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${GLIB_LIBRARIES}
|
||||
${Qt5Core_LIBRARIES}
|
||||
${Qt5Network_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
)
|
||||
|
||||
if(Backtrace_FOUND)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// Helper for lazy initialisation of objects.
|
||||
// Helper for lazy initialization of objects.
|
||||
// Usage:
|
||||
// Lazy<Foo> my_lazy_object([]() { return new Foo; });
|
||||
|
||||
@@ -48,19 +48,18 @@ class Lazy {
|
||||
// Returns true if the object is not yet initialised.
|
||||
explicit operator bool() const { return ptr_; }
|
||||
|
||||
// Deletes the underlying object and will re-run the initialisation function
|
||||
// if the object is requested again.
|
||||
void reset() { ptr_.reset(nullptr); }
|
||||
// Deletes the underlying object and will re-run the initialisation function if the object is requested again.
|
||||
void reset() { ptr_.reset(); }
|
||||
|
||||
private:
|
||||
void CheckInitialised() const {
|
||||
if (!ptr_) {
|
||||
ptr_.reset(init_());
|
||||
ptr_.reset(init_(), [](T*obj) { obj->deleteLater(); });
|
||||
}
|
||||
}
|
||||
|
||||
const std::function<T*()> init_;
|
||||
mutable std::unique_ptr<T> ptr_;
|
||||
mutable std::shared_ptr<T> ptr_;
|
||||
};
|
||||
|
||||
#endif // LAZY_H
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QDateTime>
|
||||
#include <QIODevice>
|
||||
#include <QBuffer>
|
||||
@@ -306,11 +307,12 @@ QString LinuxDemangle(const QString &symbol);
|
||||
|
||||
QString LinuxDemangle(const QString &symbol) {
|
||||
|
||||
QRegExp regex("\\(([^+]+)");
|
||||
if (!symbol.contains(regex)) {
|
||||
QRegularExpression regex("\\(([^+]+)");
|
||||
QRegularExpressionMatch match = regex.match(symbol);
|
||||
if (!match.hasMatch()) {
|
||||
return symbol;
|
||||
}
|
||||
QString mangled_function = regex.cap(1);
|
||||
QString mangled_function = match.captured(1);
|
||||
return CXXDemangle(mangled_function);
|
||||
|
||||
}
|
||||
|
||||
@@ -102,10 +102,10 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
|
||||
|
||||
// Sorry.
|
||||
if (flush_abstract_socket_) {
|
||||
((static_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
|
||||
((qobject_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
|
||||
}
|
||||
else if (flush_local_socket_) {
|
||||
((static_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
||||
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OVERRIDE_H
|
||||
#define OVERRIDE_H
|
||||
|
||||
// Defines the OVERRIDE macro as C++11's override control keyword if
|
||||
// it is available.
|
||||
|
||||
#ifndef __has_extension
|
||||
#define __has_extension(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_extension(cxx_override_control) // Clang feature checking macro.
|
||||
# define OVERRIDE override
|
||||
#else
|
||||
# define OVERRIDE
|
||||
#endif
|
||||
|
||||
#endif // OVERRIDE_H
|
||||
@@ -172,7 +172,7 @@ template <typename HandlerType>
|
||||
WorkerPool<HandlerType>::~WorkerPool() {
|
||||
for (const Worker &worker : workers_) {
|
||||
if (worker.local_socket_ && worker.process_) {
|
||||
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
||||
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
||||
|
||||
// The worker is connected. Close his socket and wait for him to exit.
|
||||
qLog(Debug) << "Closing worker socket";
|
||||
@@ -263,7 +263,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||
worker->process_ = new QProcess(this);
|
||||
|
||||
connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||
connect(worker->process_, SIGNAL(error(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
|
||||
connect(worker->process_, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
|
||||
|
||||
// Create a server, find an unused name and start listening
|
||||
forever {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(MESSAGES tagreadermessages.proto)
|
||||
set(SOURCES fmpsparser.cpp tagreader.cpp)
|
||||
set(SOURCES tagreader.cpp)
|
||||
|
||||
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||
|
||||
@@ -17,8 +17,8 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||
@@ -34,6 +34,11 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||
${GLIB_LIBRARIES}
|
||||
${PROTOBUF_LIBRARY}
|
||||
${TAGLIB_LIBRARIES}
|
||||
${Qt5Core_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
libstrawberry-common
|
||||
)
|
||||
|
||||
if(BUILD_WITH_QT6)
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE Qt6::Core5Compat)
|
||||
endif()
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QChar>
|
||||
|
||||
#include "fmpsparser.h"
|
||||
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
FMPSParser::FMPSParser() :
|
||||
// The float regex ends with (?:$|(?=::|;;)) to ensure it matches all the way
|
||||
// up to the end of the value. Without it, it would match a string that
|
||||
// starts with a number, like "123abc".
|
||||
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
|
||||
|
||||
// Matches any character except unescaped slashes, colons and semicolons.
|
||||
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
|
||||
|
||||
// Used for replacing escaped characters.
|
||||
escape_re_("\\\\([\\\\:;])") {}
|
||||
|
||||
// Parses a list of things (of type T) that are separated by two consecutive
|
||||
// Separator characters. Each individual thing is parsed by the F function.
|
||||
// For example, to parse this data:
|
||||
// foo::bar::baz
|
||||
// Use:
|
||||
// QVariantList ret;
|
||||
// ParseContainer<':'>(data, ParseValue, &ret);
|
||||
// ret will then contain "foo", "bar", and "baz".
|
||||
// Returns the number of characters that were consumed from data.
|
||||
//
|
||||
// You can parse lists of lists by using different separator characters:
|
||||
// ParseContainer<';'>(data, ParseContainer<':'>, &ret);
|
||||
template <char Separator, typename F, typename T>
|
||||
static int ParseContainer(const QStringRef& data, F f, QList<T>* ret) {
|
||||
ret->clear();
|
||||
|
||||
T value;
|
||||
int pos = 0;
|
||||
while (pos < data.length()) {
|
||||
const int len = data.length() - pos;
|
||||
int matched_len = f(QStringRef(data.string(), data.position() + pos, len), &value);
|
||||
if (matched_len == -1 || matched_len > len)
|
||||
break;
|
||||
|
||||
ret->append(value);
|
||||
pos += matched_len;
|
||||
|
||||
// Expect two separators in a row
|
||||
if (pos + 2 <= data.length() && data.at(pos) == Separator && data.at(pos+1) == Separator) {
|
||||
pos += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool FMPSParser::Parse(const QString &data) {
|
||||
|
||||
result_ = Result();
|
||||
|
||||
// Only return success if we matched the whole string
|
||||
return ParseListList(data, &result_) == data.length();
|
||||
}
|
||||
|
||||
int FMPSParser::ParseValueRef(const QStringRef& data, QVariant* ret) const {
|
||||
// Try to match a float
|
||||
int pos = float_re_.indexIn(*data.string(), data.position());
|
||||
if (pos == data.position()) {
|
||||
*ret = float_re_.cap(1).toDouble();
|
||||
return float_re_.matchedLength();
|
||||
}
|
||||
|
||||
// Otherwise try to match a string
|
||||
pos = string_re_.indexIn(*data.string(), data.position());
|
||||
if (pos == data.position()) {
|
||||
// Replace escape sequences with their actual characters
|
||||
QString value = string_re_.cap(1);
|
||||
value.replace(escape_re_, "\\1");
|
||||
*ret = value;
|
||||
return string_re_.matchedLength();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parses an inner list - a list of values
|
||||
int FMPSParser::ParseListRef(const QStringRef &data, QVariantList *ret) const {
|
||||
return ParseContainer<':'>(data, std::bind(&FMPSParser::ParseValueRef, this, _1, _2), ret);
|
||||
}
|
||||
|
||||
// Parses an outer list - a list of lists
|
||||
int FMPSParser::ParseListListRef(const QStringRef &data, Result *ret) const {
|
||||
return ParseContainer<';'>(data, std::bind(&FMPSParser::ParseListRef, this, _1, _2), ret);
|
||||
}
|
||||
|
||||
// Convenience functions that take QStrings instead of QStringRefs. Use the QStringRef versions if possible, they're faster.
|
||||
int FMPSParser::ParseValue(const QString &data, QVariant *ret) const {
|
||||
return ParseValueRef(QStringRef(&data), ret);
|
||||
}
|
||||
int FMPSParser::ParseList(const QString &data, QVariantList *ret) const {
|
||||
return ParseListRef(QStringRef(&data), ret);
|
||||
}
|
||||
int FMPSParser::ParseListList(const QString &data, Result *ret) const {
|
||||
return ParseListListRef(QStringRef(&data), ret);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FMPSPARSER_H
|
||||
#define FMPSPARSER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
|
||||
class QVariant;
|
||||
|
||||
class FMPSParser {
|
||||
public:
|
||||
FMPSParser();
|
||||
|
||||
// A FMPS result is a list of lists of values (where a value is a string or
|
||||
// a float).
|
||||
typedef QList<QVariantList> Result;
|
||||
|
||||
// Parses a FMPS value and returns true on success.
|
||||
bool Parse(const QString &data);
|
||||
|
||||
// Gets the result of the last successful Parse.
|
||||
Result result() const { return result_; }
|
||||
|
||||
// Returns true if result() is empty.
|
||||
bool is_empty() const { return result().isEmpty() || result()[0].isEmpty(); }
|
||||
|
||||
// Internal functions, public for unit tests
|
||||
int ParseValue(const QString &data, QVariant *ret) const;
|
||||
int ParseValueRef(const QStringRef &data, QVariant *ret) const;
|
||||
|
||||
int ParseList(const QString &data, QVariantList *ret) const;
|
||||
int ParseListRef(const QStringRef &data, QVariantList *ret) const;
|
||||
|
||||
int ParseListList(const QString &data, Result *ret) const;
|
||||
int ParseListListRef(const QStringRef &data, Result *ret) const;
|
||||
|
||||
private:
|
||||
QRegExp float_re_;
|
||||
QRegExp string_re_;
|
||||
QRegExp escape_re_;
|
||||
Result result_;
|
||||
};
|
||||
|
||||
#endif // FMPSPARSER_H
|
||||
@@ -90,8 +90,6 @@
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/messagehandler.h"
|
||||
|
||||
#include "fmpsparser.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
class FileRefFactory {
|
||||
@@ -177,11 +175,11 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
song->set_basefilename(DataCommaSizeFromQString(info.fileName()));
|
||||
song->set_url(url.constData(), url.size());
|
||||
song->set_filesize(info.size());
|
||||
song->set_mtime(info.lastModified().toTime_t());
|
||||
song->set_mtime(info.lastModified().toSecsSinceEpoch());
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toTime_t() : info.lastModified().toTime_t());
|
||||
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toSecsSinceEpoch() : info.lastModified().toSecsSinceEpoch());
|
||||
#else
|
||||
song->set_ctime(info.created().toTime_t());
|
||||
song->set_ctime(info.created().toSecsSinceEpoch());
|
||||
#endif
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
@@ -300,15 +298,6 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
}
|
||||
|
||||
// Parse FMPS frames
|
||||
for (uint i = 0; i < map["TXXX"].size(); ++i) {
|
||||
const TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
|
||||
|
||||
if (frame && frame->description().startsWith("FMPS_")) {
|
||||
ParseFMPSFrame(TStringToQString(frame->description()), TStringToQString(frame->fieldList()[1]), song);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +331,9 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
if (mp4_tag->item("\251grp").isValid()) {
|
||||
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), nullptr, song->mutable_grouping());
|
||||
}
|
||||
if (mp4_tag->item("\251lyr").isValid()) {
|
||||
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), nullptr, song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
|
||||
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
|
||||
@@ -355,6 +347,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
|
||||
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
|
||||
|
||||
if (file_asf->tag()) {
|
||||
Decode(file_asf->tag()->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
|
||||
|
||||
if (attributes_map.contains(kASF_OriginalDate_ID)) {
|
||||
@@ -395,7 +391,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
|
||||
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") {
|
||||
if (QStringFromStdString(song->artist()).toLower() == "various artists" || QStringFromStdString(song->albumartist()).toLower() == "various artists") {
|
||||
song->set_compilation(true);
|
||||
}
|
||||
}
|
||||
@@ -516,33 +512,6 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCode
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
qLog(Debug) << "Parsing FMPSFrame" << name << ", " << value;
|
||||
FMPSParser parser;
|
||||
|
||||
if (!parser.Parse(value) || parser.is_empty()) return;
|
||||
|
||||
QVariant var;
|
||||
|
||||
if (name == "FMPS_PlayCount") {
|
||||
var = parser.result()[0][0];
|
||||
if (var.type() == QVariant::Double) {
|
||||
song->set_playcount(var.toDouble());
|
||||
}
|
||||
}
|
||||
else if (name == "FMPS_PlayCount_User") {
|
||||
// Take a user playcount only if there's no playcount already set
|
||||
if (song->playcount() == 0 && parser.result()[0].count() >= 2) {
|
||||
var = parser.result()[0][1];
|
||||
if (var.type() == QVariant::Double) {
|
||||
song->set_playcount(var.toDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||
@@ -563,7 +532,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||
|
||||
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isNull() || filename.isEmpty()) return false;
|
||||
if (filename.isEmpty()) return false;
|
||||
|
||||
qLog(Debug) << "Saving tags to" << filename;
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
@@ -617,6 +587,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0));
|
||||
tag->setItem("\251wrt", TagLib::StringList(song.composer().c_str()));
|
||||
tag->setItem("\251grp", TagLib::StringList(song.grouping().c_str()));
|
||||
tag->setItem("\251lyr", TagLib::StringList(song.lyrics().c_str()));
|
||||
tag->setItem("aART", TagLib::StringList(song.albumartist().c_str()));
|
||||
tag->setItem("cpil", TagLib::StringList(song.compilation() ? "1" : "0"));
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
class QTextCodec;
|
||||
|
||||
|
||||
#ifndef USE_SYSTEM_TAGLIB
|
||||
using namespace Strawberry_TagLib;
|
||||
#endif
|
||||
@@ -67,7 +66,6 @@ class TagReader {
|
||||
|
||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const;
|
||||
|
||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
||||
|
||||
@@ -55,8 +55,8 @@ message SongMetadata {
|
||||
optional string basefilename = 22;
|
||||
optional FileType filetype = 23;
|
||||
optional int32 filesize = 24;
|
||||
optional int32 mtime = 25;
|
||||
optional int32 ctime = 26;
|
||||
optional int64 mtime = 25;
|
||||
optional int64 ctime = 26;
|
||||
|
||||
optional int32 playcount = 27;
|
||||
optional int32 skipcount = 28;
|
||||
|
||||
@@ -4,14 +4,19 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
|
||||
|
||||
set(SOURCES main.cpp tagreaderworker.cpp)
|
||||
|
||||
qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_add_resources(QRC data/data.qrc)
|
||||
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)
|
||||
endif()
|
||||
|
||||
link_directories(
|
||||
${GLIB_LIBRARY_DIRS}
|
||||
${TAGLIB_LIBRARY_DIRS}
|
||||
${Qt5Core_LIBRARY_DIRS}
|
||||
${Qt5Network_LIBRARY_DIRS}
|
||||
${QtCore_LIBRARY_DIRS}
|
||||
${QtNetwork_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||
@@ -19,8 +24,8 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||
${GLIB_INCLUDE_DIRS}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${QtCore_INCLUDE_DIRS}
|
||||
${QtNetwork_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_include_directories(strawberry-tagreader PRIVATE
|
||||
@@ -34,8 +39,8 @@ target_include_directories(strawberry-tagreader PRIVATE
|
||||
target_link_libraries(strawberry-tagreader PRIVATE
|
||||
${GLIB_LIBRARIES}
|
||||
${TAGLIB_LIBRARIES}
|
||||
${Qt5Core_LIBRARIES}
|
||||
${Qt5Network_LIBRARIES}
|
||||
${QtCore_LIBRARIES}
|
||||
${QtNetwork_LIBRARIES}
|
||||
libstrawberry-common
|
||||
libstrawberry-tagreader
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: strawberry
|
||||
version: '0.6.13+git'
|
||||
version: '0.8.1+git'
|
||||
summary: music player and collection organizer
|
||||
description: |
|
||||
Strawberry is a music player and collection organizer.
|
||||
@@ -99,14 +99,10 @@ parts:
|
||||
- libqt5x11extras5-dev
|
||||
- libgstreamer1.0-dev
|
||||
- libgstreamer-plugins-base1.0-dev
|
||||
- libxine2-dev
|
||||
- libvlc-dev
|
||||
- libcdio-dev
|
||||
- libgpod-dev
|
||||
- libimobiledevice-dev
|
||||
- libmtp-dev
|
||||
- libplist-dev
|
||||
- libusbmuxd-dev
|
||||
- libchromaprint-dev
|
||||
- libfftw3-dev
|
||||
|
||||
@@ -132,10 +128,6 @@ parts:
|
||||
- libcdio17
|
||||
- libgpod4
|
||||
- libmtp9
|
||||
- libimobiledevice6
|
||||
- libplist3
|
||||
- libusbmuxd4
|
||||
- libxine2
|
||||
- libvlc5
|
||||
- libvlccore9
|
||||
- libtag1v5
|
||||
@@ -144,7 +136,6 @@ parts:
|
||||
- libx11-6
|
||||
- libxcomposite1
|
||||
- libxcursor1
|
||||
- libxinerama1
|
||||
- libxrandr2
|
||||
- libdb5.3
|
||||
- libglu1
|
||||
|
||||
@@ -33,12 +33,10 @@ set(SOURCES
|
||||
core/thread.cpp
|
||||
core/urlhandler.cpp
|
||||
core/utilities.cpp
|
||||
core/scangiomodulepath.cpp
|
||||
core/iconloader.cpp
|
||||
core/qtsystemtrayicon.cpp
|
||||
core/standarditemiconloader.cpp
|
||||
core/systemtrayicon.cpp
|
||||
core/screensaver.cpp
|
||||
core/scopedtransaction.cpp
|
||||
core/translations.cpp
|
||||
|
||||
@@ -96,6 +94,7 @@ set(SOURCES
|
||||
playlist/playlistview.cpp
|
||||
playlist/songloaderinserter.cpp
|
||||
playlist/songplaylistitem.cpp
|
||||
playlist/dynamicplaylistcontrols.cpp
|
||||
|
||||
queue/queue.cpp
|
||||
queue/queueview.cpp
|
||||
@@ -111,6 +110,20 @@ set(SOURCES
|
||||
playlistparsers/xmlparser.cpp
|
||||
playlistparsers/xspfparser.cpp
|
||||
|
||||
smartplaylists/playlistgenerator.cpp
|
||||
smartplaylists/playlistgeneratorinserter.cpp
|
||||
smartplaylists/playlistquerygenerator.cpp
|
||||
smartplaylists/smartplaylistquerywizardplugin.cpp
|
||||
smartplaylists/smartplaylistsearch.cpp
|
||||
smartplaylists/smartplaylistsearchpreview.cpp
|
||||
smartplaylists/smartplaylistsearchterm.cpp
|
||||
smartplaylists/smartplaylistsearchtermwidget.cpp
|
||||
smartplaylists/smartplaylistsmodel.cpp
|
||||
smartplaylists/smartplaylistsviewcontainer.cpp
|
||||
smartplaylists/smartplaylistsview.cpp
|
||||
smartplaylists/smartplaylistwizard.cpp
|
||||
smartplaylists/smartplaylistwizardplugin.cpp
|
||||
|
||||
covermanager/albumcovermanager.cpp
|
||||
covermanager/albumcovermanagerlist.cpp
|
||||
covermanager/albumcoverloader.cpp
|
||||
@@ -168,6 +181,9 @@ set(SOURCES
|
||||
dialogs/edittagdialog.cpp
|
||||
dialogs/trackselectiondialog.cpp
|
||||
dialogs/addstreamdialog.cpp
|
||||
dialogs/userpassdialog.cpp
|
||||
dialogs/deleteconfirmationdialog.cpp
|
||||
dialogs/lastfmimportdialog.cpp
|
||||
|
||||
widgets/autoexpandingtreeview.cpp
|
||||
widgets/busyindicator.cpp
|
||||
@@ -183,8 +199,6 @@ set(SOURCES
|
||||
widgets/linetextedit.cpp
|
||||
widgets/multiloadingindicator.cpp
|
||||
widgets/playingwidget.cpp
|
||||
widgets/osd.cpp
|
||||
widgets/osdpretty.cpp
|
||||
widgets/renametablineedit.cpp
|
||||
widgets/volumeslider.cpp
|
||||
widgets/stickyslider.cpp
|
||||
@@ -193,6 +207,10 @@ set(SOURCES
|
||||
widgets/tracksliderpopup.cpp
|
||||
widgets/tracksliderslider.cpp
|
||||
widgets/loginstatewidget.cpp
|
||||
widgets/ratingwidget.cpp
|
||||
|
||||
osd/osdbase.cpp
|
||||
osd/osdpretty.cpp
|
||||
|
||||
musicbrainz/acoustidclient.cpp
|
||||
musicbrainz/musicbrainzclient.cpp
|
||||
@@ -220,11 +238,12 @@ set(SOURCES
|
||||
scrobbler/lastfmscrobbler.cpp
|
||||
scrobbler/librefmscrobbler.cpp
|
||||
scrobbler/listenbrainzscrobbler.cpp
|
||||
scrobbler/lastfmimport.cpp
|
||||
|
||||
organise/organise.cpp
|
||||
organise/organiseformat.cpp
|
||||
organise/organisedialog.cpp
|
||||
organise/organiseerrordialog.cpp
|
||||
organize/organize.cpp
|
||||
organize/organizeformat.cpp
|
||||
organize/organizedialog.cpp
|
||||
organize/organizeerrordialog.cpp
|
||||
|
||||
)
|
||||
|
||||
@@ -295,6 +314,7 @@ set(HEADERS
|
||||
playlist/playlistitemmimedata.h
|
||||
playlist/songloaderinserter.h
|
||||
playlist/songmimedata.h
|
||||
playlist/dynamicplaylistcontrols.h
|
||||
|
||||
queue/queue.h
|
||||
queue/queueview.h
|
||||
@@ -308,6 +328,18 @@ set(HEADERS
|
||||
playlistparsers/plsparser.h
|
||||
playlistparsers/xspfparser.h
|
||||
|
||||
smartplaylists/playlistgenerator.h
|
||||
smartplaylists/playlistgeneratorinserter.h
|
||||
smartplaylists/playlistgeneratormimedata.h
|
||||
smartplaylists/smartplaylistquerywizardplugin.h
|
||||
smartplaylists/smartplaylistsearchpreview.h
|
||||
smartplaylists/smartplaylistsearchtermwidget.h
|
||||
smartplaylists/smartplaylistsmodel.h
|
||||
smartplaylists/smartplaylistsviewcontainer.h
|
||||
smartplaylists/smartplaylistsview.h
|
||||
smartplaylists/smartplaylistwizard.h
|
||||
smartplaylists/smartplaylistwizardplugin.h
|
||||
|
||||
covermanager/albumcovermanager.h
|
||||
covermanager/albumcovermanagerlist.h
|
||||
covermanager/albumcoverloader.h
|
||||
@@ -364,6 +396,9 @@ set(HEADERS
|
||||
dialogs/edittagdialog.h
|
||||
dialogs/trackselectiondialog.h
|
||||
dialogs/addstreamdialog.h
|
||||
dialogs/userpassdialog.h
|
||||
dialogs/deleteconfirmationdialog.h
|
||||
dialogs/lastfmimportdialog.h
|
||||
|
||||
widgets/autoexpandingtreeview.h
|
||||
widgets/busyindicator.h
|
||||
@@ -378,8 +413,6 @@ set(HEADERS
|
||||
widgets/linetextedit.h
|
||||
widgets/multiloadingindicator.h
|
||||
widgets/playingwidget.h
|
||||
widgets/osd.h
|
||||
widgets/osdpretty.h
|
||||
widgets/renametablineedit.h
|
||||
widgets/volumeslider.h
|
||||
widgets/stickyslider.h
|
||||
@@ -389,6 +422,10 @@ set(HEADERS
|
||||
widgets/tracksliderslider.h
|
||||
widgets/loginstatewidget.h
|
||||
widgets/qsearchfield.h
|
||||
widgets/ratingwidget.h
|
||||
|
||||
osd/osdbase.h
|
||||
osd/osdpretty.h
|
||||
|
||||
musicbrainz/acoustidclient.h
|
||||
musicbrainz/musicbrainzclient.h
|
||||
@@ -414,10 +451,11 @@ set(HEADERS
|
||||
scrobbler/lastfmscrobbler.h
|
||||
scrobbler/librefmscrobbler.h
|
||||
scrobbler/listenbrainzscrobbler.h
|
||||
scrobbler/lastfmimport.h
|
||||
|
||||
organise/organise.h
|
||||
organise/organisedialog.h
|
||||
organise/organiseerrordialog.h
|
||||
organize/organize.h
|
||||
organize/organizedialog.h
|
||||
organize/organizeerrordialog.h
|
||||
|
||||
)
|
||||
|
||||
@@ -434,9 +472,17 @@ set(UI
|
||||
playlist/playlistlistcontainer.ui
|
||||
playlist/playlistsaveoptionsdialog.ui
|
||||
playlist/playlistsequence.ui
|
||||
playlist/dynamicplaylistcontrols.ui
|
||||
|
||||
queue/queueview.ui
|
||||
|
||||
smartplaylists/smartplaylistquerysearchpage.ui
|
||||
smartplaylists/smartplaylistquerysortpage.ui
|
||||
smartplaylists/smartplaylistsearchpreview.ui
|
||||
smartplaylists/smartplaylistsearchtermwidget.ui
|
||||
smartplaylists/smartplaylistsviewcontainer.ui
|
||||
smartplaylists/smartplaylistwizardfinishpage.ui
|
||||
|
||||
covermanager/albumcoverexport.ui
|
||||
covermanager/albumcovermanager.ui
|
||||
covermanager/albumcoversearcher.ui
|
||||
@@ -465,18 +511,21 @@ set(UI
|
||||
dialogs/edittagdialog.ui
|
||||
dialogs/trackselectiondialog.ui
|
||||
dialogs/addstreamdialog.ui
|
||||
dialogs/userpassdialog.ui
|
||||
dialogs/lastfmimportdialog.ui
|
||||
|
||||
widgets/trackslider.ui
|
||||
widgets/osdpretty.ui
|
||||
widgets/fileview.ui
|
||||
widgets/loginstatewidget.ui
|
||||
|
||||
osd/osdpretty.ui
|
||||
|
||||
internet/internettabsview.ui
|
||||
internet/internetcollectionviewcontainer.ui
|
||||
internet/internetsearchview.ui
|
||||
|
||||
organise/organisedialog.ui
|
||||
organise/organiseerrordialog.ui
|
||||
organize/organizedialog.ui
|
||||
organize/organizeerrordialog.ui
|
||||
|
||||
)
|
||||
|
||||
@@ -521,10 +570,12 @@ optional_source(HAVE_ALSA
|
||||
engine/alsadevicefinder.cpp
|
||||
)
|
||||
|
||||
# X11
|
||||
optional_source(X11_FOUND
|
||||
# DBUS
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES
|
||||
widgets/osd_x11.cpp
|
||||
osd/osddbus.cpp
|
||||
HEADERS
|
||||
osd/osddbus.h
|
||||
)
|
||||
|
||||
# GStreamer
|
||||
@@ -533,15 +584,6 @@ optional_source(HAVE_GSTREAMER
|
||||
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h
|
||||
)
|
||||
|
||||
# Xine
|
||||
optional_source(HAVE_XINE
|
||||
SOURCES engine/xineengine.cpp
|
||||
HEADERS engine/xineengine.h
|
||||
)
|
||||
optional_source(XINE_ANALYZER
|
||||
SOURCES engine/xinescope.c
|
||||
)
|
||||
|
||||
# VLC
|
||||
optional_source(HAVE_VLC
|
||||
SOURCES engine/vlcengine.cpp
|
||||
@@ -553,7 +595,7 @@ if(UNIX AND HAVE_DBUS)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
|
||||
|
||||
optional_source(HAVE_DBUS
|
||||
SOURCES core/mpris.cpp core/mpris2.cpp core/dbusscreensaver.cpp
|
||||
SOURCES core/mpris.cpp core/mpris2.cpp
|
||||
HEADERS core/mpris.h core/mpris2.h
|
||||
)
|
||||
optional_source(HAVE_UDISKS2
|
||||
@@ -561,31 +603,63 @@ if(UNIX AND HAVE_DBUS)
|
||||
HEADERS device/udisks2lister.h
|
||||
)
|
||||
|
||||
# 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)
|
||||
if (BUILD_WITH_QT6)
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
# org.freedesktop.Notifications DBUS interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.Notifications.xml
|
||||
dbus/notification)
|
||||
# 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.gnome.SettingsDaemon interface
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
# org.freedesktop.Notifications DBUS interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.freedesktop.Notifications.xml
|
||||
dbus/notification)
|
||||
|
||||
# org.gnome.SettingsDaemon interface
|
||||
qt6_add_dbus_interface(SOURCES
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
|
||||
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)
|
||||
|
||||
endif()
|
||||
|
||||
# org.freedesktop.Avahi.Server interface
|
||||
add_custom_command(
|
||||
@@ -626,21 +700,39 @@ if(UNIX AND HAVE_DBUS)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
endif(UNIX AND HAVE_DBUS)
|
||||
@@ -688,21 +780,6 @@ optional_source(HAVE_GIO
|
||||
HEADERS device/giolister.h
|
||||
)
|
||||
|
||||
# imobiledevice backend and device
|
||||
optional_source(HAVE_IMOBILEDEVICE
|
||||
SOURCES
|
||||
device/afcdevice.cpp
|
||||
device/afcfile.cpp
|
||||
device/afctransfer.cpp
|
||||
device/ilister.cpp
|
||||
device/imobiledeviceconnection.cpp
|
||||
HEADERS
|
||||
device/afcdevice.h
|
||||
device/afcfile.h
|
||||
device/afctransfer.h
|
||||
device/ilister.h
|
||||
)
|
||||
|
||||
# mtp device
|
||||
optional_source(HAVE_LIBMTP
|
||||
SOURCES
|
||||
@@ -783,9 +860,8 @@ optional_source(APPLE
|
||||
core/mac_utilities.mm
|
||||
core/mac_startup.mm
|
||||
core/macsystemtrayicon.mm
|
||||
core/macscreensaver.cpp
|
||||
core/macfslistener.mm
|
||||
widgets/osd_mac.mm
|
||||
osd/osdmac.mm
|
||||
widgets/qsearchfield_mac.mm
|
||||
engine/macosdevicefinder.cpp
|
||||
globalshortcuts/globalshortcutbackend-macos.mm
|
||||
@@ -793,6 +869,7 @@ optional_source(APPLE
|
||||
HEADERS
|
||||
core/macsystemtrayicon.h
|
||||
core/macfslistener.h
|
||||
osd/osdmac.h
|
||||
globalshortcuts/globalshortcutbackend-macos.h
|
||||
)
|
||||
|
||||
@@ -810,7 +887,6 @@ optional_source(WIN32
|
||||
SOURCES
|
||||
engine/directsounddevicefinder.cpp
|
||||
engine/mmdevicefinder.cpp
|
||||
widgets/osd_win.cpp
|
||||
core/windows7thumbbar.cpp
|
||||
HEADERS
|
||||
core/windows7thumbbar.h
|
||||
@@ -822,13 +898,17 @@ optional_source(HAVE_SUBSONIC
|
||||
subsonic/subsonicurlhandler.cpp
|
||||
subsonic/subsonicbaserequest.cpp
|
||||
subsonic/subsonicrequest.cpp
|
||||
subsonic/subsonicscrobblerequest.cpp
|
||||
settings/subsonicsettingspage.cpp
|
||||
scrobbler/subsonicscrobbler.cpp
|
||||
HEADERS
|
||||
subsonic/subsonicservice.h
|
||||
subsonic/subsonicurlhandler.h
|
||||
subsonic/subsonicbaserequest.h
|
||||
subsonic/subsonicrequest.h
|
||||
subsonic/subsonicscrobblerequest.h
|
||||
settings/subsonicsettingspage.h
|
||||
scrobbler/subsonicscrobbler.h
|
||||
UI
|
||||
settings/subsonicsettingspage.ui
|
||||
)
|
||||
@@ -856,6 +936,27 @@ optional_source(HAVE_TIDAL
|
||||
settings/tidalsettingspage.ui
|
||||
)
|
||||
|
||||
optional_source(HAVE_QOBUZ
|
||||
SOURCES
|
||||
qobuz/qobuzservice.cpp
|
||||
qobuz/qobuzurlhandler.cpp
|
||||
qobuz/qobuzbaserequest.cpp
|
||||
qobuz/qobuzrequest.cpp
|
||||
qobuz/qobuzstreamurlrequest.cpp
|
||||
qobuz/qobuzfavoriterequest.cpp
|
||||
settings/qobuzsettingspage.cpp
|
||||
HEADERS
|
||||
qobuz/qobuzservice.h
|
||||
qobuz/qobuzurlhandler.h
|
||||
qobuz/qobuzbaserequest.h
|
||||
qobuz/qobuzrequest.h
|
||||
qobuz/qobuzstreamurlrequest.h
|
||||
qobuz/qobuzfavoriterequest.h
|
||||
settings/qobuzsettingspage.h
|
||||
UI
|
||||
settings/qobuzsettingspage.ui
|
||||
)
|
||||
|
||||
# Moodbar
|
||||
optional_source(HAVE_MOODBAR
|
||||
SOURCES
|
||||
@@ -881,9 +982,15 @@ 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)
|
||||
|
||||
qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_wrap_ui(UIC ${UI})
|
||||
qt5_add_resources(QRC ${RESOURCES})
|
||||
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()
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
|
||||
@@ -926,6 +1033,8 @@ link_directories(
|
||||
${QT_LIBRARY_DIRS}
|
||||
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
||||
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
|
||||
${QTSPARKLE_LIBRARY_DIRS}
|
||||
${Iconv_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(HAVE_ALSA)
|
||||
@@ -947,10 +1056,6 @@ if(HAVE_GSTREAMER)
|
||||
)
|
||||
endif(HAVE_GSTREAMER)
|
||||
|
||||
if(HAVE_XINE)
|
||||
link_directories(${LIBXINE_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(HAVE_VLC)
|
||||
link_directories(${LIBVLC_LIBRARY_DIRS})
|
||||
endif()
|
||||
@@ -976,21 +1081,14 @@ if(HAVE_AUDIOCD)
|
||||
endif(HAVE_AUDIOCD)
|
||||
|
||||
if(HAVE_LIBGPOD)
|
||||
link_directories(${LIBGPOD_LIBRARIES})
|
||||
link_directories(${LIBGPOD_LIBRARY_DIRS})
|
||||
link_directories(${GDK_PIXBUF_LIBRARY_DIRS})
|
||||
endif(HAVE_LIBGPOD)
|
||||
|
||||
if(HAVE_LIBMTP)
|
||||
link_directories(${LIBMTP_LIBRARY_DIRS})
|
||||
endif(HAVE_LIBMTP)
|
||||
|
||||
if(HAVE_IMOBILEDEVICE)
|
||||
link_directories(
|
||||
${LIBUSBMUXD_LIBRARY_DIRS}
|
||||
${LIBPLIST_LIBRARY_DIRS}
|
||||
${LIBIMOBILEDEVICE_LIBRARY_DIRS}
|
||||
)
|
||||
endif(HAVE_IMOBILEDEVICE)
|
||||
|
||||
add_library(strawberry_lib STATIC
|
||||
${SOURCES}
|
||||
${MOC}
|
||||
@@ -1033,6 +1131,8 @@ target_link_libraries(strawberry_lib PUBLIC
|
||||
${QT_LIBRARIES}
|
||||
${SINGLEAPPLICATION_LIBRARIES}
|
||||
${SINGLECOREAPPLICATION_LIBRARIES}
|
||||
${QTSPARKLE_LIBRARIES}
|
||||
${Iconv_LIBRARY}
|
||||
libstrawberry-common
|
||||
libstrawberry-tagreader
|
||||
)
|
||||
@@ -1070,11 +1170,6 @@ if(HAVE_MOODBAR)
|
||||
target_link_libraries(strawberry_lib PRIVATE gstmoodbar)
|
||||
endif()
|
||||
|
||||
if(HAVE_XINE)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBXINE_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBXINE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_VLC)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBVLC_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES})
|
||||
@@ -1106,8 +1201,8 @@ if(HAVE_AUDIOCD)
|
||||
endif(HAVE_AUDIOCD)
|
||||
|
||||
if(HAVE_LIBGPOD)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBGPOD_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBGPOD_LIBRARIES})
|
||||
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)
|
||||
|
||||
if(HAVE_LIBMTP)
|
||||
@@ -1115,19 +1210,6 @@ if(HAVE_LIBMTP)
|
||||
target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES})
|
||||
endif(HAVE_LIBMTP)
|
||||
|
||||
if(HAVE_IMOBILEDEVICE)
|
||||
target_include_directories(strawberry_lib SYSTEM PRIVATE
|
||||
${LIBUSBMUXD_INCLUDE_DIRS}
|
||||
${LIBPLIST_INCLUDE_DIRS}
|
||||
${LIBIMOBILEDEVICE_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(strawberry_lib PRIVATE
|
||||
${LIBUSBMUXD_LIBRARIES}
|
||||
${LIBPLIST_LIBRARIES}
|
||||
${LIBIMOBILEDEVICE_LIBRARIES}
|
||||
)
|
||||
endif(HAVE_IMOBILEDEVICE)
|
||||
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(strawberry_lib PRIVATE
|
||||
@@ -1149,11 +1231,10 @@ if(WIN32)
|
||||
target_link_libraries(strawberry_lib PRIVATE dsound)
|
||||
endif(WIN32)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
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.
|
||||
find_package(X11)
|
||||
if(FREEBSD)
|
||||
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB})
|
||||
else()
|
||||
@@ -1196,6 +1277,10 @@ if(NOT APPLE)
|
||||
install(TARGETS strawberry RUNTIME DESTINATION bin)
|
||||
endif()
|
||||
|
||||
if(HAVE_TRANSLATIONS AND INSTALL_TRANSLATIONS AND INSTALL_TRANSLATIONS_FILES)
|
||||
install(FILES ${INSTALL_TRANSLATIONS_FILES} DESTINATION share/strawberry/translations)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist")
|
||||
endif (APPLE)
|
||||
|
||||
@@ -99,10 +99,14 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
|
||||
if (engine_->type() != Engine::EngineType::GStreamer) return;
|
||||
|
||||
if (e->button() == Qt::RightButton) {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
context_menu_->popup(e->globalPosition().toPoint());
|
||||
#else
|
||||
context_menu_->popup(e->globalPos());
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
#include <QPoint>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
|
||||
#include "engine/engine_fwd.h"
|
||||
|
||||
class QTimer;
|
||||
class QActionGroup;
|
||||
class QMouseEvent;
|
||||
class QWheelEvent;
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
|
||||
|
||||
void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
|
||||
const QColor bg = palette().color(QPalette::Background);
|
||||
const QColor bg = palette().color(QPalette::Window);
|
||||
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
|
||||
|
||||
topbarpixmap_.fill(fg);
|
||||
@@ -343,12 +343,12 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
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::Background).darker(112);
|
||||
const QColor bg2 = palette().color(QPalette::Window).darker(112);
|
||||
|
||||
// make a complimentary fadebar colour
|
||||
// TODO dark is not always correct, dumbo!
|
||||
int h, s, v;
|
||||
palette().color(QPalette::Background).darker(150).getHsv(&h, &s, &v);
|
||||
palette().color(QPalette::Window).darker(150).getHsv(&h, &s, &v);
|
||||
const QColor fg2(QColor::fromHsv(h + 120, s, v));
|
||||
|
||||
const double dr2 = fg2.red() - bg2.red();
|
||||
@@ -358,7 +358,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
|
||||
// Precalculate all fade-bar pixmaps
|
||||
for (uint y = 0; y < kFadeSize; ++y) {
|
||||
fade_bars_[y].fill(palette().color(QPalette::Background));
|
||||
fade_bars_[y].fill(palette().color(QPalette::Window));
|
||||
QPainter f(&fade_bars_[y]);
|
||||
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
|
||||
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
|
||||
@@ -377,7 +377,7 @@ void BlockAnalyzer::drawBackground() {
|
||||
return;
|
||||
}
|
||||
|
||||
const QColor bg = palette().color(QPalette::Background);
|
||||
const QColor bg = palette().color(QPalette::Window);
|
||||
const QColor bgdark = bg.darker(112);
|
||||
|
||||
background_.fill(bg);
|
||||
|
||||
@@ -87,7 +87,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
||||
canvas_ = QPixmap(size());
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
canvas_.fill(palette().color(QPalette::Window));
|
||||
|
||||
QPainter p(&barPixmap_);
|
||||
for (uint y = 0; y < HEIGHT; ++y) {
|
||||
@@ -120,7 +120,7 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
const uint MAX_HEIGHT = height() - 1;
|
||||
|
||||
QPainter canvas_painter(&canvas_);
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
canvas_.fill(palette().color(QPalette::Window));
|
||||
|
||||
Analyzer::interpolate(scope, scope_);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "collectionbackend.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
#include "scrobbler/lastfmimport.h"
|
||||
|
||||
const char *SCollection::kSongsTable = "songs";
|
||||
const char *SCollection::kDirsTable = "directories";
|
||||
@@ -106,10 +107,12 @@ void SCollection::Init() {
|
||||
connect(watcher_, SIGNAL(SubdirsDiscovered(SubdirectoryList)), backend_, SLOT(AddOrUpdateSubdirs(SubdirectoryList)));
|
||||
connect(watcher_, SIGNAL(SubdirsMTimeUpdated(SubdirectoryList)), backend_, SLOT(AddOrUpdateSubdirs(SubdirectoryList)));
|
||||
connect(watcher_, SIGNAL(CompilationsNeedUpdating()), backend_, SLOT(UpdateCompilations()));
|
||||
connect(backend_, SIGNAL(SongsStatisticsChanged(SongList)), SLOT(SongsStatisticsChanged(SongList)));
|
||||
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
|
||||
connect(app_->player(), SIGNAL(Stopped()), SLOT(Stopped()));
|
||||
|
||||
connect(app_->lastfm_import(), SIGNAL(UpdateLastPlayed(QString, QString, QString, int)), backend_, SLOT(UpdateLastPlayed(QString, QString, QString, int)));
|
||||
connect(app_->lastfm_import(), SIGNAL(UpdatePlayCount(QString, QString, int)), backend_, SLOT(UpdatePlayCount(QString, QString, int)));
|
||||
|
||||
// This will start the watcher checking for updates
|
||||
backend_->LoadDirectoriesAsync();
|
||||
|
||||
@@ -131,7 +134,7 @@ void SCollection::Exit() {
|
||||
|
||||
void SCollection::ExitReceived() {
|
||||
|
||||
QObject *obj = static_cast<QObject*>(sender());
|
||||
QObject *obj = qobject_cast<QObject*>(sender());
|
||||
disconnect(obj, nullptr, this, nullptr);
|
||||
qLog(Debug) << obj << "successfully exited.";
|
||||
wait_for_exit_.removeAll(obj);
|
||||
@@ -179,7 +182,3 @@ void SCollection::CurrentSongChanged(const Song &song) { // FIXME
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SCollection::SongsStatisticsChanged(const SongList &songs) {
|
||||
Q_UNUSED(songs);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ class SCollection : public QObject {
|
||||
void IncrementalScan();
|
||||
|
||||
void CurrentSongChanged(const Song &song);
|
||||
void SongsStatisticsChanged(const SongList& songs);
|
||||
void Stopped();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -38,12 +38,14 @@
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/database.h"
|
||||
#include "core/scopedtransaction.h"
|
||||
#include "smartplaylists/smartplaylistsearch.h"
|
||||
|
||||
#include "directory.h"
|
||||
#include "collectionbackend.h"
|
||||
@@ -108,15 +110,15 @@ void CollectionBackend::UpdateTotalAlbumCountAsync() {
|
||||
metaObject()->invokeMethod(this, "UpdateTotalAlbumCount", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void CollectionBackend::IncrementPlayCountAsync(int id) {
|
||||
void CollectionBackend::IncrementPlayCountAsync(const int id) {
|
||||
metaObject()->invokeMethod(this, "IncrementPlayCount", Qt::QueuedConnection, Q_ARG(int, id));
|
||||
}
|
||||
|
||||
void CollectionBackend::IncrementSkipCountAsync(int id, float progress) {
|
||||
void CollectionBackend::IncrementSkipCountAsync(const int id, const float progress) {
|
||||
metaObject()->invokeMethod(this, "IncrementSkipCount", Qt::QueuedConnection, Q_ARG(int, id), Q_ARG(float, progress));
|
||||
}
|
||||
|
||||
void CollectionBackend::ResetStatisticsAsync(int id) {
|
||||
void CollectionBackend::ResetStatisticsAsync(const int id) {
|
||||
metaObject()->invokeMethod(this, "ResetStatistics", Qt::QueuedConnection, Q_ARG(int, id));
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ void CollectionBackend::LoadDirectories() {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::ChangeDirPath(int id, const QString &old_path, const QString &new_path) {
|
||||
void CollectionBackend::ChangeDirPath(const int id, const QString &old_path, const QString &new_path) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
@@ -201,7 +203,7 @@ DirectoryList CollectionBackend::GetAllDirectories() {
|
||||
|
||||
}
|
||||
|
||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(int id) {
|
||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(const int id) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db = db_->Connect();
|
||||
@@ -209,7 +211,7 @@ SubdirectoryList CollectionBackend::SubdirsInDirectory(int id) {
|
||||
|
||||
}
|
||||
|
||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(int id, QSqlDatabase &db) {
|
||||
SubdirectoryList CollectionBackend::SubdirsInDirectory(const int id, QSqlDatabase &db) {
|
||||
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("SELECT path, mtime FROM %1 WHERE directory_id = :dir").arg(subdirs_table_));
|
||||
@@ -222,7 +224,7 @@ SubdirectoryList CollectionBackend::SubdirsInDirectory(int id, QSqlDatabase &db)
|
||||
Subdirectory subdir;
|
||||
subdir.directory_id = id;
|
||||
subdir.path = q.value(0).toString();
|
||||
subdir.mtime = q.value(1).toUInt();
|
||||
subdir.mtime = q.value(1).toLongLong();
|
||||
subdirs << subdir;
|
||||
}
|
||||
|
||||
@@ -327,7 +329,7 @@ void CollectionBackend::RemoveDirectory(const Directory &dir) {
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::FindSongsInDirectory(int id) {
|
||||
SongList CollectionBackend::FindSongsInDirectory(const int id) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
@@ -581,7 +583,7 @@ void CollectionBackend::DeleteSongs(const SongList &songs) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::MarkSongsUnavailable(const SongList &songs, bool unavailable) {
|
||||
void CollectionBackend::MarkSongsUnavailable(const SongList &songs, const bool unavailable) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
@@ -700,7 +702,7 @@ SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongById(int id) {
|
||||
Song CollectionBackend::GetSongById(const int id) {
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
return GetSongById(id, db);
|
||||
@@ -749,7 +751,7 @@ SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QS
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongById(int id, QSqlDatabase &db) {
|
||||
Song CollectionBackend::GetSongById(const int id, QSqlDatabase &db) {
|
||||
SongList list = GetSongsById(QStringList() << QString::number(id), db);
|
||||
if (list.isEmpty()) return Song();
|
||||
return list.first();
|
||||
@@ -999,10 +1001,8 @@ void CollectionBackend::UpdateCompilations(QSqlQuery &find_song, QSqlQuery &upda
|
||||
|
||||
CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt) {
|
||||
|
||||
AlbumList ret;
|
||||
|
||||
CollectionQuery query(opt);
|
||||
query.SetColumnSpec("album, artist, albumartist, compilation, compilation_detected, art_automatic, art_manual, url");
|
||||
query.SetColumnSpec("url, artist, albumartist, album, compilation_effective, art_automatic, art_manual");
|
||||
query.SetOrderBy("album");
|
||||
|
||||
if (compilation_required) {
|
||||
@@ -1015,23 +1015,23 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
|
||||
|
||||
{
|
||||
QMutexLocker l(db_->Mutex());
|
||||
if (!ExecQuery(&query)) return ret;
|
||||
if (!ExecQuery(&query)) return AlbumList();
|
||||
}
|
||||
|
||||
QString last_album;
|
||||
QString last_artist;
|
||||
QString last_album_artist;
|
||||
QMap<QString, Album> albums;
|
||||
while (query.Next()) {
|
||||
bool is_compilation = query.Value(3).toBool() | query.Value(4).toBool();
|
||||
bool is_compilation = query.Value(4).toBool();
|
||||
|
||||
Album info;
|
||||
info.artist = is_compilation ? QString() : query.Value(1).toString();
|
||||
info.album_artist = is_compilation ? QString() : query.Value(2).toString();
|
||||
info.album_name = query.Value(0).toString();
|
||||
info.first_url = QUrl::fromEncoded(query.Value(7).toByteArray());
|
||||
info.first_url = QUrl::fromEncoded(query.Value(0).toByteArray());
|
||||
if (!is_compilation) {
|
||||
info.artist = query.Value(1).toString();
|
||||
info.album_artist = query.Value(2).toString();
|
||||
}
|
||||
info.album_name = query.Value(3).toString();
|
||||
|
||||
QString art_automatic = query.Value(5).toString();
|
||||
if (art_automatic.contains(QRegExp("..+:.*"))) {
|
||||
if (art_automatic.contains(QRegularExpression("..+:.*"))) {
|
||||
info.art_automatic = QUrl::fromEncoded(art_automatic.toUtf8());
|
||||
}
|
||||
else {
|
||||
@@ -1039,24 +1039,30 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
|
||||
}
|
||||
|
||||
QString art_manual = query.Value(6).toString();
|
||||
if (art_manual.contains(QRegExp("..+:.*"))) {
|
||||
if (art_manual.contains(QRegularExpression("..+:.*"))) {
|
||||
info.art_manual = QUrl::fromEncoded(art_manual.toUtf8());
|
||||
}
|
||||
else {
|
||||
info.art_manual = QUrl::fromLocalFile(art_manual);
|
||||
}
|
||||
|
||||
if ((info.artist == last_artist || info.album_artist == last_album_artist) && info.album_name == last_album)
|
||||
continue;
|
||||
QString effective_albumartist = info.album_artist.isEmpty() ? info.artist : info.album_artist;
|
||||
QString key;
|
||||
if (!effective_albumartist.isEmpty()) {
|
||||
key.append(effective_albumartist);
|
||||
}
|
||||
if (!info.album_name.isEmpty()) {
|
||||
if (!key.isEmpty()) key.append("-");
|
||||
key.append(info.album_name);
|
||||
}
|
||||
|
||||
ret << info;
|
||||
if (key.isEmpty()) continue;
|
||||
|
||||
if (!albums.contains(key)) albums.insert(key, info);
|
||||
|
||||
last_album = info.album_name;
|
||||
last_artist = info.artist;
|
||||
last_album_artist = info.album_artist;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return albums.values();
|
||||
|
||||
}
|
||||
|
||||
@@ -1163,7 +1169,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::ForceCompilation(const QString &album, const QList<QString> &artists, bool on) {
|
||||
void CollectionBackend::ForceCompilation(const QString &album, const QList<QString> &artists, const bool on) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
@@ -1174,7 +1180,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
|
||||
CollectionQuery query;
|
||||
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
|
||||
query.AddWhere("album", album);
|
||||
if (!artist.isNull() && !artist.isEmpty()) query.AddWhere("artist", artist);
|
||||
if (!artist.isEmpty()) query.AddWhere("artist", artist);
|
||||
|
||||
if (!ExecQuery(&query)) return;
|
||||
|
||||
@@ -1219,7 +1225,7 @@ bool CollectionBackend::ExecQuery(CollectionQuery *q) {
|
||||
return !db_->CheckErrors(q->Exec(db_->Connect(), songs_table_, fts_table_));
|
||||
}
|
||||
|
||||
void CollectionBackend::IncrementPlayCount(int id) {
|
||||
void CollectionBackend::IncrementPlayCount(const int id) {
|
||||
|
||||
if (id == -1) return;
|
||||
|
||||
@@ -1228,7 +1234,7 @@ void CollectionBackend::IncrementPlayCount(int id) {
|
||||
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET playcount = playcount + 1, lastplayed = :now WHERE ROWID = :id").arg(songs_table_));
|
||||
q.bindValue(":now", QDateTime::currentDateTime().toTime_t());
|
||||
q.bindValue(":now", QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
q.bindValue(":id", id);
|
||||
q.exec();
|
||||
if (db_->CheckErrors(q)) return;
|
||||
@@ -1238,7 +1244,7 @@ void CollectionBackend::IncrementPlayCount(int id) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::IncrementSkipCount(int id, float progress) {
|
||||
void CollectionBackend::IncrementSkipCount(const int id, const float progress) {
|
||||
|
||||
Q_UNUSED(progress);
|
||||
|
||||
@@ -1258,7 +1264,7 @@ void CollectionBackend::IncrementSkipCount(int id, float progress) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::ResetStatistics(int id) {
|
||||
void CollectionBackend::ResetStatistics(const int id) {
|
||||
|
||||
if (id == -1) return;
|
||||
|
||||
@@ -1297,3 +1303,152 @@ void CollectionBackend::DeleteAll() {
|
||||
emit DatabaseReset();
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::FindSongs(const SmartPlaylistSearch &search) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
// Build the query
|
||||
QString sql = search.ToSql(songs_table());
|
||||
|
||||
// Run the query
|
||||
SongList ret;
|
||||
QSqlQuery query(db);
|
||||
query.prepare(sql);
|
||||
query.exec();
|
||||
if (db_->CheckErrors(query)) return ret;
|
||||
|
||||
// Read the results
|
||||
while (query.next()) {
|
||||
Song song;
|
||||
song.InitFromQuery(query, true);
|
||||
ret << song;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetAllSongs() {
|
||||
|
||||
// Get all the songs!
|
||||
return FindSongs(SmartPlaylistSearch(SmartPlaylistSearch::Type_All, SmartPlaylistSearch::TermList(), SmartPlaylistSearch::Sort_FieldAsc, SmartPlaylistSearchTerm::Field_Artist, -1));
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetSongsBy(const QString &artist, const QString &album, const QString &title) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
SongList songs;
|
||||
QSqlQuery q(db);
|
||||
if (album.isEmpty()) {
|
||||
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE artist = :artist COLLATE NOCASE AND title = :title COLLATE NOCASE").arg(songs_table_));
|
||||
}
|
||||
else {
|
||||
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE artist = :artist COLLATE NOCASE AND album = :album COLLATE NOCASE AND title = :title COLLATE NOCASE").arg(songs_table_));
|
||||
}
|
||||
q.bindValue(":artist", artist);
|
||||
if (!album.isEmpty()) q.bindValue(":album", album);
|
||||
q.bindValue(":title", title);
|
||||
q.exec();
|
||||
if (db_->CheckErrors(q)) return SongList();
|
||||
while (q.next()) {
|
||||
Song song(source_);
|
||||
song.InitFromQuery(q, true);
|
||||
songs << song;
|
||||
}
|
||||
|
||||
return songs;
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed) {
|
||||
|
||||
SongList songs = GetSongsBy(artist, album, title);
|
||||
if (songs.isEmpty()) {
|
||||
qLog(Debug) << "Could not find a matching song in the database for" << artist << album << title;
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
for (const Song &song : songs) {
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET lastplayed = :lastplayed WHERE ROWID = :id").arg(songs_table_));
|
||||
q.bindValue(":lastplayed", lastplayed);
|
||||
q.bindValue(":id", song.id());
|
||||
q.exec();
|
||||
if (db_->CheckErrors(q)) continue;
|
||||
}
|
||||
|
||||
emit SongsStatisticsChanged(SongList() << songs);
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdatePlayCount(const QString &artist, const QString &title, const int playcount) {
|
||||
|
||||
SongList songs = GetSongsBy(artist, QString(), title);
|
||||
if (songs.isEmpty()) {
|
||||
qLog(Debug) << "Could not find a matching song in the database for" << artist << title;
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
for (const Song &song : songs) {
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET playcount = :playcount WHERE ROWID = :id").arg(songs_table_));
|
||||
q.bindValue(":playcount", playcount);
|
||||
q.bindValue(":id", song.id());
|
||||
q.exec();
|
||||
if (db_->CheckErrors(q)) continue;
|
||||
}
|
||||
|
||||
emit SongsStatisticsChanged(SongList() << songs);
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongRating(const int id, const float rating) {
|
||||
|
||||
if (id == -1) return;
|
||||
|
||||
QList<int> id_list;
|
||||
id_list << id;
|
||||
UpdateSongsRating(id_list, rating);
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongsRating(const QList<int> &id_list, const float rating) {
|
||||
|
||||
if (id_list.isEmpty()) return;
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
QStringList id_str_list;
|
||||
for (int i : id_list) {
|
||||
id_str_list << QString::number(i);
|
||||
}
|
||||
QString ids = id_str_list.join(",");
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("UPDATE %1 SET rating = :rating WHERE ROWID IN (%2)").arg(songs_table_, ids));
|
||||
q.bindValue(":rating", rating);
|
||||
q.exec();
|
||||
if (db_->CheckErrors(q)) return;
|
||||
SongList new_song_list = GetSongsById(id_str_list, db);
|
||||
emit SongsRatingChanged(new_song_list);
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongRatingAsync(const int id, const float rating) {
|
||||
metaObject()->invokeMethod(this, "UpdateSongRating", Qt::QueuedConnection, Q_ARG(int, id), Q_ARG(float, rating));
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateSongsRatingAsync(const QList<int>& ids, const float rating) {
|
||||
metaObject()->invokeMethod(this, "UpdateSongsRating", Qt::QueuedConnection, Q_ARG(QList<int>, ids), Q_ARG(float, rating));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
class QThread;
|
||||
class Database;
|
||||
class SmartPlaylistSearch;
|
||||
|
||||
class CollectionBackendInterface : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -80,10 +81,10 @@ class CollectionBackendInterface : public QObject {
|
||||
virtual void UpdateTotalArtistCountAsync() = 0;
|
||||
virtual void UpdateTotalAlbumCountAsync() = 0;
|
||||
|
||||
virtual SongList FindSongsInDirectory(int id) = 0;
|
||||
virtual SubdirectoryList SubdirsInDirectory(int id) = 0;
|
||||
virtual SongList FindSongsInDirectory(const int id) = 0;
|
||||
virtual SubdirectoryList SubdirsInDirectory(const int id) = 0;
|
||||
virtual DirectoryList GetAllDirectories() = 0;
|
||||
virtual void ChangeDirPath(int id, const QString &old_path, const QString &new_path) = 0;
|
||||
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
|
||||
|
||||
virtual QStringList GetAllArtists(const QueryOptions &opt = QueryOptions()) = 0;
|
||||
virtual QStringList GetAllArtistsWithAlbums(const QueryOptions &opt = QueryOptions()) = 0;
|
||||
@@ -99,7 +100,7 @@ class CollectionBackendInterface : public QObject {
|
||||
virtual void UpdateManualAlbumArtAsync(const QString &artist, const QString &albumartist, const QString &album, const QUrl &cover_url) = 0;
|
||||
virtual Album GetAlbumArt(const QString &artist, const QString &albumartist, const QString &album) = 0;
|
||||
|
||||
virtual Song GetSongById(int id) = 0;
|
||||
virtual Song GetSongById(const int id) = 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;
|
||||
@@ -182,10 +183,16 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
Song GetSongBySongId(const QString &song_id);
|
||||
SongList GetSongsBySongId(const QStringList &song_ids);
|
||||
|
||||
SongList GetAllSongs();
|
||||
SongList FindSongs(const SmartPlaylistSearch &search);
|
||||
|
||||
Song::Source Source() const;
|
||||
|
||||
void AddOrUpdateSongsAsync(const SongList &songs);
|
||||
|
||||
void UpdateSongRatingAsync(const int id, const float rating);
|
||||
void UpdateSongsRatingAsync(const QList<int> &ids, const float rating);
|
||||
|
||||
public slots:
|
||||
void Exit();
|
||||
void LoadDirectories();
|
||||
@@ -195,7 +202,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void AddOrUpdateSongs(const SongList &songs);
|
||||
void UpdateMTimesOnly(const SongList &songs);
|
||||
void DeleteSongs(const SongList &songs);
|
||||
void MarkSongsUnavailable(const SongList &songs, bool unavailable = true);
|
||||
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
|
||||
void AddOrUpdateSubdirs(const SubdirectoryList &subdirs);
|
||||
void UpdateCompilations();
|
||||
void UpdateManualAlbumArt(const QString &artist, const QString &albumartist, const QString &album, const QUrl &cover_url);
|
||||
@@ -205,19 +212,27 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void ResetStatistics(const int id);
|
||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||
|
||||
signals:
|
||||
void DirectoryDiscovered(const Directory &dir, const SubdirectoryList &subdirs);
|
||||
void DirectoryDeleted(const Directory &dir);
|
||||
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
|
||||
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed);
|
||||
void UpdatePlayCount(const QString &artist, const QString &title, const int playcount);
|
||||
|
||||
void SongsDiscovered(const SongList &songs);
|
||||
void SongsDeleted(const SongList &songs);
|
||||
void SongsStatisticsChanged(const SongList& songs);
|
||||
void UpdateSongRating(const int id, const float rating);
|
||||
void UpdateSongsRating(const QList<int> &id_list, const float rating);
|
||||
|
||||
signals:
|
||||
void DirectoryDiscovered(Directory, SubdirectoryList);
|
||||
void DirectoryDeleted(Directory);
|
||||
|
||||
void SongsDiscovered(SongList);
|
||||
void SongsDeleted(SongList);
|
||||
void SongsStatisticsChanged(SongList);
|
||||
|
||||
void DatabaseReset();
|
||||
|
||||
void TotalSongCountUpdated(const int total);
|
||||
void TotalArtistCountUpdated(const int total);
|
||||
void TotalAlbumCountUpdated(const int total);
|
||||
void TotalSongCountUpdated(int);
|
||||
void TotalArtistCountUpdated(int);
|
||||
void TotalAlbumCountUpdated(int);
|
||||
void SongsRatingChanged(SongList);
|
||||
|
||||
void ExitFinished();
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QInputDialog>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
@@ -63,27 +63,26 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
|
||||
ui_->setupUi(this);
|
||||
|
||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), "");
|
||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
|
||||
|
||||
ui_->filter->setToolTip(
|
||||
"<html><head/><body><p>" +
|
||||
QString("<html><head/><body><p>") +
|
||||
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
|
||||
" " +
|
||||
"<span style=\"font-weight:600;\">" +
|
||||
QString(" ") +
|
||||
QString("<span style=\"font-weight:600;\">") +
|
||||
tr("artist") +
|
||||
":" +
|
||||
"</span><span style=\"font-style:italic;\">Strawbs</span>" +
|
||||
" " +
|
||||
QString(":") +
|
||||
QString("</span><span style=\"font-style:italic;\">Strawbs</span>") +
|
||||
QString(" ") +
|
||||
tr("searches the collection for all artists that contain the word") +
|
||||
"Strawbs" +
|
||||
"." +
|
||||
"</p><p><span style=\"font-weight:600;\">" +
|
||||
QString(" Strawbs.") +
|
||||
QString("</p><p><span style=\"font-weight:600;\">") +
|
||||
tr("Available fields") +
|
||||
": " +
|
||||
QString(": ") +
|
||||
"</span><span style=\"font-style:italic;\">" +
|
||||
available_fields +
|
||||
"</span>." +
|
||||
"</p></body></html>"
|
||||
QString("</span>.") +
|
||||
QString("</p></body></html>")
|
||||
);
|
||||
|
||||
connect(ui_->filter, SIGNAL(returnPressed()), SIGNAL(ReturnPressed()));
|
||||
@@ -164,6 +163,17 @@ QString CollectionFilterWidget::group_by() {
|
||||
|
||||
}
|
||||
|
||||
QString CollectionFilterWidget::group_by_version() {
|
||||
|
||||
if (settings_prefix_.isEmpty()) {
|
||||
return QString("group_by_version");
|
||||
}
|
||||
else {
|
||||
return QString("%1_group_by_version").arg(settings_prefix_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString CollectionFilterWidget::group_by(const int number) { return group_by() + QString::number(number); }
|
||||
|
||||
void CollectionFilterWidget::UpdateGroupByActions() {
|
||||
@@ -214,14 +224,26 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) {
|
||||
// read saved groupings
|
||||
QSettings s;
|
||||
s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup);
|
||||
QStringList saved = s.childKeys();
|
||||
for (int i = 0; i < saved.size(); ++i) {
|
||||
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
||||
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
||||
CollectionModel::Grouping g;
|
||||
ds >> g;
|
||||
ret->addAction(CreateGroupByAction(saved.at(i), parent, g));
|
||||
int version = s.value("version").toInt();
|
||||
if (version == 1) {
|
||||
QStringList saved = s.childKeys();
|
||||
for (int i = 0; i < saved.size(); ++i) {
|
||||
if (saved.at(i) == "version") continue;
|
||||
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
||||
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
||||
CollectionModel::Grouping g;
|
||||
ds >> g;
|
||||
ret->addAction(CreateGroupByAction(saved.at(i), parent, g));
|
||||
}
|
||||
}
|
||||
else {
|
||||
QStringList saved = s.childKeys();
|
||||
for (int i = 0; i < saved.size(); ++i) {
|
||||
if (saved.at(i) == "version") continue;
|
||||
s.remove(saved.at(i));
|
||||
}
|
||||
}
|
||||
s.endGroup();
|
||||
|
||||
QAction *sep2 = new QAction(parent);
|
||||
sep2->setSeparator(true);
|
||||
@@ -302,10 +324,18 @@ void CollectionFilterWidget::SetCollectionModel(CollectionModel *model) {
|
||||
if (!settings_group_.isEmpty()) {
|
||||
QSettings s;
|
||||
s.beginGroup(settings_group_);
|
||||
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())));
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -328,9 +358,11 @@ void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping &g)
|
||||
// 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.endGroup();
|
||||
}
|
||||
|
||||
// Now make sure the correct action is checked
|
||||
|
||||
@@ -77,6 +77,7 @@ class CollectionFilterWidget : public QWidget {
|
||||
void SetCollectionModel(CollectionModel *model);
|
||||
|
||||
QString group_by();
|
||||
QString group_by_version();
|
||||
QString group_by(const int number);
|
||||
|
||||
void ReloadSettings();
|
||||
|
||||
@@ -33,7 +33,6 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
|
||||
Type_Divider,
|
||||
Type_Container,
|
||||
Type_Song,
|
||||
Type_PlaylistContainer,
|
||||
Type_LoadingIndicator,
|
||||
};
|
||||
|
||||
|
||||
@@ -85,25 +85,27 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
// These values get saved in QSettings - don't change them
|
||||
enum GroupBy {
|
||||
GroupBy_None = 0,
|
||||
GroupBy_Artist = 1,
|
||||
GroupBy_Album = 2,
|
||||
GroupBy_YearAlbum = 3,
|
||||
GroupBy_Year = 4,
|
||||
GroupBy_Composer = 5,
|
||||
GroupBy_Genre = 6,
|
||||
GroupBy_AlbumArtist = 7,
|
||||
GroupBy_FileType = 8,
|
||||
GroupBy_Performer = 9,
|
||||
GroupBy_Grouping = 10,
|
||||
GroupBy_Bitrate = 11,
|
||||
GroupBy_Disc = 12,
|
||||
GroupBy_OriginalYearAlbum = 13,
|
||||
GroupBy_OriginalYear = 14,
|
||||
GroupBy_Samplerate = 15,
|
||||
GroupBy_Bitdepth = 16,
|
||||
GroupBy_AlbumArtist = 1,
|
||||
GroupBy_Artist = 2,
|
||||
GroupBy_Album = 3,
|
||||
GroupBy_AlbumDisc = 4,
|
||||
GroupBy_YearAlbum = 5,
|
||||
GroupBy_YearAlbumDisc = 6,
|
||||
GroupBy_OriginalYearAlbum = 7,
|
||||
GroupBy_OriginalYearAlbumDisc = 8,
|
||||
GroupBy_Disc = 9,
|
||||
GroupBy_Year = 10,
|
||||
GroupBy_OriginalYear = 11,
|
||||
GroupBy_Genre = 12,
|
||||
GroupBy_Composer = 13,
|
||||
GroupBy_Performer = 14,
|
||||
GroupBy_Grouping = 15,
|
||||
GroupBy_FileType = 16,
|
||||
GroupBy_Format = 17,
|
||||
GroupBy_AlbumDisc = 18,
|
||||
GroupBy_YearAlbumDisc = 19
|
||||
GroupBy_Samplerate = 18,
|
||||
GroupBy_Bitdepth = 19,
|
||||
GroupBy_Bitrate = 20,
|
||||
GroupByCount = 21,
|
||||
};
|
||||
|
||||
struct Grouping {
|
||||
@@ -170,6 +172,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
static QString PrettyYearAlbum(const int year, const QString &album);
|
||||
static QString PrettyAlbumDisc(const QString &album, const int disc);
|
||||
static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc);
|
||||
static QString PrettyDisc(const int disc);
|
||||
static QString SortText(QString text);
|
||||
static QString SortTextForNumber(const int number);
|
||||
static QString SortTextForArtist(QString artist);
|
||||
@@ -179,7 +182,18 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
|
||||
quint64 icon_cache_disk_size() { return sIconCache->cacheSize(); }
|
||||
|
||||
static bool IsAlbumGrouping(const GroupBy group_by) { return group_by == GroupBy_Album || group_by == GroupBy_YearAlbum || group_by == GroupBy_OriginalYearAlbum || group_by == GroupBy_AlbumDisc || group_by == GroupBy_YearAlbumDisc; }
|
||||
static bool IsArtistGroupBy(const GroupBy group_by) {
|
||||
return group_by == CollectionModel::GroupBy_Artist || group_by == CollectionModel::GroupBy_AlbumArtist;
|
||||
}
|
||||
static bool IsAlbumGroupBy(const GroupBy group_by) { return group_by == GroupBy_Album || group_by == GroupBy_YearAlbum || group_by == GroupBy_AlbumDisc || group_by == GroupBy_YearAlbumDisc || group_by == GroupBy_OriginalYearAlbum || group_by == GroupBy_OriginalYearAlbumDisc; }
|
||||
|
||||
void set_use_lazy_loading(const bool value) { use_lazy_loading_ = value; }
|
||||
|
||||
QMap<QString, CollectionItem*> container_nodes(const int i) { return container_nodes_[i]; }
|
||||
QList<CollectionItem*> song_nodes() const { return song_nodes_.values(); }
|
||||
int divider_nodes_count() const { return divider_nodes_.count(); }
|
||||
|
||||
void ExpandAll(CollectionItem *item = nullptr) const;
|
||||
|
||||
signals:
|
||||
void TotalSongCountUpdated(const int count);
|
||||
@@ -248,6 +262,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
QString DividerDisplayText(const GroupBy type, const QString &key) const;
|
||||
|
||||
// Helpers
|
||||
static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; }
|
||||
QString AlbumIconPixmapCacheKey(const QModelIndex &idx) const;
|
||||
QVariant AlbumIcon(const QModelIndex &idx);
|
||||
QVariant data(const CollectionItem *item, const int role) const;
|
||||
@@ -280,8 +295,6 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
QIcon album_icon_;
|
||||
// Used as a generic icon to show when no cover art is found, fixed to the same size as the artwork (32x32)
|
||||
QPixmap no_cover_icon_;
|
||||
QIcon playlists_dir_icon_;
|
||||
QIcon playlist_icon_;
|
||||
|
||||
static QNetworkDiskCache *sIconCache;
|
||||
|
||||
@@ -290,6 +303,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
bool use_pretty_covers_;
|
||||
bool show_dividers_;
|
||||
bool use_disk_cache_;
|
||||
bool use_lazy_loading_;
|
||||
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
|
||||
|
||||
@@ -70,6 +70,6 @@ Song CollectionPlaylistItem::Metadata() const {
|
||||
void CollectionPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
||||
|
||||
song_.set_art_manual(cover_url);
|
||||
temp_metadata_.set_art_manual(cover_url);
|
||||
if (HasTemporaryMetadata()) temp_metadata_.set_art_manual(cover_url);
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class CollectionPlaylistItem : public PlaylistItem {
|
||||
void Reload() override;
|
||||
|
||||
Song Metadata() const override;
|
||||
Song OriginalMetadata() const override { return song_; }
|
||||
void SetMetadata(const Song &song) { song_ = song; }
|
||||
|
||||
QUrl Url() const override;
|
||||
|
||||