Compare commits
539 Commits
Author | SHA1 | Date |
---|---|---|
|
6ba1fdb744 | |
|
dcef38427b | |
|
20d7ae7144 | |
|
d576777d94 | |
|
1f7344ca1b | |
|
87c69f7456 | |
|
a684b35203 | |
|
37855fe836 | |
|
f596695f61 | |
|
076d065f7c | |
|
70a7a7bbdd | |
|
5f540a4c08 | |
|
f33b30fe79 | |
|
2f546f214d | |
|
7ba4fda346 | |
|
299415a889 | |
|
718af984ab | |
|
5d51657f32 | |
|
a2958ba808 | |
|
79c2130152 | |
|
98d3cc2637 | |
|
8339aa0934 | |
|
5451c110b1 | |
|
20595a11bc | |
|
c92a1b516c | |
|
a8f1a881ff | |
|
ec21a55271 | |
|
89990624ec | |
|
6caf7f356b | |
|
241a6c5818 | |
|
57fb52e8f0 | |
|
7b00385155 | |
|
2b4aa1d6b2 | |
|
4ba5113842 | |
|
a36bf2df65 | |
|
f5002cae36 | |
|
cb8022c55d | |
|
2a65e00988 | |
|
05358cdfe4 | |
|
7b43a94055 | |
|
36e19e82e7 | |
|
c52a802b83 | |
|
b233600b8c | |
|
93df859aa4 | |
|
f1f79fb961 | |
|
92fa75b6c2 | |
|
1d5f3a0486 | |
|
b89c200076 | |
|
597a8cd6c8 | |
|
e477449cd4 | |
|
ea1e4541c0 | |
|
50572ac40f | |
|
fd81036909 | |
|
0e27886e28 | |
|
45bad3be04 | |
|
30b268dc3a | |
|
ef99f0ef36 | |
|
e357ba0125 | |
|
36b75a5928 | |
|
64d3ea2804 | |
|
0a93affeef | |
|
402d13a322 | |
|
adf0efc859 | |
|
d1c65fd273 | |
|
8a27c6a52f | |
|
d7cc52bc99 | |
|
f0f5300891 | |
|
6e90e72b4a | |
|
c655963483 | |
|
9f2e4ac312 | |
|
9e25366f85 | |
|
c102d8731a | |
|
0983ba1339 | |
|
0a99eca7cd | |
|
116bbec73e | |
|
bf19540f8d | |
|
dff3ae7410 | |
|
76614bcde0 | |
|
2953f9eefc | |
|
4a24605361 | |
|
decabe8d47 | |
|
51adcf0f1e | |
|
2a6a07fef6 | |
|
315cf63118 | |
|
e28d362aad | |
|
e0d9b8f715 | |
|
eff6b75c43 | |
|
e8be0adf37 | |
|
9f4a82bb62 | |
|
8d7e14f21d | |
|
d03d3622aa | |
|
821c32992d | |
|
b52cf9f3cd | |
|
ab8e687f96 | |
|
90703703aa | |
|
176984afe0 | |
|
542efa17ff | |
|
6a2c2dbba1 | |
|
426de61525 | |
|
24c8d06d41 | |
|
227f5e5176 | |
|
92e39a3e21 | |
|
fb2300e2fa | |
|
78becae5e9 | |
|
d10eb4370e | |
|
9c92ef941f | |
|
7aefe3d71b | |
|
398db964b8 | |
|
579349b104 | |
|
5f9a83871d | |
|
da0c5e67c5 | |
|
a0cfde18c3 | |
|
0ad4889936 | |
|
36e809d530 | |
|
78096658e2 | |
|
3edd218e3a | |
|
569bf6335b | |
|
7b8919d706 | |
|
147fd87d8c | |
|
ac0926d40b | |
|
105d472f5d | |
|
c1a49da385 | |
|
c3f596e64e | |
|
adfda84c41 | |
|
345cc118a3 | |
|
df070ac0cf | |
|
7b88be2635 | |
|
c30a39d29a | |
|
36db41a1f0 | |
|
8b249dc06a | |
|
0c6872b352 | |
|
58944993b8 | |
|
3cfffa5fbb | |
|
4873b8b413 | |
|
0b85f5192c | |
|
3e9a1776a1 | |
|
e1fbe9ae54 | |
|
f48d1a8017 | |
|
0debc90695 | |
|
8d42ea7cfd | |
|
1dae80a633 | |
|
d398c86b0c | |
|
70809e0647 | |
|
4c1a5168f0 | |
|
f9acfbc224 | |
|
5f78e1a983 | |
|
7bc5579fb7 | |
|
57750efcb2 | |
|
a33ee1cda9 | |
|
cd20a0679a | |
|
20e546e02b | |
|
f5547f093e | |
|
c00d95242d | |
|
05c4d23df6 | |
|
06fa17f33f | |
|
194285289c | |
|
a61fa61330 | |
|
68c922ee12 | |
|
d1042b276b | |
|
9bbffe150f | |
|
b95be526d3 | |
|
165f9d769b | |
|
a0ea75b74e | |
|
4075f92eec | |
|
035aff5454 | |
|
52dc7ad259 | |
|
c3c83f608c | |
|
ffba351a16 | |
|
a12623e142 | |
|
1a691a103e | |
|
5e725e0bbe | |
|
93c2fa4c73 | |
|
f412fb21d6 | |
|
bd4b6c1f01 | |
|
d1839d87e7 | |
|
1fc163eb5f | |
|
cd2b3cb73e | |
|
88b5cf2461 | |
|
2ccb0af75e | |
|
27ee6e7643 | |
|
a3207a5703 | |
|
72ff64a7f8 | |
|
9c06d1d0ae | |
|
f11afd4414 | |
|
2aa70b6ab8 | |
|
4626a6f609 | |
|
9152f8559f | |
|
7f4c61b15a | |
|
b365131363 | |
|
a6ea4dd0d7 | |
|
9c6649f077 | |
|
04ba202e12 | |
|
352a6c5691 | |
|
72bccad82d | |
|
6d52a2b409 | |
|
9b1035a5f2 | |
|
ce7c3e8039 | |
|
9baec288c3 | |
|
82894b94f4 | |
|
fb00d68aa7 | |
|
12288a2622 | |
|
f84ce3f1d1 | |
|
306b3f72d8 | |
|
593a04d047 | |
|
667548f3ed | |
|
8f89bf6402 | |
|
ff28e7c86e | |
|
67cc69179b | |
|
06fac2b7a3 | |
|
a354f6bdc5 | |
|
cb44c71733 | |
|
6b1c14f875 | |
|
7770aba877 | |
|
05381096aa | |
|
6bdd9ad4dd | |
|
19836e8898 | |
|
5e4b193260 | |
|
923d0f2b7a | |
|
56f1a93c4e | |
|
0168182af5 | |
|
679f0e1cd8 | |
|
dd6b9bb38d | |
|
5bd8f35dc0 | |
|
53fc939e35 | |
|
42d6c79710 | |
|
882869255b | |
|
19903751c1 | |
|
836074fb60 | |
|
5865a70c2e | |
|
fa057ee9d3 | |
|
2d88fcb249 | |
|
920bb04b00 | |
|
a915e62e2c | |
|
226a6c50e0 | |
|
269f13de76 | |
|
3ee796a663 | |
|
d24af2f4db | |
|
f86c3cfd91 | |
|
11061bdd07 | |
|
f901f802bb | |
|
a98c209101 | |
|
6a459682ca | |
|
1a7194b3a9 | |
|
b6c9ef4a15 | |
|
20e550bc7d | |
|
a4b7766947 | |
|
8d1a0071b6 | |
|
a3c00e607b | |
|
de7ca8b736 | |
|
04e593dc62 | |
|
2294c38aa9 | |
|
6f41d39a9c | |
|
7c4e33b676 | |
|
4cc66bccad | |
|
55b1d34f48 | |
|
4da0e8d8ad | |
|
b95bfba676 | |
|
1ff2bfd390 | |
|
a35fa5b158 | |
|
22169bda0d | |
|
faf5f6c69d | |
|
1ae01d4078 | |
|
eb6289dd1c | |
|
671df6eafb | |
|
579563b32c | |
|
135b93a5af | |
|
84c6e09c42 | |
|
b4f9808d11 | |
|
d96d4224a2 | |
|
f65927e308 | |
|
eeeea8566e | |
|
54c42b276f | |
|
8e4b4d6e41 | |
|
ac9fd9070f | |
|
6348649bc6 | |
|
c95886d8db | |
|
117b965a7b | |
|
1b6b5f9afa | |
|
83bc8d9e86 | |
|
33f0421d3f | |
|
661615e546 | |
|
c52fc90306 | |
|
eeb55fbc42 | |
|
5d77eb1901 | |
|
654a94fe3d | |
|
4238708226 | |
|
5f02072bf3 | |
|
eec5b448b6 | |
|
5cda756f92 | |
|
e9588dd85b | |
|
dda0e4aa06 | |
|
2282406166 | |
|
b3f0dee8e9 | |
|
f9f7381247 | |
|
48685325e6 | |
|
e8bcaf415c | |
|
c9197e8df7 | |
|
2bb09cf575 | |
|
7bf4ad3884 | |
|
5154d7ac84 | |
|
9299653722 | |
|
b0f0133d29 | |
|
9211b6f0c0 | |
|
ab6a0ed6dd | |
|
c975c1e4aa | |
|
f9a593dc74 | |
|
9151520d50 | |
|
8f72d877bd | |
|
e1990c9315 | |
|
4cd5dcbfcf | |
|
3ee2125e8f | |
|
4652f3b449 | |
|
697717eb1e | |
|
cbde71f5f1 | |
|
bf52afa21d | |
|
2083e008ab | |
|
2be0d23b1b | |
|
fda56dda25 | |
|
ed259781e9 | |
|
0c7fcd5a7a | |
|
310b7b9065 | |
|
cd534bbda7 | |
|
1a66eaf7bf | |
|
a94d6e3dd8 | |
|
54cfb2bbc4 | |
|
00bc3f76cf | |
|
71a6d378d9 | |
|
99a5aee8b3 | |
|
89d2a23dac | |
|
ee1bf47f5c | |
|
13ac20f8b3 | |
|
adef05bbdf | |
|
f03ff452b8 | |
|
c39489060b | |
|
002fa8f4aa | |
|
d2c747258c | |
|
53e3664726 | |
|
26ff9f6b53 | |
|
f542f1c854 | |
|
33041ffa75 | |
|
1493164df9 | |
|
8ffef558ff | |
|
8b3f44ffca | |
|
2706529006 | |
|
7e331a2055 | |
|
505329730c | |
|
1a07404c10 | |
|
b02adc7758 | |
|
952252ebcd | |
|
eee0c40132 | |
|
567bad33e1 | |
|
b5c0e93989 | |
|
ac17df2a86 | |
|
a9a5899252 | |
|
395d85c1b4 | |
|
52ba1ce17f | |
|
604a246fe8 | |
|
e172c4871c | |
|
2a9b32690d | |
|
76fa4745d0 | |
|
6f4d26e9d3 | |
|
f40f43861d | |
|
717ebbbb24 | |
|
79c69e1b1e | |
|
8fc95e08dc | |
|
3f06528ba3 | |
|
0e44b10eec | |
|
d74fe92ce8 | |
|
8037948f7f | |
|
ab29170972 | |
|
6f843e2499 | |
|
6b8a816ce6 | |
|
2c0541fb79 | |
|
cb22890d79 | |
|
5a9346cc80 | |
|
1c85220ffa | |
|
39d4818def | |
|
db0cc66ba0 | |
|
c10a64f08a | |
|
5a3d60b203 | |
|
28b9bf1f76 | |
|
27dbb2dd9e | |
|
9b7b790f21 | |
|
e3666e5bf3 | |
|
9e38cd8dc2 | |
|
b105b9fd8f | |
|
6a018f3e25 | |
|
ab26d422e9 | |
|
f4e18fb87c | |
|
2b20ff4e67 | |
|
28d5cd481b | |
|
42f3340190 | |
|
0ef50e1b6d | |
|
01ddded603 | |
|
fe7e0ffbba | |
|
5df6066150 | |
|
8393cdb2de | |
|
da19272eb6 | |
|
60fb83d770 | |
|
1c90b03476 | |
|
50502ce720 | |
|
39f9d02454 | |
|
ce627b0e18 | |
|
cdb4980337 | |
|
878148ac32 | |
|
0bc29f0563 | |
|
e201f71a74 | |
|
7e684885cf | |
|
e600c1b50c | |
|
5da2faa400 | |
|
05cd134a24 | |
|
2f27a7d00b | |
|
d61d4d0a74 | |
|
4033cd61c2 | |
|
7cd6f372e6 | |
|
ffe6eb3de7 | |
|
e2b0b1b1fb | |
|
1f15ca70a3 | |
|
904ee6a9b3 | |
|
0461e98a4c | |
|
dc01a18b87 | |
|
7be0e284c2 | |
|
6dd79d5b8f | |
|
f81b725d42 | |
|
cfeecd98f6 | |
|
d054dd33e2 | |
|
45ad84a9bc | |
|
a333662f56 | |
|
59f716563f | |
|
6815f8c9b7 | |
|
8e5360ac38 | |
|
aa6809ad5f | |
|
d8a7d427c3 | |
|
bc1b45d912 | |
|
50c5283599 | |
|
02ef65bcfd | |
|
904245bb21 | |
|
57e29b2be9 | |
|
53a5603f64 | |
|
8805a21567 | |
|
c6fee92450 | |
|
46c90f3712 | |
|
e3c1c6ee9a | |
|
8bfbd69a2c | |
|
6a6649823e | |
|
bfb95d503a | |
|
d1b4736ef9 | |
|
e56e58b634 | |
|
fed5b6b695 | |
|
e96870cfbd | |
|
acda7c13b2 | |
|
7d5c7f8493 | |
|
4ef3f3568d | |
|
f81bd26649 | |
|
2a407bfe47 | |
|
f70f126f76 | |
|
f06591fde8 | |
|
e0c9a9dc17 | |
|
0f16fc2776 | |
|
7aa7cdf6f3 | |
|
82a8a890de | |
|
f8df901963 | |
|
8b08d1d599 | |
|
f3ddba3edc | |
|
acbec6db7e | |
|
d2390473bc | |
|
e273d64be3 | |
|
2a90256d32 | |
|
560712db21 | |
|
483b42d2b8 | |
|
d1a6e53f5c | |
|
f5a55abf58 | |
|
0bc94b90d7 | |
|
9ed4bd9366 | |
|
d3352e476f | |
|
4b4c5fc0ab | |
|
38b9c7c38a | |
|
c71ce41c83 | |
|
4cd030215d | |
|
2ce5d6f727 | |
|
b55a0df8e1 | |
|
ee5fa23a7a | |
|
75ab6f25f4 | |
|
eaed82c9b2 | |
|
2a4be6fcd7 | |
|
e6198500f7 | |
|
7db36c83c1 | |
|
0e1921698c | |
|
95eed1ecec | |
|
2e61235403 | |
|
d6b53f78ab | |
|
a2c7ff63df | |
|
9fb15545bd | |
|
277e08b94a | |
|
46224fe9b8 | |
|
56180ca419 | |
|
dc65753a0b | |
|
d8857d8e72 | |
|
fdc3e0a5f5 | |
|
8f7180eb6c | |
|
8945602eae | |
|
7826f77425 | |
|
00372e85c5 | |
|
a1ffc5c33b | |
|
8a44a41abb | |
|
23f0c56eba | |
|
3d25863ccb | |
|
bb6daca735 | |
|
4bd993b1e3 | |
|
f81816b0cd | |
|
7ac605c038 | |
|
2a8b67d11e | |
|
16893cca24 | |
|
94ab788032 | |
|
e3a333564a | |
|
13d6cf201f | |
|
40ef3191fc | |
|
bda2b91c92 | |
|
1462bfa297 | |
|
bafcb97fa1 | |
|
f905676b1c | |
|
0ea81b13b9 | |
|
9a7949297e | |
|
29342fa9ac | |
|
bd4438d99b | |
|
f8e14e8fd5 | |
|
b2c66c9cda | |
|
44e5c32bcb | |
|
e7fc4e7f89 | |
|
e589486907 | |
|
459c4c5d86 | |
|
73c56f038e | |
|
0a4888f861 | |
|
da27ca98b3 | |
|
f1e1ccad28 | |
|
7616c06ff9 | |
|
0c1f4750ea | |
|
0a26c295a0 | |
|
32d23e0484 |
|
@ -1,3 +1,4 @@
|
||||||
github: jonaski
|
github: jonaski
|
||||||
patreon: jonaskvinge
|
patreon: jonaskvinge
|
||||||
|
ko_fi: jonaskvinge
|
||||||
custom: https://paypal.me/jonaskvinge
|
custom: https://paypal.me/jonaskvinge
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "3rdparty/kdsingleapplication/KDSingleApplication"]
|
||||||
|
path = 3rdparty/kdsingleapplication/KDSingleApplication
|
||||||
|
url = https://github.com/KDAB/KDSingleApplication.git
|
||||||
|
branch = master
|
|
@ -16,13 +16,6 @@ Used on macOS to exclusively enable strawberry to grab global media shortcuts.
|
||||||
Can safely be deleted on other platforms.
|
Can safely be deleted on other platforms.
|
||||||
|
|
||||||
|
|
||||||
macdeployqt
|
|
||||||
-----------
|
|
||||||
A modified version of Qt's official macdeployqt utility that fixes some issues,
|
|
||||||
this version also deploys gstreamer plugins.
|
|
||||||
Can safely be deleted on other platforms.
|
|
||||||
|
|
||||||
|
|
||||||
getopt
|
getopt
|
||||||
------
|
------
|
||||||
getopt included only when compiling on Windows.
|
getopt included only when compiling on Windows.
|
||||||
|
|
|
@ -62,7 +62,7 @@ enum ENUM_ORDERING {
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Ansi structures and functions follow
|
// Ansi structures and functions follow
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ int _getopt_long_only_r_a(int argc, char *const *argv, const char *options, cons
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Unicode Structures and Functions
|
// Unicode Structures and Functions
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -750,4 +750,4 @@ int _getopt_long_r_w(int argc, wchar_t *const *argv, const wchar_t *options, con
|
||||||
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d);
|
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d);
|
||||||
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) {
|
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) {
|
||||||
return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 1, d, 0);
|
return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 1, d, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
set(SOURCES kdsingleapplication.cpp kdsingleapplication_localsocket.cpp)
|
set(SOURCES KDSingleApplication/src/kdsingleapplication.cpp KDSingleApplication/src/kdsingleapplication_localsocket.cpp)
|
||||||
set(HEADERS kdsingleapplication.h kdsingleapplication_localsocket_p.h)
|
set(HEADERS KDSingleApplication/src/kdsingleapplication.h KDSingleApplication/src/kdsingleapplication_localsocket_p.h)
|
||||||
qt_wrap_cpp(MOC ${HEADERS})
|
qt_wrap_cpp(MOC ${HEADERS})
|
||||||
add_library(kdsingleapplication STATIC ${SOURCES} ${MOC})
|
add_library(kdsingleapplication STATIC ${SOURCES} ${MOC})
|
||||||
target_compile_definitions(kdsingleapplication PRIVATE -DKDSINGLEAPPLICATION_STATIC_BUILD)
|
target_compile_definitions(kdsingleapplication PRIVATE -DKDSINGLEAPPLICATION_STATIC_BUILD)
|
||||||
target_include_directories(kdsingleapplication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(kdsingleapplication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(kdsingleapplication PUBLIC ${QtCore_LIBRARIES} ${QtNetwork_LIBRARIES} )
|
target_link_libraries(kdsingleapplication PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit cb0c664b40d3b31bad30aa3521eff603162ed0bd
|
|
@ -1,6 +0,0 @@
|
||||||
KDSingleApplication is (C) 2019-2023, Klarälvdalens Datakonsult AB,
|
|
||||||
and is available under the terms of the MIT license.
|
|
||||||
|
|
||||||
See the full license text in the LICENSES folder.
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> to inquire about commercial licensing.
|
|
|
@ -1,53 +0,0 @@
|
||||||
# KDSingleApplication
|
|
||||||
|
|
||||||
`KDSingleApplication` is a helper class for single-instance policy applications
|
|
||||||
written by [KDAB](https://www.kdab.com).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Currently the documentation is woefully lacking, but see the examples or tests
|
|
||||||
for inspiration. Basically it involves:
|
|
||||||
|
|
||||||
1. Create a `Q(Core|Gui)Application` object.
|
|
||||||
2. Create a `KDSingleApplication` object.
|
|
||||||
3. Check if the current instance is *primary* (or "master") or
|
|
||||||
*secondary* (or "slave") by calling `isPrimaryInstance`:
|
|
||||||
* the *primary* instance needs to listen from messages coming from the
|
|
||||||
secondary instances, by connecting a slot to the `messageReceived` signal;
|
|
||||||
* the *secondary* instances can send messages to the primary instance
|
|
||||||
by calling `sendMessage`.
|
|
||||||
|
|
||||||
## Licensing
|
|
||||||
|
|
||||||
KDSingleApplication is (C) 2019-2023, Klarälvdalens Datakonsult AB, and is available
|
|
||||||
under the terms of the [MIT license](LICENSES/MIT.txt).
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> if you need different licensing options.
|
|
||||||
|
|
||||||
## Get Involved
|
|
||||||
|
|
||||||
KDAB will happily accept external contributions.
|
|
||||||
|
|
||||||
Please submit your contributions or issue reports from our GitHub space at
|
|
||||||
<https://github.com/KDAB/KDSingleApplication>.
|
|
||||||
|
|
||||||
## About KDAB
|
|
||||||
|
|
||||||
KDSingleApplication is supported and maintained by Klarälvdalens Datakonsult AB (KDAB).
|
|
||||||
|
|
||||||
The KDAB Group is the global No.1 software consultancy for Qt, C++ and
|
|
||||||
OpenGL applications across desktop, embedded and mobile platforms.
|
|
||||||
|
|
||||||
The KDAB Group provides consulting and mentoring for developing Qt applications
|
|
||||||
from scratch and in porting from all popular and legacy frameworks to Qt.
|
|
||||||
We continue to help develop parts of Qt and are one of the major contributors
|
|
||||||
to the Qt Project. We can give advanced or standard trainings anywhere
|
|
||||||
around the globe on Qt as well as C++, OpenGL, 3D and more.
|
|
||||||
|
|
||||||
Please visit <https://www.kdab.com> to meet the people who write code like this.
|
|
||||||
|
|
||||||
Stay up-to-date with KDAB product announcements:
|
|
||||||
|
|
||||||
* [KDAB Newsletter](https://news.kdab.com)
|
|
||||||
* [KDAB Blogs](https://www.kdab.com/category/blogs)
|
|
||||||
* [KDAB on Twitter](https://twitter.com/KDABQt)
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of KDSingleApplication.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "kdsingleapplication.h"
|
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
|
||||||
#include <QtCore/QFileInfo>
|
|
||||||
|
|
||||||
// TODO: make this pluggable.
|
|
||||||
#include "kdsingleapplication_localsocket_p.h"
|
|
||||||
|
|
||||||
// Avoiding dragging in Qt private APIs for now, so this does not inherit
|
|
||||||
// from QObjectPrivate.
|
|
||||||
class KDSingleApplicationPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit KDSingleApplicationPrivate(const QString &name, KDSingleApplication *q);
|
|
||||||
|
|
||||||
QString name() const
|
|
||||||
{
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPrimaryInstance() const
|
|
||||||
{
|
|
||||||
return m_impl.isPrimaryInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sendMessage(const QByteArray &message, int timeout)
|
|
||||||
{
|
|
||||||
return m_impl.sendMessage(message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Q_DECLARE_PUBLIC(KDSingleApplication)
|
|
||||||
|
|
||||||
KDSingleApplication *q_ptr;
|
|
||||||
QString m_name;
|
|
||||||
|
|
||||||
KDSingleApplicationLocalSocket m_impl;
|
|
||||||
};
|
|
||||||
|
|
||||||
KDSingleApplicationPrivate::KDSingleApplicationPrivate(const QString &name, KDSingleApplication *q)
|
|
||||||
: q_ptr(q)
|
|
||||||
, m_name(name)
|
|
||||||
, m_impl(name)
|
|
||||||
{
|
|
||||||
if (Q_UNLIKELY(name.isEmpty()))
|
|
||||||
qFatal("KDSingleApplication requires a non-empty application name");
|
|
||||||
|
|
||||||
if (isPrimaryInstance()) {
|
|
||||||
QObject::connect(&m_impl, &KDSingleApplicationLocalSocket::messageReceived,
|
|
||||||
q, &KDSingleApplication::messageReceived);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString extractExecutableName(const QString &applicationFilePath)
|
|
||||||
{
|
|
||||||
return QFileInfo(applicationFilePath).fileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
KDSingleApplication::KDSingleApplication(QObject *parent)
|
|
||||||
: KDSingleApplication(extractExecutableName(QCoreApplication::applicationFilePath()), parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
KDSingleApplication::KDSingleApplication(const QString &name, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, d_ptr(new KDSingleApplicationPrivate(name, this))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString KDSingleApplication::name() const
|
|
||||||
{
|
|
||||||
Q_D(const KDSingleApplication);
|
|
||||||
return d->name();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KDSingleApplication::isPrimaryInstance() const
|
|
||||||
{
|
|
||||||
Q_D(const KDSingleApplication);
|
|
||||||
return d->isPrimaryInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KDSingleApplication::sendMessage(const QByteArray &message)
|
|
||||||
{
|
|
||||||
return sendMessageWithTimeout(message, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KDSingleApplication::sendMessageWithTimeout(const QByteArray &message, int timeout)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!isPrimaryInstance());
|
|
||||||
|
|
||||||
Q_D(KDSingleApplication);
|
|
||||||
return d->sendMessage(message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
KDSingleApplication::~KDSingleApplication() = default;
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of KDSingleApplication.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
||||||
*/
|
|
||||||
#ifndef KDSINGLEAPPLICATION_H
|
|
||||||
#define KDSINGLEAPPLICATION_H
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "kdsingleapplication_lib.h"
|
|
||||||
|
|
||||||
class KDSingleApplicationPrivate;
|
|
||||||
|
|
||||||
class KDSINGLEAPPLICATION_EXPORT KDSingleApplication : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QString name READ name CONSTANT)
|
|
||||||
Q_PROPERTY(bool isPrimaryInstance READ isPrimaryInstance CONSTANT)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit KDSingleApplication(QObject *parent = nullptr);
|
|
||||||
explicit KDSingleApplication(const QString &name, QObject *parent = nullptr);
|
|
||||||
~KDSingleApplication();
|
|
||||||
|
|
||||||
QString name() const;
|
|
||||||
bool isPrimaryInstance() const;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
// avoid default arguments and overloads, as they don't mix with connections
|
|
||||||
bool sendMessage(const QByteArray &message);
|
|
||||||
bool sendMessageWithTimeout(const QByteArray &message, int timeout);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void messageReceived(const QByteArray &message);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Q_DECLARE_PRIVATE(KDSingleApplication)
|
|
||||||
std::unique_ptr<KDSingleApplicationPrivate> d_ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KDSINGLEAPPLICATION_H
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of KDSingleApplication.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
||||||
*/
|
|
||||||
#ifndef KDSINGLEAPPLICATION_LIB_H
|
|
||||||
#define KDSINGLEAPPLICATION_LIB_H
|
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
|
||||||
|
|
||||||
#if defined(KDSINGLEAPPLICATION_STATIC_BUILD)
|
|
||||||
#define KDSINGLEAPPLICATION_EXPORT
|
|
||||||
#elif defined(KDSINGLEAPPLICATION_SHARED_BUILD)
|
|
||||||
#define KDSINGLEAPPLICATION_EXPORT Q_DECL_EXPORT
|
|
||||||
#else
|
|
||||||
#define KDSINGLEAPPLICATION_EXPORT Q_DECL_IMPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // KDSINGLEAPPLICATION_LIB_H
|
|
|
@ -1,315 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of KDSingleApplication.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "kdsingleapplication_localsocket_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
|
||||||
#include <QtCore/QDeadlineTimer>
|
|
||||||
#include <QtCore/QTimer>
|
|
||||||
#include <QtCore/QLockFile>
|
|
||||||
#include <QtCore/QDataStream>
|
|
||||||
|
|
||||||
#include <QtCore/QtDebug>
|
|
||||||
#include <QtCore/QLoggingCategory>
|
|
||||||
|
|
||||||
#include <QtNetwork/QLocalServer>
|
|
||||||
#include <QtNetwork/QLocalSocket>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
|
||||||
// for ::getuid()
|
|
||||||
# include <sys/types.h>
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
# include <qt_windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static constexpr auto LOCALSOCKET_CONNECTION_TIMEOUT = std::chrono::seconds(5);
|
|
||||||
static constexpr char LOCALSOCKET_PROTOCOL_VERSION = 2;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wmissing-declarations"
|
|
||||||
#endif
|
|
||||||
Q_LOGGING_CATEGORY(kdsaLocalSocket, "kdsingleapplication.localsocket", QtWarningMsg)
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KDSingleApplicationLocalSocket::KDSingleApplicationLocalSocket(const QString &name, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_UNIX)
|
|
||||||
/* cppcheck-suppress useInitializationList */
|
|
||||||
m_socketName = QStringLiteral("kdsingleapp-%1-%2")
|
|
||||||
.arg(::getuid())
|
|
||||||
.arg(name);
|
|
||||||
#elif defined(Q_OS_WIN)
|
|
||||||
// I'm not sure of a "global session identifier" on Windows; are
|
|
||||||
// multiple logins from the same user a possibility? For now, following this:
|
|
||||||
// https://docs.microsoft.com/en-us/windows/desktop/devnotes/getting-the-session-id-of-the-current-process
|
|
||||||
|
|
||||||
DWORD sessionId;
|
|
||||||
BOOL haveSessionId = ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
|
|
||||||
|
|
||||||
m_socketName = QString::fromUtf8("kdsingleapp-%1-%2")
|
|
||||||
.arg(haveSessionId ? sessionId : 0)
|
|
||||||
.arg(name);
|
|
||||||
#else
|
|
||||||
#error "KDSingleApplication has not been ported to this platform"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const QString lockFilePath =
|
|
||||||
QDir::tempPath() + QLatin1Char('/') + m_socketName + QLatin1String(".lock");
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Socket name is" << m_socketName;
|
|
||||||
qCDebug(kdsaLocalSocket) << "Lock file path is" << lockFilePath;
|
|
||||||
|
|
||||||
std::unique_ptr<QLockFile> lockFile(new QLockFile(lockFilePath));
|
|
||||||
lockFile->setStaleLockTime(0);
|
|
||||||
|
|
||||||
if (!lockFile->tryLock()) {
|
|
||||||
// someone else has the lock => we're secondary
|
|
||||||
qCDebug(kdsaLocalSocket) << "Secondary instance";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Primary instance";
|
|
||||||
|
|
||||||
std::unique_ptr<QLocalServer> server = std::make_unique<QLocalServer>();
|
|
||||||
if (!server->listen(m_socketName)) {
|
|
||||||
// maybe the primary crashed, leaving a stale socket; delete it and try again
|
|
||||||
QLocalServer::removeServer(m_socketName);
|
|
||||||
if (!server->listen(m_socketName)) {
|
|
||||||
// TODO: better error handling.
|
|
||||||
qWarning("KDSingleApplication: unable to make the primary instance listen on %ls: %ls",
|
|
||||||
qUtf16Printable(m_socketName),
|
|
||||||
qUtf16Printable(server->errorString()));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(server.get(), &QLocalServer::newConnection,
|
|
||||||
this, &KDSingleApplicationLocalSocket::handleNewConnection);
|
|
||||||
|
|
||||||
m_lockFile = std::move(lockFile);
|
|
||||||
m_localServer = std::move(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
KDSingleApplicationLocalSocket::~KDSingleApplicationLocalSocket() = default;
|
|
||||||
|
|
||||||
bool KDSingleApplicationLocalSocket::isPrimaryInstance() const
|
|
||||||
{
|
|
||||||
return m_localServer != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KDSingleApplicationLocalSocket::sendMessage(const QByteArray &message, int timeout)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!isPrimaryInstance());
|
|
||||||
QLocalSocket socket;
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Preparing to send message" << message << "with timeout" << timeout;
|
|
||||||
|
|
||||||
QDeadlineTimer deadline(timeout);
|
|
||||||
|
|
||||||
// There is an inherent race here with the setup of the server side.
|
|
||||||
// Even if the socket lock is held by the server, the server may not
|
|
||||||
// be listening yet. So this connection may fail; keep retrying
|
|
||||||
// until we hit the timeout.
|
|
||||||
do {
|
|
||||||
socket.connectToServer(m_socketName);
|
|
||||||
if (socket.waitForConnected(static_cast<int>(deadline.remainingTime())))
|
|
||||||
break;
|
|
||||||
} while (!deadline.hasExpired());
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Socket state:" << socket.state() << "Timer remaining" << deadline.remainingTime() << "Expired?" << deadline.hasExpired();
|
|
||||||
|
|
||||||
if (deadline.hasExpired()) {
|
|
||||||
qCWarning(kdsaLocalSocket) << "Connection timed out";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.write(&LOCALSOCKET_PROTOCOL_VERSION, 1);
|
|
||||||
|
|
||||||
{
|
|
||||||
QByteArray encodedMessage;
|
|
||||||
QDataStream ds(&encodedMessage, QIODevice::WriteOnly);
|
|
||||||
ds << message;
|
|
||||||
socket.write(encodedMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Wrote message in the socket"
|
|
||||||
<< "Timer remaining" << deadline.remainingTime() << "Expired?" << deadline.hasExpired();
|
|
||||||
|
|
||||||
// There is no acknowledgement mechanism here.
|
|
||||||
// Should there be one?
|
|
||||||
|
|
||||||
while (socket.bytesToWrite() > 0) {
|
|
||||||
if (!socket.waitForBytesWritten(static_cast<int>(deadline.remainingTime()))) {
|
|
||||||
qCWarning(kdsaLocalSocket) << "Message to primary timed out";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Bytes written, now disconnecting"
|
|
||||||
<< "Timer remaining" << deadline.remainingTime() << "Expired?" << deadline.hasExpired();
|
|
||||||
|
|
||||||
socket.disconnectFromServer();
|
|
||||||
|
|
||||||
if (socket.state() == QLocalSocket::UnconnectedState) {
|
|
||||||
qCDebug(kdsaLocalSocket) << "Disconnected -- success!";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!socket.waitForDisconnected(static_cast<int>(deadline.remainingTime()))) {
|
|
||||||
qCWarning(kdsaLocalSocket) << "Disconnection from primary timed out";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Disconnected -- success!";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KDSingleApplicationLocalSocket::handleNewConnection()
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_localServer);
|
|
||||||
|
|
||||||
QLocalSocket *socket = nullptr;
|
|
||||||
while ((socket = m_localServer->nextPendingConnection())) {
|
|
||||||
qCDebug(kdsaLocalSocket) << "Got new connection on" << m_socketName << "state" << socket->state();
|
|
||||||
|
|
||||||
Connection c(socket);
|
|
||||||
socket = c.socket.get();
|
|
||||||
|
|
||||||
c.readDataConnection = QObjectConnectionHolder(
|
|
||||||
connect(socket, &QLocalSocket::readyRead,
|
|
||||||
this, &KDSingleApplicationLocalSocket::readDataFromSecondary));
|
|
||||||
|
|
||||||
c.secondaryDisconnectedConnection = QObjectConnectionHolder(
|
|
||||||
connect(socket, &QLocalSocket::disconnected,
|
|
||||||
this, &KDSingleApplicationLocalSocket::secondaryDisconnected));
|
|
||||||
|
|
||||||
c.abortConnection = QObjectConnectionHolder(
|
|
||||||
connect(c.timeoutTimer.get(), &QTimer::timeout,
|
|
||||||
this, &KDSingleApplicationLocalSocket::abortConnectionToSecondary));
|
|
||||||
|
|
||||||
m_clients.push_back(std::move(c));
|
|
||||||
|
|
||||||
// Note that by the time we get here, the socket could've already been closed,
|
|
||||||
// and no signals emitted (hello, Windows!). Read what's already in the socket.
|
|
||||||
if (readDataFromSecondarySocket(socket))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (socket->state() == QLocalSocket::UnconnectedState)
|
|
||||||
secondarySocketDisconnected(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Container>
|
|
||||||
static auto findConnectionBySocket(Container &container, QLocalSocket *socket)
|
|
||||||
{
|
|
||||||
auto i = std::find_if(container.begin(),
|
|
||||||
container.end(),
|
|
||||||
[socket](const auto &c) { return c.socket.get() == socket; });
|
|
||||||
Q_ASSERT(i != container.end());
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Container>
|
|
||||||
static auto findConnectionByTimer(Container &container, QTimer *timer)
|
|
||||||
{
|
|
||||||
auto i = std::find_if(container.begin(),
|
|
||||||
container.end(),
|
|
||||||
[timer](const auto &c) { return c.timeoutTimer.get() == timer; });
|
|
||||||
Q_ASSERT(i != container.end());
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KDSingleApplicationLocalSocket::readDataFromSecondary()
|
|
||||||
{
|
|
||||||
QLocalSocket *socket = static_cast<QLocalSocket *>(sender());
|
|
||||||
readDataFromSecondarySocket(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KDSingleApplicationLocalSocket::readDataFromSecondarySocket(QLocalSocket *socket)
|
|
||||||
{
|
|
||||||
auto i = findConnectionBySocket(m_clients, socket);
|
|
||||||
Connection &c = *i;
|
|
||||||
c.readData.append(socket->readAll());
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Got more data from a secondary. Data read so far:" << c.readData;
|
|
||||||
|
|
||||||
const QByteArray &data = c.readData;
|
|
||||||
|
|
||||||
if (data.size() >= 1) {
|
|
||||||
if (data[0] != LOCALSOCKET_PROTOCOL_VERSION) {
|
|
||||||
qCDebug(kdsaLocalSocket) << "Got an invalid protocol version";
|
|
||||||
m_clients.erase(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream ds(data);
|
|
||||||
ds.skipRawData(1);
|
|
||||||
|
|
||||||
ds.startTransaction();
|
|
||||||
QByteArray message;
|
|
||||||
ds >> message;
|
|
||||||
|
|
||||||
if (ds.commitTransaction()) {
|
|
||||||
qCDebug(kdsaLocalSocket) << "Got a complete message:" << message;
|
|
||||||
Q_EMIT messageReceived(message);
|
|
||||||
m_clients.erase(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KDSingleApplicationLocalSocket::secondaryDisconnected()
|
|
||||||
{
|
|
||||||
QLocalSocket *socket = static_cast<QLocalSocket *>(sender());
|
|
||||||
secondarySocketDisconnected(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KDSingleApplicationLocalSocket::secondarySocketDisconnected(QLocalSocket *socket)
|
|
||||||
{
|
|
||||||
auto i = findConnectionBySocket(m_clients, socket);
|
|
||||||
Connection c = std::move(*i);
|
|
||||||
m_clients.erase(i);
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Secondary disconnected. Data read:" << c.readData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KDSingleApplicationLocalSocket::abortConnectionToSecondary()
|
|
||||||
{
|
|
||||||
QTimer *timer = static_cast<QTimer *>(sender());
|
|
||||||
|
|
||||||
auto i = findConnectionByTimer(m_clients, timer);
|
|
||||||
Connection c = std::move(*i);
|
|
||||||
m_clients.erase(i);
|
|
||||||
|
|
||||||
qCDebug(kdsaLocalSocket) << "Secondary timed out. Data read:" << c.readData;
|
|
||||||
}
|
|
||||||
|
|
||||||
KDSingleApplicationLocalSocket::Connection::Connection(QLocalSocket *_socket)
|
|
||||||
: socket(_socket)
|
|
||||||
, timeoutTimer(new QTimer)
|
|
||||||
{
|
|
||||||
timeoutTimer->start(LOCALSOCKET_CONNECTION_TIMEOUT);
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of KDSingleApplication.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KDSINGLEAPPLICATION_LOCALSOCKET_P_H
|
|
||||||
#define KDSINGLEAPPLICATION_LOCALSOCKET_P_H
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QByteArray>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QLockFile;
|
|
||||||
class QLocalServer;
|
|
||||||
class QLocalSocket;
|
|
||||||
class QTimer;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct QObjectDeleteLater
|
|
||||||
{
|
|
||||||
void operator()(QObject *o)
|
|
||||||
{
|
|
||||||
o->deleteLater();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class QObjectConnectionHolder
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY(QObjectConnectionHolder)
|
|
||||||
QMetaObject::Connection c;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QObjectConnectionHolder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit QObjectConnectionHolder(QMetaObject::Connection _c)
|
|
||||||
: c(std::move(_c))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~QObjectConnectionHolder()
|
|
||||||
{
|
|
||||||
QObject::disconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
QObjectConnectionHolder(QObjectConnectionHolder &&other) noexcept
|
|
||||||
: c(std::exchange(other.c, {}))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QObjectConnectionHolder &operator=(QObjectConnectionHolder &&other) noexcept
|
|
||||||
{
|
|
||||||
QObjectConnectionHolder moved(std::move(other));
|
|
||||||
swap(moved);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(QObjectConnectionHolder &other) noexcept
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
swap(c, other.c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class KDSingleApplicationLocalSocket : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit KDSingleApplicationLocalSocket(const QString &name,
|
|
||||||
QObject *parent = nullptr);
|
|
||||||
~KDSingleApplicationLocalSocket();
|
|
||||||
|
|
||||||
bool isPrimaryInstance() const;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
bool sendMessage(const QByteArray &message, int timeout);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void messageReceived(const QByteArray &message);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void handleNewConnection();
|
|
||||||
void readDataFromSecondary();
|
|
||||||
bool readDataFromSecondarySocket(QLocalSocket *socket);
|
|
||||||
void secondaryDisconnected();
|
|
||||||
void secondarySocketDisconnected(QLocalSocket *socket);
|
|
||||||
void abortConnectionToSecondary();
|
|
||||||
|
|
||||||
QString m_socketName;
|
|
||||||
|
|
||||||
std::unique_ptr<QLockFile> m_lockFile; // protects m_localServer
|
|
||||||
std::unique_ptr<QLocalServer> m_localServer;
|
|
||||||
|
|
||||||
struct Connection
|
|
||||||
{
|
|
||||||
explicit Connection(QLocalSocket *s);
|
|
||||||
|
|
||||||
std::unique_ptr<QLocalSocket, QObjectDeleteLater> socket;
|
|
||||||
std::unique_ptr<QTimer, QObjectDeleteLater> timeoutTimer;
|
|
||||||
QByteArray readData;
|
|
||||||
|
|
||||||
// socket/timeoutTimer are deleted via deleteLater (as we delete them
|
|
||||||
// in slots connected to their signals). Before the deleteLater is acted upon,
|
|
||||||
// they may emit further signals, triggering logic that it's not supposed
|
|
||||||
// to be triggered (as the Connection has already been destroyed).
|
|
||||||
// Use this Holder to break the connections.
|
|
||||||
QObjectConnectionHolder readDataConnection;
|
|
||||||
QObjectConnectionHolder secondaryDisconnectedConnection;
|
|
||||||
QObjectConnectionHolder abortConnection;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Connection> m_clients;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KDSINGLEAPPLICATION_LOCALSOCKET_P_H
|
|
|
@ -1,7 +0,0 @@
|
||||||
add_executable(macdeployqt main.cpp shared.cpp)
|
|
||||||
target_link_libraries(macdeployqt PRIVATE
|
|
||||||
"-framework AppKit"
|
|
||||||
${QtCore_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
#execute_process(COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/macdeployqt ${CMAKE_BINARY_DIR})
|
|
|
@ -1,286 +0,0 @@
|
||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the tools applications of the Qt Toolkit.
|
|
||||||
**
|
|
||||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
||||||
** Commercial License Usage
|
|
||||||
** Licensees holding valid commercial Qt licenses may use this file in
|
|
||||||
** accordance with the commercial license agreement provided with the
|
|
||||||
** Software or, alternatively, in accordance with the terms contained in
|
|
||||||
** a written agreement between you and The Qt Company. For licensing terms
|
|
||||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
||||||
** information use the contact form at https://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU
|
|
||||||
** General Public License version 3 as published by the Free Software
|
|
||||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
||||||
** included in the packaging of this file. Please review the following
|
|
||||||
** information to ensure the GNU General Public License requirements will
|
|
||||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
||||||
**
|
|
||||||
** $QT_END_LICENSE$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#undef QT_NO_DEBUG_OUTPUT
|
|
||||||
#undef QT_NO_WARNING_OUTPUT
|
|
||||||
#undef QT_NO_INFO_OUTPUT
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QLibraryInfo>
|
|
||||||
|
|
||||||
#include "shared.h"
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
QCoreApplication app(argc, argv);
|
|
||||||
|
|
||||||
QString appBundlePath;
|
|
||||||
if (argc > 1)
|
|
||||||
appBundlePath = QString::fromLocal8Bit(argv[1]);
|
|
||||||
|
|
||||||
if (argc < 2 || appBundlePath.startsWith("-")) {
|
|
||||||
qDebug() << "Usage: macdeployqt app-bundle [options]";
|
|
||||||
qDebug() << "";
|
|
||||||
qDebug() << "Options:";
|
|
||||||
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
|
|
||||||
qDebug() << " -no-plugins : Skip plugin deployment";
|
|
||||||
qDebug() << " -dmg : Create a .dmg disk image";
|
|
||||||
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
|
|
||||||
qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
|
|
||||||
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
|
|
||||||
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
|
|
||||||
qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
|
|
||||||
qDebug() << " -always-overwrite : Copy files even if the target file exists";
|
|
||||||
qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
|
|
||||||
qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
|
|
||||||
qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
|
|
||||||
qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
|
|
||||||
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
|
|
||||||
qDebug() << " -libpath=<path> : Add the given path to the library search path";
|
|
||||||
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
|
|
||||||
qDebug() << "";
|
|
||||||
qDebug() << "macdeployqt takes an application bundle as input and makes it";
|
|
||||||
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
|
|
||||||
qDebug() << "the application uses.";
|
|
||||||
qDebug() << "";
|
|
||||||
qDebug() << "Plugins related to a framework are copied in with the";
|
|
||||||
qDebug() << "framework. The accessibility, image formats, and text codec";
|
|
||||||
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
|
|
||||||
qDebug() << "";
|
|
||||||
qDebug() << "Qt plugins may use private API and will cause the app to be";
|
|
||||||
qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
|
|
||||||
qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
|
|
||||||
qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
|
|
||||||
qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
|
|
||||||
qDebug() << "";
|
|
||||||
qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
|
|
||||||
qDebug() << "documentation for more information about deployment on OS X.";
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
appBundlePath = QDir::cleanPath(appBundlePath);
|
|
||||||
|
|
||||||
if (!QDir(appBundlePath).exists()) {
|
|
||||||
qDebug() << "Error: Could not find app bundle" << appBundlePath;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool plugins = true;
|
|
||||||
bool dmg = false;
|
|
||||||
QByteArray filesystem("HFS+");
|
|
||||||
bool useDebugLibs = false;
|
|
||||||
extern bool runStripEnabled;
|
|
||||||
extern bool alwaysOwerwriteEnabled;
|
|
||||||
extern QStringList librarySearchPath;
|
|
||||||
QStringList additionalExecutables;
|
|
||||||
bool qmldirArgumentUsed = false;
|
|
||||||
QStringList qmlDirs;
|
|
||||||
QStringList qmlImportPaths;
|
|
||||||
extern bool runCodesign;
|
|
||||||
extern QString codesignIdentiy;
|
|
||||||
extern bool hardenedRuntime;
|
|
||||||
extern bool appstoreCompliant;
|
|
||||||
extern bool deployFramework;
|
|
||||||
extern bool secureTimestamp;
|
|
||||||
|
|
||||||
for (int i = 2; i < argc; ++i) {
|
|
||||||
QByteArray argument = QByteArray(argv[i]);
|
|
||||||
if (argument == QByteArray("-no-plugins")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
plugins = false;
|
|
||||||
} else if (argument == QByteArray("-dmg")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
dmg = true;
|
|
||||||
} else if (argument == QByteArray("-no-strip")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
runStripEnabled = false;
|
|
||||||
} else if (argument == QByteArray("-use-debug-libs")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
useDebugLibs = true;
|
|
||||||
runStripEnabled = false;
|
|
||||||
} else if (argument.startsWith(QByteArray("-verbose"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf("=");
|
|
||||||
bool ok = false;
|
|
||||||
int number = argument.mid(index+1).toInt(&ok);
|
|
||||||
if (!ok)
|
|
||||||
LogError() << "Could not parse verbose level";
|
|
||||||
else
|
|
||||||
logLevel = number;
|
|
||||||
} else if (argument.startsWith(QByteArray("-executable"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf('=');
|
|
||||||
if (index == -1)
|
|
||||||
LogError() << "Missing executable path";
|
|
||||||
else
|
|
||||||
additionalExecutables << argument.mid(index+1);
|
|
||||||
} else if (argument.startsWith(QByteArray("-qmldir"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
qmldirArgumentUsed = true;
|
|
||||||
int index = argument.indexOf('=');
|
|
||||||
if (index == -1)
|
|
||||||
LogError() << "Missing qml directory path";
|
|
||||||
else
|
|
||||||
qmlDirs << argument.mid(index+1);
|
|
||||||
} else if (argument.startsWith(QByteArray("-qmlimport"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf('=');
|
|
||||||
if (index == -1)
|
|
||||||
LogError() << "Missing qml import path";
|
|
||||||
else
|
|
||||||
qmlImportPaths << argument.mid(index+1);
|
|
||||||
} else if (argument.startsWith(QByteArray("-libpath"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf('=');
|
|
||||||
if (index == -1)
|
|
||||||
LogError() << "Missing library search path";
|
|
||||||
else
|
|
||||||
librarySearchPath << argument.mid(index+1);
|
|
||||||
} else if (argument == QByteArray("-always-overwrite")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
alwaysOwerwriteEnabled = true;
|
|
||||||
} else if (argument.startsWith(QByteArray("-codesign"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf("=");
|
|
||||||
if (index < 0 || index >= argument.size()) {
|
|
||||||
LogError() << "Missing code signing identity";
|
|
||||||
} else {
|
|
||||||
runCodesign = true;
|
|
||||||
codesignIdentiy = argument.mid(index+1);
|
|
||||||
}
|
|
||||||
} else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf("=");
|
|
||||||
if (index < 0 || index >= argument.size()) {
|
|
||||||
LogError() << "Missing code signing identity";
|
|
||||||
} else {
|
|
||||||
runCodesign = true;
|
|
||||||
hardenedRuntime = true;
|
|
||||||
secureTimestamp = true;
|
|
||||||
codesignIdentiy = argument.mid(index+1);
|
|
||||||
}
|
|
||||||
} else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
hardenedRuntime = true;
|
|
||||||
} else if (argument.startsWith(QByteArray("-timestamp"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
secureTimestamp = true;
|
|
||||||
} else if (argument == QByteArray("-appstore-compliant")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
appstoreCompliant = true;
|
|
||||||
|
|
||||||
// Undocumented option, may not work as intended
|
|
||||||
} else if (argument == QByteArray("-deploy-framework")) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
deployFramework = true;
|
|
||||||
|
|
||||||
} else if (argument.startsWith(QByteArray("-fs"))) {
|
|
||||||
LogDebug() << "Argument found:" << argument;
|
|
||||||
int index = argument.indexOf('=');
|
|
||||||
if (index == -1)
|
|
||||||
LogError() << "Missing filesystem type";
|
|
||||||
else
|
|
||||||
filesystem = argument.mid(index+1);
|
|
||||||
} else if (argument.startsWith("-")) {
|
|
||||||
LogError() << "Unknown argument" << argument << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
|
|
||||||
|
|
||||||
if (deploymentInfo.isDebug)
|
|
||||||
useDebugLibs = true;
|
|
||||||
|
|
||||||
if (deployFramework && deploymentInfo.isFramework)
|
|
||||||
fixupFramework(appBundlePath);
|
|
||||||
|
|
||||||
// Convenience: Look for .qml files in the current directory if no -qmldir specified.
|
|
||||||
if (qmlDirs.isEmpty()) {
|
|
||||||
QDir dir;
|
|
||||||
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
|
|
||||||
qmlDirs += QStringLiteral(".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!qmlDirs.isEmpty()) {
|
|
||||||
bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
|
|
||||||
if (!ok && qmldirArgumentUsed)
|
|
||||||
return 1; // exit if the user explicitly asked for qml import deployment
|
|
||||||
|
|
||||||
// Update deploymentInfo.deployedFrameworks - the QML imports
|
|
||||||
// may have brought in extra frameworks as dependencies.
|
|
||||||
deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
|
|
||||||
deploymentInfo.deployedFrameworks =
|
|
||||||
QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
|
|
||||||
deploymentInfo.deployedFrameworks.end()).values();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle plugins
|
|
||||||
if (plugins) {
|
|
||||||
// Set the plugins search directory
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
|
|
||||||
#else
|
|
||||||
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
|
|
||||||
#endif
|
|
||||||
// Sanity checks
|
|
||||||
if (deploymentInfo.pluginPath.isEmpty()) {
|
|
||||||
LogError() << "Missing Qt plugins path\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!QDir(deploymentInfo.pluginPath).exists()) {
|
|
||||||
LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deploy plugins
|
|
||||||
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
|
|
||||||
if (!deploymentInfo.pluginPath.isEmpty()) {
|
|
||||||
LogNormal();
|
|
||||||
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
|
|
||||||
createQtConf(appBundlePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runStripEnabled)
|
|
||||||
stripAppBinary(appBundlePath);
|
|
||||||
|
|
||||||
if (runCodesign)
|
|
||||||
codesign(codesignIdentiy, appBundlePath);
|
|
||||||
|
|
||||||
if (dmg) {
|
|
||||||
LogNormal();
|
|
||||||
createDiskImage(appBundlePath, filesystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the tools applications of the Qt Toolkit.
|
|
||||||
**
|
|
||||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
||||||
** Commercial License Usage
|
|
||||||
** Licensees holding valid commercial Qt licenses may use this file in
|
|
||||||
** accordance with the commercial license agreement provided with the
|
|
||||||
** Software or, alternatively, in accordance with the terms contained in
|
|
||||||
** a written agreement between you and The Qt Company. For licensing terms
|
|
||||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
||||||
** information use the contact form at https://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU
|
|
||||||
** General Public License version 3 as published by the Free Software
|
|
||||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
||||||
** included in the packaging of this file. Please review the following
|
|
||||||
** information to ensure the GNU General Public License requirements will
|
|
||||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
||||||
**
|
|
||||||
** $QT_END_LICENSE$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
#ifndef MAC_DEPLOMYMENT_SHARED_H
|
|
||||||
#define MAC_DEPLOMYMENT_SHARED_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QVersionNumber>
|
|
||||||
|
|
||||||
extern int logLevel;
|
|
||||||
#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
|
|
||||||
#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
|
|
||||||
#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
|
|
||||||
#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
|
|
||||||
|
|
||||||
extern bool runStripEnabled;
|
|
||||||
|
|
||||||
class FrameworkInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool isDylib;
|
|
||||||
QString frameworkDirectory;
|
|
||||||
QString frameworkName;
|
|
||||||
QString frameworkPath;
|
|
||||||
QString binaryDirectory;
|
|
||||||
QString binaryName;
|
|
||||||
QString binaryPath;
|
|
||||||
QString rpathUsed;
|
|
||||||
QString version;
|
|
||||||
QString installName;
|
|
||||||
QString deployedInstallName;
|
|
||||||
QString sourceFilePath;
|
|
||||||
QString frameworkDestinationDirectory;
|
|
||||||
QString binaryDestinationDirectory;
|
|
||||||
|
|
||||||
bool isDebugLibrary() const
|
|
||||||
{
|
|
||||||
return binaryName.endsWith(QStringLiteral("_debug"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DylibInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString binaryPath;
|
|
||||||
QVersionNumber currentVersion;
|
|
||||||
QVersionNumber compatibilityVersion;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OtoolInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString installName;
|
|
||||||
QString binaryPath;
|
|
||||||
QVersionNumber currentVersion;
|
|
||||||
QVersionNumber compatibilityVersion;
|
|
||||||
QList<DylibInfo> dependencies;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
|
|
||||||
QDebug operator<<(QDebug debug, const FrameworkInfo &info);
|
|
||||||
|
|
||||||
class ApplicationBundleInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString path;
|
|
||||||
QString binaryPath;
|
|
||||||
QStringList libraryPaths;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeploymentInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString qtPath;
|
|
||||||
QString pluginPath;
|
|
||||||
QStringList deployedFrameworks;
|
|
||||||
QList<QString> rpathsUsed;
|
|
||||||
bool useLoaderPath;
|
|
||||||
bool isFramework;
|
|
||||||
bool isDebug;
|
|
||||||
|
|
||||||
bool containsModule(const QString &module, const QString &libInFix) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
|
|
||||||
|
|
||||||
OtoolInfo findDependencyInfo(const QString &binaryPath);
|
|
||||||
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
|
||||||
QString findAppBinary(const QString &appBundlePath);
|
|
||||||
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
|
||||||
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
|
|
||||||
QString copyFramework(const FrameworkInfo &framework, const QString path);
|
|
||||||
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
|
|
||||||
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
|
|
||||||
void createQtConf(const QString &appBundlePath);
|
|
||||||
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
|
|
||||||
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
|
|
||||||
void changeIdentification(const QString &id, const QString &binaryPath);
|
|
||||||
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
|
|
||||||
void runStrip(const QString &binaryPath);
|
|
||||||
void stripAppBinary(const QString &bundlePath);
|
|
||||||
QString findAppBinary(const QString &appBundlePath);
|
|
||||||
QStringList findAppFrameworkNames(const QString &appBundlePath);
|
|
||||||
QStringList findAppFrameworkPaths(const QString &appBundlePath);
|
|
||||||
void codesignFile(const QString &identity, const QString &filePath);
|
|
||||||
QSet<QString> codesignBundle(const QString &identity,
|
|
||||||
const QString &appBundlePath,
|
|
||||||
QList<QString> additionalBinariesContainingRpaths);
|
|
||||||
void codesign(const QString &identity, const QString &appBundlePath);
|
|
||||||
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
|
|
||||||
void fixupFramework(const QString &appBundlePath);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
245
CMakeLists.txt
|
@ -99,27 +99,15 @@ if(CCACHE_EXECUTABLE)
|
||||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
|
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(USE_ICU "Use ICU" ON)
|
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(Boost REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(Threads)
|
|
||||||
find_package(Backtrace)
|
find_package(Backtrace)
|
||||||
if(Backtrace_FOUND)
|
if(Backtrace_FOUND)
|
||||||
set(HAVE_BACKTRACE ON)
|
set(HAVE_BACKTRACE ON)
|
||||||
endif()
|
endif()
|
||||||
if(USE_ICU)
|
find_package(Boost REQUIRED)
|
||||||
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
||||||
if(ICU_FOUND)
|
find_package(Protobuf CONFIG)
|
||||||
set(HAVE_ICU ON)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
find_package(Iconv)
|
|
||||||
endif()
|
|
||||||
find_package(GnuTLS REQUIRED)
|
|
||||||
if(NOT APPLE)
|
|
||||||
find_package(Protobuf CONFIG)
|
|
||||||
endif()
|
|
||||||
if(NOT Protobuf_FOUND)
|
if(NOT Protobuf_FOUND)
|
||||||
find_package(Protobuf REQUIRED)
|
find_package(Protobuf REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
@ -165,84 +153,52 @@ find_package(FFTW3)
|
||||||
find_package(GTest)
|
find_package(GTest)
|
||||||
find_library(GMOCK_LIBRARY gmock)
|
find_library(GMOCK_LIBRARY gmock)
|
||||||
|
|
||||||
option(QT_VERSION_MAJOR "Qt version to use (5 or 6)")
|
|
||||||
option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
|
option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
|
||||||
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
|
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
|
||||||
|
|
||||||
if(WITH_QT6)
|
if(BUILD_WITH_QT6)
|
||||||
set(BUILD_WITH_QT6 ON)
|
set(QT_VERSION_MAJOR 6)
|
||||||
endif()
|
elseif(BUILD_WITH_QT5)
|
||||||
if(QT_MAJOR_VERSION)
|
set(QT_VERSION_MAJOR 5)
|
||||||
set(QT_VERSION_MAJOR ${QT_MAJOR_VERSION})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(QT_VERSION_MAJOR)
|
if(NOT QT_VERSION_MAJOR)
|
||||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
message(STATUS "QT_VERSION_MAJOR, BUILD_WITH_QT5 or BUILD_WITH_QT6 not set, detecting Qt version...")
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
set(QT_MIN_VERSION 6.0)
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
set(QT_MIN_VERSION 5.12)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Invalid QT_VERSION_MAJOR.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
||||||
|
|
||||||
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
|
||||||
|
set(QT_OPTIONAL_COMPONENTS LinguistTools Test)
|
||||||
if(DBUS_FOUND AND NOT WIN32)
|
if(DBUS_FOUND AND NOT WIN32)
|
||||||
list(APPEND QT_COMPONENTS DBus)
|
list(APPEND QT_COMPONENTS DBus)
|
||||||
endif()
|
endif()
|
||||||
set(QT_OPTIONAL_COMPONENTS Test)
|
if(X11_FOUND AND QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(QT_MIN_VERSION 5.12)
|
list(APPEND QT_COMPONENTS X11Extras)
|
||||||
|
|
||||||
if(BUILD_WITH_QT6 OR QT_VERSION_MAJOR EQUAL 6)
|
|
||||||
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
|
|
||||||
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
|
|
||||||
elseif(BUILD_WITH_QT5 OR QT_VERSION_MAJOR EQUAL 5)
|
|
||||||
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
|
|
||||||
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
|
|
||||||
else()
|
|
||||||
# Automatically detect Qt version.
|
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
|
|
||||||
if(QT_FOUND AND QT_VERSION_MAJOR EQUAL 6)
|
|
||||||
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
|
|
||||||
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
|
|
||||||
elseif(QT_FOUND AND QT_VERSION_MAJOR EQUAL 5)
|
|
||||||
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
|
|
||||||
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Missing Qt.")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(QT_VERSION_MAJOR)
|
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
||||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(X11_FOUND AND BUILD_WITH_QT5)
|
|
||||||
list(APPEND QT_OPTIONAL_COMPONENTS X11Extras)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
|
|
||||||
|
|
||||||
set(QtCore_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
|
|
||||||
set(QtConcurrent_LIBRARIES Qt${QT_VERSION_MAJOR}::Concurrent)
|
|
||||||
set(QtGui_LIBRARIES Qt${QT_VERSION_MAJOR}::Gui)
|
|
||||||
set(QtWidgets_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets)
|
|
||||||
set(QtNetwork_LIBRARIES Qt${QT_VERSION_MAJOR}::Network)
|
|
||||||
set(QtSql_LIBRARIES Qt${QT_VERSION_MAJOR}::Sql)
|
|
||||||
set(QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql)
|
|
||||||
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||||
set(QtDBus_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
|
|
||||||
list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
|
|
||||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
|
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
|
||||||
endif()
|
endif()
|
||||||
if(BUILD_WITH_QT5 AND Qt5X11Extras_FOUND)
|
|
||||||
set(HAVE_X11EXTRAS ON)
|
|
||||||
set(QtX11Extras_LIBRARIES Qt5::X11Extras)
|
|
||||||
list(APPEND QT_LIBRARIES Qt5::X11Extras)
|
|
||||||
endif()
|
|
||||||
if(Qt${QT_VERSION_MAJOR}Test_FOUND)
|
|
||||||
set(QtTest_LIBRARIES Qt${QT_VERSION_MAJOR}::Test)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS LinguistTools CONFIG)
|
|
||||||
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
|
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
|
||||||
set(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert)
|
get_target_property(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert LOCATION)
|
||||||
|
endif()
|
||||||
|
if(Qt${QT_VERSION_MAJOR}X11Extras_FOUND)
|
||||||
|
set(HAVE_X11EXTRAS ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_WITH_QT5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
|
if(QT_VERSION_MAJOR EQUAL 5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
|
||||||
macro(qt_add_resources)
|
macro(qt_add_resources)
|
||||||
qt5_add_resources(${ARGN})
|
qt5_add_resources(${ARGN})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
@ -283,9 +239,9 @@ if(X11_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for QX11Application (Qt 6 compiled with XCB).
|
# Check for QX11Application (Qt 6 compiled with XCB).
|
||||||
if(BUILD_WITH_QT6 AND Qt6Gui_VERSION VERSION_GREATER_EQUAL 6.2.0)
|
if(QT_VERSION_MAJOR EQUAL 6 AND Qt6Gui_VERSION VERSION_GREATER_EQUAL 6.2.0)
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtGui_LIBRARIES})
|
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
|
||||||
check_cxx_source_compiles("
|
check_cxx_source_compiles("
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
int main() {
|
int main() {
|
||||||
|
@ -301,25 +257,19 @@ if(X11_FOUND)
|
||||||
|
|
||||||
endif(X11_FOUND)
|
endif(X11_FOUND)
|
||||||
|
|
||||||
option(USE_TAGLIB "Build with TagLib" OFF)
|
option(USE_TAGLIB "Build with TagLib" ON)
|
||||||
option(USE_TAGPARSER "Build with TagParser" OFF)
|
option(USE_TAGPARSER "Build with TagParser" OFF)
|
||||||
|
|
||||||
if(NOT USE_TAGLIB AND NOT USE_TAGPARSER)
|
|
||||||
set(USE_TAGLIB ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# TAGLIB
|
# TAGLIB
|
||||||
if(USE_TAGLIB)
|
if(USE_TAGLIB)
|
||||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
find_package(TagLib 2.0)
|
||||||
if(TAGLIB_FOUND)
|
if(TARGET TagLib::TagLib)
|
||||||
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
|
set(TAGLIB_FOUND ON)
|
||||||
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
|
set(TAGLIB_LIBRARIES TagLib::TagLib)
|
||||||
if(HAVE_TAGLIB_DSFFILE_H)
|
set(HAVE_TAGLIB_DSFFILE ON)
|
||||||
set(HAVE_TAGLIB_DSFFILE ON)
|
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
||||||
endif(HAVE_TAGLIB_DSFFILE_H)
|
else()
|
||||||
if(HAVE_TAGLIB_DSDIFFFILE_H)
|
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
||||||
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
|
||||||
endif(HAVE_TAGLIB_DSDIFFFILE_H)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -328,30 +278,45 @@ if(USE_TAGPARSER)
|
||||||
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
||||||
|
|
||||||
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
||||||
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SingleApplication
|
# SingleApplication
|
||||||
add_subdirectory(3rdparty/kdsingleapplication)
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/kdsingleapplication)
|
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication")
|
||||||
set(SINGLEAPPLICATION_LIBRARIES kdsingleapplication)
|
else()
|
||||||
add_definitions(-DKDSINGLEAPPLICATION_STATIC_BUILD)
|
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication-qt${QT_VERSION_MAJOR}")
|
||||||
|
endif()
|
||||||
|
find_package(${KDSINGLEAPPLICATION_NAME} 1.1.0)
|
||||||
|
if(TARGET KDAB::kdsingleapplication)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication_VERSION}")
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication-qt6_VERSION}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Using system KDSingleApplication (Version ${KDSINGLEAPPLICATION_VERSION})")
|
||||||
|
set(SINGLEAPPLICATION_LIBRARIES KDAB::kdsingleapplication)
|
||||||
|
else()
|
||||||
|
message(STATUS "Using 3rdparty KDSingleApplication")
|
||||||
|
set(HAVE_KDSINGLEAPPLICATION_OPTIONS ON)
|
||||||
|
add_subdirectory(3rdparty/kdsingleapplication)
|
||||||
|
set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/kdsingleapplication/KDSingleApplication/src)
|
||||||
|
set(SINGLEAPPLICATION_LIBRARIES kdsingleapplication)
|
||||||
|
add_definitions(-DKDSINGLEAPPLICATION_STATIC_BUILD)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_subdirectory(3rdparty/SPMediaKeyTap)
|
add_subdirectory(3rdparty/SPMediaKeyTap)
|
||||||
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
|
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
|
||||||
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
|
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
|
||||||
add_subdirectory(3rdparty/macdeployqt)
|
|
||||||
add_subdirectory(ext/macdeploycheck)
|
add_subdirectory(ext/macdeploycheck)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(BUILD_WITH_QT6)
|
pkg_check_modules(QTSPARKLE qtsparkle-qt${QT_VERSION_MAJOR})
|
||||||
pkg_check_modules(QTSPARKLE qtsparkle-qt6)
|
|
||||||
else()
|
|
||||||
pkg_check_modules(QTSPARKLE qtsparkle-qt5)
|
|
||||||
endif()
|
|
||||||
if(QTSPARKLE_FOUND)
|
if(QTSPARKLE_FOUND)
|
||||||
set(HAVE_QTSPARKLE ON)
|
set(HAVE_QTSPARKLE ON)
|
||||||
endif()
|
endif()
|
||||||
|
@ -385,6 +350,7 @@ optional_component(LIBPULSE ON "PulseAudio integration"
|
||||||
|
|
||||||
optional_component(DBUS ON "D-Bus support"
|
optional_component(DBUS ON "D-Bus support"
|
||||||
DEPENDS "D-Bus" DBUS_FOUND
|
DEPENDS "D-Bus" DBUS_FOUND
|
||||||
|
DEPENDS "Qt D-Bus" Qt${QT_VERSION_MAJOR}DBus_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(GSTREAMER ON "Engine: GStreamer backend"
|
optional_component(GSTREAMER ON "Engine: GStreamer backend"
|
||||||
|
@ -410,7 +376,7 @@ optional_component(MUSICBRAINZ ON "MusicBrainz integration"
|
||||||
DEPENDS "gstreamer" HAVE_GSTREAMER
|
DEPENDS "gstreamer" HAVE_GSTREAMER
|
||||||
)
|
)
|
||||||
|
|
||||||
if(X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
|
if(X11_FOUND OR (HAVE_DBUS AND Qt${QT_VERSION_MAJOR}DBus_FOUND) OR APPLE OR WIN32)
|
||||||
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
|
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -432,7 +398,8 @@ optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
||||||
DEPENDS "D-Bus support" DBUS_FOUND
|
DEPENDS "D-Bus" DBUS_FOUND
|
||||||
|
DEPENDS "Qt D-Bus" Qt${QT_VERSION_MAJOR}DBus_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(GIO ON "Devices: GIO device backend"
|
optional_component(GIO ON "Devices: GIO device backend"
|
||||||
|
@ -442,7 +409,7 @@ optional_component(GIO ON "Devices: GIO device backend"
|
||||||
|
|
||||||
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
|
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
|
||||||
DEPENDS "libgio-unix" GIO_UNIX_FOUND
|
DEPENDS "libgio-unix" GIO_UNIX_FOUND
|
||||||
DEPENDS "Unix" "UNIX"
|
DEPENDS "Unix or Windows" "NOT APPLE"
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(LIBGPOD ON "Devices: iPod classic support"
|
optional_component(LIBGPOD ON "Devices: iPod classic support"
|
||||||
|
@ -454,22 +421,16 @@ optional_component(LIBMTP ON "Devices: MTP support"
|
||||||
DEPENDS "libmtp" LIBMTP_FOUND
|
DEPENDS "libmtp" LIBMTP_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
if(BUILD_WITH_QT6)
|
optional_component(TRANSLATIONS ON "Translations"
|
||||||
optional_component(TRANSLATIONS ON "Translations"
|
DEPENDS "gettext" GETTEXT_FOUND
|
||||||
DEPENDS "gettext" GETTEXT_FOUND
|
DEPENDS "Qt LinguistTools" Qt${QT_VERSION_MAJOR}LinguistTools_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)
|
option(INSTALL_TRANSLATIONS "Install translations" OFF)
|
||||||
|
|
||||||
optional_component(SUBSONIC ON "Streaming: Subsonic")
|
optional_component(SUBSONIC ON "Streaming: Subsonic")
|
||||||
optional_component(TIDAL ON "Streaming: Tidal")
|
optional_component(TIDAL ON "Streaming: Tidal")
|
||||||
|
optional_component(SPOTIFY ON "Streaming: Spotify" DEPENDS "gstreamer" GSTREAMER_FOUND)
|
||||||
optional_component(QOBUZ ON "Streaming: Qobuz")
|
optional_component(QOBUZ ON "Streaming: Qobuz")
|
||||||
|
|
||||||
optional_component(MOODBAR ON "Moodbar"
|
optional_component(MOODBAR ON "Moodbar"
|
||||||
|
@ -477,25 +438,22 @@ optional_component(MOODBAR ON "Moodbar"
|
||||||
DEPENDS "gstreamer" HAVE_GSTREAMER
|
DEPENDS "gstreamer" HAVE_GSTREAMER
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE OR WIN32)
|
optional_component(EBUR128 ON "EBU R 128 loudness normalization"
|
||||||
option(USE_BUNDLE "Bundle dependencies" ON)
|
DEPENDS "libebur128" LIBEBUR128_FOUND
|
||||||
else()
|
DEPENDS "gstreamer" HAVE_GSTREAMER
|
||||||
option(USE_BUNDLE "Bundle dependencies" OFF)
|
)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
if(APPLE OR WIN32)
|
||||||
if(LINUX)
|
set(USE_BUNDLE_DEFAULT ON)
|
||||||
set(USE_BUNDLE_DIR "../plugins")
|
else()
|
||||||
endif()
|
set(USE_BUNDLE_DEFAULT OFF)
|
||||||
if(APPLE)
|
|
||||||
set(USE_BUNDLE_DIR "../PlugIns")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
option(USE_BUNDLE "Bundle dependencies" ${USE_BUNDLE_DEFAULT})
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
# Check that we have Qt with sqlite driver
|
# Check that we have Qt with sqlite driver
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql)
|
||||||
check_cxx_source_runs("
|
check_cxx_source_runs("
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
@ -510,25 +468,6 @@ if(NOT CMAKE_CROSSCOMPILING)
|
||||||
"
|
"
|
||||||
QT_SQLITE_TEST
|
QT_SQLITE_TEST
|
||||||
)
|
)
|
||||||
if(QT_SQLITE_TEST)
|
|
||||||
# Check that we have sqlite3 with FTS5
|
|
||||||
check_cxx_source_runs("
|
|
||||||
#include <QSqlDatabase>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
int main() {
|
|
||||||
QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");
|
|
||||||
db.setDatabaseName(\":memory:\");
|
|
||||||
if (!db.open()) { return 1; }
|
|
||||||
QSqlQuery q(db);
|
|
||||||
q.prepare(\"CREATE VIRTUAL TABLE test_fts USING fts5(test, tokenize = 'unicode61 remove_diacritics 0');\");
|
|
||||||
if (!q.exec()) return 1;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
SQLITE_FTS5_TEST
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
unset(CMAKE_REQUIRED_FLAGS)
|
|
||||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set up definitions
|
# Set up definitions
|
||||||
|
@ -542,6 +481,8 @@ add_definitions(
|
||||||
-DQT_NO_CAST_TO_ASCII
|
-DQT_NO_CAST_TO_ASCII
|
||||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||||
-DQT_NO_FOREACH
|
-DQT_NO_FOREACH
|
||||||
|
-DQT_ASCII_CAST_WARNINGS
|
||||||
|
-DQT_NO_CAST_FROM_ASCII
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
@ -561,7 +502,7 @@ if(HAVE_MOODBAR)
|
||||||
add_subdirectory(ext/gstmoodbar)
|
add_subdirectory(ext/gstmoodbar)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(GTest_FOUND AND GMOCK_LIBRARY AND QtTest_LIBRARIES)
|
if(GTest_FOUND AND GMOCK_LIBRARY AND Qt${QT_VERSION_MAJOR}Test_FOUND)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -587,11 +528,7 @@ if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
if(QT_SQLITE_TEST)
|
if(NOT QT_SQLITE_TEST)
|
||||||
if(NOT SQLITE_FTS5_TEST)
|
|
||||||
message(WARNING "sqlite must be enabled with FTS5. See: https://www.sqlite.org/fts5.html")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "The Qt sqlite driver test failed.")
|
message(WARNING "The Qt sqlite driver test failed.")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -48,24 +48,27 @@ small as possible.
|
||||||
|
|
||||||
### Commit messages
|
### Commit messages
|
||||||
|
|
||||||
The first line should start with "Class:", which referer to the class
|
The first line should start with the name of the class that is changed
|
||||||
that is changed. Don't use a trailing period after the first line.
|
followed by a colon then a short explanation of the commit.
|
||||||
If this change affects more than one class, omit the class and write a
|
Don't use a trailing period after the first line.
|
||||||
|
If this change affects more than one class, omit the class name and write a
|
||||||
more general message.
|
more general message.
|
||||||
|
|
||||||
You only need to include a main description (body) for larger changes
|
You only need to include a main description (body) for larger changes
|
||||||
where the one line is not enough to describe everything.
|
where the one line is not enough to describe everything.
|
||||||
The main description starts after two newlines, it is normal prose and
|
The main description starts after two newlines, it is normal prose and
|
||||||
should use normal punctuation and capital letters where appropriate.
|
should use normal punctuation and capital letters where appropriate.
|
||||||
|
It should explain exactly what's changed, why it's changed,
|
||||||
|
and what bugs were fixed.
|
||||||
|
|
||||||
An example of the expected format for git commit messages is as follows:
|
An example of the expected format for git commit messages is as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
class: Short explanation of the commit
|
StretchHeaderView: Set default section size
|
||||||
|
|
||||||
Longer explanation explaining exactly what's changed, why it's changed,
|
As of Qt 6.6.1, style changes are resetting the column sizes. To prevent this, we set a default section size.
|
||||||
and what bugs were fixed.
|
|
||||||
|
|
||||||
Fixes #1234
|
Fixes #1328
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
116
Changelog
|
@ -2,6 +2,122 @@ Strawberry Music Player
|
||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
|
Unreleased:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed crash when pressing CTRL + C (#1359).
|
||||||
|
* Pass on scroll events to page in settings to avoid changing settings when scrolling with mouse (#1380).
|
||||||
|
* Fixed misredered playlist search field with Wayland using scaling (#1255).
|
||||||
|
* Fixed Azlyrics lyrics provider because of website changes.
|
||||||
|
* Fixed Musixmatch lyrics provider because of website changes.
|
||||||
|
* Fixed application exiting when closing file dialog (#1401).
|
||||||
|
* Fixed playlist shuffle randomness (#707).
|
||||||
|
* Fixed playlist shuffle order always the same when restarting playback (#1381).
|
||||||
|
* Fixed dynamic random mix not always ignoring shuffle and repeat mode (#1366).
|
||||||
|
* Fixed manual shuffle while playing not setting current song to new index (##1353).
|
||||||
|
* Fixed volume sync with PA when output is set to auto (#1123).
|
||||||
|
* Fixed mpris:trackid type with KDE 6 (#1397).
|
||||||
|
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
|
||||||
|
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Improve error messages when connecting and copying to devices.
|
||||||
|
* Allow enter to be used with multiselection to add songs to playlist (#1360)
|
||||||
|
* Add song progress to taskbar using D-Bus.
|
||||||
|
* Use API to receive Radio Paradise channels.
|
||||||
|
* Added letras lyrics provider.
|
||||||
|
* Added Open Tidal API (openapi.tidal.com) cover provider.
|
||||||
|
* Added button for fetching lyrics to tag editor (#1391).
|
||||||
|
* Added option not to skip "A", "An" and "The” when sorting artist names in collection (#1393).
|
||||||
|
* Improved album and title disc, remastered, etc matching and stripping (#1387).
|
||||||
|
* (Unix) Add experimental GStreamer pipewire support.
|
||||||
|
* (Windows) Add experimental exclusive mode for WASAPI.
|
||||||
|
* (Windows MSVC) Add ASIO support.
|
||||||
|
* (Windows MSVC) Add back WASAPI2.
|
||||||
|
|
||||||
|
Removed features:
|
||||||
|
* Removed now broken lyricsmode.com lyrics provider because of website changes.
|
||||||
|
|
||||||
|
Version 1.0.23 (2024.01.11):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed possible duplication of song entries after organizing (#1341).
|
||||||
|
* Fixed possible crash when connecting devices (#1313).
|
||||||
|
* Fixed playlist sorting of original year (#1349).
|
||||||
|
* (macOS) Fixed crash when adding collection directory (QTBUG-120469) (#1350).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Treat all stream errors as non-fatal (#1347).
|
||||||
|
* Require KDSingleApplication 1.1.0.
|
||||||
|
* Fix logging of restored unavailable songs.
|
||||||
|
|
||||||
|
Version 1.0.22 (2023.12.09):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed KDSingleApplication cmake version check.
|
||||||
|
* Fixed KDSingleApplication Qt 5 detection (#1299).
|
||||||
|
* Fixed timer started in wrong thread (#1302).
|
||||||
|
* Fixed erratic seeking behaviour if buffer duration is set to zero (#1302).
|
||||||
|
* Fixed SCollection related crash on exit with Qt 5 (#1316).
|
||||||
|
* Fixed track about to end related crash on playback failure (#1332).
|
||||||
|
* Fixed playlist column widths not remembered if stretch mode is off with Qt 6.6.1 and higher (#1328).
|
||||||
|
* (Windows) Properly handle silent uninstall (#1323).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Increase thread priority for playback threads.
|
||||||
|
* Allow drag and drop of songs to favorite playlists.
|
||||||
|
|
||||||
|
Version 1.0.21 (2023.10.21):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed seekbar position resetting to zero before showing actual position when seeking.
|
||||||
|
* Fixed compressed files showing up in collection (#1274).
|
||||||
|
* Fixed connecting devices (#1288).
|
||||||
|
* Fixed device schema missing ebur128 fields.
|
||||||
|
* Fixed collection search by tag not working with space between colon and search term (#1290).
|
||||||
|
* Fixed seeking when 5 seconds is remaining of the song resetting position to beginning (#1258).
|
||||||
|
* Fixed intermittent crash when seeking with Auto as output (#1123).
|
||||||
|
* (Windows) Fixed playlist header colors in dark mode (#1275).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Support using system KDSingleApplication when available.
|
||||||
|
* Improved lyrics matching.
|
||||||
|
* (macOS) Fully codesign binaries and DMG.
|
||||||
|
|
||||||
|
Version 1.0.20 (2023.09.24):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed appdata validation.
|
||||||
|
|
||||||
|
Version 1.0.19 (2023.09.24):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Use shared pointers for objects to fix potential crashes on exit (#1239).
|
||||||
|
* Fixed smart playlist search not matching unrated songs (#1244).
|
||||||
|
* Fixed reading FMPS_Playcount for MP3 ID3v2 tags (#1248).
|
||||||
|
* Always stop playing after 100 errors to prevent flooding the error dialog (#1220).
|
||||||
|
* Fixed volume going to 100% when decreasing volume beyond zero (#1262).
|
||||||
|
* Fixed error dialog sometimes showing empty.
|
||||||
|
* (Windows) Removed broken volume sync (#1220).
|
||||||
|
* (Windows) Fixed shuttering / choppy audio (#1227).
|
||||||
|
* (macOS) Fixed missing search bars (#1221).
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Add Mpris2 property to read/write rating (#1246).
|
||||||
|
* Capitalize playlist column names (#1264).
|
||||||
|
* Added lyrics from songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com.
|
||||||
|
* (Windows) Add gst-play-1.0.exe for debugging purposes.
|
||||||
|
|
||||||
|
New features
|
||||||
|
* Support performing song loudness analysis using `libebur128` (#1216).
|
||||||
|
* Support song playback loudness normalization, as per EBU R 128 (#1216).
|
||||||
|
|
||||||
|
Other:
|
||||||
|
* Removed last.fm HTTPS workaround and GnuTLS dependency
|
||||||
|
* Removed broken lyrics.com lyrics provider.
|
||||||
|
* (Windows) Use DirectSound as default sink.
|
||||||
|
* (Windows) Remove WASPI2 plugin because of GStreamer bug.
|
||||||
|
|
||||||
Version 1.0.18 (2023.07.02):
|
Version 1.0.18 (2023.07.02):
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
34
README.md
|
@ -1,4 +1,4 @@
|
||||||
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
|
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/Build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||||
=======================
|
=======================
|
||||||
[![Sponsor](https://img.shields.io/badge/-Sponsor-green?logo=github)](https://github.com/sponsors/jonaski)
|
[![Sponsor](https://img.shields.io/badge/-Sponsor-green?logo=github)](https://github.com/sponsors/jonaski)
|
||||||
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
|
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
|
||||||
|
@ -19,27 +19,28 @@ Resources:
|
||||||
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
|
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
|
||||||
* Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
|
* Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
|
||||||
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
|
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
|
||||||
* Translations: https://translate.zanata.org/iteration/view/strawberry/master
|
|
||||||
|
|
||||||
### :bangbang: Opening an issue:
|
### :bangbang: Opening an issue
|
||||||
|
|
||||||
* Read the FAQ: https://wiki.strawberrymusicplayer.org/wiki/FAQ
|
* Read the FAQ: https://wiki.strawberrymusicplayer.org/wiki/FAQ
|
||||||
* Search for the issue to see if it is already solved, or if there is an open issue for it already. If there is an open issue already, you can comment on it if you have additional information that could be useful to us.
|
* Search for the issue to see if it is already solved, or if there is an open issue for it already. If there is an open issue already, you can comment on it if you have additional information that could be useful to us.
|
||||||
* For technical problems, discussion, questions and feature suggestions use the forum (https://forum.strawberrymusicplayer.org/) instead. The forum is better suited for discussion.
|
* For technical problems, discussion, questions and feature suggestions use the forum (https://forum.strawberrymusicplayer.org/) instead. The forum is better suited for discussion.
|
||||||
* We do not take feature requests from users on GitHub. Any issues related to feature requests will be closed. This does not necessarily mean that we won't add new features, but we don't have time to take feature requests or answer questions about new features from users. It is still possible to suggest or discuss new features on the forum (https://forum.strawberrymusicplayer.org/).
|
* We do not take feature requests from users on GitHub. Any issues related to feature requests will be closed. This does not necessarily mean that we won't add new features, but we don't have time to take feature requests or answer questions about new features from users. It is still possible to suggest or discuss new features on the forum (https://forum.strawberrymusicplayer.org/).
|
||||||
|
* We do not maintain the Flatpak package. Do not report issues related to Flatpak unless the issue can be reproduced with a native package, use Flatpak support instead https://flatpak.org/about/
|
||||||
|
|
||||||
### :moneybag: Sponsoring:
|
### :moneybag: Sponsoring
|
||||||
|
|
||||||
The program is free software, released under GPL. If you like this program and can make use of it, consider sponsoring or donating to help fund the project.
|
The program is free software, released under GPL. If you like this program and can make use of it, consider sponsoring or donating to help fund the project.
|
||||||
There are currently 3 options for sponsoring:
|
There are currently 4 options for sponsoring:
|
||||||
|
|
||||||
1. [GitHub Sponsors](https://github.com/sponsors/jonaski)
|
1. [GitHub](https://github.com/sponsors/jonaski)
|
||||||
2. [Patreon](https://www.patreon.com/jonaskvinge)
|
2. [Patreon](https://www.patreon.com/jonaskvinge)
|
||||||
3. [PayPal](https://paypal.me/jonaskvinge)
|
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
|
||||||
|
4. [PayPal](https://paypal.me/jonaskvinge)
|
||||||
|
|
||||||
Funding developers is a way to contribute to open source projects you appreciate, it helps developers get the resources they need, and recognize contributors working behind the scenes to make open source better for everyone.
|
Funding developers is a way to contribute to open source projects you appreciate, it helps developers get the resources they need, and recognize contributors working behind the scenes to make open source better for everyone.
|
||||||
|
|
||||||
### :heavy_check_mark: Features:
|
### :heavy_check_mark: Features
|
||||||
|
|
||||||
* Play and organize music
|
* Play and organize music
|
||||||
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
||||||
|
@ -48,21 +49,22 @@ Funding developers is a way to contribute to open source projects you appreciate
|
||||||
* Playlist management
|
* Playlist management
|
||||||
* Smart and dynamic playlists
|
* Smart and dynamic playlists
|
||||||
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||||
|
* In-player song loudness analysis and song playback loudness normalization, as per EBU R 128
|
||||||
* Edit tags on audio files
|
* Edit tags on audio files
|
||||||
* Fetch tags from MusicBrainz
|
* Fetch tags from MusicBrainz
|
||||||
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
|
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
|
||||||
* Song lyrics from [Lyrics.com](https://www.lyrics.com/), [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
|
* Song lyrics from [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics.com](https://www.lololyrics.com/), [songlyrics.com](https://www.songlyrics.com/), [azlyrics.com](https://www.azlyrics.com/) and [elyrics.net](https://www.elyrics.net/)
|
||||||
* Support for multiple backends
|
* Support for multiple backends
|
||||||
* Audio analyzer
|
* Audio analyzer
|
||||||
* Audio equalizer
|
* Audio equalizer
|
||||||
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||||
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
||||||
* Subsonic, Tidal and Qobuz streaming support
|
* Subsonic, Tidal, Spotify and Qobuz streaming support
|
||||||
|
|
||||||
|
|
||||||
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
||||||
|
|
||||||
**There currently isn't any macOS developers actively working on this project, so we might not be able to help you with issues related to macOS.**
|
**macOS releases are currently limited to sponsors. This is because Strawberry mainly has one contributor/developer and supporting macOS requires Apple hardware, building libraries Strawberry depends and a Apple developer account for signing releases. If you are sponsoring strawberry through Patreon, releases are available directly on Patreon, if you are sponsoring through GitHub, Ko-fi or Paypal, please e-mail support@strawberrymusicplayer.org for access to downloads.**
|
||||||
|
|
||||||
### :heavy_exclamation_mark: Requirements
|
### :heavy_exclamation_mark: Requirements
|
||||||
|
|
||||||
|
@ -74,12 +76,11 @@ To build Strawberry from source you need the following installed on your system
|
||||||
* [Boost](https://www.boost.org/)
|
* [Boost](https://www.boost.org/)
|
||||||
* [GLib](https://developer.gnome.org/glib/)
|
* [GLib](https://developer.gnome.org/glib/)
|
||||||
* [Qt 6 or Qt 5.12 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
* [Qt 6 or Qt 5.12 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
||||||
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
|
* [SQLite 3.9 or newer](https://www.sqlite.org)
|
||||||
* [Protobuf](https://developers.google.com/protocol-buffers/)
|
* [Protobuf](https://developers.google.com/protocol-buffers/)
|
||||||
* [ALSA (Required on Linux)](https://www.alsa-project.org/)
|
* [ALSA (Required on Linux)](https://www.alsa-project.org/)
|
||||||
* [D-Bus (Required on Linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
* [D-Bus (Required on Linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||||
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
||||||
* [GnuTLS](https://www.gnutls.org/)
|
|
||||||
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
|
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
|
||||||
* [ICU](https://unicode-org.github.io/icu/)
|
* [ICU](https://unicode-org.github.io/icu/)
|
||||||
|
|
||||||
|
@ -91,14 +92,15 @@ Optional dependencies:
|
||||||
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
||||||
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
||||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||||
|
* EBU R 128 loudness normalization [libebur128](https://github.com/jiixyj/libebur128)
|
||||||
|
|
||||||
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
|
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
|
||||||
|
|
||||||
### :wrench: Compiling from source
|
### :wrench: Compiling from source
|
||||||
|
|
||||||
### Get the code:
|
### Get the code:
|
||||||
|
|
||||||
git clone https://github.com/strawberrymusicplayer/strawberry
|
git clone --recursive https://github.com/strawberrymusicplayer/strawberry
|
||||||
|
|
||||||
### Compile and install:
|
### Compile and install:
|
||||||
|
|
||||||
|
@ -115,6 +117,6 @@ Strawberry is backwards compatible with Qt 5, to compile with Qt 5 use:
|
||||||
|
|
||||||
To compile on Windows with Visual Studio 2019 or 2022, see https://github.com/strawberrymusicplayer/strawberry-msvc
|
To compile on Windows with Visual Studio 2019 or 2022, see https://github.com/strawberrymusicplayer/strawberry-msvc
|
||||||
|
|
||||||
### :penguin: Packaging status
|
### :penguin: Packaging status
|
||||||
|
|
||||||
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)
|
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
|
find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/bin /usr/local/bin /opt/local/bin /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin REQUIRED)
|
||||||
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
|
|
||||||
if(MACDEPLOYQT_EXECUTABLE)
|
if(MACDEPLOYQT_EXECUTABLE)
|
||||||
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
||||||
else()
|
else()
|
||||||
|
@ -14,16 +13,23 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MACDEPLOYQT_EXECUTABLE)
|
if(MACDEPLOYQT_EXECUTABLE)
|
||||||
add_custom_target(copy_gstreamer_plugins
|
|
||||||
#COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app
|
if(APPLE_DEVELOPER_ID)
|
||||||
)
|
set(MACDEPLOYQT_CODESIGN -codesign=${APPLE_DEVELOPER_ID})
|
||||||
|
set(CREATEDMG_CODESIGN --codesign ${APPLE_DEVELOPER_ID})
|
||||||
|
endif()
|
||||||
|
if(CREATEDMG_SKIP_JENKINS)
|
||||||
|
set(CREATEDMG_SKIP_JENKINS_ARG "--skip-jenkins")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_custom_target(deploy
|
add_custom_target(deploy
|
||||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
||||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
||||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
||||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/strawberry.app
|
||||||
|
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt
|
DEPENDS strawberry strawberry-tagreader
|
||||||
)
|
)
|
||||||
add_custom_target(deploycheck
|
add_custom_target(deploycheck
|
||||||
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
|
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
|
||||||
|
@ -31,9 +37,8 @@ if(MACDEPLOYQT_EXECUTABLE)
|
||||||
)
|
)
|
||||||
if(CREATEDMG_EXECUTABLE)
|
if(CREATEDMG_EXECUTABLE)
|
||||||
add_custom_target(dmg
|
add_custom_target(dmg
|
||||||
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 ${CREATEDMG_CODESIGN} ${CREATEDMG_SKIP_JENKINS_ARG} strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
DEPENDS deploy deploycheck
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,21 +1,33 @@
|
||||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext REQUIRED)
|
||||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
find_program(CAT_EXECUTABLE cat REQUIRED)
|
||||||
message(FATAL_ERROR "Could not find xgettext executable")
|
|
||||||
endif(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
|
||||||
|
|
||||||
set (XGETTEXT_OPTIONS
|
list(APPEND XGETTEXT_OPTIONS
|
||||||
--qt
|
--qt
|
||||||
--keyword=tr:1,2c
|
--keyword=tr:1,2c
|
||||||
--keyword=tr --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
--keyword=tr
|
||||||
--keyword=trUtf8 --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
--flag=tr:1:pass-c-format
|
||||||
--keyword=translate:2,3c
|
--flag=tr:1:pass-qt-format
|
||||||
--keyword=translate:2 --flag=translate:2:pass-c-format --flag=translate:2:pass-qt-format
|
--keyword=trUtf8
|
||||||
--keyword=QT_TR_NOOP --flag=QT_TR_NOOP:1:pass-c-format --flag=QT_TR_NOOP:1:pass-qt-format
|
--flag=tr:1:pass-c-format
|
||||||
--keyword=QT_TRANSLATE_NOOP:2 --flag=QT_TRANSLATE_NOOP:2:pass-c-format --flag=QT_TRANSLATE_NOOP:2:pass-qt-format
|
--flag=tr:1:pass-qt-format
|
||||||
--keyword=_ --flag=_:1:pass-c-format --flag=_:1:pass-qt-format
|
--keyword=translate:2,3c
|
||||||
--keyword=N_ --flag=N_:1:pass-c-format --flag=N_:1:pass-qt-format
|
--keyword=translate:2
|
||||||
--from-code=utf-8
|
--flag=translate:2:pass-c-format
|
||||||
)
|
--flag=translate:2:pass-qt-format
|
||||||
|
--keyword=QT_TR_NOOP
|
||||||
|
--flag=QT_TR_NOOP:1:pass-c-format
|
||||||
|
--flag=QT_TR_NOOP:1:pass-qt-format
|
||||||
|
--keyword=QT_TRANSLATE_NOOP:2
|
||||||
|
--flag=QT_TRANSLATE_NOOP:2:pass-c-format
|
||||||
|
--flag=QT_TRANSLATE_NOOP:2:pass-qt-format
|
||||||
|
--keyword=_
|
||||||
|
--flag=_:1:pass-c-format
|
||||||
|
--flag=_:1:pass-qt-format
|
||||||
|
--keyword=N_
|
||||||
|
--flag=N_:1:pass-c-format
|
||||||
|
--flag=N_:1:pass-qt-format
|
||||||
|
--from-code=utf-8
|
||||||
|
)
|
||||||
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
|
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
|
||||||
|
|
||||||
|
@ -32,7 +44,7 @@ macro(add_pot outfiles header pot)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${pot}
|
OUTPUT ${pot}
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTIONS} -s -C --omit-header --output=${CMAKE_CURRENT_BINARY_DIR}/pot.temp ${add_pot_sources}
|
COMMAND xgettext ${XGETTEXT_OPTIONS} -s -C --omit-header --output="${CMAKE_CURRENT_BINARY_DIR}/pot.temp" ${add_pot_sources}
|
||||||
COMMAND cat ${header} ${CMAKE_CURRENT_BINARY_DIR}/pot.temp > ${pot}
|
COMMAND cat ${header} ${CMAKE_CURRENT_BINARY_DIR}/pot.temp > ${pot}
|
||||||
DEPENDS ${add_pot_sources} ${header}
|
DEPENDS ${add_pot_sources} ${header}
|
||||||
)
|
)
|
||||||
|
@ -59,7 +71,7 @@ macro(add_po outfiles po_prefix)
|
||||||
# Convert the .po files to .qm files
|
# Convert the .po files to .qm files
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${_qm_filepath}
|
OUTPUT ${_qm_filepath}
|
||||||
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm
|
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm -target-language ${_lang}
|
||||||
DEPENDS ${_po_filepath} ${_po_filepath}
|
DEPENDS ${_po_filepath} ${_po_filepath}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||||
set(STRAWBERRY_VERSION_MINOR 0)
|
set(STRAWBERRY_VERSION_MINOR 0)
|
||||||
set(STRAWBERRY_VERSION_PATCH 18)
|
set(STRAWBERRY_VERSION_PATCH 23)
|
||||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||||
|
|
||||||
set(INCLUDE_GIT_REVISION OFF)
|
set(INCLUDE_GIT_REVISION ON)
|
||||||
|
|
||||||
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
|
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
|
|
||||||
find_program(GIT_EXECUTABLE git)
|
find_program(GIT_EXECUTABLE git)
|
||||||
if(NOT GIT_EXECUTABLE OR GIT_EXECUTABLE-NOTFOUND)
|
if(NOT GIT_EXECUTABLE OR GIT_EXECUTABLE-NOTFOUND)
|
||||||
message(FATAL_ERROR "Missing GIT executable." )
|
message(FATAL_ERROR "Missing Git executable." )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Get the current working branch
|
# Get the current working branch
|
||||||
|
@ -48,7 +48,7 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT ${GIT_CMD_RESULT_REVISION} EQUAL 0)
|
if(NOT ${GIT_CMD_RESULT_REVISION} EQUAL 0)
|
||||||
message(FATAL_ERROR "GIT command failed to get revision string '${GIT_REVISION}'")
|
message(FATAL_ERROR "Git command failed to get revision string '${GIT_REVISION}'")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -67,7 +67,7 @@ if(GIT_REVISION)
|
||||||
|
|
||||||
list(LENGTH GIT_PARTS GIT_PARTS_LENGTH)
|
list(LENGTH GIT_PARTS GIT_PARTS_LENGTH)
|
||||||
if(NOT GIT_PARTS_LENGTH EQUAL 3)
|
if(NOT GIT_PARTS_LENGTH EQUAL 3)
|
||||||
message(FATAL_ERROR "Failed to parse git revision string '${GIT_REVISION}'")
|
set(GIT_PARTS "${majorminorpatch};0;${GIT_REVISION}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(GET GIT_PARTS 0 GIT_TAGNAME)
|
list(GET GIT_PARTS 0 GIT_TAGNAME)
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
<file>schema/schema-15.sql</file>
|
<file>schema/schema-15.sql</file>
|
||||||
<file>schema/schema-16.sql</file>
|
<file>schema/schema-16.sql</file>
|
||||||
<file>schema/schema-17.sql</file>
|
<file>schema/schema-17.sql</file>
|
||||||
|
<file>schema/schema-18.sql</file>
|
||||||
|
<file>schema/schema-19.sql</file>
|
||||||
|
<file>schema/schema-20.sql</file>
|
||||||
<file>schema/device-schema.sql</file>
|
<file>schema/device-schema.sql</file>
|
||||||
<file>style/strawberry.css</file>
|
<file>style/strawberry.css</file>
|
||||||
<file>style/smartplaylistsearchterm.css</file>
|
<file>style/smartplaylistsearchterm.css</file>
|
||||||
|
@ -41,5 +44,6 @@
|
||||||
<file>pictures/star-off.png</file>
|
<file>pictures/star-off.png</file>
|
||||||
<file>mood/sample.mood</file>
|
<file>mood/sample.mood</file>
|
||||||
<file>text/ghosts.txt</file>
|
<file>text/ghosts.txt</file>
|
||||||
|
<file>pictures/sidebar-background.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -91,6 +91,7 @@
|
||||||
<file>icons/128x128/love.png</file>
|
<file>icons/128x128/love.png</file>
|
||||||
<file>icons/128x128/subsonic.png</file>
|
<file>icons/128x128/subsonic.png</file>
|
||||||
<file>icons/128x128/tidal.png</file>
|
<file>icons/128x128/tidal.png</file>
|
||||||
|
<file>icons/128x128/spotify.png</file>
|
||||||
<file>icons/128x128/qobuz.png</file>
|
<file>icons/128x128/qobuz.png</file>
|
||||||
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
|
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/128x128/radio.png</file>
|
<file>icons/128x128/radio.png</file>
|
||||||
|
@ -189,6 +190,7 @@
|
||||||
<file>icons/64x64/love.png</file>
|
<file>icons/64x64/love.png</file>
|
||||||
<file>icons/64x64/subsonic.png</file>
|
<file>icons/64x64/subsonic.png</file>
|
||||||
<file>icons/64x64/tidal.png</file>
|
<file>icons/64x64/tidal.png</file>
|
||||||
|
<file>icons/64x64/spotify.png</file>
|
||||||
<file>icons/64x64/qobuz.png</file>
|
<file>icons/64x64/qobuz.png</file>
|
||||||
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
|
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/64x64/radio.png</file>
|
<file>icons/64x64/radio.png</file>
|
||||||
|
@ -290,8 +292,9 @@
|
||||||
<file>icons/48x48/moodbar.png</file>
|
<file>icons/48x48/moodbar.png</file>
|
||||||
<file>icons/48x48/love.png</file>
|
<file>icons/48x48/love.png</file>
|
||||||
<file>icons/48x48/subsonic.png</file>
|
<file>icons/48x48/subsonic.png</file>
|
||||||
<file>icons/48x48/tidal.png</file>
|
<file>icons/48x48/tidal.png</file>
|
||||||
<file>icons/48x48/qobuz.png</file>
|
<file>icons/48x48/spotify.png</file>
|
||||||
|
<file>icons/48x48/qobuz.png</file>
|
||||||
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
|
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/48x48/radio.png</file>
|
<file>icons/48x48/radio.png</file>
|
||||||
<file>icons/48x48/somafm.png</file>
|
<file>icons/48x48/somafm.png</file>
|
||||||
|
@ -393,6 +396,7 @@
|
||||||
<file>icons/32x32/love.png</file>
|
<file>icons/32x32/love.png</file>
|
||||||
<file>icons/32x32/subsonic.png</file>
|
<file>icons/32x32/subsonic.png</file>
|
||||||
<file>icons/32x32/tidal.png</file>
|
<file>icons/32x32/tidal.png</file>
|
||||||
|
<file>icons/32x32/spotify.png</file>
|
||||||
<file>icons/32x32/qobuz.png</file>
|
<file>icons/32x32/qobuz.png</file>
|
||||||
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
|
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/32x32/radio.png</file>
|
<file>icons/32x32/radio.png</file>
|
||||||
|
@ -495,6 +499,7 @@
|
||||||
<file>icons/22x22/love.png</file>
|
<file>icons/22x22/love.png</file>
|
||||||
<file>icons/22x22/subsonic.png</file>
|
<file>icons/22x22/subsonic.png</file>
|
||||||
<file>icons/22x22/tidal.png</file>
|
<file>icons/22x22/tidal.png</file>
|
||||||
|
<file>icons/22x22/spotify.png</file>
|
||||||
<file>icons/22x22/qobuz.png</file>
|
<file>icons/22x22/qobuz.png</file>
|
||||||
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
|
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/22x22/radio.png</file>
|
<file>icons/22x22/radio.png</file>
|
||||||
|
|
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 7.3 KiB |
|
@ -18,7 +18,7 @@ CREATE TABLE device_%deviceid_songs (
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
|
@ -83,7 +83,10 @@ CREATE TABLE device_%deviceid_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -96,4 +99,4 @@ CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
||||||
tokenize = "unicode61 remove_diacritics 1"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
UPDATE devices SET schema_version=4 WHERE ROWID=%deviceid;
|
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
ALTER TABLE songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE subsonic_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE subsonic_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_artists_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_artists_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_albums_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_albums_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE tidal_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_artists_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_artists_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_albums_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_albums_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE qobuz_songs ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
ALTER TABLE playlist_items ADD COLUMN ebur128_integrated_loudness_lufs REAL;
|
||||||
|
|
||||||
|
ALTER TABLE playlist_items ADD COLUMN ebur128_loudness_range_lu REAL;
|
||||||
|
|
||||||
|
UPDATE schema_version SET version=18;
|
|
@ -0,0 +1,19 @@
|
||||||
|
DROP TABLE IF EXISTS %allsongstables_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS subsonic_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tidal_artists_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tidal_albums_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tidal_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS qobuz_artists_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS qobuz_albums_songs_fts;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS qobuz_songs_fts;
|
||||||
|
|
||||||
|
UPDATE schema_version SET version=19;
|
|
@ -0,0 +1,244 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE schema_version SET version=20;
|
|
@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
||||||
|
|
||||||
DELETE FROM schema_version;
|
DELETE FROM schema_version;
|
||||||
|
|
||||||
INSERT INTO schema_version (version) VALUES (17);
|
INSERT INTO schema_version (version) VALUES (19);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS directories (
|
CREATE TABLE IF NOT EXISTS directories (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
|
@ -91,7 +91,10 @@ CREATE TABLE IF NOT EXISTS songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -169,7 +172,10 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -247,7 +253,10 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -325,7 +334,10 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -403,7 +415,253 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS spotify_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,
|
||||||
|
|
||||||
|
fingerprint TEXT,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
lastseen INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_embedded INTEGER DEFAULT 0,
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
art_unset INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1,
|
||||||
|
|
||||||
|
acoustid_id TEXT,
|
||||||
|
acoustid_fingerprint TEXT,
|
||||||
|
|
||||||
|
musicbrainz_album_artist_id TEXT,
|
||||||
|
musicbrainz_artist_id TEXT,
|
||||||
|
musicbrainz_original_artist_id TEXT,
|
||||||
|
musicbrainz_album_id TEXT,
|
||||||
|
musicbrainz_original_album_id TEXT,
|
||||||
|
musicbrainz_recording_id TEXT,
|
||||||
|
musicbrainz_track_id TEXT,
|
||||||
|
musicbrainz_disc_id TEXT,
|
||||||
|
musicbrainz_release_group_id TEXT,
|
||||||
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -481,7 +739,10 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -559,7 +820,10 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -637,7 +901,10 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -735,7 +1002,10 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
||||||
musicbrainz_track_id TEXT,
|
musicbrainz_track_id TEXT,
|
||||||
musicbrainz_disc_id TEXT,
|
musicbrainz_disc_id TEXT,
|
||||||
musicbrainz_release_group_id TEXT,
|
musicbrainz_release_group_id TEXT,
|
||||||
musicbrainz_work_id TEXT
|
musicbrainz_work_id TEXT,
|
||||||
|
|
||||||
|
ebur128_integrated_loudness_lufs REAL,
|
||||||
|
ebur128_loudness_range_lu REAL
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -769,138 +1039,3 @@ CREATE INDEX IF NOT EXISTS idx_album ON songs (album);
|
||||||
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
|
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
|
||||||
|
|
||||||
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
|
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
|
|
||||||
|
|
||||||
ftstitle,
|
|
||||||
ftsalbum,
|
|
||||||
ftsartist,
|
|
||||||
ftsalbumartist,
|
|
||||||
ftscomposer,
|
|
||||||
ftsperformer,
|
|
||||||
ftsgrouping,
|
|
||||||
ftsgenre,
|
|
||||||
ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 1"
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_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_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 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"
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts5(
|
|
||||||
|
|
||||||
ftstitle,
|
|
||||||
ftsalbum,
|
|
||||||
ftsartist,
|
|
||||||
ftsalbumartist,
|
|
||||||
ftscomposer,
|
|
||||||
ftsperformer,
|
|
||||||
ftsgrouping,
|
|
||||||
ftsgenre,
|
|
||||||
ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 1"
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
|
@ -35,32 +35,32 @@
|
||||||
background-color: %palette-base;
|
background-color: %palette-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton {
|
QToolButton[accessibleName="MenuPopupToolButton"] {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton:hover {
|
QToolButton:hover[accessibleName="MenuPopupToolButton"] {
|
||||||
border: 2px solid %palette-highlight;
|
border: 2px solid %palette-highlight;
|
||||||
background-color: %palette-highlight-lighter;
|
background-color: %palette-highlight-lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton:pressed {
|
QToolButton:pressed[accessibleName="MenuPopupToolButton"] {
|
||||||
border: 2px solid %palette-highlight-darker;
|
border: 2px solid %palette-highlight-darker;
|
||||||
background-color: %palette-highlight-lighter;
|
background-color: %palette-highlight-lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton[popupMode="MenuButtonPopup"], QToolButton:hover[popupMode="MenuButtonPopup"], QToolButton:pressed[popupMode="MenuButtonPopup"] {
|
QToolButton[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"], QToolButton:hover[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"], QToolButton:pressed[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"] {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For backwards compatibility with Qt 5 as it does not support property name */
|
/* For backwards compatibility with Qt 5 as it does not support property name */
|
||||||
QToolButton[popupMode="1"], QToolButton:hover[popupMode="1"], QToolButton:pressed[popupMode="1"] {
|
QToolButton[popupMode="1"][accessibleName="MenuPopupToolButton"], QToolButton:hover[popupMode="1"][accessibleName="MenuPopupToolButton"], QToolButton:pressed[popupMode="1"][accessibleName="MenuPopupToolButton"] {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton::menu-button {
|
QToolButton::menu-button[accessibleName="MenuPopupToolButton"] {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ if(LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
||||||
|
|
||||||
if(DEB_CODENAME AND DEB_DATE)
|
if(DEB_CODENAME AND DEB_DATE)
|
||||||
|
|
||||||
if(BUILD_WITH_QT5)
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qtbase5-dev,qtbase5-dev-tools,qttools5-dev,qttools5-dev-tools,libqt5x11extras5-dev)
|
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qtbase5-dev,qtbase5-dev-tools,qttools5-dev,qttools5-dev-tools,libqt5x11extras5-dev)
|
||||||
set(DEBIAN_DEPENDS_QT_PACKAGES libqt5sql5-sqlite)
|
set(DEBIAN_DEPENDS_QT_PACKAGES libqt5sql5-sqlite)
|
||||||
endif()
|
endif()
|
||||||
if(BUILD_WITH_QT6)
|
if(QT_VERSION_MAJOR EQUAL 6)
|
||||||
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qt6-base-dev,qt6-base-dev-tools,qt6-tools-dev,qt6-tools-dev-tools,qt6-l10n-tools)
|
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qt6-base-dev,qt6-base-dev-tools,qt6-tools-dev,qt6-tools-dev-tools,qt6-l10n-tools)
|
||||||
set(DEBIAN_DEPENDS_QT_PACKAGES libqt6sql6-sqlite,qt6-qpa-plugins)
|
set(DEBIAN_DEPENDS_QT_PACKAGES libqt6sql6-sqlite,qt6-qpa-plugins)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -11,7 +11,6 @@ Build-Depends: debhelper (>= 11),
|
||||||
protobuf-compiler,
|
protobuf-compiler,
|
||||||
libglib2.0-dev,
|
libglib2.0-dev,
|
||||||
libdbus-1-dev,
|
libdbus-1-dev,
|
||||||
libgnutls28-dev,
|
|
||||||
libprotobuf-dev,
|
libprotobuf-dev,
|
||||||
libboost-dev,
|
libboost-dev,
|
||||||
libsqlite3-dev,
|
libsqlite3-dev,
|
||||||
|
@ -26,7 +25,8 @@ Build-Depends: debhelper (>= 11),
|
||||||
libgpod-dev,
|
libgpod-dev,
|
||||||
libmtp-dev,
|
libmtp-dev,
|
||||||
libchromaprint-dev,
|
libchromaprint-dev,
|
||||||
libfftw3-dev
|
libfftw3-dev,
|
||||||
|
libebur128-dev
|
||||||
Standards-Version: 4.6.1
|
Standards-Version: 4.6.1
|
||||||
|
|
||||||
Package: strawberry
|
Package: strawberry
|
||||||
|
@ -53,7 +53,7 @@ Description: music player and music collection organizer
|
||||||
- Edit tags on audio files
|
- Edit tags on audio files
|
||||||
- Automatically retrieve tags from MusicBrainz
|
- Automatically retrieve tags from MusicBrainz
|
||||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||||
- Song lyrics from Lyrics.com, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
|
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
- Audio equalizer
|
- Audio equalizer
|
||||||
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
|
||||||
|
|
|
@ -4,6 +4,11 @@ if(RPM_DISTRO AND RPM_DATE)
|
||||||
endif(RPM_DISTRO AND RPM_DATE)
|
endif(RPM_DISTRO AND RPM_DATE)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
|
||||||
|
set(LSMinimumSystemVersion $ENV{MACOSX_DEPLOYMENT_TARGET})
|
||||||
|
else()
|
||||||
|
set(LSMinimumSystemVersion 12.0)
|
||||||
|
endif()
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.music</string>
|
<string>public.app-category.music</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>11.0</string>
|
<string>@LSMinimumSystemVersion@</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
||||||
<key>SUPublicEDKey</key>
|
<key>SUPublicEDKey</key>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Script to copy gstreamer plugins before macdeployqt is run.
|
# Script to copy gio modules and gstreamer plugins before macdeployqt is run.
|
||||||
|
|
||||||
if [ "$1" = "" ]; then
|
if [ "$1" = "" ]; then
|
||||||
echo "Usage: $0 <bundledir>"
|
echo "Usage: $0 <bundledir>"
|
||||||
|
@ -8,104 +8,144 @@ if [ "$1" = "" ]; then
|
||||||
fi
|
fi
|
||||||
bundledir=$1
|
bundledir=$1
|
||||||
|
|
||||||
if [ "$GIO_EXTRA_MODULES" = "" ]; then
|
if [ "${GIO_EXTRA_MODULES}" = "" ]; then
|
||||||
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing libgiognutls.so."
|
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing gio modules."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$GST_PLUGIN_SCANNER" = "" ]; then
|
if [ "${GST_PLUGIN_SCANNER}" = "" ]; then
|
||||||
echo "Error: Set the GST_PLUGIN_SCANNER environment variable to the gst-plugin-scanner."
|
echo "Error: Set the GST_PLUGIN_SCANNER environment variable to the gst-plugin-scanner."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$GST_PLUGIN_PATH" = "" ]; then
|
if [ "${GST_PLUGIN_PATH}" = "" ]; then
|
||||||
echo "Error: Set the GST_PLUGIN_PATH environment variable to the path containing gstreamer plugins."
|
echo "Error: Set the GST_PLUGIN_PATH environment variable to the path containing gstreamer plugins."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! [ -e "${GIO_EXTRA_MODULES}/libgiognutls.so" ] && ! [ -e "${GIO_EXTRA_MODULES}/libgioopenssl.so" ]; then
|
||||||
|
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so or ${GIO_EXTRA_MODULES}/libgioopenssl.so."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -e "${GST_PLUGIN_SCANNER}" ]; then
|
||||||
|
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -d "${GST_PLUGIN_PATH}" ]; then
|
||||||
|
echo "Error: GStreamer plugins path ${GST_PLUGIN_PATH} does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -e "${GST_PLUGIN_PATH}/libgstcoreelements.so" ] && ! [ -e "${GST_PLUGIN_PATH}/libgstcoreelements.dylib" ]; then
|
||||||
|
echo "Error: Missing libgstcoreelements.{so,dylib} in GStreamer plugins path ${GST_PLUGIN_PATH}."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p "${bundledir}/Contents/PlugIns/gio-modules" || exit 1
|
mkdir -p "${bundledir}/Contents/PlugIns/gio-modules" || exit 1
|
||||||
mkdir -p "${bundledir}/Contents/PlugIns/gstreamer" || exit 1
|
mkdir -p "${bundledir}/Contents/PlugIns/gstreamer" || exit 1
|
||||||
|
|
||||||
if ! [ -f "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
|
if [ -e "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
|
||||||
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
|
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
|
||||||
exit 1
|
else
|
||||||
|
echo "Warning: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
|
||||||
fi
|
fi
|
||||||
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
|
|
||||||
|
|
||||||
if ! [ -f "${GST_PLUGIN_SCANNER}" ]; then
|
if [ -e "${GIO_EXTRA_MODULES}/libgioopenssl.so" ]; then
|
||||||
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
|
cp -v -f "${GIO_EXTRA_MODULES}/libgioopenssl.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
|
||||||
exit 1
|
else
|
||||||
|
echo "Warning: Missing ${GIO_EXTRA_MODULES}/libgioopenssl.so"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp -v -f "${GST_PLUGIN_SCANNER}" "${bundledir}/Contents/PlugIns/" || exit 1
|
cp -v -f "${GST_PLUGIN_SCANNER}" "${bundledir}/Contents/PlugIns/" || exit 1
|
||||||
|
install_name_tool -add_rpath "@loader_path/../Frameworks" "${bundledir}/Contents/PlugIns/$(basename ${GST_PLUGIN_SCANNER})" || exit 1
|
||||||
|
|
||||||
gst_plugins="
|
gst_plugins="
|
||||||
libgstaes.dylib
|
libgstadaptivedemux2
|
||||||
libgstaiff.dylib
|
libgstaes
|
||||||
libgstapetag.dylib
|
libgstaiff
|
||||||
libgstapp.dylib
|
libgstapetag
|
||||||
libgstasf.dylib
|
libgstapp
|
||||||
libgstasfmux.dylib
|
libgstasf
|
||||||
libgstaudioconvert.dylib
|
libgstasfmux
|
||||||
libgstaudiofx.dylib
|
libgstaudioconvert
|
||||||
libgstaudiomixer.dylib
|
libgstaudiofx
|
||||||
libgstaudioparsers.dylib
|
libgstaudioparsers
|
||||||
libgstaudiorate.dylib
|
libgstaudioresample
|
||||||
libgstaudioresample.dylib
|
libgstautodetect
|
||||||
libgstaudiotestsrc.dylib
|
libgstbs2b
|
||||||
libgstautodetect.dylib
|
libgstcdio
|
||||||
libgstbs2b.dylib
|
libgstcoreelements
|
||||||
libgstcdio.dylib
|
libgstdash
|
||||||
libgstcoreelements.dylib
|
libgstdsd
|
||||||
libgstdash.dylib
|
libgstequalizer
|
||||||
libgstequalizer.dylib
|
libgstfaac
|
||||||
libgstflac.dylib
|
libgstfaad
|
||||||
libgstfaac.dylib
|
libgstfdkaac
|
||||||
libgstfaad.dylib
|
libgstflac
|
||||||
libgstfdkaac.dylib
|
libgstgio
|
||||||
libgstgio.dylib
|
libgsthls
|
||||||
libgsticydemux.dylib
|
libgsticydemux
|
||||||
libgstid3demux.dylib
|
libgstid3demux
|
||||||
libgstisomp4.dylib
|
libgstid3tag
|
||||||
libgstlame.dylib
|
libgstisomp4
|
||||||
libgstlibav.dylib
|
libgstlame
|
||||||
libgstmpg123.dylib
|
libgstmpegpsdemux
|
||||||
libgstmusepack.dylib
|
libgstmpegpsmux
|
||||||
libgstogg.dylib
|
libgstmpegtsdemux
|
||||||
libgstopenmpt.dylib
|
libgstmpegtsmux
|
||||||
libgstopus.dylib
|
libgstlibav
|
||||||
libgstopusparse.dylib
|
libgstmpg123
|
||||||
libgstosxaudio.dylib
|
libgstmusepack
|
||||||
libgstpbtypes.dylib
|
libgstogg
|
||||||
libgstplayback.dylib
|
libgstopenmpt
|
||||||
libgstreplaygain.dylib
|
libgstopus
|
||||||
libgstrtp.dylib
|
libgstopusparse
|
||||||
libgstrtsp.dylib
|
libgstosxaudio
|
||||||
libgstsoup.dylib
|
libgstpbtypes
|
||||||
libgstspectrum.dylib
|
libgstplayback
|
||||||
libgstspeex.dylib
|
libgstreplaygain
|
||||||
libgsttaglib.dylib
|
libgstrtp
|
||||||
libgsttcp.dylib
|
libgstrtsp
|
||||||
libgsttypefindfunctions.dylib
|
libgstsoup
|
||||||
libgstudp.dylib
|
libgstspectrum
|
||||||
libgstvolume.dylib
|
libgstspeex
|
||||||
libgstvorbis.dylib
|
libgsttaglib
|
||||||
libgstwavpack.dylib
|
libgsttcp
|
||||||
libgstwavparse.dylib
|
libgsttwolame
|
||||||
libgstxingmux.dylib;
|
libgsttypefindfunctions
|
||||||
|
libgstudp
|
||||||
|
libgstvolume
|
||||||
|
libgstvorbis
|
||||||
|
libgstwavenc
|
||||||
|
libgstwavpack
|
||||||
|
libgstwavparse
|
||||||
|
libgstxingmux
|
||||||
"
|
"
|
||||||
|
|
||||||
gst_plugins=$(echo "$gst_plugins" | tr '\n' ' ' | sed -e 's/^ //g' | sed -e 's/ / /g')
|
gst_plugins=$(echo "$gst_plugins" | tr '\n' ' ' | sed -e 's/^ //g' | sed -e 's/ / /g')
|
||||||
|
for gst_plugin in $gst_plugins; do
|
||||||
for gst_plugin in $gst_plugins
|
if [ -e "${GST_PLUGIN_PATH}/${gst_plugin}.dylib" ]; then
|
||||||
do
|
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}.dylib" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
|
||||||
if [ -f "${GST_PLUGIN_PATH}/${gst_plugin}" ]; then
|
install_name_tool -id "@rpath/${gst_plugin}.dylib" "${bundledir}/Contents/PlugIns/gstreamer/${gst_plugin}.dylib"
|
||||||
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
|
elif [ -e "${GST_PLUGIN_PATH}/${gst_plugin}.so" ]; then
|
||||||
|
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}.so" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
|
||||||
|
install_name_tool -id "@rpath/${gst_plugin}.so" "${bundledir}/Contents/PlugIns/gstreamer/${gst_plugin}.so"
|
||||||
else
|
else
|
||||||
echo "Warning: Missing gstreamer plugin ${GST_PLUGIN_PATH}/${gst_plugin}"
|
echo "Warning: Missing gstreamer plugin ${gst_plugin}."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -f "/usr/local/lib/libbrotlicommon.1.dylib" ]; then
|
# libsoup is dynamically loaded by gstreamer, so it needs to be copied.
|
||||||
mkdir -p ${bundledir}/Contents/Frameworks
|
if [ "${LIBSOUP_LIBRARY_PATH}" = "" ]; then
|
||||||
cp -v -f "/usr/local/lib/libbrotlicommon.1.dylib" "${bundledir}/Contents/Frameworks/"
|
echo "Warning: Set the LIBSOUP_LIBRARY_PATH environment variable for copying libsoup."
|
||||||
|
else
|
||||||
|
if [ -e "${LIBSOUP_LIBRARY_PATH}" ]; then
|
||||||
|
mkdir -p "${bundledir}/Contents/Frameworks" || exit 1
|
||||||
|
cp -v -f "${LIBSOUP_LIBRARY_PATH}" "${bundledir}/Contents/Frameworks/" || exit 1
|
||||||
|
install_name_tool -id "@rpath/$(basename ${LIBSOUP_LIBRARY_PATH})" "${bundledir}/Contents/Frameworks/$(basename ${LIBSOUP_LIBRARY_PATH})"
|
||||||
|
else
|
||||||
|
echo "Warning: Missing libsoup ${LIBSOUP_LIBRARY_PATH}."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<li>Edit tags on audio files</li>
|
<li>Edit tags on audio files</li>
|
||||||
<li>Automatically retrieve tags from MusicBrainz</li>
|
<li>Automatically retrieve tags from MusicBrainz</li>
|
||||||
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
|
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
|
||||||
<li>Song lyrics from Lyrics.com, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com</li>
|
<li>Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net</li>
|
||||||
<li>Support for multiple backends</li>
|
<li>Support for multiple backends</li>
|
||||||
<li>Audio analyzer and equalizer</li>
|
<li>Audio analyzer and equalizer</li>
|
||||||
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
|
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
|
||||||
|
@ -50,6 +50,11 @@
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.0.23" date="2024-01-11"/>
|
||||||
|
<release version="1.0.22" date="2023-12-09"/>
|
||||||
|
<release version="1.0.21" date="2023-10-21"/>
|
||||||
|
<release version="1.0.20" date="2023-09-24"/>
|
||||||
|
<release version="1.0.19" date="2023-09-24"/>
|
||||||
<release version="1.0.18" date="2023-07-02"/>
|
<release version="1.0.18" date="2023-07-02"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -3,8 +3,10 @@ Version=1.0
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=Strawberry
|
Name=Strawberry
|
||||||
GenericName=Strawberry Music Player
|
GenericName=Strawberry Music Player
|
||||||
|
GenericName[fr]=Lecteur de musique Strawberry
|
||||||
GenericName[ru]=Музыкальный проигрыватель Strawberry
|
GenericName[ru]=Музыкальный проигрыватель Strawberry
|
||||||
Comment=Plays music
|
Comment=Plays music
|
||||||
|
Comment[fr]=Joue de la musique
|
||||||
Comment[ru]=Прослушивание музыки
|
Comment[ru]=Прослушивание музыки
|
||||||
Exec=strawberry %U
|
Exec=strawberry %U
|
||||||
TryExec=strawberry
|
TryExec=strawberry
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Name: strawberry
|
Name: strawberry
|
||||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} || "%{?_vendor}" == "openmandriva"
|
%if 0%{?suse_version}
|
||||||
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
|
|
||||||
%else
|
|
||||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||||
|
%else
|
||||||
|
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
|
||||||
%endif
|
%endif
|
||||||
Summary: A music player and music collection organizer
|
Summary: A music player and music collection organizer
|
||||||
Group: Productivity/Multimedia/Sound/Players
|
Group: Productivity/Multimedia/Sound/Players
|
||||||
|
@ -11,7 +11,7 @@ License: GPL-3.0+
|
||||||
URL: https://www.strawberrymusicplayer.org/
|
URL: https://www.strawberrymusicplayer.org/
|
||||||
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
%if 0%{?suse_version}
|
||||||
BuildRequires: libboost_headers-devel
|
BuildRequires: libboost_headers-devel
|
||||||
%else
|
%else
|
||||||
BuildRequires: boost-devel
|
BuildRequires: boost-devel
|
||||||
|
@ -25,15 +25,13 @@ BuildRequires: gettext
|
||||||
BuildRequires: desktop-file-utils
|
BuildRequires: desktop-file-utils
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
BuildRequires: update-desktop-files
|
BuildRequires: update-desktop-files
|
||||||
%endif
|
|
||||||
%if 0%{?suse_version}
|
|
||||||
BuildRequires: appstream-glib
|
BuildRequires: appstream-glib
|
||||||
%else
|
%endif
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
BuildRequires: libappstream-glib
|
BuildRequires: libappstream-glib
|
||||||
%else
|
%endif
|
||||||
|
%if 0%{?mageia} || "%{?_vendor}" == "openmandriva"
|
||||||
BuildRequires: appstream-util
|
BuildRequires: appstream-util
|
||||||
%endif
|
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig
|
BuildRequires: pkgconfig
|
||||||
BuildRequires: pkgconfig(glib-2.0)
|
BuildRequires: pkgconfig(glib-2.0)
|
||||||
|
@ -41,52 +39,34 @@ BuildRequires: pkgconfig(gio-2.0)
|
||||||
BuildRequires: pkgconfig(gio-unix-2.0)
|
BuildRequires: pkgconfig(gio-unix-2.0)
|
||||||
BuildRequires: pkgconfig(gthread-2.0)
|
BuildRequires: pkgconfig(gthread-2.0)
|
||||||
BuildRequires: pkgconfig(dbus-1)
|
BuildRequires: pkgconfig(dbus-1)
|
||||||
BuildRequires: pkgconfig(gnutls)
|
|
||||||
BuildRequires: pkgconfig(alsa)
|
BuildRequires: pkgconfig(alsa)
|
||||||
BuildRequires: pkgconfig(protobuf)
|
BuildRequires: pkgconfig(protobuf)
|
||||||
BuildRequires: pkgconfig(sqlite3) >= 3.9
|
BuildRequires: pkgconfig(sqlite3) >= 3.9
|
||||||
%if ! 0%{?centos} && ! 0%{?mageia}
|
|
||||||
BuildRequires: pkgconfig(taglib)
|
BuildRequires: pkgconfig(taglib)
|
||||||
%endif
|
|
||||||
BuildRequires: pkgconfig(fftw3)
|
BuildRequires: pkgconfig(fftw3)
|
||||||
BuildRequires: pkgconfig(icu-uc)
|
BuildRequires: pkgconfig(icu-uc)
|
||||||
BuildRequires: pkgconfig(icu-i18n)
|
BuildRequires: pkgconfig(icu-i18n)
|
||||||
%if "@QT_VERSION_MAJOR@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Core)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Gui)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Widgets)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Concurrent)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Network)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Sql)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@DBus)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Test)
|
|
||||||
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@X11Extras)
|
|
||||||
%else
|
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
|
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
|
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Sql)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Sql)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@DBus)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@DBus)
|
||||||
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
|
||||||
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Test)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Test)
|
||||||
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
|
||||||
%if "@QT_VERSION_MAJOR@" == "5"
|
%if "@QT_VERSION_MAJOR@" == "5"
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@X11Extras)
|
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@X11Extras)
|
||||||
%endif
|
%endif
|
||||||
%endif
|
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
|
||||||
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
|
|
||||||
%endif
|
|
||||||
BuildRequires: pkgconfig(gstreamer-1.0)
|
BuildRequires: pkgconfig(gstreamer-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-base-1.0)
|
BuildRequires: pkgconfig(gstreamer-base-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-tag-1.0)
|
BuildRequires: pkgconfig(gstreamer-tag-1.0)
|
||||||
%if ! 0%{?centos}
|
|
||||||
BuildRequires: pkgconfig(libchromaprint)
|
BuildRequires: pkgconfig(libchromaprint)
|
||||||
%endif
|
|
||||||
BuildRequires: pkgconfig(libpulse)
|
BuildRequires: pkgconfig(libpulse)
|
||||||
BuildRequires: pkgconfig(libcdio)
|
BuildRequires: pkgconfig(libcdio)
|
||||||
|
BuildRequires: pkgconfig(libebur128)
|
||||||
BuildRequires: pkgconfig(libgpod-1.0)
|
BuildRequires: pkgconfig(libgpod-1.0)
|
||||||
BuildRequires: pkgconfig(libmtp)
|
BuildRequires: pkgconfig(libmtp)
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version}
|
%if 0%{?suse_version} || 0%{?fedora_version}
|
||||||
|
@ -119,7 +99,7 @@ Features:
|
||||||
- Edit tags on audio files
|
- Edit tags on audio files
|
||||||
- Automatically retrieve tags from MusicBrainz
|
- Automatically retrieve tags from MusicBrainz
|
||||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
|
||||||
- Song lyrics from Lyrics.com, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
|
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
|
||||||
- Support for multiple backends
|
- Support for multiple backends
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
- Audio equalizer
|
- Audio equalizer
|
||||||
|
@ -139,22 +119,19 @@ Features:
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
||||||
%endif
|
%endif
|
||||||
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
|
%if "%{?_vendor}" == "openmandriva"
|
||||||
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7) || "%{?_vendor}" == "openmandriva"
|
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@ -DENABLE_TRANSLATIONS=OFF
|
||||||
%make_build
|
%make_build
|
||||||
%else
|
%else
|
||||||
%cmake_build
|
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
|
||||||
|
%cmake_build
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%install
|
%install
|
||||||
%if 0%{?centos}
|
%if "%{?_vendor}" == "openmandriva"
|
||||||
%make_install
|
%make_install -C build
|
||||||
%else
|
%else
|
||||||
%if 0%{?mageia} || "%{?_vendor}" == "openmandriva"
|
%cmake_install
|
||||||
%make_install -C build
|
|
||||||
%else
|
|
||||||
%cmake_install
|
|
||||||
%endif
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
|
@ -163,10 +140,10 @@ Features:
|
||||||
|
|
||||||
%check
|
%check
|
||||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
||||||
%if 0%{?fedora_version}
|
%if 0%{?suse_version}
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
|
|
||||||
%else
|
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||||
|
%else
|
||||||
|
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%files
|
%files
|
||||||
|
@ -177,13 +154,13 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicpl
|
||||||
%{_bindir}/strawberry-tagreader
|
%{_bindir}/strawberry-tagreader
|
||||||
%{_datadir}/applications/*.desktop
|
%{_datadir}/applications/*.desktop
|
||||||
%{_datadir}/icons/hicolor/*/apps/strawberry.*
|
%{_datadir}/icons/hicolor/*/apps/strawberry.*
|
||||||
%if 0%{?fedora_version}
|
|
||||||
%{_metainfodir}/*.appdata.xml
|
|
||||||
%else
|
|
||||||
%{_datadir}/metainfo/*.appdata.xml
|
|
||||||
%endif
|
|
||||||
%{_mandir}/man1/%{name}.1.*
|
%{_mandir}/man1/%{name}.1.*
|
||||||
%{_mandir}/man1/%{name}-tagreader.1.*
|
%{_mandir}/man1/%{name}-tagreader.1.*
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
%{_datadir}/metainfo/*.appdata.xml
|
||||||
|
%else
|
||||||
|
%{_metainfodir}/*.appdata.xml
|
||||||
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* @RPM_DATE@ Jonas Kvinge <jonas@jkvinge.net> - @STRAWBERRY_VERSION_RPM_V@
|
* @RPM_DATE@ Jonas Kvinge <jonas@jkvinge.net> - @STRAWBERRY_VERSION_RPM_V@
|
||||||
|
|
|
@ -109,7 +109,11 @@
|
||||||
|
|
||||||
Unicode True
|
Unicode True
|
||||||
|
|
||||||
|
!ifdef debug
|
||||||
|
SetCompressor lzma
|
||||||
|
!else
|
||||||
SetCompressor /SOLID lzma
|
SetCompressor /SOLID lzma
|
||||||
|
!endif
|
||||||
|
|
||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
!include "FileAssociation.nsh"
|
!include "FileAssociation.nsh"
|
||||||
|
@ -136,8 +140,6 @@ SetCompressor /SOLID lzma
|
||||||
ReserveFile "${NSISDIR}\Plugins\x86-unicode\INetC.dll"
|
ReserveFile "${NSISDIR}\Plugins\x86-unicode\INetC.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!define LockedListPATH $InstallDir
|
|
||||||
|
|
||||||
; Installer pages
|
; Installer pages
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
!insertmacro MUI_PAGE_LICENSE COPYING
|
!insertmacro MUI_PAGE_LICENSE COPYING
|
||||||
|
@ -184,19 +186,22 @@ FunctionEnd
|
||||||
Function CheckPreviousInstall
|
Function CheckPreviousInstall
|
||||||
|
|
||||||
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||||
StrCmp $R0 "" done
|
StrCmp $R0 "" Done
|
||||||
|
|
||||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
${IfNot} ${Silent}
|
||||||
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
|
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 Uninstall
|
||||||
previous version or `Cancel` to cancel this upgrade." \
|
|
||||||
IDOK uninst
|
|
||||||
Abort
|
Abort
|
||||||
; Run the uninstaller
|
${EndIf}
|
||||||
uninst:
|
|
||||||
ClearErrors
|
|
||||||
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
|
|
||||||
|
|
||||||
done:
|
Uninstall:
|
||||||
|
ClearErrors
|
||||||
|
${If} ${Silent}
|
||||||
|
ExecWait '$R0 /S'
|
||||||
|
${Else}
|
||||||
|
ExecWait '$R0'
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
Done:
|
||||||
|
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
|
@ -235,6 +240,7 @@ Section "Strawberry" Strawberry
|
||||||
File "strawberry.ico"
|
File "strawberry.ico"
|
||||||
File "sqlite3.exe"
|
File "sqlite3.exe"
|
||||||
File "gst-launch-1.0.exe"
|
File "gst-launch-1.0.exe"
|
||||||
|
File "gst-play-1.0.exe"
|
||||||
File "gst-discoverer-1.0.exe"
|
File "gst-discoverer-1.0.exe"
|
||||||
|
|
||||||
; MinGW specific files
|
; MinGW specific files
|
||||||
|
@ -252,10 +258,6 @@ Section "Strawberry" Strawberry
|
||||||
File "libssl-3-x64.dll"
|
File "libssl-3-x64.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
File "avcodec-60.dll"
|
|
||||||
File "avfilter-9.dll"
|
|
||||||
File "avformat-60.dll"
|
|
||||||
File "avutil-58.dll"
|
|
||||||
File "libFLAC-12.dll"
|
File "libFLAC-12.dll"
|
||||||
File "libbrotlicommon.dll"
|
File "libbrotlicommon.dll"
|
||||||
File "libbrotlidec.dll"
|
File "libbrotlidec.dll"
|
||||||
|
@ -264,6 +266,7 @@ Section "Strawberry" Strawberry
|
||||||
File "libbz2.dll"
|
File "libbz2.dll"
|
||||||
File "libchromaprint.dll"
|
File "libchromaprint.dll"
|
||||||
File "libdl.dll"
|
File "libdl.dll"
|
||||||
|
File "libebur128.dll"
|
||||||
File "libfaac-0.dll"
|
File "libfaac-0.dll"
|
||||||
File "libfaad-2.dll"
|
File "libfaad-2.dll"
|
||||||
File "libfdk-aac-2.dll"
|
File "libfdk-aac-2.dll"
|
||||||
|
@ -283,8 +286,10 @@ Section "Strawberry" Strawberry
|
||||||
File "libgstaudio-1.0-0.dll"
|
File "libgstaudio-1.0-0.dll"
|
||||||
File "libgstbadaudio-1.0-0.dll"
|
File "libgstbadaudio-1.0-0.dll"
|
||||||
File "libgstbase-1.0-0.dll"
|
File "libgstbase-1.0-0.dll"
|
||||||
|
File "libgstcodecparsers-1.0-0.dll"
|
||||||
File "libgstfft-1.0-0.dll"
|
File "libgstfft-1.0-0.dll"
|
||||||
File "libgstisoff-1.0-0.dll"
|
File "libgstisoff-1.0-0.dll"
|
||||||
|
File "libgstmpegts-1.0-0.dll"
|
||||||
File "libgstnet-1.0-0.dll"
|
File "libgstnet-1.0-0.dll"
|
||||||
File "libgstpbutils-1.0-0.dll"
|
File "libgstpbutils-1.0-0.dll"
|
||||||
File "libgstreamer-1.0-0.dll"
|
File "libgstreamer-1.0-0.dll"
|
||||||
|
@ -301,6 +306,7 @@ Section "Strawberry" Strawberry
|
||||||
File "libidn2-0.dll"
|
File "libidn2-0.dll"
|
||||||
File "libintl-8.dll"
|
File "libintl-8.dll"
|
||||||
File "libjpeg-9.dll"
|
File "libjpeg-9.dll"
|
||||||
|
File "libkdsingleapplication-qt6.dll"
|
||||||
File "liblzma-5.dll"
|
File "liblzma-5.dll"
|
||||||
File "libmp3lame-0.dll"
|
File "libmp3lame-0.dll"
|
||||||
File "libmpcdec.dll"
|
File "libmpcdec.dll"
|
||||||
|
@ -328,11 +334,7 @@ Section "Strawberry" Strawberry
|
||||||
File "libvorbisfile-3.dll"
|
File "libvorbisfile-3.dll"
|
||||||
File "libwavpack-1.dll"
|
File "libwavpack-1.dll"
|
||||||
File "libwinpthread-1.dll"
|
File "libwinpthread-1.dll"
|
||||||
File "libxml2-2.dll"
|
|
||||||
File "libzstd.dll"
|
File "libzstd.dll"
|
||||||
File "postproc-57.dll"
|
|
||||||
File "swresample-4.dll"
|
|
||||||
File "swscale-7.dll"
|
|
||||||
File "zlib1.dll"
|
File "zlib1.dll"
|
||||||
|
|
||||||
File "libabsl_base.dll"
|
File "libabsl_base.dll"
|
||||||
|
@ -348,8 +350,10 @@ Section "Strawberry" Strawberry
|
||||||
File "libabsl_examine_stack.dll"
|
File "libabsl_examine_stack.dll"
|
||||||
File "libabsl_hash.dll"
|
File "libabsl_hash.dll"
|
||||||
File "libabsl_int128.dll"
|
File "libabsl_int128.dll"
|
||||||
|
File "libabsl_kernel_timeout_internal.dll"
|
||||||
File "libabsl_log_globals.dll"
|
File "libabsl_log_globals.dll"
|
||||||
File "libabsl_log_internal_check_op.dll"
|
File "libabsl_log_internal_check_op.dll"
|
||||||
|
File "libabsl_log_internal_conditions.dll"
|
||||||
File "libabsl_log_internal_format.dll"
|
File "libabsl_log_internal_format.dll"
|
||||||
File "libabsl_log_internal_globals.dll"
|
File "libabsl_log_internal_globals.dll"
|
||||||
File "libabsl_log_internal_log_sink_set.dll"
|
File "libabsl_log_internal_log_sink_set.dll"
|
||||||
|
@ -406,29 +410,27 @@ Section "Strawberry" Strawberry
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
File "FLAC.dll"
|
File "FLAC.dll"
|
||||||
File "avcodec-58.dll"
|
|
||||||
File "avfilter-7.dll"
|
|
||||||
File "avformat-58.dll"
|
|
||||||
File "avutil-56.dll"
|
|
||||||
File "brotlicommon.dll"
|
File "brotlicommon.dll"
|
||||||
File "brotlidec.dll"
|
File "brotlidec.dll"
|
||||||
File "chromaprint.dll"
|
File "chromaprint.dll"
|
||||||
File "faad.dll"
|
File "ebur128.dll"
|
||||||
|
File "faad-2.dll"
|
||||||
File "fdk-aac.dll"
|
File "fdk-aac.dll"
|
||||||
File "ffi-7.dll"
|
File "ffi-7.dll"
|
||||||
File "gio-2.0-0.dll"
|
File "gio-2.0-0.dll"
|
||||||
File "glib-2.0-0.dll"
|
File "glib-2.0-0.dll"
|
||||||
File "gme.dll"
|
File "gme.dll"
|
||||||
File "gmodule-2.0-0.dll"
|
File "gmodule-2.0-0.dll"
|
||||||
File "gnutls.dll"
|
|
||||||
File "gobject-2.0-0.dll"
|
File "gobject-2.0-0.dll"
|
||||||
File "gstadaptivedemux-1.0-0.dll"
|
File "gstadaptivedemux-1.0-0.dll"
|
||||||
File "gstapp-1.0-0.dll"
|
File "gstapp-1.0-0.dll"
|
||||||
File "gstaudio-1.0-0.dll"
|
File "gstaudio-1.0-0.dll"
|
||||||
File "gstbadaudio-1.0-0.dll"
|
File "gstbadaudio-1.0-0.dll"
|
||||||
File "gstbase-1.0-0.dll"
|
File "gstbase-1.0-0.dll"
|
||||||
|
File "gstcodecparsers-1.0-0.dll"
|
||||||
File "gstfft-1.0-0.dll"
|
File "gstfft-1.0-0.dll"
|
||||||
File "gstisoff-1.0-0.dll"
|
File "gstisoff-1.0-0.dll"
|
||||||
|
File "gstmpegts-1.0-0.dll"
|
||||||
File "gstnet-1.0-0.dll"
|
File "gstnet-1.0-0.dll"
|
||||||
File "gstpbutils-1.0-0.dll"
|
File "gstpbutils-1.0-0.dll"
|
||||||
File "gstreamer-1.0-0.dll"
|
File "gstreamer-1.0-0.dll"
|
||||||
|
@ -443,25 +445,22 @@ Section "Strawberry" Strawberry
|
||||||
File "harfbuzz.dll"
|
File "harfbuzz.dll"
|
||||||
File "intl-8.dll"
|
File "intl-8.dll"
|
||||||
File "jpeg62.dll"
|
File "jpeg62.dll"
|
||||||
|
File "kdsingleapplication-qt6.dll"
|
||||||
File "libbs2b.dll"
|
File "libbs2b.dll"
|
||||||
File "libfaac_dll.dll"
|
File "libfaac_dll.dll"
|
||||||
File "liblzma.dll"
|
File "liblzma.dll"
|
||||||
File "libmp3lame.dll"
|
File "libmp3lame.dll"
|
||||||
File "libopenmpt.dll"
|
File "libopenmpt.dll"
|
||||||
File "libspeex.dll"
|
|
||||||
File "mpcdec.dll"
|
File "mpcdec.dll"
|
||||||
File "mpg123.dll"
|
File "mpg123.dll"
|
||||||
File "nghttp2.dll"
|
File "nghttp2.dll"
|
||||||
File "ogg.dll"
|
File "ogg.dll"
|
||||||
File "opus.dll"
|
File "opus.dll"
|
||||||
File "orc-0.4-0.dll"
|
File "orc-0.4-0.dll"
|
||||||
File "postproc-55.dll"
|
|
||||||
File "psl-5.dll"
|
File "psl-5.dll"
|
||||||
File "qtsparkle-qt6.dll"
|
File "qtsparkle-qt6.dll"
|
||||||
File "soup-3.0-0.dll"
|
File "soup-3.0-0.dll"
|
||||||
File "sqlite3.dll"
|
File "sqlite3.dll"
|
||||||
File "swresample-3.dll"
|
|
||||||
File "swscale-5.dll"
|
|
||||||
File "tag.dll"
|
File "tag.dll"
|
||||||
File "vorbis.dll"
|
File "vorbis.dll"
|
||||||
File "vorbisfile.dll"
|
File "vorbisfile.dll"
|
||||||
|
@ -472,7 +471,7 @@ Section "Strawberry" Strawberry
|
||||||
File "freetype.dll"
|
File "freetype.dll"
|
||||||
File "libiconv.dll"
|
File "libiconv.dll"
|
||||||
File "libpng16.dll"
|
File "libpng16.dll"
|
||||||
File "libxml2.dll"
|
File "libspeex.dll"
|
||||||
File "pcre2-8.dll"
|
File "pcre2-8.dll"
|
||||||
File "pcre2-16.dll"
|
File "pcre2-16.dll"
|
||||||
File "twolame.dll"
|
File "twolame.dll"
|
||||||
|
@ -482,7 +481,7 @@ Section "Strawberry" Strawberry
|
||||||
File "freetyped.dll"
|
File "freetyped.dll"
|
||||||
File "libiconvd.dll"
|
File "libiconvd.dll"
|
||||||
File "libpng16d.dll"
|
File "libpng16d.dll"
|
||||||
File "libxml2d.dll"
|
File "libspeexd.dll"
|
||||||
File "pcre2-8d.dll"
|
File "pcre2-8d.dll"
|
||||||
File "pcre2-16d.dll"
|
File "pcre2-16d.dll"
|
||||||
File "twolamed.dll"
|
File "twolamed.dll"
|
||||||
|
@ -499,7 +498,7 @@ Section "Strawberry" Strawberry
|
||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
File "icudt73.dll"
|
File "icudt75.dll"
|
||||||
File "libfftw3-3.dll"
|
File "libfftw3-3.dll"
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
File "libprotobufd.dll"
|
File "libprotobufd.dll"
|
||||||
|
@ -507,8 +506,9 @@ Section "Strawberry" Strawberry
|
||||||
File "libprotobuf.dll"
|
File "libprotobuf.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
File "icuin73d.dll"
|
File "icuin75d.dll"
|
||||||
File "icuuc73d.dll"
|
File "icuuc75d.dll"
|
||||||
|
File "libxml2d.dll"
|
||||||
File "Qt6Concurrentd.dll"
|
File "Qt6Concurrentd.dll"
|
||||||
File "Qt6Cored.dll"
|
File "Qt6Cored.dll"
|
||||||
File "Qt6Guid.dll"
|
File "Qt6Guid.dll"
|
||||||
|
@ -516,8 +516,9 @@ Section "Strawberry" Strawberry
|
||||||
File "Qt6Sqld.dll"
|
File "Qt6Sqld.dll"
|
||||||
File "Qt6Widgetsd.dll"
|
File "Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
File "icuin73.dll"
|
File "icuin75.dll"
|
||||||
File "icuuc73.dll"
|
File "icuuc75.dll"
|
||||||
|
File "libxml2.dll"
|
||||||
File "Qt6Concurrent.dll"
|
File "Qt6Concurrent.dll"
|
||||||
File "Qt6Core.dll"
|
File "Qt6Core.dll"
|
||||||
File "Qt6Gui.dll"
|
File "Qt6Gui.dll"
|
||||||
|
@ -526,6 +527,14 @@ Section "Strawberry" Strawberry
|
||||||
File "Qt6Widgets.dll"
|
File "Qt6Widgets.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
File "avcodec-60.dll"
|
||||||
|
File "avfilter-9.dll"
|
||||||
|
File "avformat-60.dll"
|
||||||
|
File "avutil-58.dll"
|
||||||
|
File "postproc-57.dll"
|
||||||
|
File "swresample-4.dll"
|
||||||
|
File "swscale-7.dll"
|
||||||
|
|
||||||
; Register Strawberry with Default Programs
|
; Register Strawberry with Default Programs
|
||||||
Var /GLOBAL AppIcon
|
Var /GLOBAL AppIcon
|
||||||
Var /GLOBAL AppExe
|
Var /GLOBAL AppExe
|
||||||
|
@ -582,9 +591,9 @@ SectionEnd
|
||||||
Section "Qt styles" styles
|
Section "Qt styles" styles
|
||||||
SetOutPath "$INSTDIR\styles"
|
SetOutPath "$INSTDIR\styles"
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
File "/oname=qwindowsvistastyled.dll" "styles\qwindowsvistastyled.dll"
|
File "/oname=qmodernwindowsstyled.dll" "styles\qmodernwindowsstyled.dll"
|
||||||
!else
|
!else
|
||||||
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
|
File "/oname=qmodernwindowsstyle.dll" "styles\qmodernwindowsstyle.dll"
|
||||||
!endif
|
!endif
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
@ -625,6 +634,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
SetOutPath "$INSTDIR\gstreamer-plugins"
|
SetOutPath "$INSTDIR\gstreamer-plugins"
|
||||||
|
|
||||||
!ifdef mingw
|
!ifdef mingw
|
||||||
|
File "/oname=libgstadaptivedemux2.dll" "gstreamer-plugins\libgstadaptivedemux2.dll"
|
||||||
File "/oname=libgstaes.dll" "gstreamer-plugins\libgstaes.dll"
|
File "/oname=libgstaes.dll" "gstreamer-plugins\libgstaes.dll"
|
||||||
File "/oname=libgstaiff.dll" "gstreamer-plugins\libgstaiff.dll"
|
File "/oname=libgstaiff.dll" "gstreamer-plugins\libgstaiff.dll"
|
||||||
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
|
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
|
||||||
|
@ -633,16 +643,14 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
File "/oname=libgstasfmux.dll" "gstreamer-plugins\libgstasfmux.dll"
|
File "/oname=libgstasfmux.dll" "gstreamer-plugins\libgstasfmux.dll"
|
||||||
File "/oname=libgstaudioconvert.dll" "gstreamer-plugins\libgstaudioconvert.dll"
|
File "/oname=libgstaudioconvert.dll" "gstreamer-plugins\libgstaudioconvert.dll"
|
||||||
File "/oname=libgstaudiofx.dll" "gstreamer-plugins\libgstaudiofx.dll"
|
File "/oname=libgstaudiofx.dll" "gstreamer-plugins\libgstaudiofx.dll"
|
||||||
File "/oname=libgstaudiomixer.dll" "gstreamer-plugins\libgstaudiomixer.dll"
|
|
||||||
File "/oname=libgstaudioparsers.dll" "gstreamer-plugins\libgstaudioparsers.dll"
|
File "/oname=libgstaudioparsers.dll" "gstreamer-plugins\libgstaudioparsers.dll"
|
||||||
File "/oname=libgstaudiorate.dll" "gstreamer-plugins\libgstaudiorate.dll"
|
|
||||||
File "/oname=libgstaudioresample.dll" "gstreamer-plugins\libgstaudioresample.dll"
|
File "/oname=libgstaudioresample.dll" "gstreamer-plugins\libgstaudioresample.dll"
|
||||||
File "/oname=libgstaudiotestsrc.dll" "gstreamer-plugins\libgstaudiotestsrc.dll"
|
|
||||||
File "/oname=libgstautodetect.dll" "gstreamer-plugins\libgstautodetect.dll"
|
File "/oname=libgstautodetect.dll" "gstreamer-plugins\libgstautodetect.dll"
|
||||||
File "/oname=libgstbs2b.dll" "gstreamer-plugins\libgstbs2b.dll"
|
File "/oname=libgstbs2b.dll" "gstreamer-plugins\libgstbs2b.dll"
|
||||||
File "/oname=libgstcoreelements.dll" "gstreamer-plugins\libgstcoreelements.dll"
|
File "/oname=libgstcoreelements.dll" "gstreamer-plugins\libgstcoreelements.dll"
|
||||||
File "/oname=libgstdash.dll" "gstreamer-plugins\libgstdash.dll"
|
File "/oname=libgstdash.dll" "gstreamer-plugins\libgstdash.dll"
|
||||||
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
|
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
|
||||||
|
File "/oname=libgstdsd.dll" "gstreamer-plugins\libgstdsd.dll"
|
||||||
File "/oname=libgstequalizer.dll" "gstreamer-plugins\libgstequalizer.dll"
|
File "/oname=libgstequalizer.dll" "gstreamer-plugins\libgstequalizer.dll"
|
||||||
File "/oname=libgstfaac.dll" "gstreamer-plugins\libgstfaac.dll"
|
File "/oname=libgstfaac.dll" "gstreamer-plugins\libgstfaac.dll"
|
||||||
File "/oname=libgstfaad.dll" "gstreamer-plugins\libgstfaad.dll"
|
File "/oname=libgstfaad.dll" "gstreamer-plugins\libgstfaad.dll"
|
||||||
|
@ -657,6 +665,10 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
|
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
|
||||||
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
|
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
|
||||||
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
|
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
|
||||||
|
File "/oname=libgstmpegpsdemux.dll" "gstreamer-plugins\libgstmpegpsdemux.dll"
|
||||||
|
File "/oname=libgstmpegpsmux.dll" "gstreamer-plugins\libgstmpegpsmux.dll"
|
||||||
|
File "/oname=libgstmpegtsdemux.dll" "gstreamer-plugins\libgstmpegtsdemux.dll"
|
||||||
|
File "/oname=libgstmpegtsmux.dll" "gstreamer-plugins\libgstmpegtsmux.dll"
|
||||||
File "/oname=libgstmpg123.dll" "gstreamer-plugins\libgstmpg123.dll"
|
File "/oname=libgstmpg123.dll" "gstreamer-plugins\libgstmpg123.dll"
|
||||||
File "/oname=libgstmusepack.dll" "gstreamer-plugins\libgstmusepack.dll"
|
File "/oname=libgstmusepack.dll" "gstreamer-plugins\libgstmusepack.dll"
|
||||||
File "/oname=libgstogg.dll" "gstreamer-plugins\libgstogg.dll"
|
File "/oname=libgstogg.dll" "gstreamer-plugins\libgstogg.dll"
|
||||||
|
@ -679,6 +691,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
|
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
|
||||||
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
|
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
|
||||||
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
||||||
|
File "/oname=libgstwaveform.dll" "gstreamer-plugins\libgstwaveform.dll"
|
||||||
File "/oname=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
|
File "/oname=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
|
||||||
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
|
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
|
||||||
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
|
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
|
||||||
|
@ -686,24 +699,24 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
!endif ; MinGW
|
!endif ; MinGW
|
||||||
|
|
||||||
!ifdef msvc
|
!ifdef msvc
|
||||||
|
File "/oname=gstadaptivedemux2.dll" "gstreamer-plugins\gstadaptivedemux2.dll"
|
||||||
File "/oname=gstaes.dll" "gstreamer-plugins\gstaes.dll"
|
File "/oname=gstaes.dll" "gstreamer-plugins\gstaes.dll"
|
||||||
File "/oname=gstaiff.dll" "gstreamer-plugins\gstaiff.dll"
|
File "/oname=gstaiff.dll" "gstreamer-plugins\gstaiff.dll"
|
||||||
File "/oname=gstapetag.dll" "gstreamer-plugins\gstapetag.dll"
|
File "/oname=gstapetag.dll" "gstreamer-plugins\gstapetag.dll"
|
||||||
File "/oname=gstapp.dll" "gstreamer-plugins\gstapp.dll"
|
File "/oname=gstapp.dll" "gstreamer-plugins\gstapp.dll"
|
||||||
File "/oname=gstasf.dll" "gstreamer-plugins\gstasf.dll"
|
File "/oname=gstasf.dll" "gstreamer-plugins\gstasf.dll"
|
||||||
File "/oname=gstasfmux.dll" "gstreamer-plugins\gstasfmux.dll"
|
File "/oname=gstasfmux.dll" "gstreamer-plugins\gstasfmux.dll"
|
||||||
|
File "/oname=gstasio.dll" "gstreamer-plugins\gstasio.dll"
|
||||||
File "/oname=gstaudioconvert.dll" "gstreamer-plugins\gstaudioconvert.dll"
|
File "/oname=gstaudioconvert.dll" "gstreamer-plugins\gstaudioconvert.dll"
|
||||||
File "/oname=gstaudiofx.dll" "gstreamer-plugins\gstaudiofx.dll"
|
File "/oname=gstaudiofx.dll" "gstreamer-plugins\gstaudiofx.dll"
|
||||||
File "/oname=gstaudiomixer.dll" "gstreamer-plugins\gstaudiomixer.dll"
|
|
||||||
File "/oname=gstaudioparsers.dll" "gstreamer-plugins\gstaudioparsers.dll"
|
File "/oname=gstaudioparsers.dll" "gstreamer-plugins\gstaudioparsers.dll"
|
||||||
File "/oname=gstaudiorate.dll" "gstreamer-plugins\gstaudiorate.dll"
|
|
||||||
File "/oname=gstaudioresample.dll" "gstreamer-plugins\gstaudioresample.dll"
|
File "/oname=gstaudioresample.dll" "gstreamer-plugins\gstaudioresample.dll"
|
||||||
File "/oname=gstaudiotestsrc.dll" "gstreamer-plugins\gstaudiotestsrc.dll"
|
|
||||||
File "/oname=gstautodetect.dll" "gstreamer-plugins\gstautodetect.dll"
|
File "/oname=gstautodetect.dll" "gstreamer-plugins\gstautodetect.dll"
|
||||||
File "/oname=gstbs2b.dll" "gstreamer-plugins\gstbs2b.dll"
|
File "/oname=gstbs2b.dll" "gstreamer-plugins\gstbs2b.dll"
|
||||||
File "/oname=gstcoreelements.dll" "gstreamer-plugins\gstcoreelements.dll"
|
File "/oname=gstcoreelements.dll" "gstreamer-plugins\gstcoreelements.dll"
|
||||||
File "/oname=gstdash.dll" "gstreamer-plugins\gstdash.dll"
|
File "/oname=gstdash.dll" "gstreamer-plugins\gstdash.dll"
|
||||||
File "/oname=gstdirectsound.dll" "gstreamer-plugins\gstdirectsound.dll"
|
File "/oname=gstdirectsound.dll" "gstreamer-plugins\gstdirectsound.dll"
|
||||||
|
File "/oname=gstdsd.dll" "gstreamer-plugins\gstdsd.dll"
|
||||||
File "/oname=gstequalizer.dll" "gstreamer-plugins\gstequalizer.dll"
|
File "/oname=gstequalizer.dll" "gstreamer-plugins\gstequalizer.dll"
|
||||||
File "/oname=gstfaac.dll" "gstreamer-plugins\gstfaac.dll"
|
File "/oname=gstfaac.dll" "gstreamer-plugins\gstfaac.dll"
|
||||||
File "/oname=gstfaad.dll" "gstreamer-plugins\gstfaad.dll"
|
File "/oname=gstfaad.dll" "gstreamer-plugins\gstfaad.dll"
|
||||||
|
@ -718,6 +731,10 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
File "/oname=gstisomp4.dll" "gstreamer-plugins\gstisomp4.dll"
|
File "/oname=gstisomp4.dll" "gstreamer-plugins\gstisomp4.dll"
|
||||||
File "/oname=gstlame.dll" "gstreamer-plugins\gstlame.dll"
|
File "/oname=gstlame.dll" "gstreamer-plugins\gstlame.dll"
|
||||||
File "/oname=gstlibav.dll" "gstreamer-plugins\gstlibav.dll"
|
File "/oname=gstlibav.dll" "gstreamer-plugins\gstlibav.dll"
|
||||||
|
File "/oname=gstmpegpsdemux.dll" "gstreamer-plugins\gstmpegpsdemux.dll"
|
||||||
|
File "/oname=gstmpegpsmux.dll" "gstreamer-plugins\gstmpegpsmux.dll"
|
||||||
|
File "/oname=gstmpegtsdemux.dll" "gstreamer-plugins\gstmpegtsdemux.dll"
|
||||||
|
File "/oname=gstmpegtsmux.dll" "gstreamer-plugins\gstmpegtsmux.dll"
|
||||||
File "/oname=gstmpg123.dll" "gstreamer-plugins\gstmpg123.dll"
|
File "/oname=gstmpg123.dll" "gstreamer-plugins\gstmpg123.dll"
|
||||||
File "/oname=gstmusepack.dll" "gstreamer-plugins\gstmusepack.dll"
|
File "/oname=gstmusepack.dll" "gstreamer-plugins\gstmusepack.dll"
|
||||||
File "/oname=gstogg.dll" "gstreamer-plugins\gstogg.dll"
|
File "/oname=gstogg.dll" "gstreamer-plugins\gstogg.dll"
|
||||||
|
@ -741,6 +758,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||||
File "/oname=gstvorbis.dll" "gstreamer-plugins\gstvorbis.dll"
|
File "/oname=gstvorbis.dll" "gstreamer-plugins\gstvorbis.dll"
|
||||||
File "/oname=gstwasapi.dll" "gstreamer-plugins\gstwasapi.dll"
|
File "/oname=gstwasapi.dll" "gstreamer-plugins\gstwasapi.dll"
|
||||||
File "/oname=gstwasapi2.dll" "gstreamer-plugins\gstwasapi2.dll"
|
File "/oname=gstwasapi2.dll" "gstreamer-plugins\gstwasapi2.dll"
|
||||||
|
File "/oname=gstwaveform.dll" "gstreamer-plugins\gstwaveform.dll"
|
||||||
File "/oname=gstwavenc.dll" "gstreamer-plugins\gstwavenc.dll"
|
File "/oname=gstwavenc.dll" "gstreamer-plugins\gstwavenc.dll"
|
||||||
File "/oname=gstwavpack.dll" "gstreamer-plugins\gstwavpack.dll"
|
File "/oname=gstwavpack.dll" "gstreamer-plugins\gstwavpack.dll"
|
||||||
File "/oname=gstwavparse.dll" "gstreamer-plugins\gstwavparse.dll"
|
File "/oname=gstwavparse.dll" "gstreamer-plugins\gstwavparse.dll"
|
||||||
|
@ -789,6 +807,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\strawberry.ico"
|
Delete "$INSTDIR\strawberry.ico"
|
||||||
Delete "$INSTDIR\sqlite3.exe"
|
Delete "$INSTDIR\sqlite3.exe"
|
||||||
Delete "$INSTDIR\gst-launch-1.0.exe"
|
Delete "$INSTDIR\gst-launch-1.0.exe"
|
||||||
|
Delete "$INSTDIR\gst-play-1.0.exe"
|
||||||
Delete "$INSTDIR\gst-discoverer-1.0.exe"
|
Delete "$INSTDIR\gst-discoverer-1.0.exe"
|
||||||
|
|
||||||
; MinGW specific files
|
; MinGW specific files
|
||||||
|
@ -807,10 +826,6 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libssl-3-x64.dll"
|
Delete "$INSTDIR\libssl-3-x64.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
Delete "$INSTDIR\avcodec-60.dll"
|
|
||||||
Delete "$INSTDIR\avfilter-9.dll"
|
|
||||||
Delete "$INSTDIR\avformat-60.dll"
|
|
||||||
Delete "$INSTDIR\avutil-58.dll"
|
|
||||||
Delete "$INSTDIR\libFLAC-12.dll"
|
Delete "$INSTDIR\libFLAC-12.dll"
|
||||||
Delete "$INSTDIR\libbrotlicommon.dll"
|
Delete "$INSTDIR\libbrotlicommon.dll"
|
||||||
Delete "$INSTDIR\libbrotlidec.dll"
|
Delete "$INSTDIR\libbrotlidec.dll"
|
||||||
|
@ -819,6 +834,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libbz2.dll"
|
Delete "$INSTDIR\libbz2.dll"
|
||||||
Delete "$INSTDIR\libchromaprint.dll"
|
Delete "$INSTDIR\libchromaprint.dll"
|
||||||
Delete "$INSTDIR\libdl.dll"
|
Delete "$INSTDIR\libdl.dll"
|
||||||
|
Delete "$INSTDIR\libebur128.dll"
|
||||||
Delete "$INSTDIR\libfaac-0.dll"
|
Delete "$INSTDIR\libfaac-0.dll"
|
||||||
Delete "$INSTDIR\libfaad-2.dll"
|
Delete "$INSTDIR\libfaad-2.dll"
|
||||||
Delete "$INSTDIR\libfdk-aac-2.dll"
|
Delete "$INSTDIR\libfdk-aac-2.dll"
|
||||||
|
@ -838,8 +854,10 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
|
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstbadaudio-1.0-0.dll"
|
Delete "$INSTDIR\libgstbadaudio-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstbase-1.0-0.dll"
|
Delete "$INSTDIR\libgstbase-1.0-0.dll"
|
||||||
|
Delete "$INSTDIR\libgstcodecparsers-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstfft-1.0-0.dll"
|
Delete "$INSTDIR\libgstfft-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstisoff-1.0-0.dll"
|
Delete "$INSTDIR\libgstisoff-1.0-0.dll"
|
||||||
|
Delete "$INSTDIR\libgstmpegts-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstnet-1.0-0.dll"
|
Delete "$INSTDIR\libgstnet-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
|
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
|
||||||
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
|
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
|
||||||
|
@ -856,6 +874,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libidn2-0.dll"
|
Delete "$INSTDIR\libidn2-0.dll"
|
||||||
Delete "$INSTDIR\libintl-8.dll"
|
Delete "$INSTDIR\libintl-8.dll"
|
||||||
Delete "$INSTDIR\libjpeg-9.dll"
|
Delete "$INSTDIR\libjpeg-9.dll"
|
||||||
|
Delete "$INSTDIR\libkdsingleapplication-qt6.dll"
|
||||||
Delete "$INSTDIR\liblzma-5.dll"
|
Delete "$INSTDIR\liblzma-5.dll"
|
||||||
Delete "$INSTDIR\libmp3lame-0.dll"
|
Delete "$INSTDIR\libmp3lame-0.dll"
|
||||||
Delete "$INSTDIR\libmpcdec.dll"
|
Delete "$INSTDIR\libmpcdec.dll"
|
||||||
|
@ -883,11 +902,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libvorbisfile-3.dll"
|
Delete "$INSTDIR\libvorbisfile-3.dll"
|
||||||
Delete "$INSTDIR\libwavpack-1.dll"
|
Delete "$INSTDIR\libwavpack-1.dll"
|
||||||
Delete "$INSTDIR\libwinpthread-1.dll"
|
Delete "$INSTDIR\libwinpthread-1.dll"
|
||||||
Delete "$INSTDIR\libxml2-2.dll"
|
|
||||||
Delete "$INSTDIR\libzstd.dll"
|
Delete "$INSTDIR\libzstd.dll"
|
||||||
Delete "$INSTDIR\postproc-57.dll"
|
|
||||||
Delete "$INSTDIR\swresample-4.dll"
|
|
||||||
Delete "$INSTDIR\swscale-7.dll"
|
|
||||||
Delete "$INSTDIR\zlib1.dll"
|
Delete "$INSTDIR\zlib1.dll"
|
||||||
|
|
||||||
Delete "$INSTDIR\libabsl_base.dll"
|
Delete "$INSTDIR\libabsl_base.dll"
|
||||||
|
@ -903,8 +918,10 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libabsl_examine_stack.dll"
|
Delete "$INSTDIR\libabsl_examine_stack.dll"
|
||||||
Delete "$INSTDIR\libabsl_hash.dll"
|
Delete "$INSTDIR\libabsl_hash.dll"
|
||||||
Delete "$INSTDIR\libabsl_int128.dll"
|
Delete "$INSTDIR\libabsl_int128.dll"
|
||||||
|
Delete "$INSTDIR\libabsl_kernel_timeout_internal.dll"
|
||||||
Delete "$INSTDIR\libabsl_log_globals.dll"
|
Delete "$INSTDIR\libabsl_log_globals.dll"
|
||||||
Delete "$INSTDIR\libabsl_log_internal_check_op.dll"
|
Delete "$INSTDIR\libabsl_log_internal_check_op.dll"
|
||||||
|
Delete "$INSTDIR\libabsl_log_internal_conditions.dll"
|
||||||
Delete "$INSTDIR\libabsl_log_internal_format.dll"
|
Delete "$INSTDIR\libabsl_log_internal_format.dll"
|
||||||
Delete "$INSTDIR\libabsl_log_internal_globals.dll"
|
Delete "$INSTDIR\libabsl_log_internal_globals.dll"
|
||||||
Delete "$INSTDIR\libabsl_log_internal_log_sink_set.dll"
|
Delete "$INSTDIR\libabsl_log_internal_log_sink_set.dll"
|
||||||
|
@ -961,29 +978,27 @@ Section "Uninstall"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
Delete "$INSTDIR\FLAC.dll"
|
Delete "$INSTDIR\FLAC.dll"
|
||||||
Delete "$INSTDIR\avcodec-58.dll"
|
|
||||||
Delete "$INSTDIR\avfilter-7.dll"
|
|
||||||
Delete "$INSTDIR\avformat-58.dll"
|
|
||||||
Delete "$INSTDIR\avutil-56.dll"
|
|
||||||
Delete "$INSTDIR\brotlicommon.dll"
|
Delete "$INSTDIR\brotlicommon.dll"
|
||||||
Delete "$INSTDIR\brotlidec.dll"
|
Delete "$INSTDIR\brotlidec.dll"
|
||||||
Delete "$INSTDIR\chromaprint.dll"
|
Delete "$INSTDIR\chromaprint.dll"
|
||||||
Delete "$INSTDIR\faad.dll"
|
Delete "$INSTDIR\ebur128.dll"
|
||||||
|
Delete "$INSTDIR\faad-2.dll"
|
||||||
Delete "$INSTDIR\fdk-aac.dll"
|
Delete "$INSTDIR\fdk-aac.dll"
|
||||||
Delete "$INSTDIR\ffi-7.dll"
|
Delete "$INSTDIR\ffi-7.dll"
|
||||||
Delete "$INSTDIR\gio-2.0-0.dll"
|
Delete "$INSTDIR\gio-2.0-0.dll"
|
||||||
Delete "$INSTDIR\glib-2.0-0.dll"
|
Delete "$INSTDIR\glib-2.0-0.dll"
|
||||||
Delete "$INSTDIR\gme.dll"
|
Delete "$INSTDIR\gme.dll"
|
||||||
Delete "$INSTDIR\gmodule-2.0-0.dll"
|
Delete "$INSTDIR\gmodule-2.0-0.dll"
|
||||||
Delete "$INSTDIR\gnutls.dll"
|
|
||||||
Delete "$INSTDIR\gobject-2.0-0.dll"
|
Delete "$INSTDIR\gobject-2.0-0.dll"
|
||||||
Delete "$INSTDIR\gstadaptivedemux-1.0-0.dll"
|
Delete "$INSTDIR\gstadaptivedemux-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstapp-1.0-0.dll"
|
Delete "$INSTDIR\gstapp-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstaudio-1.0-0.dll"
|
Delete "$INSTDIR\gstaudio-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstbadaudio-1.0-0.dll"
|
Delete "$INSTDIR\gstbadaudio-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstbase-1.0-0.dll"
|
Delete "$INSTDIR\gstbase-1.0-0.dll"
|
||||||
|
Delete "$INSTDIR\gstcodecparsers-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstfft-1.0-0.dll"
|
Delete "$INSTDIR\gstfft-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstisoff-1.0-0.dll"
|
Delete "$INSTDIR\gstisoff-1.0-0.dll"
|
||||||
|
Delete "$INSTDIR\gstmpegts-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstnet-1.0-0.dll"
|
Delete "$INSTDIR\gstnet-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstpbutils-1.0-0.dll"
|
Delete "$INSTDIR\gstpbutils-1.0-0.dll"
|
||||||
Delete "$INSTDIR\gstreamer-1.0-0.dll"
|
Delete "$INSTDIR\gstreamer-1.0-0.dll"
|
||||||
|
@ -998,25 +1013,22 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\harfbuzz.dll"
|
Delete "$INSTDIR\harfbuzz.dll"
|
||||||
Delete "$INSTDIR\intl-8.dll"
|
Delete "$INSTDIR\intl-8.dll"
|
||||||
Delete "$INSTDIR\jpeg62.dll"
|
Delete "$INSTDIR\jpeg62.dll"
|
||||||
|
Delete "$INSTDIR\kdsingleapplication-qt6.dll"
|
||||||
Delete "$INSTDIR\libbs2b.dll"
|
Delete "$INSTDIR\libbs2b.dll"
|
||||||
Delete "$INSTDIR\libfaac_dll.dll"
|
Delete "$INSTDIR\libfaac_dll.dll"
|
||||||
Delete "$INSTDIR\liblzma.dll"
|
Delete "$INSTDIR\liblzma.dll"
|
||||||
Delete "$INSTDIR\libmp3lame.dll"
|
Delete "$INSTDIR\libmp3lame.dll"
|
||||||
Delete "$INSTDIR\libopenmpt.dll"
|
Delete "$INSTDIR\libopenmpt.dll"
|
||||||
Delete "$INSTDIR\libspeex.dll"
|
|
||||||
Delete "$INSTDIR\mpcdec.dll"
|
Delete "$INSTDIR\mpcdec.dll"
|
||||||
Delete "$INSTDIR\mpg123.dll"
|
Delete "$INSTDIR\mpg123.dll"
|
||||||
Delete "$INSTDIR\nghttp2.dll"
|
Delete "$INSTDIR\nghttp2.dll"
|
||||||
Delete "$INSTDIR\ogg.dll"
|
Delete "$INSTDIR\ogg.dll"
|
||||||
Delete "$INSTDIR\opus.dll"
|
Delete "$INSTDIR\opus.dll"
|
||||||
Delete "$INSTDIR\orc-0.4-0.dll"
|
Delete "$INSTDIR\orc-0.4-0.dll"
|
||||||
Delete "$INSTDIR\postproc-55.dll"
|
|
||||||
Delete "$INSTDIR\psl-5.dll"
|
Delete "$INSTDIR\psl-5.dll"
|
||||||
Delete "$INSTDIR\qtsparkle-qt6.dll"
|
Delete "$INSTDIR\qtsparkle-qt6.dll"
|
||||||
Delete "$INSTDIR\soup-3.0-0.dll"
|
Delete "$INSTDIR\soup-3.0-0.dll"
|
||||||
Delete "$INSTDIR\sqlite3.dll"
|
Delete "$INSTDIR\sqlite3.dll"
|
||||||
Delete "$INSTDIR\swresample-3.dll"
|
|
||||||
Delete "$INSTDIR\swscale-5.dll"
|
|
||||||
Delete "$INSTDIR\tag.dll"
|
Delete "$INSTDIR\tag.dll"
|
||||||
Delete "$INSTDIR\vorbis.dll"
|
Delete "$INSTDIR\vorbis.dll"
|
||||||
Delete "$INSTDIR\vorbisfile.dll"
|
Delete "$INSTDIR\vorbisfile.dll"
|
||||||
|
@ -1027,7 +1039,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\freetype.dll"
|
Delete "$INSTDIR\freetype.dll"
|
||||||
Delete "$INSTDIR\libiconv.dll"
|
Delete "$INSTDIR\libiconv.dll"
|
||||||
Delete "$INSTDIR\libpng16.dll"
|
Delete "$INSTDIR\libpng16.dll"
|
||||||
Delete "$INSTDIR\libxml2.dll"
|
Delete "$INSTDIR\libspeex.dll"
|
||||||
Delete "$INSTDIR\pcre2-8.dll"
|
Delete "$INSTDIR\pcre2-8.dll"
|
||||||
Delete "$INSTDIR\pcre2-16.dll"
|
Delete "$INSTDIR\pcre2-16.dll"
|
||||||
Delete "$INSTDIR\twolame.dll"
|
Delete "$INSTDIR\twolame.dll"
|
||||||
|
@ -1037,7 +1049,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\freetyped.dll"
|
Delete "$INSTDIR\freetyped.dll"
|
||||||
Delete "$INSTDIR\libiconvd.dll"
|
Delete "$INSTDIR\libiconvd.dll"
|
||||||
Delete "$INSTDIR\libpng16d.dll"
|
Delete "$INSTDIR\libpng16d.dll"
|
||||||
Delete "$INSTDIR\libxml2d.dll"
|
Delete "$INSTDIR\libspeexd.dll"
|
||||||
Delete "$INSTDIR\pcre2-8d.dll"
|
Delete "$INSTDIR\pcre2-8d.dll"
|
||||||
Delete "$INSTDIR\pcre2-16d.dll"
|
Delete "$INSTDIR\pcre2-16d.dll"
|
||||||
Delete "$INSTDIR\twolamed.dll"
|
Delete "$INSTDIR\twolamed.dll"
|
||||||
|
@ -1053,7 +1065,7 @@ Section "Uninstall"
|
||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
Delete "$INSTDIR\icudt73.dll"
|
Delete "$INSTDIR\icudt75.dll"
|
||||||
Delete "$INSTDIR\libfftw3-3.dll"
|
Delete "$INSTDIR\libfftw3-3.dll"
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
Delete "$INSTDIR\libprotobufd.dll"
|
Delete "$INSTDIR\libprotobufd.dll"
|
||||||
|
@ -1061,8 +1073,9 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\libprotobuf.dll"
|
Delete "$INSTDIR\libprotobuf.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
Delete "$INSTDIR\icuin73d.dll"
|
Delete "$INSTDIR\icuin75d.dll"
|
||||||
Delete "$INSTDIR\icuuc73d.dll"
|
Delete "$INSTDIR\icuuc75d.dll"
|
||||||
|
Delete "$INSTDIR\libxml2d.dll"
|
||||||
Delete "$INSTDIR\Qt6Concurrentd.dll"
|
Delete "$INSTDIR\Qt6Concurrentd.dll"
|
||||||
Delete "$INSTDIR\Qt6Cored.dll"
|
Delete "$INSTDIR\Qt6Cored.dll"
|
||||||
Delete "$INSTDIR\Qt6Guid.dll"
|
Delete "$INSTDIR\Qt6Guid.dll"
|
||||||
|
@ -1070,8 +1083,9 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\Qt6Sqld.dll"
|
Delete "$INSTDIR\Qt6Sqld.dll"
|
||||||
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
Delete "$INSTDIR\icuin73.dll"
|
Delete "$INSTDIR\icuin75.dll"
|
||||||
Delete "$INSTDIR\icuuc73.dll"
|
Delete "$INSTDIR\icuuc75.dll"
|
||||||
|
Delete "$INSTDIR\libxml2.dll"
|
||||||
Delete "$INSTDIR\Qt6Concurrent.dll"
|
Delete "$INSTDIR\Qt6Concurrent.dll"
|
||||||
Delete "$INSTDIR\Qt6Core.dll"
|
Delete "$INSTDIR\Qt6Core.dll"
|
||||||
Delete "$INSTDIR\Qt6Gui.dll"
|
Delete "$INSTDIR\Qt6Gui.dll"
|
||||||
|
@ -1080,6 +1094,14 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\Qt6Widgets.dll"
|
Delete "$INSTDIR\Qt6Widgets.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
Delete "$INSTDIR\avcodec-60.dll"
|
||||||
|
Delete "$INSTDIR\avfilter-9.dll"
|
||||||
|
Delete "$INSTDIR\avformat-60.dll"
|
||||||
|
Delete "$INSTDIR\avutil-58.dll"
|
||||||
|
Delete "$INSTDIR\postproc-57.dll"
|
||||||
|
Delete "$INSTDIR\swresample-4.dll"
|
||||||
|
Delete "$INSTDIR\swscale-7.dll"
|
||||||
|
|
||||||
!ifdef mingw
|
!ifdef mingw
|
||||||
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
||||||
Delete "$INSTDIR\gio-modules\libgioopenssl.dll"
|
Delete "$INSTDIR\gio-modules\libgioopenssl.dll"
|
||||||
|
@ -1091,7 +1113,7 @@ Section "Uninstall"
|
||||||
|
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
Delete "$INSTDIR\platforms\qwindowsd.dll"
|
Delete "$INSTDIR\platforms\qwindowsd.dll"
|
||||||
Delete "$INSTDIR\styles\qwindowsvistastyled.dll"
|
Delete "$INSTDIR\styles\qmodernwindowsstyled.dll"
|
||||||
Delete "$INSTDIR\tls\qschannelbackendd.dll"
|
Delete "$INSTDIR\tls\qschannelbackendd.dll"
|
||||||
Delete "$INSTDIR\tls\qopensslbackendd.dll"
|
Delete "$INSTDIR\tls\qopensslbackendd.dll"
|
||||||
Delete "$INSTDIR\sqldrivers\qsqlited.dll"
|
Delete "$INSTDIR\sqldrivers\qsqlited.dll"
|
||||||
|
@ -1100,7 +1122,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\imageformats\qjpegd.dll"
|
Delete "$INSTDIR\imageformats\qjpegd.dll"
|
||||||
!else
|
!else
|
||||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||||
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
|
Delete "$INSTDIR\styles\qmodernwindowsstyle.dll"
|
||||||
Delete "$INSTDIR\tls\qschannelbackend.dll"
|
Delete "$INSTDIR\tls\qschannelbackend.dll"
|
||||||
Delete "$INSTDIR\tls\qopensslbackend.dll"
|
Delete "$INSTDIR\tls\qopensslbackend.dll"
|
||||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||||
|
@ -1112,6 +1134,7 @@ Section "Uninstall"
|
||||||
; MinGW GStreamer plugins
|
; MinGW GStreamer plugins
|
||||||
|
|
||||||
!ifdef mingw
|
!ifdef mingw
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstadaptivedemux2.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaes.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaes.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaiff.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaiff.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
|
||||||
|
@ -1120,16 +1143,14 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstasfmux.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstasfmux.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiomixer.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiorate.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstbs2b.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstbs2b.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstdash.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstdash.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstdsd.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstfaac.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstfaac.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
|
||||||
|
@ -1144,6 +1165,10 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstmpegpsdemux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstmpegpsmux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstmpegtsdemux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstmpegtsmux.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstmpg123.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstmpg123.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstmusepack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstmusepack.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
|
||||||
|
@ -1166,6 +1191,7 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\libgstwaveform.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
|
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
|
||||||
|
@ -1175,24 +1201,24 @@ Section "Uninstall"
|
||||||
; MSVC GStreamer plugins
|
; MSVC GStreamer plugins
|
||||||
|
|
||||||
!ifdef msvc
|
!ifdef msvc
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstadaptivedemux2.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaes.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaes.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaiff.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaiff.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstapetag.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstapetag.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstapp.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstapp.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstasf.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstasf.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstasfmux.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstasfmux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstasio.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudioconvert.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaudioconvert.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudiofx.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaudiofx.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudiomixer.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudioparsers.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaudioparsers.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudiorate.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudioresample.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstaudioresample.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstaudiotestsrc.dll"
|
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstautodetect.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstautodetect.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstbs2b.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstbs2b.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstcoreelements.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstcoreelements.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstdash.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstdash.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstdirectsound.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstdirectsound.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstdsd.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstequalizer.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstequalizer.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstfaac.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstfaac.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstfaad.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstfaad.dll"
|
||||||
|
@ -1207,6 +1233,10 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstisomp4.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstisomp4.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstlame.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstlame.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstlibav.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstlibav.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstmpegpsdemux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstmpegpsmux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstmpegtsdemux.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstmpegtsmux.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstmpg123.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstmpg123.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstmusepack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstmusepack.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstogg.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstogg.dll"
|
||||||
|
@ -1230,13 +1260,14 @@ Section "Uninstall"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstvorbis.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstvorbis.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwasapi.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwasapi.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwasapi2.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwasapi2.dll"
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstwaveform.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwavenc.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwavenc.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwavpack.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwavpack.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstwavparse.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstwavparse.dll"
|
||||||
Delete "$INSTDIR\gstreamer-plugins\gstxingmux.dll"
|
Delete "$INSTDIR\gstreamer-plugins\gstxingmux.dll"
|
||||||
!endif ; msvc
|
!endif ; msvc
|
||||||
|
|
||||||
Delete $INSTDIR\Uninstall.exe"
|
Delete "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
; Remove the installation folders.
|
; Remove the installation folders.
|
||||||
RMDir "$INSTDIR\platforms"
|
RMDir "$INSTDIR\platforms"
|
||||||
|
|
|
@ -31,5 +31,5 @@ target_link_libraries(gstmoodbar PRIVATE
|
||||||
${GSTREAMER_BASE_LIBRARIES}
|
${GSTREAMER_BASE_LIBRARIES}
|
||||||
${GSTREAMER_AUDIO_LIBRARIES}
|
${GSTREAMER_AUDIO_LIBRARIES}
|
||||||
${FFTW3_FFTW_LIBRARY}
|
${FFTW3_FFTW_LIBRARY}
|
||||||
${QtCore_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
#include "gstfastspectrum.h"
|
#include "gstfastspectrum.h"
|
||||||
#include "gstmoodbarplugin.h"
|
#include "gstmoodbarplugin.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
|
static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
|
||||||
|
|
||||||
if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
|
if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
|
||||||
|
@ -32,8 +30,6 @@ static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int gstfastspectrum_register_static() {
|
int gstfastspectrum_register_static() {
|
||||||
|
|
||||||
return gst_plugin_register_static(
|
return gst_plugin_register_static(
|
||||||
|
|
|
@ -34,8 +34,8 @@ endif()
|
||||||
target_link_libraries(libstrawberry-common PRIVATE
|
target_link_libraries(libstrawberry-common PRIVATE
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${QtCore_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
${QtNetwork_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
)
|
)
|
||||||
|
|
||||||
if(Backtrace_FOUND)
|
if(Backtrace_FOUND)
|
||||||
|
|
|
@ -22,12 +22,22 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
# include <cxxabi.h>
|
# include <cxxabi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||||
|
#endif
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#ifdef __clang__
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_BACKTRACE
|
#ifdef HAVE_BACKTRACE
|
||||||
# include <execinfo.h>
|
# include <execinfo.h>
|
||||||
|
@ -57,8 +67,8 @@ static QIODevice *sNullDevice = nullptr;
|
||||||
|
|
||||||
const char *kDefaultLogLevels = "*:3";
|
const char *kDefaultLogLevels = "*:3";
|
||||||
|
|
||||||
static const char *kMessageHandlerMagic = "__logging_message__";
|
static constexpr char kMessageHandlerMagic[] = "__logging_message__";
|
||||||
static const size_t kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
static const size_t kMessageHandlerMagicLen = strlen(kMessageHandlerMagic);
|
||||||
static QtMessageHandler sOriginalMessageHandler = nullptr;
|
static QtMessageHandler sOriginalMessageHandler = nullptr;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
@ -125,9 +135,9 @@ class LoggedDebug : public DebugBase<LoggedDebug> {
|
||||||
|
|
||||||
static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QString &message) {
|
static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QString &message) {
|
||||||
|
|
||||||
if (message.startsWith(kMessageHandlerMagic)) {
|
if (message.startsWith(QLatin1String(kMessageHandlerMagic))) {
|
||||||
QByteArray message_data = message.toUtf8();
|
QByteArray message_data = message.toUtf8();
|
||||||
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", message_data.constData() + kMessageHandlerMagicLength);
|
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", message_data.constData() + kMessageHandlerMagicLen);
|
||||||
fflush(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout);
|
fflush(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -147,8 +157,8 @@ static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QStr
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QString &line : message.split('\n')) {
|
for (const QString &line : message.split(QLatin1Char('\n'))) {
|
||||||
BufferedDebug d = CreateLogger<BufferedDebug>(level, "unknown", -1, nullptr);
|
BufferedDebug d = CreateLogger<BufferedDebug>(level, QStringLiteral("unknown"), -1, nullptr);
|
||||||
d << line.toLocal8Bit().constData();
|
d << line.toLocal8Bit().constData();
|
||||||
if (d.buf_) {
|
if (d.buf_) {
|
||||||
d.buf_->close();
|
d.buf_->close();
|
||||||
|
@ -183,8 +193,8 @@ void SetLevels(const QString &levels) {
|
||||||
|
|
||||||
if (!sClassLevels) return;
|
if (!sClassLevels) return;
|
||||||
|
|
||||||
for (const QString &item : levels.split(',')) {
|
for (const QString &item : levels.split(QLatin1Char(','))) {
|
||||||
const QStringList class_level = item.split(':');
|
const QStringList class_level = item.split(QLatin1Char(':'));
|
||||||
|
|
||||||
QString class_name;
|
QString class_name;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
@ -202,7 +212,7 @@ void SetLevels(const QString &levels) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_name.isEmpty() || class_name == "*") {
|
if (class_name.isEmpty() || class_name == QLatin1Char('*')) {
|
||||||
sDefaultLevel = static_cast<Level>(level);
|
sDefaultLevel = static_cast<Level>(level);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -215,10 +225,10 @@ void SetLevels(const QString &levels) {
|
||||||
static QString ParsePrettyFunction(const char *pretty_function) {
|
static QString ParsePrettyFunction(const char *pretty_function) {
|
||||||
|
|
||||||
// Get the class name out of the function name.
|
// Get the class name out of the function name.
|
||||||
QString class_name = pretty_function;
|
QString class_name = QLatin1String(pretty_function);
|
||||||
const qint64 paren = class_name.indexOf('(');
|
const qint64 paren = class_name.indexOf(QLatin1Char('('));
|
||||||
if (paren != -1) {
|
if (paren != -1) {
|
||||||
const qint64 colons = class_name.lastIndexOf("::", paren);
|
const qint64 colons = class_name.lastIndexOf(QLatin1String("::"), paren);
|
||||||
if (colons != -1) {
|
if (colons != -1) {
|
||||||
class_name = class_name.left(colons);
|
class_name = class_name.left(colons);
|
||||||
}
|
}
|
||||||
|
@ -227,7 +237,7 @@ static QString ParsePrettyFunction(const char *pretty_function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const qint64 space = class_name.lastIndexOf(' ');
|
const qint64 space = class_name.lastIndexOf(QLatin1Char(' '));
|
||||||
if (space != -1) {
|
if (space != -1) {
|
||||||
class_name = class_name.mid(space + 1);
|
class_name = class_name.mid(space + 1);
|
||||||
}
|
}
|
||||||
|
@ -249,7 +259,7 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
|
||||||
case Level_Fatal: level_name = " FATAL "; break;
|
case Level_Fatal: level_name = " FATAL "; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filter_category = (category != nullptr) ? category : class_name;
|
QString filter_category = (category != nullptr) ? QLatin1String(category) : class_name;
|
||||||
// Check the settings to see if we're meant to show or hide this message.
|
// Check the settings to see if we're meant to show or hide this message.
|
||||||
Level threshold_level = sDefaultLevel;
|
Level threshold_level = sDefaultLevel;
|
||||||
if (sClassLevels && sClassLevels->contains(filter_category)) {
|
if (sClassLevels && sClassLevels->contains(filter_category)) {
|
||||||
|
@ -262,10 +272,10 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
|
||||||
|
|
||||||
QString function_line = class_name;
|
QString function_line = class_name;
|
||||||
if (line != -1) {
|
if (line != -1) {
|
||||||
function_line += ":" + QString::number(line);
|
function_line += QLatin1Char(':') + QString::number(line);
|
||||||
}
|
}
|
||||||
if (category) {
|
if (category) {
|
||||||
function_line += "(" + QString(category) + ")";
|
function_line += QLatin1Char('(') + QLatin1String(category) + QLatin1Char(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
QtMsgType type = QtDebugMsg;
|
QtMsgType type = QtDebugMsg;
|
||||||
|
@ -274,7 +284,7 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
|
||||||
}
|
}
|
||||||
|
|
||||||
T ret(type);
|
T ret(type);
|
||||||
ret.nospace() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz").toLatin1().constData() << level_name << function_line.leftJustified(32).toLatin1().constData();
|
ret.nospace() << QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss.zzz")).toLatin1().constData() << level_name << function_line.leftJustified(32).toLatin1().constData();
|
||||||
|
|
||||||
return ret.space();
|
return ret.space();
|
||||||
|
|
||||||
|
@ -300,7 +310,7 @@ QString CXXDemangle(const QString &mangled_function) {
|
||||||
QString LinuxDemangle(const QString &symbol);
|
QString LinuxDemangle(const QString &symbol);
|
||||||
QString LinuxDemangle(const QString &symbol) {
|
QString LinuxDemangle(const QString &symbol) {
|
||||||
|
|
||||||
QRegularExpression regex("\\(([^+]+)");
|
QRegularExpression regex(QStringLiteral("\\(([^+]+)"));
|
||||||
QRegularExpressionMatch match = regex.match(symbol);
|
QRegularExpressionMatch match = regex.match(symbol);
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch()) {
|
||||||
return symbol;
|
return symbol;
|
||||||
|
@ -316,9 +326,9 @@ QString DarwinDemangle(const QString &symbol);
|
||||||
QString DarwinDemangle(const QString &symbol) {
|
QString DarwinDemangle(const QString &symbol) {
|
||||||
|
|
||||||
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
QStringList split = symbol.split(' ', Qt::SkipEmptyParts);
|
QStringList split = symbol.split(QLatin1Char(' '), Qt::SkipEmptyParts);
|
||||||
# else
|
# else
|
||||||
QStringList split = symbol.split(' ', QString::SkipEmptyParts);
|
QStringList split = symbol.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||||
# endif
|
# endif
|
||||||
QString mangled_function = split[3];
|
QString mangled_function = split[3];
|
||||||
return CXXDemangle(mangled_function);
|
return CXXDemangle(mangled_function);
|
||||||
|
@ -382,7 +392,7 @@ namespace {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
QString print_duration(T duration, const std::string &unit) {
|
QString print_duration(T duration, const std::string &unit) {
|
||||||
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
|
return QStringLiteral("%1%2").arg(duration.count()).arg(QString::fromStdString(unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
|
@ -170,7 +172,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||||
local_server_name_ = qApp->applicationName().toLower();
|
local_server_name_ = qApp->applicationName().toLower();
|
||||||
|
|
||||||
if (local_server_name_.isEmpty()) {
|
if (local_server_name_.isEmpty()) {
|
||||||
local_server_name_ = "workerpool";
|
local_server_name_ = QStringLiteral("workerpool");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -242,15 +244,15 @@ void WorkerPool<HandlerType>::DoStart() {
|
||||||
QStringList search_path;
|
QStringList search_path;
|
||||||
search_path << QCoreApplication::applicationDirPath();
|
search_path << QCoreApplication::applicationDirPath();
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_UNIX)
|
||||||
search_path << "/usr/libexec";
|
search_path << QStringLiteral("/usr/libexec");
|
||||||
search_path << "/usr/local/libexec";
|
search_path << QStringLiteral("/usr/local/libexec");
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MACOS) && defined(USE_BUNDLE)
|
#if defined(Q_OS_MACOS)
|
||||||
search_path << QCoreApplication::applicationDirPath() + "/" + USE_BUNDLE_DIR;
|
search_path << QDir::cleanPath(QCoreApplication::applicationDirPath() + QStringLiteral("/../PlugIns"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (const QString &path_prefix : search_path) {
|
for (const QString &path_prefix : std::as_const(search_path)) {
|
||||||
const QString executable_path = path_prefix + "/" + executable_name_;
|
const QString executable_path = path_prefix + QLatin1Char('/') + executable_name_;
|
||||||
if (QFile::exists(executable_path)) {
|
if (QFile::exists(executable_path)) {
|
||||||
executable_path_ = executable_path;
|
executable_path_ = executable_path;
|
||||||
qLog(Debug) << "Using worker" << executable_name_ << "from" << path_prefix;
|
qLog(Debug) << "Using worker" << executable_name_ << "from" << path_prefix;
|
||||||
|
@ -293,7 +295,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||||
// Create a server, find an unused name and start listening
|
// Create a server, find an unused name and start listening
|
||||||
forever {
|
forever {
|
||||||
const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
|
const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
|
||||||
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);
|
const QString name = QStringLiteral("%1_%2").arg(local_server_name_).arg(unique_number);
|
||||||
|
|
||||||
if (worker->local_server_->listen(name)) {
|
if (worker->local_server_->listen(name)) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6,7 +6,7 @@ if(NOT protobuf_PROTOC_EXE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT Protobuf_LIBRARIES)
|
if(NOT Protobuf_LIBRARIES)
|
||||||
set(Protobuf_LIBRARIES protobuf::libprotobuf protobuf::libprotoc)
|
set(Protobuf_LIBRARIES protobuf::libprotobuf)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
|
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
|
||||||
|
@ -50,9 +50,9 @@ target_include_directories(libstrawberry-tagreader PRIVATE
|
||||||
target_link_libraries(libstrawberry-tagreader PRIVATE
|
target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${Protobuf_LIBRARIES}
|
${Protobuf_LIBRARIES}
|
||||||
${QtCore_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
${QtNetwork_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
${QtGui_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -31,15 +31,15 @@
|
||||||
#include "tagreaderbase.h"
|
#include "tagreaderbase.h"
|
||||||
|
|
||||||
TagReaderBase::TagReaderBase() = default;
|
TagReaderBase::TagReaderBase() = default;
|
||||||
TagReaderBase::~TagReaderBase() = default;
|
|
||||||
|
|
||||||
float TagReaderBase::ConvertPOPMRating(const int POPM_rating) {
|
float TagReaderBase::ConvertPOPMRating(const int POPM_rating) {
|
||||||
|
|
||||||
if (POPM_rating < 0x01) return 0.0F;
|
if (POPM_rating < 0x01) return 0.0F;
|
||||||
else if (POPM_rating < 0x40) return 0.20F;
|
if (POPM_rating < 0x40) return 0.20F;
|
||||||
else if (POPM_rating < 0x80) return 0.40F;
|
if (POPM_rating < 0x80) return 0.40F;
|
||||||
else if (POPM_rating < 0xC0) return 0.60F;
|
if (POPM_rating < 0xC0) return 0.60F;
|
||||||
else if (POPM_rating < 0xFC) return 0.80F;
|
if (POPM_rating < 0xFC) return 0.80F;
|
||||||
|
|
||||||
return 1.0F;
|
return 1.0F;
|
||||||
|
|
||||||
|
@ -48,10 +48,10 @@ float TagReaderBase::ConvertPOPMRating(const int POPM_rating) {
|
||||||
int TagReaderBase::ConvertToPOPMRating(const float rating) {
|
int TagReaderBase::ConvertToPOPMRating(const float rating) {
|
||||||
|
|
||||||
if (rating < 0.20) return 0x00;
|
if (rating < 0.20) return 0x00;
|
||||||
else if (rating < 0.40) return 0x01;
|
if (rating < 0.40) return 0x01;
|
||||||
else if (rating < 0.60) return 0x40;
|
if (rating < 0.60) return 0x40;
|
||||||
else if (rating < 0.80) return 0x80;
|
if (rating < 0.80) return 0x80;
|
||||||
else if (rating < 1.0) return 0xC0;
|
if (rating < 1.0) return 0xC0;
|
||||||
|
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::S
|
||||||
}
|
}
|
||||||
QString cover_mime_type;
|
QString cover_mime_type;
|
||||||
if (request.has_cover_mime_type()) {
|
if (request.has_cover_mime_type()) {
|
||||||
cover_mime_type = QByteArray(request.cover_mime_type().data(), static_cast<qint64>(request.cover_mime_type().size()));
|
cover_mime_type = QString::fromStdString(request.cover_mime_type());
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
|
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
|
||||||
|
@ -94,7 +94,7 @@ TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::S
|
||||||
}
|
}
|
||||||
QString cover_mime_type;
|
QString cover_mime_type;
|
||||||
if (request.has_cover_mime_type()) {
|
if (request.has_cover_mime_type()) {
|
||||||
cover_mime_type = QByteArray(request.cover_mime_type().data(), static_cast<qint64>(request.cover_mime_type().size()));
|
cover_mime_type = QString::fromStdString(request.cover_mime_type());
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
|
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
|
||||||
|
@ -118,24 +118,28 @@ TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_fil
|
||||||
if (cover_mime_type.isEmpty()) {
|
if (cover_mime_type.isEmpty()) {
|
||||||
cover_mime_type = QMimeDatabase().mimeTypeForData(cover_data).name();
|
cover_mime_type = QMimeDatabase().mimeTypeForData(cover_data).name();
|
||||||
}
|
}
|
||||||
if (cover_mime_type == "image/jpeg") {
|
if (cover_mime_type == QLatin1String("image/jpeg")) {
|
||||||
qLog(Debug) << "Using cover from JPEG data for" << song_filename;
|
qLog(Debug) << "Using cover from JPEG data for" << song_filename;
|
||||||
return Cover(cover_data, cover_mime_type);
|
return Cover(cover_data, cover_mime_type);
|
||||||
}
|
}
|
||||||
if (cover_mime_type == "image/png") {
|
if (cover_mime_type == QLatin1String("image/png")) {
|
||||||
qLog(Debug) << "Using cover from PNG data for" << song_filename;
|
qLog(Debug) << "Using cover from PNG data for" << song_filename;
|
||||||
return Cover(cover_data, cover_mime_type);
|
return Cover(cover_data, cover_mime_type);
|
||||||
}
|
}
|
||||||
// Convert image to JPEG.
|
// Convert image to JPEG.
|
||||||
qLog(Debug) << "Converting cover to JPEG data for" << song_filename;
|
qLog(Debug) << "Converting cover to JPEG data for" << song_filename;
|
||||||
QImage cover_image(cover_data);
|
QImage cover_image;
|
||||||
|
if (!cover_image.loadFromData(cover_data)) {
|
||||||
|
qLog(Error) << "Failed to load image from cover data for" << song_filename;
|
||||||
|
return Cover();
|
||||||
|
}
|
||||||
cover_data.clear();
|
cover_data.clear();
|
||||||
QBuffer buffer(&cover_data);
|
QBuffer buffer(&cover_data);
|
||||||
if (buffer.open(QIODevice::WriteOnly)) {
|
if (buffer.open(QIODevice::WriteOnly)) {
|
||||||
cover_image.save(&buffer, "JPEG");
|
cover_image.save(&buffer, "JPEG");
|
||||||
buffer.close();
|
buffer.close();
|
||||||
}
|
}
|
||||||
return Cover(cover_data, "image/jpeg");
|
return Cover(cover_data, QStringLiteral("image/jpeg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Cover();
|
return Cover();
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
class TagReaderBase {
|
class TagReaderBase {
|
||||||
public:
|
public:
|
||||||
explicit TagReaderBase();
|
explicit TagReaderBase();
|
||||||
~TagReaderBase();
|
~TagReaderBase() = default;
|
||||||
|
|
||||||
class Cover {
|
class Cover {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -36,16 +36,16 @@
|
||||||
#include "tagreadertaglib.h"
|
#include "tagreadertaglib.h"
|
||||||
|
|
||||||
bool GME::IsSupportedFormat(const QFileInfo &file_info) {
|
bool GME::IsSupportedFormat(const QFileInfo &file_info) {
|
||||||
return file_info.exists() && (file_info.completeSuffix().endsWith("spc", Qt::CaseInsensitive) || file_info.completeSuffix().endsWith("vgm"), Qt::CaseInsensitive);
|
return file_info.exists() && (file_info.completeSuffix().endsWith(QLatin1String("spc"), Qt::CaseInsensitive) || file_info.completeSuffix().endsWith(QLatin1String("vgm")), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GME::ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
bool GME::ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
||||||
|
|
||||||
if (file_info.completeSuffix().endsWith("spc"), Qt::CaseInsensitive) {
|
if (file_info.completeSuffix().endsWith(QLatin1String("spc")), Qt::CaseInsensitive) {
|
||||||
SPC::Read(file_info, song_info);
|
SPC::Read(file_info, song_info);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (file_info.completeSuffix().endsWith("vgm", Qt::CaseInsensitive)) {
|
if (file_info.completeSuffix().endsWith(QLatin1String("vgm"), Qt::CaseInsensitive)) {
|
||||||
VGM::Read(file_info, song_info);
|
VGM::Read(file_info, song_info);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
qLog(Debug) << "Reading tags from SPC file" << file_info.fileName();
|
qLog(Debug) << "Reading tags from SPC file" << file_info.fileName();
|
||||||
|
|
||||||
// Check for header -- more reliable than file name alone.
|
// Check for header -- more reliable than file name alone.
|
||||||
if (!file.read(33).startsWith(QString("SNES-SPC700").toLatin1())) return;
|
if (!file.read(33).startsWith(QStringLiteral("SNES-SPC700").toLatin1())) return;
|
||||||
|
|
||||||
// First order of business -- get any tag values that exist within the core file information.
|
// First order of business -- get any tag values that exist within the core file information.
|
||||||
// These only allow for a certain number of bytes per field,
|
// These only allow for a certain number of bytes per field,
|
||||||
|
@ -98,9 +98,8 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
|
|
||||||
file.seek(INTRO_LENGTH_OFFSET);
|
file.seek(INTRO_LENGTH_OFFSET);
|
||||||
QByteArray length_bytes = file.read(INTRO_LENGTH_SIZE);
|
QByteArray length_bytes = file.read(INTRO_LENGTH_SIZE);
|
||||||
quint64 length_in_sec = 0;
|
|
||||||
if (length_bytes.size() >= INTRO_LENGTH_SIZE) {
|
if (length_bytes.size() >= INTRO_LENGTH_SIZE) {
|
||||||
length_in_sec = ConvertSPCStringToNum(length_bytes);
|
quint64 length_in_sec = ConvertSPCStringToNum(length_bytes);
|
||||||
|
|
||||||
if (!length_in_sec || length_in_sec >= 0x1FFF) {
|
if (!length_in_sec || length_in_sec >= 0x1FFF) {
|
||||||
// This means that parsing the length as a string failed, so get value LE.
|
// This means that parsing the length as a string failed, so get value LE.
|
||||||
|
@ -120,12 +119,13 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
if (fade_length_in_ms > 0x7FFF) {
|
if (fade_length_in_ms > 0x7FFF) {
|
||||||
fade_length_in_ms = fade_bytes[0] | (fade_bytes[1] << 8) | (fade_bytes[2] << 16) | (fade_bytes[3] << 24);
|
fade_length_in_ms = fade_bytes[0] | (fade_bytes[1] << 8) | (fade_bytes[2] << 16) | (fade_bytes[3] << 24);
|
||||||
}
|
}
|
||||||
|
Q_UNUSED(fade_length_in_ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for XID6 data -- this is infrequently used, but being able to fill in data from this is ideal before trying to rely on APETAG values.
|
// Check for XID6 data -- this is infrequently used, but being able to fill in data from this is ideal before trying to rely on APETAG values.
|
||||||
// XID6 format follows EA's binary file format standard named "IFF"
|
// XID6 format follows EA's binary file format standard named "IFF"
|
||||||
file.seek(XID6_OFFSET);
|
file.seek(XID6_OFFSET);
|
||||||
if (has_id6 && file.read(4) == QString("xid6").toLatin1()) {
|
if (has_id6 && file.read(4) == QStringLiteral("xid6").toLatin1()) {
|
||||||
QByteArray xid6_head_data = file.read(4);
|
QByteArray xid6_head_data = file.read(4);
|
||||||
if (xid6_head_data.size() >= 4) {
|
if (xid6_head_data.size() >= 4) {
|
||||||
qint64 xid6_size = xid6_head_data[0] | (xid6_head_data[1] << 8) | (xid6_head_data[2] << 16) | xid6_head_data[3];
|
qint64 xid6_size = xid6_head_data[0] | (xid6_head_data[1] << 8) | (xid6_head_data[2] << 16) | xid6_head_data[3];
|
||||||
|
@ -141,7 +141,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
qint8 type = arr[1];
|
qint8 type = arr[1];
|
||||||
Q_UNUSED(id);
|
Q_UNUSED(id);
|
||||||
Q_UNUSED(type);
|
Q_UNUSED(type);
|
||||||
qint16 length = arr[2] | (arr[3] << 8);
|
qint16 length = static_cast<qint16>(arr[2] | (arr[3] << 8));
|
||||||
|
|
||||||
file.read(GetNextMemAddressAlign32bit(length));
|
file.read(GetNextMemAddressAlign32bit(length));
|
||||||
}
|
}
|
||||||
|
@ -161,8 +161,8 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
TagReaderTagLib::TStringToStdString(tag->album(), song_info->mutable_album());
|
TagReaderTagLib::TStringToStdString(tag->album(), song_info->mutable_album());
|
||||||
TagReaderTagLib::TStringToStdString(tag->title(), song_info->mutable_title());
|
TagReaderTagLib::TStringToStdString(tag->title(), song_info->mutable_title());
|
||||||
TagReaderTagLib::TStringToStdString(tag->genre(), song_info->mutable_genre());
|
TagReaderTagLib::TStringToStdString(tag->genre(), song_info->mutable_genre());
|
||||||
song_info->set_track(tag->track());
|
song_info->set_track(static_cast<std::int32_t>(tag->track()));
|
||||||
song_info->set_year(tag->year());
|
song_info->set_year(static_cast<std::int32_t>(tag->year()));
|
||||||
}
|
}
|
||||||
|
|
||||||
song_info->set_valid(true);
|
song_info->set_valid(true);
|
||||||
|
@ -171,7 +171,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
}
|
}
|
||||||
|
|
||||||
qint16 GME::SPC::GetNextMemAddressAlign32bit(qint16 input) {
|
qint16 GME::SPC::GetNextMemAddressAlign32bit(qint16 input) {
|
||||||
return ((input + 0x3) & ~0x3);
|
return static_cast<qint16>((input + 0x3) & ~0x3);
|
||||||
// Plus 0x3 for rounding up (not down), AND NOT to flatten out on a 32 bit level.
|
// Plus 0x3 for rounding up (not down), AND NOT to flatten out on a 32 bit level.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
|
|
||||||
qLog(Debug) << "Reading tags from VGM file" << file_info.fileName();
|
qLog(Debug) << "Reading tags from VGM file" << file_info.fileName();
|
||||||
|
|
||||||
if (!file.read(4).startsWith(QString("Vgm ").toLatin1())) return;
|
if (!file.read(4).startsWith(QStringLiteral("Vgm ").toLatin1())) return;
|
||||||
|
|
||||||
file.seek(GD3_TAG_PTR);
|
file.seek(GD3_TAG_PTR);
|
||||||
QByteArray gd3_head = file.read(4);
|
QByteArray gd3_head = file.read(4);
|
||||||
|
@ -211,7 +211,7 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
|
|
||||||
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) return;
|
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) return;
|
||||||
|
|
||||||
file.seek(GD3_TAG_PTR + pt);
|
file.seek(static_cast<qint64>(GD3_TAG_PTR + pt));
|
||||||
QByteArray gd3_version = file.read(4);
|
QByteArray gd3_version = file.read(4);
|
||||||
|
|
||||||
file.seek(file.pos() + 4);
|
file.seek(file.pos() + 4);
|
||||||
|
@ -226,7 +226,7 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||||
#else
|
#else
|
||||||
fileTagStream.setCodec("UTF-16");
|
fileTagStream.setCodec("UTF-16");
|
||||||
#endif
|
#endif
|
||||||
QStringList strings = fileTagStream.readLine(0).split(QChar('\0'));
|
QStringList strings = fileTagStream.readLine(0).split(QLatin1Char('\0'));
|
||||||
if (strings.count() < 10) return;
|
if (strings.count() < 10) return;
|
||||||
|
|
||||||
// VGM standard dictates string tag data exist in specific order.
|
// VGM standard dictates string tag data exist in specific order.
|
||||||
|
@ -249,11 +249,11 @@ bool GME::VGM::GetPlaybackLength(const QByteArray &sample_count_bytes, const QBy
|
||||||
|
|
||||||
quint64 sample_count = GME::UnpackBytes32(sample_count_bytes.constData(), sample_count_bytes.size());
|
quint64 sample_count = GME::UnpackBytes32(sample_count_bytes.constData(), sample_count_bytes.size());
|
||||||
|
|
||||||
if (sample_count <= 0) return false;
|
if (sample_count == 0) return false;
|
||||||
|
|
||||||
quint64 loop_sample_count = GME::UnpackBytes32(loop_count_bytes.constData(), loop_count_bytes.size());
|
quint64 loop_sample_count = GME::UnpackBytes32(loop_count_bytes.constData(), loop_count_bytes.size());
|
||||||
|
|
||||||
if (loop_sample_count <= 0) {
|
if (loop_sample_count == 0) {
|
||||||
out_length = sample_count * 1000 / SAMPLE_TIMEBASE;
|
out_length = sample_count * 1000 / SAMPLE_TIMEBASE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ bool GME::VGM::GetPlaybackLength(const QByteArray &sample_count_bytes, const QBy
|
||||||
}
|
}
|
||||||
|
|
||||||
TagReaderGME::TagReaderGME() = default;
|
TagReaderGME::TagReaderGME() = default;
|
||||||
TagReaderGME::~TagReaderGME() = default;
|
|
||||||
|
|
||||||
bool TagReaderGME::IsMediaFile(const QString &filename) const {
|
bool TagReaderGME::IsMediaFile(const QString &filename) const {
|
||||||
QFileInfo fileinfo(filename);
|
QFileInfo fileinfo(filename);
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace GME {
|
||||||
bool IsSupportedFormat(const QFileInfo &file_info);
|
bool IsSupportedFormat(const QFileInfo &file_info);
|
||||||
bool ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
bool ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
||||||
|
|
||||||
uint32_t UnpackBytes32(const char *const arr, size_t length);
|
uint32_t UnpackBytes32(const char *const bytes, size_t length);
|
||||||
|
|
||||||
namespace SPC {
|
namespace SPC {
|
||||||
// SPC SPEC: http://vspcplay.raphnet.net/spc_file_format.txt
|
// SPC SPEC: http://vspcplay.raphnet.net/spc_file_format.txt
|
||||||
|
@ -102,7 +102,7 @@ class TagReaderGME : public TagReaderBase {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TagReaderGME();
|
explicit TagReaderGME();
|
||||||
~TagReaderGME();
|
~TagReaderGME() = default;
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const override;
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||||
song->set_ctime(song->mtime());
|
song->set_ctime(song->mtime());
|
||||||
}
|
}
|
||||||
|
|
||||||
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
|
song->set_lastseen(QDateTime::currentSecsSinceEpoch());
|
||||||
|
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||||
if (fileref->isNull()) {
|
if (fileref->isNull()) {
|
||||||
|
@ -328,127 +328,8 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
|
if (file_mpeg->hasID3v2Tag()) {
|
||||||
if (file_mpeg->ID3v2Tag()) {
|
ParseID3v2Tag(file_mpeg->ID3v2Tag(), &disc, &compilation, song);
|
||||||
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
|
|
||||||
|
|
||||||
if (map.contains("TPOS")) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
|
||||||
if (map.contains("TCOM")) TStringToStdString(map["TCOM"].front()->toString(), song->mutable_composer());
|
|
||||||
|
|
||||||
// content group
|
|
||||||
if (map.contains("TIT1")) TStringToStdString(map["TIT1"].front()->toString(), song->mutable_grouping());
|
|
||||||
|
|
||||||
// original artist/performer
|
|
||||||
if (map.contains("TOPE")) TStringToStdString(map["TOPE"].front()->toString(), song->mutable_performer());
|
|
||||||
|
|
||||||
// Skip TPE1 (which is the artist) here because we already fetched it
|
|
||||||
|
|
||||||
// non-standard: Apple, Microsoft
|
|
||||||
if (map.contains("TPE2")) TStringToStdString(map["TPE2"].front()->toString(), song->mutable_albumartist());
|
|
||||||
|
|
||||||
if (map.contains("TCMP")) compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
|
||||||
|
|
||||||
if (map.contains("TDOR")) {
|
|
||||||
song->set_originalyear(map["TDOR"].front()->toString().substr(0, 4).toInt());
|
|
||||||
}
|
|
||||||
else if (map.contains("TORY")) {
|
|
||||||
song->set_originalyear(map["TORY"].front()->toString().substr(0, 4).toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.contains("USLT")) {
|
|
||||||
TStringToStdString(map["USLT"].front()->toString(), song->mutable_lyrics());
|
|
||||||
}
|
|
||||||
else if (map.contains("SYLT")) {
|
|
||||||
TStringToStdString(map["SYLT"].front()->toString(), song->mutable_lyrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.contains("APIC")) song->set_art_embedded(true);
|
|
||||||
|
|
||||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
|
||||||
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
|
||||||
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
|
||||||
|
|
||||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
|
||||||
TStringToStdString(frame->text(), song->mutable_comment());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TagLib::ID3v2::UserTextIdentificationFrame *frame_fmps_rating = TagLib::ID3v2::UserTextIdentificationFrame::find(file_mpeg->ID3v2Tag(), "FMPS_Rating")) {
|
|
||||||
TagLib::StringList frame_field_list = frame_fmps_rating->fieldList();
|
|
||||||
if (frame_field_list.size() > 1) {
|
|
||||||
float rating = TStringToQString(frame_field_list[1]).toFloat();
|
|
||||||
if (song->rating() <= 0 && rating > 0 && rating <= 1.0) {
|
|
||||||
song->set_rating(rating);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.contains("POPM")) {
|
|
||||||
const TagLib::ID3v2::PopularimeterFrame *frame = dynamic_cast<const TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
|
||||||
if (frame) {
|
|
||||||
if (song->playcount() <= 0 && frame->counter() > 0) {
|
|
||||||
song->set_playcount(frame->counter());
|
|
||||||
}
|
|
||||||
if (song->rating() <= 0 && frame->rating() > 0) {
|
|
||||||
song->set_rating(ConvertPOPMRating(frame->rating()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.contains("UFID")) {
|
|
||||||
for (uint i = 0; i < map["UFID"].size(); ++i) {
|
|
||||||
if (TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(map["UFID"][i])) {
|
|
||||||
const TagLib::PropertyMap property_map = frame->asProperties();
|
|
||||||
if (property_map.contains(kID3v2_MusicBrainz_RecordingID)) {
|
|
||||||
TStringToStdString(property_map[kID3v2_MusicBrainz_RecordingID].toString(), song->mutable_musicbrainz_recording_id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.contains("TXXX")) {
|
|
||||||
for (uint i = 0; i < map["TXXX"].size(); ++i) {
|
|
||||||
if (TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i])) {
|
|
||||||
const TagLib::StringList frame_field_list = frame->fieldList();
|
|
||||||
if (frame_field_list.size() != 2) continue;
|
|
||||||
if (frame->description() == kID3v2_AcoustID_ID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_acoustid_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_AcoustID_Fingerprint) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_acoustid_fingerprint());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_AlbumArtistID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_album_artist_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_ArtistID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_artist_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_OriginalArtistID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_original_artist_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_AlbumID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_album_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_OriginalAlbumID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_original_album_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_TrackID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_track_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_DiscID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_disc_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_ReleaseGroupID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_release_group_id());
|
|
||||||
}
|
|
||||||
if (frame->description() == kID3v2_MusicBrainz_WorkID) {
|
|
||||||
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_work_id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,12 +529,18 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||||
if (tag) TStringToStdString(tag->comment(), song->mutable_comment());
|
if (tag) TStringToStdString(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (TagLib::RIFF::WAV::File *file_wav = dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) {
|
||||||
|
if (file_wav->hasID3v2Tag()) {
|
||||||
|
ParseID3v2Tag(file_wav->ID3v2Tag(), &disc, &compilation, song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (tag) {
|
else if (tag) {
|
||||||
TStringToStdString(tag->comment(), song->mutable_comment());
|
TStringToStdString(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!disc.isEmpty()) {
|
if (!disc.isEmpty()) {
|
||||||
const qint64 i = disc.indexOf('/');
|
const qint64 i = disc.indexOf(QLatin1Char('/'));
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
// disc.right( i ).toInt() is total number of discs, we don't use this at the moment
|
// disc.right( i ).toInt() is total number of discs, we don't use this at the moment
|
||||||
song->set_disc(disc.left(i).toInt());
|
song->set_disc(disc.left(i).toInt());
|
||||||
|
@ -667,7 +554,7 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||||
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
||||||
const QString albumartist = QString::fromUtf8(song->albumartist().data(), static_cast<qint64>(song->albumartist().size()));
|
const QString albumartist = QString::fromUtf8(song->albumartist().data(), static_cast<qint64>(song->albumartist().size()));
|
||||||
const QString artist = QString::fromUtf8(song->artist().data(), static_cast<qint64>(song->artist().size()));
|
const QString artist = QString::fromUtf8(song->artist().data(), static_cast<qint64>(song->artist().size()));
|
||||||
if (artist.compare("various artists") == 0 || albumartist.compare("various artists") == 0) {
|
if (artist.compare(QLatin1String("various artists")) == 0 || albumartist.compare(QLatin1String("various artists")) == 0) {
|
||||||
song->set_compilation(true);
|
song->set_compilation(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,6 +587,139 @@ void TagReaderTagLib::TStringToStdString(const TagLib::String &tag, std::string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagReaderTagLib::ParseID3v2Tag(TagLib::ID3v2::Tag *tag, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
|
TagLib::ID3v2::FrameListMap map = tag->frameListMap();
|
||||||
|
|
||||||
|
if (map.contains("TPOS")) *disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
||||||
|
if (map.contains("TCOM")) TStringToStdString(map["TCOM"].front()->toString(), song->mutable_composer());
|
||||||
|
|
||||||
|
// content group
|
||||||
|
if (map.contains("TIT1")) TStringToStdString(map["TIT1"].front()->toString(), song->mutable_grouping());
|
||||||
|
|
||||||
|
// original artist/performer
|
||||||
|
if (map.contains("TOPE")) TStringToStdString(map["TOPE"].front()->toString(), song->mutable_performer());
|
||||||
|
|
||||||
|
// Skip TPE1 (which is the artist) here because we already fetched it
|
||||||
|
|
||||||
|
// non-standard: Apple, Microsoft
|
||||||
|
if (map.contains("TPE2")) TStringToStdString(map["TPE2"].front()->toString(), song->mutable_albumartist());
|
||||||
|
|
||||||
|
if (map.contains("TCMP")) *compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
||||||
|
|
||||||
|
if (map.contains("TDOR")) {
|
||||||
|
song->set_originalyear(map["TDOR"].front()->toString().substr(0, 4).toInt());
|
||||||
|
}
|
||||||
|
else if (map.contains("TORY")) {
|
||||||
|
song->set_originalyear(map["TORY"].front()->toString().substr(0, 4).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.contains("USLT")) {
|
||||||
|
TStringToStdString(map["USLT"].front()->toString(), song->mutable_lyrics());
|
||||||
|
}
|
||||||
|
else if (map.contains("SYLT")) {
|
||||||
|
TStringToStdString(map["SYLT"].front()->toString(), song->mutable_lyrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.contains("APIC")) song->set_art_embedded(true);
|
||||||
|
|
||||||
|
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||||
|
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
||||||
|
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||||
|
|
||||||
|
if (frame && TStringToQString(frame->description()) != QLatin1String("iTunNORM")) {
|
||||||
|
TStringToStdString(frame->text(), song->mutable_comment());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TagLib::ID3v2::UserTextIdentificationFrame *frame_fmps_playcount = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "FMPS_Playcount")) {
|
||||||
|
TagLib::StringList frame_field_list = frame_fmps_playcount->fieldList();
|
||||||
|
if (frame_field_list.size() > 1) {
|
||||||
|
int playcount = TStringToQString(frame_field_list[1]).toInt();
|
||||||
|
if (song->playcount() <= 0 && playcount > 0) {
|
||||||
|
song->set_playcount(playcount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TagLib::ID3v2::UserTextIdentificationFrame *frame_fmps_rating = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "FMPS_Rating")) {
|
||||||
|
TagLib::StringList frame_field_list = frame_fmps_rating->fieldList();
|
||||||
|
if (frame_field_list.size() > 1) {
|
||||||
|
float rating = TStringToQString(frame_field_list[1]).toFloat();
|
||||||
|
if (song->rating() <= 0 && rating > 0 && rating <= 1.0) {
|
||||||
|
song->set_rating(rating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.contains("POPM")) {
|
||||||
|
const TagLib::ID3v2::PopularimeterFrame *frame = dynamic_cast<const TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
||||||
|
if (frame) {
|
||||||
|
if (song->playcount() <= 0 && frame->counter() > 0) {
|
||||||
|
song->set_playcount(frame->counter());
|
||||||
|
}
|
||||||
|
if (song->rating() <= 0 && frame->rating() > 0) {
|
||||||
|
song->set_rating(ConvertPOPMRating(frame->rating()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.contains("UFID")) {
|
||||||
|
for (uint i = 0; i < map["UFID"].size(); ++i) {
|
||||||
|
if (TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(map["UFID"][i])) {
|
||||||
|
const TagLib::PropertyMap property_map = frame->asProperties();
|
||||||
|
if (property_map.contains(kID3v2_MusicBrainz_RecordingID)) {
|
||||||
|
TStringToStdString(property_map[kID3v2_MusicBrainz_RecordingID].toString(), song->mutable_musicbrainz_recording_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.contains("TXXX")) {
|
||||||
|
for (uint i = 0; i < map["TXXX"].size(); ++i) {
|
||||||
|
if (TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i])) {
|
||||||
|
const TagLib::StringList frame_field_list = frame->fieldList();
|
||||||
|
if (frame_field_list.size() != 2) continue;
|
||||||
|
if (frame->description() == kID3v2_AcoustID_ID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_acoustid_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_AcoustID_Fingerprint) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_acoustid_fingerprint());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_AlbumArtistID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_album_artist_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_ArtistID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_artist_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_OriginalArtistID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_original_artist_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_AlbumID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_album_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_OriginalAlbumID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_original_album_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_TrackID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_track_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_DiscID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_disc_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_ReleaseGroupID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_release_group_id());
|
||||||
|
}
|
||||||
|
if (frame->description() == kID3v2_MusicBrainz_WorkID) {
|
||||||
|
TStringToStdString(frame_field_list.back(), song->mutable_musicbrainz_work_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
if (map.contains("COMPOSER")) TStringToStdString(map["COMPOSER"].front(), song->mutable_composer());
|
if (map.contains("COMPOSER")) TStringToStdString(map["COMPOSER"].front(), song->mutable_composer());
|
||||||
|
@ -814,7 +834,7 @@ void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment
|
||||||
vorbis_comment->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
vorbis_comment->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||||
vorbis_comment->addField("GROUPING", StdStringToTaglibString(song.grouping()), true);
|
vorbis_comment->addField("GROUPING", StdStringToTaglibString(song.grouping()), true);
|
||||||
vorbis_comment->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
vorbis_comment->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
vorbis_comment->addField("COMPILATION", QStringToTaglibString(song.compilation() ? "1" : QString()), true);
|
vorbis_comment->addField("COMPILATION", QStringToTaglibString(song.compilation() ? QStringLiteral("1") : QString()), true);
|
||||||
|
|
||||||
// Try to be coherent, the two forms are used but the first one is preferred
|
// Try to be coherent, the two forms are used but the first one is preferred
|
||||||
|
|
||||||
|
@ -839,19 +859,19 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||||
|
|
||||||
QStringList save_tags_options;
|
QStringList save_tags_options;
|
||||||
if (save_tags) {
|
if (save_tags) {
|
||||||
save_tags_options << "tags";
|
save_tags_options << QStringLiteral("tags");
|
||||||
}
|
}
|
||||||
if (save_playcount) {
|
if (save_playcount) {
|
||||||
save_tags_options << "playcount";
|
save_tags_options << QStringLiteral("playcount");
|
||||||
}
|
}
|
||||||
if (save_rating) {
|
if (save_rating) {
|
||||||
save_tags_options << "rating";
|
save_tags_options << QStringLiteral("rating");
|
||||||
}
|
}
|
||||||
if (save_cover) {
|
if (save_cover) {
|
||||||
save_tags_options << "embedded cover";
|
save_tags_options << QStringLiteral("embedded cover");
|
||||||
}
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Saving" << save_tags_options.join(", ") << "to" << filename;
|
qLog(Debug) << "Saving" << save_tags_options.join(QLatin1String(", ")) << "to" << filename;
|
||||||
|
|
||||||
const Cover cover = LoadCoverFromRequest(request);
|
const Cover cover = LoadCoverFromRequest(request);
|
||||||
|
|
||||||
|
@ -933,14 +953,7 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||||
if (!tag) return false;
|
if (!tag) return false;
|
||||||
if (save_tags) {
|
if (save_tags) {
|
||||||
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
SaveID3v2Tag(tag, song);
|
||||||
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
|
||||||
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
|
||||||
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
|
||||||
// Skip TPE1 (which is the artist) here because we already set it
|
|
||||||
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
|
||||||
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
|
||||||
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
|
||||||
}
|
}
|
||||||
if (save_playcount) {
|
if (save_playcount) {
|
||||||
SetPlaycount(tag, song);
|
SetPlaycount(tag, song);
|
||||||
|
@ -949,7 +962,7 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||||
SetRating(tag, song);
|
SetRating(tag, song);
|
||||||
}
|
}
|
||||||
if (save_cover) {
|
if (save_cover) {
|
||||||
SetEmbeddedArt(file_mpeg, tag, cover.data, cover.mime_type);
|
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -975,6 +988,22 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (TagLib::RIFF::WAV::File *file_wav = dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) {
|
||||||
|
TagLib::ID3v2::Tag *tag = file_wav->ID3v2Tag();
|
||||||
|
if (save_tags) {
|
||||||
|
SaveID3v2Tag(tag, song);
|
||||||
|
}
|
||||||
|
if (save_playcount) {
|
||||||
|
SetPlaycount(tag, song);
|
||||||
|
}
|
||||||
|
if (save_rating) {
|
||||||
|
SetRating(tag, song);
|
||||||
|
}
|
||||||
|
if (save_cover) {
|
||||||
|
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
||||||
// apart, so we keep specific behavior for some formats by adding another "else if" block above.
|
// apart, so we keep specific behavior for some formats by adding another "else if" block above.
|
||||||
if (!is_flac) {
|
if (!is_flac) {
|
||||||
|
@ -1006,6 +1035,19 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagReaderTagLib::SaveID3v2Tag(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
|
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
||||||
|
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
||||||
|
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
||||||
|
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
||||||
|
// Skip TPE1 (which is the artist) here because we already set it
|
||||||
|
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
||||||
|
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
||||||
|
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void TagReaderTagLib::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
void TagReaderTagLib::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||||
|
@ -1268,9 +1310,7 @@ void TagReaderTagLib::SetEmbeddedArt(TagLib::Ogg::XiphComment *xiph_comment, con
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReaderTagLib::SetEmbeddedArt(TagLib::MPEG::File *file_mp3, TagLib::ID3v2::Tag *tag, const QByteArray &data, const QString &mime_type) const {
|
void TagReaderTagLib::SetEmbeddedArt(TagLib::ID3v2::Tag *tag, const QByteArray &data, const QString &mime_type) const {
|
||||||
|
|
||||||
(void)file_mp3;
|
|
||||||
|
|
||||||
// Remove existing covers
|
// Remove existing covers
|
||||||
TagLib::ID3v2::FrameList apiclist = tag->frameListMap()["APIC"];
|
TagLib::ID3v2::FrameList apiclist = tag->frameListMap()["APIC"];
|
||||||
|
@ -1301,10 +1341,10 @@ void TagReaderTagLib::SetEmbeddedArt(TagLib::MP4::File *aac_file, TagLib::MP4::T
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TagLib::MP4::CoverArt::Format cover_format = TagLib::MP4::CoverArt::Format::JPEG;
|
TagLib::MP4::CoverArt::Format cover_format = TagLib::MP4::CoverArt::Format::JPEG;
|
||||||
if (mime_type == "image/jpeg") {
|
if (mime_type == QLatin1String("image/jpeg")) {
|
||||||
cover_format = TagLib::MP4::CoverArt::Format::JPEG;
|
cover_format = TagLib::MP4::CoverArt::Format::JPEG;
|
||||||
}
|
}
|
||||||
else if (mime_type == "image/png") {
|
else if (mime_type == QLatin1String("image/png")) {
|
||||||
cover_format = TagLib::MP4::CoverArt::Format::PNG;
|
cover_format = TagLib::MP4::CoverArt::Format::PNG;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1350,7 +1390,7 @@ bool TagReaderTagLib::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtReque
|
||||||
else if (TagLib::MPEG::File *file_mp3 = dynamic_cast<TagLib::MPEG::File*>(fileref.file())) {
|
else if (TagLib::MPEG::File *file_mp3 = dynamic_cast<TagLib::MPEG::File*>(fileref.file())) {
|
||||||
TagLib::ID3v2::Tag *tag = file_mp3->ID3v2Tag();
|
TagLib::ID3v2::Tag *tag = file_mp3->ID3v2Tag();
|
||||||
if (!tag) return false;
|
if (!tag) return false;
|
||||||
SetEmbeddedArt(file_mp3, tag, cover.data, cover.mime_type);
|
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MP4/AAC
|
// MP4/AAC
|
||||||
|
@ -1417,7 +1457,7 @@ void TagReaderTagLib::SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::
|
||||||
|
|
||||||
void TagReaderTagLib::SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
void TagReaderTagLib::SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
SetUserTextFrame("FMPS_Playcount", QString::number(song.playcount()), tag);
|
SetUserTextFrame(QStringLiteral("FMPS_Playcount"), QString::number(song.playcount()), tag);
|
||||||
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->setCounter(song.playcount());
|
frame->setCounter(song.playcount());
|
||||||
|
@ -1536,7 +1576,7 @@ void TagReaderTagLib::SetRating(TagLib::APE::Tag *tag, const spb::tagreader::Son
|
||||||
|
|
||||||
void TagReaderTagLib::SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
void TagReaderTagLib::SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
SetUserTextFrame("FMPS_Rating", QString::number(song.rating()), tag);
|
SetUserTextFrame(QStringLiteral("FMPS_Rating"), QString::number(song.rating()), tag);
|
||||||
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->setRating(ConvertToPOPMRating(song.rating()));
|
frame->setRating(ConvertToPOPMRating(song.rating()));
|
||||||
|
|
|
@ -68,10 +68,12 @@ class TagReaderTagLib : public TagReaderBase {
|
||||||
private:
|
private:
|
||||||
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
|
void ParseID3v2Tag(TagLib::ID3v2::Tag *tag, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
|
|
||||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment, const spb::tagreader::SongMetadata &song) const;
|
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment, const spb::tagreader::SongMetadata &song) const;
|
||||||
|
void SaveID3v2Tag(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||||
void SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||||
|
|
||||||
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
|
@ -98,7 +100,7 @@ class TagReaderTagLib : public TagReaderBase {
|
||||||
|
|
||||||
void SetEmbeddedArt(TagLib::FLAC::File *flac_file, TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
void SetEmbeddedArt(TagLib::FLAC::File *flac_file, TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
||||||
void SetEmbeddedArt(TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
void SetEmbeddedArt(TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
||||||
void SetEmbeddedArt(TagLib::MPEG::File *file_mp3, TagLib::ID3v2::Tag *tag, const QByteArray &data, const QString &mime_type) const;
|
void SetEmbeddedArt(TagLib::ID3v2::Tag *tag, const QByteArray &data, const QString &mime_type) const;
|
||||||
void SetEmbeddedArt(TagLib::MP4::File *aac_file, TagLib::MP4::Tag *tag, const QByteArray &data, const QString &mime_type) const;
|
void SetEmbeddedArt(TagLib::MP4::File *aac_file, TagLib::MP4::Tag *tag, const QByteArray &data, const QString &mime_type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -11,5 +11,5 @@ target_include_directories(macdeploycheck PUBLIC
|
||||||
target_link_libraries(macdeploycheck PUBLIC
|
target_link_libraries(macdeploycheck PUBLIC
|
||||||
"-framework AppKit"
|
"-framework AppKit"
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${QtCore_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
)
|
)
|
||||||
|
|
|
@ -105,7 +105,7 @@ int main(int argc, char **argv) {
|
||||||
else if (library.startsWith("@executable_path")) {
|
else if (library.startsWith("@executable_path")) {
|
||||||
QString real_path = library;
|
QString real_path = library;
|
||||||
real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS");
|
real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS");
|
||||||
if (!QFile(real_path).exists()) {
|
if (!QFile::exists(real_path)) {
|
||||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ int main(int argc, char **argv) {
|
||||||
else if (library.startsWith("@rpath")) {
|
else if (library.startsWith("@rpath")) {
|
||||||
QString real_path = library;
|
QString real_path = library;
|
||||||
real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks");
|
real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks");
|
||||||
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
|
if (!QFile::exists(real_path) && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
|
||||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ int main(int argc, char **argv) {
|
||||||
QString loader_path = QFileInfo(filepath).path();
|
QString loader_path = QFileInfo(filepath).path();
|
||||||
QString real_path = library;
|
QString real_path = library;
|
||||||
real_path = real_path.replace("@loader_path", loader_path);
|
real_path = real_path.replace("@loader_path", loader_path);
|
||||||
if (!QFile(real_path).exists()) {
|
if (!QFile::exists(real_path)) {
|
||||||
qLog(Error) << real_path << "does not exist for" << filepath;
|
qLog(Error) << real_path << "does not exist for" << filepath;
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ target_include_directories(strawberry-tagreader PRIVATE
|
||||||
|
|
||||||
target_link_libraries(strawberry-tagreader PRIVATE
|
target_link_libraries(strawberry-tagreader PRIVATE
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${QtCore_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
${QtNetwork_LIBRARIES}
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
libstrawberry-tagreader
|
libstrawberry-tagreader
|
||||||
)
|
)
|
||||||
|
|
|
@ -61,34 +61,34 @@ bool TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb:
|
||||||
reply.mutable_is_media_file_response()->set_success(success);
|
reply.mutable_is_media_file_response()->set_success(success);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else if (message.has_read_file_request()) {
|
if (message.has_read_file_request()) {
|
||||||
const QString filename = QString::fromUtf8(message.read_file_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.read_file_request().filename().size())));
|
const QString filename = QString::fromUtf8(message.read_file_request().filename().data(), static_cast<qint64>(message.read_file_request().filename().size()));
|
||||||
bool success = reader->ReadFile(filename, reply.mutable_read_file_response()->mutable_metadata());
|
bool success = reader->ReadFile(filename, reply.mutable_read_file_response()->mutable_metadata());
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else if (message.has_save_file_request()) {
|
if (message.has_save_file_request()) {
|
||||||
bool success = reader->SaveFile(message.save_file_request());
|
bool success = reader->SaveFile(message.save_file_request());
|
||||||
reply.mutable_save_file_response()->set_success(success);
|
reply.mutable_save_file_response()->set_success(success);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else if (message.has_load_embedded_art_request()) {
|
if (message.has_load_embedded_art_request()) {
|
||||||
const QString filename = QString::fromUtf8(message.load_embedded_art_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.load_embedded_art_request().filename().size())));
|
const QString filename = QString::fromUtf8(message.load_embedded_art_request().filename().data(), static_cast<qint64>(message.load_embedded_art_request().filename().size()));
|
||||||
QByteArray data = reader->LoadEmbeddedArt(filename);
|
QByteArray data = reader->LoadEmbeddedArt(filename);
|
||||||
reply.mutable_load_embedded_art_response()->set_data(data.constData(), data.size());
|
reply.mutable_load_embedded_art_response()->set_data(data.constData(), data.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (message.has_save_embedded_art_request()) {
|
if (message.has_save_embedded_art_request()) {
|
||||||
bool success = reader->SaveEmbeddedArt(message.save_embedded_art_request());
|
bool success = reader->SaveEmbeddedArt(message.save_embedded_art_request());
|
||||||
reply.mutable_save_embedded_art_response()->set_success(success);
|
reply.mutable_save_embedded_art_response()->set_success(success);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else if (message.has_save_song_playcount_to_file_request()) {
|
if (message.has_save_song_playcount_to_file_request()) {
|
||||||
const QString filename = QString::fromUtf8(message.save_song_playcount_to_file_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.save_song_playcount_to_file_request().filename().size())));
|
const QString filename = QString::fromUtf8(message.save_song_playcount_to_file_request().filename().data(), static_cast<qint64>(message.save_song_playcount_to_file_request().filename().size()));
|
||||||
bool success = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().metadata());
|
bool success = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().metadata());
|
||||||
reply.mutable_save_song_playcount_to_file_response()->set_success(success);
|
reply.mutable_save_song_playcount_to_file_response()->set_success(success);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else if (message.has_save_song_rating_to_file_request()) {
|
if (message.has_save_song_rating_to_file_request()) {
|
||||||
const QString filename = QString::fromUtf8(message.save_song_rating_to_file_request().filename().data(), static_cast<qint64>(message.save_song_rating_to_file_request().filename().size()));
|
const QString filename = QString::fromUtf8(message.save_song_rating_to_file_request().filename().data(), static_cast<qint64>(message.save_song_rating_to_file_request().filename().size()));
|
||||||
bool success = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().metadata());
|
bool success = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().metadata());
|
||||||
reply.mutable_save_song_rating_to_file_response()->set_success(success);
|
reply.mutable_save_song_rating_to_file_response()->set_success(success);
|
||||||
|
|
|
@ -24,6 +24,7 @@ set(SOURCES
|
||||||
core/networktimeouts.cpp
|
core/networktimeouts.cpp
|
||||||
core/networkproxyfactory.cpp
|
core/networkproxyfactory.cpp
|
||||||
core/qtfslistener.cpp
|
core/qtfslistener.cpp
|
||||||
|
core/settings.cpp
|
||||||
core/settingsprovider.cpp
|
core/settingsprovider.cpp
|
||||||
core/signalchecker.cpp
|
core/signalchecker.cpp
|
||||||
core/song.cpp
|
core/song.cpp
|
||||||
|
@ -39,7 +40,7 @@ set(SOURCES
|
||||||
core/scopedtransaction.cpp
|
core/scopedtransaction.cpp
|
||||||
core/translations.cpp
|
core/translations.cpp
|
||||||
core/systemtrayicon.cpp
|
core/systemtrayicon.cpp
|
||||||
|
core/localredirectserver.cpp
|
||||||
utilities/strutils.cpp
|
utilities/strutils.cpp
|
||||||
utilities/envutils.cpp
|
utilities/envutils.cpp
|
||||||
utilities/colorutils.cpp
|
utilities/colorutils.cpp
|
||||||
|
@ -57,6 +58,8 @@ set(SOURCES
|
||||||
utilities/filemanagerutils.cpp
|
utilities/filemanagerutils.cpp
|
||||||
utilities/coverutils.cpp
|
utilities/coverutils.cpp
|
||||||
utilities/screenutils.cpp
|
utilities/screenutils.cpp
|
||||||
|
utilities/searchparserutils.cpp
|
||||||
|
utilities/textencodingutils.cpp
|
||||||
|
|
||||||
engine/enginebase.cpp
|
engine/enginebase.cpp
|
||||||
engine/enginedevice.cpp
|
engine/enginedevice.cpp
|
||||||
|
@ -71,6 +74,7 @@ set(SOURCES
|
||||||
analyzer/boomanalyzer.cpp
|
analyzer/boomanalyzer.cpp
|
||||||
analyzer/rainbowanalyzer.cpp
|
analyzer/rainbowanalyzer.cpp
|
||||||
analyzer/sonogram.cpp
|
analyzer/sonogram.cpp
|
||||||
|
analyzer/waverubber.cpp
|
||||||
|
|
||||||
equalizer/equalizer.cpp
|
equalizer/equalizer.cpp
|
||||||
equalizer/equalizerslider.cpp
|
equalizer/equalizerslider.cpp
|
||||||
|
@ -88,12 +92,14 @@ set(SOURCES
|
||||||
collection/collectiondirectorymodel.cpp
|
collection/collectiondirectorymodel.cpp
|
||||||
collection/collectionfilteroptions.cpp
|
collection/collectionfilteroptions.cpp
|
||||||
collection/collectionfilterwidget.cpp
|
collection/collectionfilterwidget.cpp
|
||||||
|
collection/collectionfilter.cpp
|
||||||
collection/collectionplaylistitem.cpp
|
collection/collectionplaylistitem.cpp
|
||||||
collection/collectionquery.cpp
|
collection/collectionquery.cpp
|
||||||
collection/collectionqueryoptions.cpp
|
collection/collectionqueryoptions.cpp
|
||||||
collection/savedgroupingmanager.cpp
|
collection/savedgroupingmanager.cpp
|
||||||
collection/groupbydialog.cpp
|
collection/groupbydialog.cpp
|
||||||
collection/collectiontask.cpp
|
collection/collectiontask.cpp
|
||||||
|
collection/collectionmodelupdate.cpp
|
||||||
|
|
||||||
playlist/playlist.cpp
|
playlist/playlist.cpp
|
||||||
playlist/playlistbackend.cpp
|
playlist/playlistbackend.cpp
|
||||||
|
@ -112,6 +118,7 @@ set(SOURCES
|
||||||
playlist/playlisttabbar.cpp
|
playlist/playlisttabbar.cpp
|
||||||
playlist/playlistundocommands.cpp
|
playlist/playlistundocommands.cpp
|
||||||
playlist/playlistview.cpp
|
playlist/playlistview.cpp
|
||||||
|
playlist/playlistproxystyle.cpp
|
||||||
playlist/songloaderinserter.cpp
|
playlist/songloaderinserter.cpp
|
||||||
playlist/songplaylistitem.cpp
|
playlist/songplaylistitem.cpp
|
||||||
playlist/dynamicplaylistcontrols.cpp
|
playlist/dynamicplaylistcontrols.cpp
|
||||||
|
@ -168,7 +175,7 @@ set(SOURCES
|
||||||
covermanager/deezercoverprovider.cpp
|
covermanager/deezercoverprovider.cpp
|
||||||
covermanager/qobuzcoverprovider.cpp
|
covermanager/qobuzcoverprovider.cpp
|
||||||
covermanager/musixmatchcoverprovider.cpp
|
covermanager/musixmatchcoverprovider.cpp
|
||||||
covermanager/spotifycoverprovider.cpp
|
covermanager/opentidalcoverprovider.cpp
|
||||||
|
|
||||||
lyrics/lyricsproviders.cpp
|
lyrics/lyricsproviders.cpp
|
||||||
lyrics/lyricsprovider.cpp
|
lyrics/lyricsprovider.cpp
|
||||||
|
@ -177,12 +184,16 @@ set(SOURCES
|
||||||
lyrics/lyricsfetcher.cpp
|
lyrics/lyricsfetcher.cpp
|
||||||
lyrics/lyricsfetchersearch.cpp
|
lyrics/lyricsfetchersearch.cpp
|
||||||
lyrics/jsonlyricsprovider.cpp
|
lyrics/jsonlyricsprovider.cpp
|
||||||
|
lyrics/htmllyricsprovider.cpp
|
||||||
lyrics/ovhlyricsprovider.cpp
|
lyrics/ovhlyricsprovider.cpp
|
||||||
lyrics/lololyricsprovider.cpp
|
lyrics/lololyricsprovider.cpp
|
||||||
lyrics/geniuslyricsprovider.cpp
|
lyrics/geniuslyricsprovider.cpp
|
||||||
lyrics/musixmatchlyricsprovider.cpp
|
lyrics/musixmatchlyricsprovider.cpp
|
||||||
lyrics/chartlyricsprovider.cpp
|
lyrics/chartlyricsprovider.cpp
|
||||||
lyrics/lyricscomlyricsprovider.cpp
|
lyrics/songlyricscomlyricsprovider.cpp
|
||||||
|
lyrics/azlyricscomlyricsprovider.cpp
|
||||||
|
lyrics/elyricsnetlyricsprovider.cpp
|
||||||
|
lyrics/letraslyricsprovider.cpp
|
||||||
|
|
||||||
providers/musixmatchprovider.cpp
|
providers/musixmatchprovider.cpp
|
||||||
|
|
||||||
|
@ -190,6 +201,7 @@ set(SOURCES
|
||||||
settings/settingspage.cpp
|
settings/settingspage.cpp
|
||||||
settings/behavioursettingspage.cpp
|
settings/behavioursettingspage.cpp
|
||||||
settings/collectionsettingspage.cpp
|
settings/collectionsettingspage.cpp
|
||||||
|
settings/collectionsettingsdirectorymodel.cpp
|
||||||
settings/backendsettingspage.cpp
|
settings/backendsettingspage.cpp
|
||||||
settings/playlistsettingspage.cpp
|
settings/playlistsettingspage.cpp
|
||||||
settings/scrobblersettingspage.cpp
|
settings/scrobblersettingspage.cpp
|
||||||
|
@ -243,19 +255,18 @@ set(SOURCES
|
||||||
osd/osdbase.cpp
|
osd/osdbase.cpp
|
||||||
osd/osdpretty.cpp
|
osd/osdpretty.cpp
|
||||||
|
|
||||||
internet/internetservices.cpp
|
streaming/streamingservices.cpp
|
||||||
internet/internetservice.cpp
|
streaming/streamingservice.cpp
|
||||||
internet/internetplaylistitem.cpp
|
streaming/streamplaylistitem.cpp
|
||||||
internet/internetsearchview.cpp
|
streaming/streamingsearchview.cpp
|
||||||
internet/internetsearchmodel.cpp
|
streaming/streamingsearchmodel.cpp
|
||||||
internet/internetsearchsortmodel.cpp
|
streaming/streamingsearchsortmodel.cpp
|
||||||
internet/internetsearchitemdelegate.cpp
|
streaming/streamingsearchitemdelegate.cpp
|
||||||
internet/localredirectserver.cpp
|
streaming/streamingsongsview.cpp
|
||||||
internet/internetsongsview.cpp
|
streaming/streamingtabsview.cpp
|
||||||
internet/internettabsview.cpp
|
streaming/streamingcollectionview.cpp
|
||||||
internet/internetcollectionview.cpp
|
streaming/streamingcollectionviewcontainer.cpp
|
||||||
internet/internetcollectionviewcontainer.cpp
|
streaming/streamingsearchview.cpp
|
||||||
internet/internetsearchview.cpp
|
|
||||||
|
|
||||||
radios/radioservices.cpp
|
radios/radioservices.cpp
|
||||||
radios/radiobackend.cpp
|
radios/radiobackend.cpp
|
||||||
|
@ -269,6 +280,7 @@ set(SOURCES
|
||||||
radios/radioparadiseservice.cpp
|
radios/radioparadiseservice.cpp
|
||||||
|
|
||||||
scrobbler/audioscrobbler.cpp
|
scrobbler/audioscrobbler.cpp
|
||||||
|
scrobbler/scrobblersettings.cpp
|
||||||
scrobbler/scrobblerservice.cpp
|
scrobbler/scrobblerservice.cpp
|
||||||
scrobbler/scrobblercache.cpp
|
scrobbler/scrobblercache.cpp
|
||||||
scrobbler/scrobblercacheitem.cpp
|
scrobbler/scrobblercacheitem.cpp
|
||||||
|
@ -299,6 +311,7 @@ set(HEADERS
|
||||||
core/threadsafenetworkdiskcache.h
|
core/threadsafenetworkdiskcache.h
|
||||||
core/networktimeouts.h
|
core/networktimeouts.h
|
||||||
core/qtfslistener.h
|
core/qtfslistener.h
|
||||||
|
core/settings.h
|
||||||
core/songloader.h
|
core/songloader.h
|
||||||
core/tagreaderclient.h
|
core/tagreaderclient.h
|
||||||
core/taskmanager.h
|
core/taskmanager.h
|
||||||
|
@ -309,6 +322,7 @@ set(HEADERS
|
||||||
core/potranslator.h
|
core/potranslator.h
|
||||||
core/mimedata.h
|
core/mimedata.h
|
||||||
core/stylesheetloader.h
|
core/stylesheetloader.h
|
||||||
|
core/localredirectserver.h
|
||||||
|
|
||||||
engine/enginebase.h
|
engine/enginebase.h
|
||||||
engine/devicefinders.h
|
engine/devicefinders.h
|
||||||
|
@ -319,6 +333,7 @@ set(HEADERS
|
||||||
analyzer/boomanalyzer.h
|
analyzer/boomanalyzer.h
|
||||||
analyzer/rainbowanalyzer.h
|
analyzer/rainbowanalyzer.h
|
||||||
analyzer/sonogram.h
|
analyzer/sonogram.h
|
||||||
|
analyzer/waverubber.h
|
||||||
|
|
||||||
equalizer/equalizer.h
|
equalizer/equalizer.h
|
||||||
equalizer/equalizerslider.h
|
equalizer/equalizerslider.h
|
||||||
|
@ -335,6 +350,7 @@ set(HEADERS
|
||||||
collection/collectionviewcontainer.h
|
collection/collectionviewcontainer.h
|
||||||
collection/collectiondirectorymodel.h
|
collection/collectiondirectorymodel.h
|
||||||
collection/collectionfilterwidget.h
|
collection/collectionfilterwidget.h
|
||||||
|
collection/collectionfilter.h
|
||||||
collection/savedgroupingmanager.h
|
collection/savedgroupingmanager.h
|
||||||
collection/groupbydialog.h
|
collection/groupbydialog.h
|
||||||
|
|
||||||
|
@ -353,6 +369,7 @@ set(HEADERS
|
||||||
playlist/playlistsequence.h
|
playlist/playlistsequence.h
|
||||||
playlist/playlisttabbar.h
|
playlist/playlisttabbar.h
|
||||||
playlist/playlistview.h
|
playlist/playlistview.h
|
||||||
|
playlist/playlistproxystyle.h
|
||||||
playlist/playlistitemmimedata.h
|
playlist/playlistitemmimedata.h
|
||||||
playlist/songloaderinserter.h
|
playlist/songloaderinserter.h
|
||||||
playlist/songmimedata.h
|
playlist/songmimedata.h
|
||||||
|
@ -407,24 +424,29 @@ set(HEADERS
|
||||||
covermanager/deezercoverprovider.h
|
covermanager/deezercoverprovider.h
|
||||||
covermanager/qobuzcoverprovider.h
|
covermanager/qobuzcoverprovider.h
|
||||||
covermanager/musixmatchcoverprovider.h
|
covermanager/musixmatchcoverprovider.h
|
||||||
covermanager/spotifycoverprovider.h
|
covermanager/opentidalcoverprovider.h
|
||||||
|
|
||||||
lyrics/lyricsproviders.h
|
lyrics/lyricsproviders.h
|
||||||
lyrics/lyricsprovider.h
|
lyrics/lyricsprovider.h
|
||||||
lyrics/lyricsfetcher.h
|
lyrics/lyricsfetcher.h
|
||||||
lyrics/lyricsfetchersearch.h
|
lyrics/lyricsfetchersearch.h
|
||||||
lyrics/jsonlyricsprovider.h
|
lyrics/jsonlyricsprovider.h
|
||||||
|
lyrics/htmllyricsprovider.h
|
||||||
lyrics/ovhlyricsprovider.h
|
lyrics/ovhlyricsprovider.h
|
||||||
lyrics/lololyricsprovider.h
|
lyrics/lololyricsprovider.h
|
||||||
lyrics/geniuslyricsprovider.h
|
lyrics/geniuslyricsprovider.h
|
||||||
lyrics/musixmatchlyricsprovider.h
|
lyrics/musixmatchlyricsprovider.h
|
||||||
lyrics/chartlyricsprovider.h
|
lyrics/chartlyricsprovider.h
|
||||||
lyrics/lyricscomlyricsprovider.h
|
lyrics/songlyricscomlyricsprovider.h
|
||||||
|
lyrics/azlyricscomlyricsprovider.h
|
||||||
|
lyrics/elyricsnetlyricsprovider.h
|
||||||
|
lyrics/letraslyricsprovider.h
|
||||||
|
|
||||||
settings/settingsdialog.h
|
settings/settingsdialog.h
|
||||||
settings/settingspage.h
|
settings/settingspage.h
|
||||||
settings/behavioursettingspage.h
|
settings/behavioursettingspage.h
|
||||||
settings/collectionsettingspage.h
|
settings/collectionsettingspage.h
|
||||||
|
settings/collectionsettingsdirectorymodel.h
|
||||||
settings/backendsettingspage.h
|
settings/backendsettingspage.h
|
||||||
settings/playlistsettingspage.h
|
settings/playlistsettingspage.h
|
||||||
settings/scrobblersettingspage.h
|
settings/scrobblersettingspage.h
|
||||||
|
@ -474,22 +496,22 @@ set(HEADERS
|
||||||
widgets/qsearchfield.h
|
widgets/qsearchfield.h
|
||||||
widgets/ratingwidget.h
|
widgets/ratingwidget.h
|
||||||
widgets/forcescrollperpixel.h
|
widgets/forcescrollperpixel.h
|
||||||
|
widgets/resizabletextedit.h
|
||||||
|
|
||||||
osd/osdbase.h
|
osd/osdbase.h
|
||||||
osd/osdpretty.h
|
osd/osdpretty.h
|
||||||
|
|
||||||
internet/internetservices.h
|
streaming/streamingservices.h
|
||||||
internet/internetservice.h
|
streaming/streamingservice.h
|
||||||
internet/internetsongmimedata.h
|
streaming/streamsongmimedata.h
|
||||||
internet/internetsearchmodel.h
|
streaming/streamingsearchmodel.h
|
||||||
internet/internetsearchsortmodel.h
|
streaming/streamingsearchsortmodel.h
|
||||||
internet/internetsearchitemdelegate.h
|
streaming/streamingsearchitemdelegate.h
|
||||||
internet/internetsearchview.h
|
streaming/streamingsearchview.h
|
||||||
internet/localredirectserver.h
|
streaming/streamingsongsview.h
|
||||||
internet/internetsongsview.h
|
streaming/streamingtabsview.h
|
||||||
internet/internettabsview.h
|
streaming/streamingcollectionview.h
|
||||||
internet/internetcollectionview.h
|
streaming/streamingcollectionviewcontainer.h
|
||||||
internet/internetcollectionviewcontainer.h
|
|
||||||
|
|
||||||
radios/radioservices.h
|
radios/radioservices.h
|
||||||
radios/radiobackend.h
|
radios/radiobackend.h
|
||||||
|
@ -502,6 +524,7 @@ set(HEADERS
|
||||||
radios/radioparadiseservice.h
|
radios/radioparadiseservice.h
|
||||||
|
|
||||||
scrobbler/audioscrobbler.h
|
scrobbler/audioscrobbler.h
|
||||||
|
scrobbler/scrobblersettings.h
|
||||||
scrobbler/scrobblerservice.h
|
scrobbler/scrobblerservice.h
|
||||||
scrobbler/scrobblercache.h
|
scrobbler/scrobblercache.h
|
||||||
scrobbler/scrobblingapi20.h
|
scrobbler/scrobblingapi20.h
|
||||||
|
@ -579,9 +602,9 @@ set(UI
|
||||||
|
|
||||||
osd/osdpretty.ui
|
osd/osdpretty.ui
|
||||||
|
|
||||||
internet/internettabsview.ui
|
streaming/streamingtabsview.ui
|
||||||
internet/internetcollectionviewcontainer.ui
|
streaming/streamingcollectionviewcontainer.ui
|
||||||
internet/internetsearchview.ui
|
streaming/streamingsearchview.ui
|
||||||
|
|
||||||
radios/radioviewcontainer.ui
|
radios/radioviewcontainer.ui
|
||||||
|
|
||||||
|
@ -591,7 +614,6 @@ set(UI
|
||||||
)
|
)
|
||||||
|
|
||||||
set(RESOURCES ../data/data.qrc ../data/icons.qrc)
|
set(RESOURCES ../data/data.qrc ../data/icons.qrc)
|
||||||
set(OTHER_SOURCES)
|
|
||||||
|
|
||||||
option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
|
option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
|
||||||
|
|
||||||
|
@ -838,7 +860,7 @@ optional_source(WIN32
|
||||||
HEADERS
|
HEADERS
|
||||||
core/windows7thumbbar.h
|
core/windows7thumbbar.h
|
||||||
)
|
)
|
||||||
optional_source(MSVC SOURCES engine/uwpdevicefinder.cpp)
|
optional_source(MSVC SOURCES engine/uwpdevicefinder.cpp engine/asiodevicefinder.cpp)
|
||||||
|
|
||||||
optional_source(HAVE_SUBSONIC
|
optional_source(HAVE_SUBSONIC
|
||||||
SOURCES
|
SOURCES
|
||||||
|
@ -884,6 +906,25 @@ optional_source(HAVE_TIDAL
|
||||||
settings/tidalsettingspage.ui
|
settings/tidalsettingspage.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
optional_source(HAVE_SPOTIFY
|
||||||
|
SOURCES
|
||||||
|
spotify/spotifyservice.cpp
|
||||||
|
spotify/spotifybaserequest.cpp
|
||||||
|
spotify/spotifyrequest.cpp
|
||||||
|
spotify/spotifyfavoriterequest.cpp
|
||||||
|
settings/spotifysettingspage.cpp
|
||||||
|
covermanager/spotifycoverprovider.cpp
|
||||||
|
HEADERS
|
||||||
|
spotify/spotifyservice.h
|
||||||
|
spotify/spotifybaserequest.h
|
||||||
|
spotify/spotifyrequest.h
|
||||||
|
spotify/spotifyfavoriterequest.h
|
||||||
|
settings/spotifysettingspage.h
|
||||||
|
covermanager/spotifycoverprovider.h
|
||||||
|
UI
|
||||||
|
settings/spotifysettingspage.ui
|
||||||
|
)
|
||||||
|
|
||||||
optional_source(HAVE_QOBUZ
|
optional_source(HAVE_QOBUZ
|
||||||
SOURCES
|
SOURCES
|
||||||
qobuz/qobuzservice.cpp
|
qobuz/qobuzservice.cpp
|
||||||
|
@ -927,6 +968,11 @@ optional_source(HAVE_MOODBAR
|
||||||
settings/moodbarsettingspage.ui
|
settings/moodbarsettingspage.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# EBU R 128
|
||||||
|
optional_source(HAVE_EBUR128
|
||||||
|
SOURCES engine/ebur128analysis.cpp
|
||||||
|
)
|
||||||
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
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)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||||
|
|
||||||
|
@ -958,8 +1004,7 @@ if(HAVE_TRANSLATIONS)
|
||||||
${SOURCES}
|
${SOURCES}
|
||||||
${MOC}
|
${MOC}
|
||||||
${UIC}
|
${UIC}
|
||||||
${OTHER_SOURCES}
|
${CMAKE_SOURCE_DIR}/data/html/oauthsuccess.html
|
||||||
../data/html/oauthsuccess.html
|
|
||||||
)
|
)
|
||||||
add_po(PO strawberry_ LANGUAGES ${LANGUAGES} DIRECTORY translations)
|
add_po(PO strawberry_ LANGUAGES ${LANGUAGES} DIRECTORY translations)
|
||||||
|
|
||||||
|
@ -969,18 +1014,12 @@ link_directories(
|
||||||
${Boost_LIBRARY_DIRS}
|
${Boost_LIBRARY_DIRS}
|
||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${GOBJECT_LIBRARY_DIRS}
|
${GOBJECT_LIBRARY_DIRS}
|
||||||
${GNUTLS_LIBRARY_DIRS}
|
|
||||||
${SQLITE_LIBRARY_DIRS}
|
${SQLITE_LIBRARY_DIRS}
|
||||||
${PROTOBUF_LIBRARY_DIRS}
|
${PROTOBUF_LIBRARY_DIRS}
|
||||||
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
${SINGLEAPPLICATION_LIBRARY_DIRS}
|
||||||
|
${ICU_LIBRARY_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(HAVE_ICU)
|
|
||||||
link_directories(${ICU_LIBRARY_DIRS})
|
|
||||||
else()
|
|
||||||
link_directories(${Iconv_LIBRARY_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_ALSA)
|
if(HAVE_ALSA)
|
||||||
link_directories(${ALSA_LIBRARY_DIRS})
|
link_directories(${ALSA_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
@ -1062,9 +1101,9 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
|
||||||
${Boost_INCLUDE_DIRS}
|
${Boost_INCLUDE_DIRS}
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${GOBJECT_INCLUDE_DIRS}
|
${GOBJECT_INCLUDE_DIRS}
|
||||||
${GNUTLS_INCLUDE_DIRS}
|
|
||||||
${SQLITE_INCLUDE_DIRS}
|
${SQLITE_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
|
${ICU_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
|
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
|
||||||
|
@ -1086,24 +1125,26 @@ target_link_libraries(strawberry_lib PUBLIC
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${GOBJECT_LIBRARIES}
|
${GOBJECT_LIBRARIES}
|
||||||
${GNUTLS_LIBRARIES}
|
|
||||||
${SQLITE_LIBRARIES}
|
${SQLITE_LIBRARIES}
|
||||||
${QT_LIBRARIES}
|
${ICU_LIBRARIES}
|
||||||
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
|
Qt${QT_VERSION_MAJOR}::Sql
|
||||||
${Protobuf_LIBRARIES}
|
${Protobuf_LIBRARIES}
|
||||||
${SINGLEAPPLICATION_LIBRARIES}
|
${SINGLEAPPLICATION_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
libstrawberry-tagreader
|
libstrawberry-tagreader
|
||||||
)
|
)
|
||||||
|
|
||||||
if(HAVE_ICU)
|
if(HAVE_DBUS)
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ICU_INCLUDE_DIRS})
|
target_link_libraries(strawberry_lib PUBLIC Qt${QT_VERSION_MAJOR}::DBus)
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${ICU_LIBRARIES})
|
endif()
|
||||||
else()
|
|
||||||
if(FREEBSD AND NOT Iconv_LIBRARIES)
|
if(HAVE_X11_GLOBALSHORTCUTS AND HAVE_X11EXTRAS)
|
||||||
set(Iconv_LIBRARIES iconv)
|
target_link_libraries(strawberry_lib PUBLIC Qt${QT_VERSION_MAJOR}::X11Extras)
|
||||||
endif()
|
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${Iconv_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${Iconv_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAVE_ALSA)
|
if(HAVE_ALSA)
|
||||||
|
@ -1149,6 +1190,10 @@ if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES})
|
target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(HAVE_EBUR128)
|
||||||
|
target_link_libraries(strawberry_lib PRIVATE PkgConfig::LIBEBUR128)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(X11_FOUND)
|
if(X11_FOUND)
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR})
|
target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR})
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES})
|
target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES})
|
||||||
|
|
|
@ -24,9 +24,9 @@
|
||||||
|
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
@ -88,7 +88,7 @@ void AnalyzerBase::ChangeTimeout(const int timeout) {
|
||||||
void AnalyzerBase::transform(Scope &scope) {
|
void AnalyzerBase::transform(Scope &scope) {
|
||||||
|
|
||||||
QVector<float> aux(fht_->size());
|
QVector<float> aux(fht_->size());
|
||||||
if (static_cast<unsigned long int>(aux.size()) >= scope.size()) {
|
if (static_cast<quint64>(aux.size()) >= scope.size()) {
|
||||||
std::copy(scope.begin(), scope.end(), aux.begin());
|
std::copy(scope.begin(), scope.end(), aux.begin());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -108,7 +108,7 @@ void AnalyzerBase::paintEvent(QPaintEvent *e) {
|
||||||
p.fillRect(e->rect(), palette().color(QPalette::Window));
|
p.fillRect(e->rect(), palette().color(QPalette::Window));
|
||||||
|
|
||||||
switch (engine_->state()) {
|
switch (engine_->state()) {
|
||||||
case EngineBase::State::Playing: {
|
case EngineBase::State::Playing:{
|
||||||
const EngineBase::Scope &thescope = engine_->scope(timeout_);
|
const EngineBase::Scope &thescope = engine_->scope(timeout_);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "analyzer/fht.h"
|
#include "analyzer/fht.h"
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ class AnalyzerBase : public QWidget {
|
||||||
|
|
||||||
int timeout() const { return timeout_; }
|
int timeout() const { return timeout_; }
|
||||||
|
|
||||||
void set_engine(EngineBase *engine) { engine_ = engine; }
|
void set_engine(SharedPtr<EngineBase> engine) { engine_ = engine; }
|
||||||
|
|
||||||
void ChangeTimeout(const int timeout);
|
void ChangeTimeout(const int timeout);
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ class AnalyzerBase : public QWidget {
|
||||||
protected:
|
protected:
|
||||||
QBasicTimer timer_;
|
QBasicTimer timer_;
|
||||||
FHT *fht_;
|
FHT *fht_;
|
||||||
EngineBase *engine_;
|
SharedPtr<EngineBase> engine_;
|
||||||
Scope lastscope_;
|
Scope lastscope_;
|
||||||
|
|
||||||
bool new_frame_;
|
bool new_frame_;
|
||||||
|
|
|
@ -41,8 +41,11 @@
|
||||||
#include "boomanalyzer.h"
|
#include "boomanalyzer.h"
|
||||||
#include "rainbowanalyzer.h"
|
#include "rainbowanalyzer.h"
|
||||||
#include "sonogram.h"
|
#include "sonogram.h"
|
||||||
|
#include "waverubber.h"
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
@ -86,6 +89,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
AddAnalyzerType<NyanCatAnalyzer>();
|
AddAnalyzerType<NyanCatAnalyzer>();
|
||||||
AddAnalyzerType<RainbowDashAnalyzer>();
|
AddAnalyzerType<RainbowDashAnalyzer>();
|
||||||
AddAnalyzerType<Sonogram>();
|
AddAnalyzerType<Sonogram>();
|
||||||
|
AddAnalyzerType<WaveRubber>();
|
||||||
|
|
||||||
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, &AnalyzerContainer::DisableAnalyzer);
|
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, &AnalyzerContainer::DisableAnalyzer);
|
||||||
disable_action_->setCheckable(true);
|
disable_action_->setCheckable(true);
|
||||||
|
@ -125,7 +129,7 @@ void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
|
||||||
emit WheelEvent(e->angleDelta().y());
|
emit WheelEvent(e->angleDelta().y());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::SetEngine(EngineBase *engine) {
|
void AnalyzerContainer::SetEngine(SharedPtr<EngineBase> engine) {
|
||||||
|
|
||||||
if (current_analyzer_) current_analyzer_->set_engine(engine);
|
if (current_analyzer_) current_analyzer_->set_engine(engine);
|
||||||
engine_ = engine;
|
engine_ = engine;
|
||||||
|
@ -177,9 +181,9 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
||||||
|
|
||||||
void AnalyzerContainer::Load() {
|
void AnalyzerContainer::Load() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
QString type = s.value("type", "BlockAnalyzer").toString();
|
QString type = s.value("type", QStringLiteral("BlockAnalyzer")).toString();
|
||||||
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
|
@ -190,7 +194,7 @@ void AnalyzerContainer::Load() {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < analyzer_types_.count(); ++i) {
|
for (int i = 0; i < analyzer_types_.count(); ++i) {
|
||||||
if (type == analyzer_types_[i]->className()) {
|
if (type == QString::fromLatin1(analyzer_types_[i]->className())) {
|
||||||
ChangeAnalyzer(i);
|
ChangeAnalyzer(i);
|
||||||
actions_[i]->setChecked(true);
|
actions_[i]->setChecked(true);
|
||||||
break;
|
break;
|
||||||
|
@ -214,7 +218,7 @@ void AnalyzerContainer::SaveFramerate(const int framerate) {
|
||||||
|
|
||||||
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
||||||
current_framerate_ = framerate;
|
current_framerate_ = framerate;
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
s.setValue(kSettingsFramerate, current_framerate_);
|
s.setValue(kSettingsFramerate, current_framerate_);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
@ -223,9 +227,9 @@ void AnalyzerContainer::SaveFramerate(const int framerate) {
|
||||||
|
|
||||||
void AnalyzerContainer::Save() {
|
void AnalyzerContainer::Save() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
|
s.setValue("type", current_analyzer_ ? QString::fromLatin1(current_analyzer_->metaObject()->className()) : QVariant());
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
@ -44,7 +45,7 @@ class AnalyzerContainer : public QWidget {
|
||||||
public:
|
public:
|
||||||
explicit AnalyzerContainer(QWidget *parent);
|
explicit AnalyzerContainer(QWidget *parent);
|
||||||
|
|
||||||
void SetEngine(EngineBase *engine);
|
void SetEngine(SharedPtr<EngineBase> engine);
|
||||||
void SetActions(QAction *visualisation);
|
void SetActions(QAction *visualisation);
|
||||||
|
|
||||||
static const char *kSettingsGroup;
|
static const char *kSettingsGroup;
|
||||||
|
@ -93,7 +94,7 @@ class AnalyzerContainer : public QWidget {
|
||||||
bool ignore_next_click_;
|
bool ignore_next_click_;
|
||||||
|
|
||||||
AnalyzerBase *current_analyzer_;
|
AnalyzerBase *current_analyzer_;
|
||||||
EngineBase *engine_;
|
SharedPtr<EngineBase> engine_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -165,7 +165,7 @@ void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
||||||
|
|
||||||
for (int x = 0, y = 0; x < static_cast<int>(scope_.size()); ++x) {
|
for (int x = 0, y = 0; x < static_cast<int>(scope_.size()); ++x) {
|
||||||
// determine y
|
// determine y
|
||||||
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
for (y = 0; scope_[x] < yscale_[y]; ++y);
|
||||||
|
|
||||||
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||||
if (static_cast<double>(y) > store_[x]) {
|
if (static_cast<double>(y) > store_[x]) {
|
||||||
|
@ -266,12 +266,12 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||||
|
|
||||||
// value is the best measure of contrast
|
// value is the best measure of contrast
|
||||||
// if there is enough difference in value already, return fg unchanged
|
// if there is enough difference in value already, return fg unchanged
|
||||||
if (dv > static_cast<int>(amount)) return fg;
|
if (dv > amount) return fg;
|
||||||
|
|
||||||
int ds = abs(bs - fs);
|
int ds = abs(bs - fs);
|
||||||
|
|
||||||
// saturation is good enough too. But not as good. TODO adapt this a little
|
// saturation is good enough too. But not as good. TODO adapt this a little
|
||||||
if (ds > static_cast<int>(amount)) return fg;
|
if (ds > amount) return fg;
|
||||||
|
|
||||||
int dh = abs(bh - fh);
|
int dh = abs(bh - fh);
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||||
if (ds > amount / 2 && (bs > 125 && fs > 125)) {
|
if (ds > amount / 2 && (bs > 125 && fs > 125)) {
|
||||||
return fg;
|
return fg;
|
||||||
}
|
}
|
||||||
else if (dv > amount / 2 && (bv > 125 && fv > 125)) {
|
if (dv > amount / 2 && (bv > 125 && fv > 125)) {
|
||||||
return fg;
|
return fg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||||
// low saturation on a low saturation is sad
|
// low saturation on a low saturation is sad
|
||||||
const int tmp = 50 - fs;
|
const int tmp = 50 - fs;
|
||||||
fs = 50;
|
fs = 50;
|
||||||
if (static_cast<int>(amount) > tmp) {
|
if (amount > tmp) {
|
||||||
amount -= tmp;
|
amount -= tmp;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -310,25 +310,25 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
|
||||||
if (amount > 0) adjustToLimits(bs, fs, amount);
|
if (amount > 0) adjustToLimits(bs, fs, amount);
|
||||||
|
|
||||||
// see if we need to adjust the hue
|
// see if we need to adjust the hue
|
||||||
if (static_cast<int>(amount) > 0)
|
if (amount > 0)
|
||||||
fh += static_cast<int>(amount); // cycles around;
|
fh += amount; // cycles around;
|
||||||
|
|
||||||
return QColor::fromHsv(fh, fs, fv);
|
return QColor::fromHsv(fh, fs, fv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fv > bv && bv > static_cast<int>(amount)) {
|
if (fv > bv && bv > amount) {
|
||||||
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
|
return QColor::fromHsv(fh, fs, bv - amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fv < bv && fv > static_cast<int>(amount)) {
|
if (fv < bv && fv > amount) {
|
||||||
return QColor::fromHsv(fh, fs, fv - amount);
|
return QColor::fromHsv(fh, fs, fv - amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fv > bv && (255 - fv > static_cast<int>(amount))) {
|
if (fv > bv && (255 - fv > amount)) {
|
||||||
return QColor::fromHsv(fh, fs, fv + amount);
|
return QColor::fromHsv(fh, fs, fv + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fv < bv && (255 - bv > static_cast<int>(amount))) {
|
if (fv < bv && (255 - bv > amount)) {
|
||||||
return QColor::fromHsv(fh, fs, bv + amount);
|
return QColor::fromHsv(fh, fs, bv + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
|
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
|
||||||
scope_.resize(bands_);
|
scope_.resize(bands_);
|
||||||
|
|
||||||
F_ = static_cast<double>(HEIGHT) / (log10(256) * static_cast<double>(1.1) /*<- max. amplitude*/);
|
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
|
||||||
|
|
||||||
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
||||||
canvas_ = QPixmap(size());
|
canvas_ = QPixmap(size());
|
||||||
|
@ -111,7 +111,7 @@ void BoomAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double h = 0.0;
|
|
||||||
const uint MAX_HEIGHT = height() - 1;
|
const uint MAX_HEIGHT = height() - 1;
|
||||||
|
|
||||||
QPainter canvas_painter(&canvas_);
|
QPainter canvas_painter(&canvas_);
|
||||||
|
@ -120,7 +120,7 @@ void BoomAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame
|
||||||
interpolate(scope, scope_);
|
interpolate(scope, scope_);
|
||||||
|
|
||||||
for (int i = 0, x = 0, y = 0; i < bands_; ++i, x += kColumnWidth + 1) {
|
for (int i = 0, x = 0, y = 0; i < bands_; ++i, x += kColumnWidth + 1) {
|
||||||
h = log10(scope_[i] * 256.0) * F_;
|
double h = log10(scope_[i] * 256.0) * F_;
|
||||||
|
|
||||||
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
FHT::FHT(uint n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? static_cast<int>(-1) : static_cast<int>(n)) {
|
FHT::FHT(uint n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : static_cast<int>(n)) {
|
||||||
|
|
||||||
if (n > 3) {
|
if (n > 3) {
|
||||||
buf_vector_.resize(num_);
|
buf_vector_.resize(num_);
|
||||||
|
@ -47,7 +47,7 @@ float *FHT::buf_() { return buf_vector_.data(); }
|
||||||
float *FHT::tab_() { return tab_vector_.data(); }
|
float *FHT::tab_() { return tab_vector_.data(); }
|
||||||
int *FHT::log_() { return log_vector_.data(); }
|
int *FHT::log_() { return log_vector_.data(); }
|
||||||
|
|
||||||
void FHT::makeCasTable(void) {
|
void FHT::makeCasTable() {
|
||||||
|
|
||||||
float *costab = tab_();
|
float *costab = tab_();
|
||||||
float *sintab = tab_() + num_ / 2 + 1;
|
float *sintab = tab_() + num_ / 2 + 1;
|
||||||
|
|
|
@ -65,8 +65,8 @@ RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
||||||
background_brush_(QColor(0x0f, 0x43, 0x73)) {
|
background_brush_(QColor(0x0f, 0x43, 0x73)) {
|
||||||
|
|
||||||
rainbowtype = rbtype;
|
rainbowtype = rbtype;
|
||||||
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
|
cat_dash_[0] = QPixmap(QStringLiteral(":/pictures/nyancat.png"));
|
||||||
cat_dash_[1] = QPixmap(":/pictures/rainbowdash.png");
|
cat_dash_[1] = QPixmap(QStringLiteral(":/pictures/rainbowdash.png"));
|
||||||
memset(history_, 0, sizeof(history_));
|
memset(history_, 0, sizeof(history_));
|
||||||
|
|
||||||
for (int i = 0; i < kRainbowBands; ++i) {
|
for (int i = 0; i < kRainbowBands; ++i) {
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
Copyright 2024, Gustavo L Conte <suporte@gu.pro.br>
|
||||||
|
|
||||||
|
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 <QPainter>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include "engine/enginebase.h"
|
||||||
|
#include "waverubber.h"
|
||||||
|
|
||||||
|
const char *WaveRubber::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "WaveRubber");
|
||||||
|
|
||||||
|
WaveRubber::WaveRubber(QWidget *parent)
|
||||||
|
: AnalyzerBase(parent, 9) {}
|
||||||
|
|
||||||
|
void WaveRubber::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
|
Q_UNUSED(e)
|
||||||
|
|
||||||
|
canvas_ = QPixmap(size());
|
||||||
|
canvas_.fill(palette().color(QPalette::AlternateBase));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveRubber::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the canvas
|
||||||
|
canvas_ = QPixmap(size());
|
||||||
|
canvas_.fill(palette().color(QPalette::Window));
|
||||||
|
|
||||||
|
QPainter canvas_painter(&canvas_);
|
||||||
|
|
||||||
|
// Set the pen color to the QT palette highlight color
|
||||||
|
canvas_painter.setPen(palette().color(QPalette::Highlight));
|
||||||
|
// Get pointer to amplitude data
|
||||||
|
const float *amplitude_data = s.data();
|
||||||
|
|
||||||
|
const int mid_y = height() / 4;
|
||||||
|
const int num_samples = static_cast<int>(s.size());
|
||||||
|
|
||||||
|
const float x_scale = static_cast<float>(width()) / static_cast<float>(num_samples);
|
||||||
|
float prev_y = static_cast<float>(mid_y);
|
||||||
|
|
||||||
|
// Draw the waveform
|
||||||
|
for (int i = 0; i < num_samples; ++i) {
|
||||||
|
|
||||||
|
// Normalize amplitude to 0-1 range
|
||||||
|
const float color_factor = amplitude_data[i] / 2.0F + 0.5F;
|
||||||
|
const int rgb_value = static_cast<int>(255 - color_factor * 255);
|
||||||
|
QColor highlight_color = palette().color(QPalette::Highlight);
|
||||||
|
// Blend blue and green with highlight color from QT palette based on amplitude
|
||||||
|
QColor blended_color = QColor(rgb_value, highlight_color.green(), highlight_color.blue());
|
||||||
|
canvas_painter.setPen(blended_color);
|
||||||
|
|
||||||
|
const int x = static_cast<int>(static_cast<float>(i) * x_scale);
|
||||||
|
const int y = static_cast<int>(static_cast<float>(mid_y) - (s[i] * static_cast<float>(mid_y)));
|
||||||
|
|
||||||
|
canvas_painter.drawLine(x, static_cast<int>(prev_y + static_cast<float>(mid_y)), static_cast<int>(static_cast<float>(x) + x_scale), static_cast<int>(static_cast<float>(y + mid_y))); // Draw
|
||||||
|
prev_y = static_cast<float>(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_painter.end();
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveRubber::transform(Scope &s) {
|
||||||
|
// No need transformation for waveform analyzer
|
||||||
|
Q_UNUSED(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveRubber::demo(QPainter &p) {
|
||||||
|
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
Copyright 2024, Gustavo L Conte <suporte@gu.pro.br>
|
||||||
|
|
||||||
|
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 <QPixmap>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
|
class WaveRubber : public AnalyzerBase {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE explicit WaveRubber(QWidget *parent);
|
||||||
|
|
||||||
|
static const char *kName;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
|
void transform(Scope &scope) override;
|
||||||
|
void demo(QPainter &p) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPixmap canvas_;
|
||||||
|
};
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
@ -31,22 +33,22 @@
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
#include "core/player.h"
|
|
||||||
#include "core/tagreaderclient.h"
|
#include "core/tagreaderclient.h"
|
||||||
#include "core/thread.h"
|
#include "core/thread.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "utilities/threadutils.h"
|
#include "utilities/threadutils.h"
|
||||||
#include "collection.h"
|
#include "collection.h"
|
||||||
#include "collectionwatcher.h"
|
#include "collectionwatcher.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
#include "playlist/playlistmanager.h"
|
|
||||||
#include "scrobbler/lastfmimport.h"
|
#include "scrobbler/lastfmimport.h"
|
||||||
#include "settings/collectionsettingspage.h"
|
#include "settings/collectionsettingspage.h"
|
||||||
|
|
||||||
|
using std::make_shared;
|
||||||
|
|
||||||
const char *SCollection::kSongsTable = "songs";
|
const char *SCollection::kSongsTable = "songs";
|
||||||
const char *SCollection::kFtsTable = "songs_fts";
|
|
||||||
const char *SCollection::kDirsTable = "directories";
|
const char *SCollection::kDirsTable = "directories";
|
||||||
const char *SCollection::kSubdirsTable = "subdirectories";
|
const char *SCollection::kSubdirsTable = "subdirectories";
|
||||||
|
|
||||||
|
@ -63,11 +65,11 @@ SCollection::SCollection(Application *app, QObject *parent)
|
||||||
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
backend_ = new CollectionBackend();
|
backend_ = make_shared<CollectionBackend>();
|
||||||
backend()->moveToThread(app->database()->thread());
|
backend()->moveToThread(app->database()->thread());
|
||||||
qLog(Debug) << backend_ << "moved to thread" << app->database()->thread();
|
qLog(Debug) << &*backend_ << "moved to thread" << app->database()->thread();
|
||||||
|
|
||||||
backend_->Init(app->database(), app->task_manager(), Song::Source::Collection, kSongsTable, kFtsTable, kDirsTable, kSubdirsTable);
|
backend_->Init(app->database(), app->task_manager(), Song::Source::Collection, QLatin1String(kSongsTable), QLatin1String(kDirsTable), QLatin1String(kSubdirsTable));
|
||||||
|
|
||||||
model_ = new CollectionModel(backend_, app_, this);
|
model_ = new CollectionModel(backend_, app_, this);
|
||||||
|
|
||||||
|
@ -85,7 +87,6 @@ SCollection::~SCollection() {
|
||||||
watcher_thread_->exit();
|
watcher_thread_->exit();
|
||||||
watcher_thread_->wait(5000);
|
watcher_thread_->wait(5000);
|
||||||
}
|
}
|
||||||
backend_->deleteLater();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,24 +106,24 @@ void SCollection::Init() {
|
||||||
watcher_->set_backend(backend_);
|
watcher_->set_backend(backend_);
|
||||||
watcher_->set_task_manager(app_->task_manager());
|
watcher_->set_task_manager(app_->task_manager());
|
||||||
|
|
||||||
QObject::connect(backend_, &CollectionBackend::Error, this, &SCollection::Error);
|
QObject::connect(&*backend_, &CollectionBackend::Error, this, &SCollection::Error);
|
||||||
QObject::connect(backend_, &CollectionBackend::DirectoryDiscovered, watcher_, &CollectionWatcher::AddDirectory);
|
QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, watcher_, &CollectionWatcher::AddDirectory);
|
||||||
QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
|
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
|
||||||
QObject::connect(backend_, &CollectionBackend::SongsRatingChanged, this, &SCollection::SongsRatingChanged);
|
QObject::connect(&*backend_, &CollectionBackend::SongsRatingChanged, this, &SCollection::SongsRatingChanged);
|
||||||
QObject::connect(backend_, &CollectionBackend::SongsStatisticsChanged, this, &SCollection::SongsPlaycountChanged);
|
QObject::connect(&*backend_, &CollectionBackend::SongsStatisticsChanged, this, &SCollection::SongsPlaycountChanged);
|
||||||
|
|
||||||
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, backend_, &CollectionBackend::AddOrUpdateSongs);
|
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, &*backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SongsMTimeUpdated, backend_, &CollectionBackend::UpdateMTimesOnly);
|
QObject::connect(watcher_, &CollectionWatcher::SongsMTimeUpdated, &*backend_, &CollectionBackend::UpdateMTimesOnly);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SongsDeleted, backend_, &CollectionBackend::DeleteSongs);
|
QObject::connect(watcher_, &CollectionWatcher::SongsDeleted, &*backend_, &CollectionBackend::DeleteSongs);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SongsUnavailable, backend_, &CollectionBackend::MarkSongsUnavailable);
|
QObject::connect(watcher_, &CollectionWatcher::SongsUnavailable, &*backend_, &CollectionBackend::MarkSongsUnavailable);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SongsReadded, backend_, &CollectionBackend::MarkSongsUnavailable);
|
QObject::connect(watcher_, &CollectionWatcher::SongsReadded, &*backend_, &CollectionBackend::MarkSongsUnavailable);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SubdirsDiscovered, backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
QObject::connect(watcher_, &CollectionWatcher::SubdirsDiscovered, &*backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, &*backend_, &CollectionBackend::AddOrUpdateSubdirs);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, backend_, &CollectionBackend::CompilationsNeedUpdating);
|
QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, &*backend_, &CollectionBackend::CompilationsNeedUpdating);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, backend_, &CollectionBackend::UpdateLastSeen);
|
QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, &*backend_, &CollectionBackend::UpdateLastSeen);
|
||||||
|
|
||||||
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, backend_, &CollectionBackend::UpdateLastPlayed);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*backend_, &CollectionBackend::UpdateLastPlayed);
|
||||||
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdatePlayCount, backend_, &CollectionBackend::UpdatePlayCount);
|
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*backend_, &CollectionBackend::UpdatePlayCount);
|
||||||
|
|
||||||
// This will start the watcher checking for updates
|
// This will start the watcher checking for updates
|
||||||
backend_->LoadDirectoriesAsync();
|
backend_->LoadDirectoriesAsync();
|
||||||
|
@ -131,12 +132,12 @@ void SCollection::Init() {
|
||||||
|
|
||||||
void SCollection::Exit() {
|
void SCollection::Exit() {
|
||||||
|
|
||||||
wait_for_exit_ << backend_ << watcher_;
|
wait_for_exit_ << &*backend_ << watcher_;
|
||||||
|
|
||||||
QObject::disconnect(backend_, nullptr, watcher_, nullptr);
|
QObject::disconnect(&*backend_, nullptr, watcher_, nullptr);
|
||||||
QObject::disconnect(watcher_, nullptr, backend_, nullptr);
|
QObject::disconnect(watcher_, nullptr, &*backend_, nullptr);
|
||||||
|
|
||||||
QObject::connect(backend_, &CollectionBackend::ExitFinished, this, &SCollection::ExitReceived);
|
QObject::connect(&*backend_, &CollectionBackend::ExitFinished, this, &SCollection::ExitReceived);
|
||||||
QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &SCollection::ExitReceived);
|
QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &SCollection::ExitReceived);
|
||||||
backend_->ExitAsync();
|
backend_->ExitAsync();
|
||||||
watcher_->Abort();
|
watcher_->Abort();
|
||||||
|
@ -178,7 +179,7 @@ void SCollection::ReloadSettings() {
|
||||||
watcher_->ReloadSettingsAsync();
|
watcher_->ReloadSettingsAsync();
|
||||||
model_->ReloadSettings();
|
model_->ReloadSettings();
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||||
save_playcounts_to_files_ = s.value("save_playcounts", false).toBool();
|
save_playcounts_to_files_ = s.value("save_playcounts", false).toBool();
|
||||||
save_ratings_to_files_ = s.value("save_ratings", false).toBool();
|
save_ratings_to_files_ = s.value("save_ratings", false).toBool();
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
|
@ -42,7 +43,7 @@ class SCollection : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SCollection(Application *app, QObject *parent);
|
explicit SCollection(Application *app, QObject *parent = nullptr);
|
||||||
~SCollection() override;
|
~SCollection() override;
|
||||||
|
|
||||||
static const char *kSongsTable;
|
static const char *kSongsTable;
|
||||||
|
@ -53,7 +54,7 @@ class SCollection : public QObject {
|
||||||
void Init();
|
void Init();
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
CollectionBackend *backend() const { return backend_; }
|
SharedPtr<CollectionBackend> backend() const { return backend_; }
|
||||||
CollectionModel *model() const { return model_; }
|
CollectionModel *model() const { return model_; }
|
||||||
|
|
||||||
QString full_rescan_reason(int schema_version) const { return full_rescan_revisions_.value(schema_version, QString()); }
|
QString full_rescan_reason(int schema_version) const { return full_rescan_revisions_.value(schema_version, QString()); }
|
||||||
|
@ -86,7 +87,7 @@ class SCollection : public QObject {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application *app_;
|
Application *app_;
|
||||||
CollectionBackend *backend_;
|
SharedPtr<CollectionBackend> backend_;
|
||||||
CollectionModel *model_;
|
CollectionModel *model_;
|
||||||
|
|
||||||
CollectionWatcher *watcher_;
|
CollectionWatcher *watcher_;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
@ -34,10 +35,9 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/sqlquery.h"
|
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "collectiondirectory.h"
|
#include "collectiondirectory.h"
|
||||||
|
@ -80,11 +80,12 @@ class CollectionBackendInterface : public QObject {
|
||||||
using AlbumList = QList<Album>;
|
using AlbumList = QList<Album>;
|
||||||
|
|
||||||
virtual QString songs_table() const = 0;
|
virtual QString songs_table() const = 0;
|
||||||
virtual QString fts_table() const = 0;
|
|
||||||
|
|
||||||
virtual Song::Source source() const = 0;
|
virtual Song::Source source() const = 0;
|
||||||
|
|
||||||
virtual Database *db() const = 0;
|
virtual SharedPtr<Database> db() const = 0;
|
||||||
|
|
||||||
|
virtual void GetAllSongsAsync(const int id = 0) = 0;
|
||||||
|
|
||||||
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
|
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
|
||||||
virtual void LoadDirectoriesAsync() = 0;
|
virtual void LoadDirectoriesAsync() = 0;
|
||||||
|
@ -95,6 +96,7 @@ class CollectionBackendInterface : public QObject {
|
||||||
|
|
||||||
virtual SongList FindSongsInDirectory(const int id) = 0;
|
virtual SongList FindSongsInDirectory(const int id) = 0;
|
||||||
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
|
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
|
||||||
|
virtual SongList SongsWithMissingLoudnessCharacteristics(const int id) = 0;
|
||||||
virtual CollectionSubdirectoryList SubdirsInDirectory(const int id) = 0;
|
virtual CollectionSubdirectoryList SubdirsInDirectory(const int id) = 0;
|
||||||
virtual CollectionDirectoryList GetAllDirectories() = 0;
|
virtual CollectionDirectoryList GetAllDirectories() = 0;
|
||||||
virtual void ChangeDirPath(const 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;
|
||||||
|
@ -129,9 +131,10 @@ class CollectionBackendInterface : public QObject {
|
||||||
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
|
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
|
||||||
// Using default beginning value is suitable when searching for single-section songs.
|
// Using default beginning value is suitable when searching for single-section songs.
|
||||||
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
||||||
|
virtual Song GetSongByUrlAndTrack(const QUrl &url, const int track) = 0;
|
||||||
|
|
||||||
virtual void AddDirectory(const QString &path) = 0;
|
virtual void AddDirectoryAsync(const QString &path) = 0;
|
||||||
virtual void RemoveDirectory(const CollectionDirectory &dir) = 0;
|
virtual void RemoveDirectoryAsync(const CollectionDirectory &dir) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CollectionBackend : public CollectionBackendInterface {
|
class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
@ -141,7 +144,10 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
|
||||||
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
|
||||||
|
|
||||||
void Init(Database *db, TaskManager *task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
~CollectionBackend();
|
||||||
|
|
||||||
|
void Init(SharedPtr<Database> db, SharedPtr<TaskManager> task_manager, const Song::Source source, const QString &songs_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
void ExitAsync();
|
void ExitAsync();
|
||||||
|
@ -150,13 +156,14 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
|
||||||
Song::Source source() const override { return source_; }
|
Song::Source source() const override { return source_; }
|
||||||
|
|
||||||
Database *db() const override { return db_; }
|
SharedPtr<Database> db() const override { return db_; }
|
||||||
|
|
||||||
QString songs_table() const override { return songs_table_; }
|
QString songs_table() const override { return songs_table_; }
|
||||||
QString fts_table() const override { return fts_table_; }
|
|
||||||
QString dirs_table() const { return dirs_table_; }
|
QString dirs_table() const { return dirs_table_; }
|
||||||
QString subdirs_table() const { return subdirs_table_; }
|
QString subdirs_table() const { return subdirs_table_; }
|
||||||
|
|
||||||
|
void GetAllSongsAsync(const int id = 0) override;
|
||||||
|
|
||||||
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
|
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
|
||||||
void LoadDirectoriesAsync() override;
|
void LoadDirectoriesAsync() override;
|
||||||
|
|
||||||
|
@ -166,6 +173,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
|
||||||
SongList FindSongsInDirectory(const int id) override;
|
SongList FindSongsInDirectory(const int id) override;
|
||||||
SongList SongsWithMissingFingerprint(const int id) override;
|
SongList SongsWithMissingFingerprint(const int id) override;
|
||||||
|
SongList SongsWithMissingLoudnessCharacteristics(const int id) override;
|
||||||
CollectionSubdirectoryList SubdirsInDirectory(const int id) override;
|
CollectionSubdirectoryList SubdirsInDirectory(const int id) override;
|
||||||
CollectionDirectoryList GetAllDirectories() override;
|
CollectionDirectoryList GetAllDirectories() override;
|
||||||
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
|
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
|
||||||
|
@ -199,9 +207,10 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
|
||||||
SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) override;
|
SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) override;
|
||||||
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
|
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
|
||||||
|
Song GetSongByUrlAndTrack(const QUrl &url, const int track) override;
|
||||||
|
|
||||||
void AddDirectory(const QString &path) override;
|
void AddDirectoryAsync(const QString &path) override;
|
||||||
void RemoveDirectory(const CollectionDirectory &dir) override;
|
void RemoveDirectoryAsync(const CollectionDirectory &dir) override;
|
||||||
|
|
||||||
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
|
||||||
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
|
||||||
|
@ -229,10 +238,13 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void Exit();
|
void Exit();
|
||||||
|
void GetAllSongs(const int id);
|
||||||
void LoadDirectories();
|
void LoadDirectories();
|
||||||
void UpdateTotalSongCount();
|
void UpdateTotalSongCount();
|
||||||
void UpdateTotalArtistCount();
|
void UpdateTotalArtistCount();
|
||||||
void UpdateTotalAlbumCount();
|
void UpdateTotalAlbumCount();
|
||||||
|
void AddDirectory(const QString &path);
|
||||||
|
void RemoveDirectory(const CollectionDirectory &dir);
|
||||||
void AddOrUpdateSongs(const SongList &songs);
|
void AddOrUpdateSongs(const SongList &songs);
|
||||||
void UpdateSongsBySongID(const SongMap &new_songs);
|
void UpdateSongsBySongID(const SongMap &new_songs);
|
||||||
void UpdateMTimesOnly(const SongList &songs);
|
void UpdateMTimesOnly(const SongList &songs);
|
||||||
|
@ -244,7 +256,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
void UpdateManualAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &art_manual);
|
void UpdateManualAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &art_manual);
|
||||||
void UnsetAlbumArt(const QString &effective_albumartist, const QString &album);
|
void UnsetAlbumArt(const QString &effective_albumartist, const QString &album);
|
||||||
void ClearAlbumArt(const QString &effective_albumartist, const QString &album, const bool art_unset);
|
void ClearAlbumArt(const QString &effective_albumartist, const QString &album, const bool art_unset);
|
||||||
void ForceCompilation(const QString &album, const QList<QString> &artists, const bool on);
|
void ForceCompilation(const QString &album, const QStringList &artists, const bool on);
|
||||||
void IncrementPlayCount(const int id);
|
void IncrementPlayCount(const int id);
|
||||||
void IncrementSkipCount(const int id, const float progress);
|
void IncrementSkipCount(const int id, const float progress);
|
||||||
void ResetPlayStatistics(const int id, const bool save_tags = false);
|
void ResetPlayStatistics(const int id, const bool save_tags = false);
|
||||||
|
@ -264,11 +276,13 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
|
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void DirectoryDiscovered(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdir);
|
void DirectoryAdded(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdir);
|
||||||
void DirectoryDeleted(const CollectionDirectory &dir);
|
void DirectoryDeleted(const CollectionDirectory &dir);
|
||||||
|
|
||||||
void SongsDiscovered(const SongList &songs);
|
void GotSongs(const SongList &songs, const int id);
|
||||||
|
void SongsAdded(const SongList &songs);
|
||||||
void SongsDeleted(const SongList &songs);
|
void SongsDeleted(const SongList &songs);
|
||||||
|
void SongsChanged(const SongList &songs);
|
||||||
void SongsStatisticsChanged(const SongList &songs, const bool save_tags = false);
|
void SongsStatisticsChanged(const SongList &songs, const bool save_tags = false);
|
||||||
|
|
||||||
void DatabaseReset();
|
void DatabaseReset();
|
||||||
|
@ -293,7 +307,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
int has_not_compilation_detected;
|
int has_not_compilation_detected;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
bool UpdateCompilations(const QSqlDatabase &db, SongList &changed_songs, const QUrl &url, const bool compilation_detected);
|
||||||
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
||||||
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const CollectionFilterOptions &opt = CollectionFilterOptions());
|
||||||
CollectionSubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
|
CollectionSubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
|
||||||
|
@ -305,13 +319,12 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||||
SongList GetSongsBySongId(const QStringList &song_ids, QSqlDatabase &db);
|
SongList GetSongsBySongId(const QStringList &song_ids, QSqlDatabase &db);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Database *db_;
|
SharedPtr<Database> db_;
|
||||||
TaskManager *task_manager_;
|
SharedPtr<TaskManager> task_manager_;
|
||||||
Song::Source source_;
|
Song::Source source_;
|
||||||
QString songs_table_;
|
QString songs_table_;
|
||||||
QString dirs_table_;
|
QString dirs_table_;
|
||||||
QString subdirs_table_;
|
QString subdirs_table_;
|
||||||
QString fts_table_;
|
|
||||||
QThread *original_thread_;
|
QThread *original_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,12 +21,15 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "core/filesystemmusicstorage.h"
|
#include "core/filesystemmusicstorage.h"
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/musicstorage.h"
|
#include "core/musicstorage.h"
|
||||||
|
@ -34,29 +38,35 @@
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
#include "collectiondirectorymodel.h"
|
#include "collectiondirectorymodel.h"
|
||||||
|
|
||||||
CollectionDirectoryModel::CollectionDirectoryModel(CollectionBackend *backend, QObject *parent)
|
using std::make_shared;
|
||||||
|
|
||||||
|
CollectionDirectoryModel::CollectionDirectoryModel(SharedPtr<CollectionBackend> backend, QObject *parent)
|
||||||
: QStandardItemModel(parent),
|
: QStandardItemModel(parent),
|
||||||
dir_icon_(IconLoader::Load("document-open-folder")),
|
dir_icon_(IconLoader::Load(QStringLiteral("document-open-folder"))),
|
||||||
backend_(backend) {
|
backend_(backend) {
|
||||||
|
|
||||||
QObject::connect(backend_, &CollectionBackend::DirectoryDiscovered, this, &CollectionDirectoryModel::DirectoryDiscovered);
|
QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, this, &CollectionDirectoryModel::AddDirectory);
|
||||||
QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, this, &CollectionDirectoryModel::DirectoryDeleted);
|
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, this, &CollectionDirectoryModel::RemoveDirectory);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionDirectoryModel::~CollectionDirectoryModel() = default;
|
void CollectionDirectoryModel::AddDirectory(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &dir) {
|
directories_.insert(dir.id, dir);
|
||||||
|
paths_.append(dir.path);
|
||||||
|
|
||||||
QStandardItem *item = new QStandardItem(dir.path);
|
QStandardItem *item = new QStandardItem(dir.path);
|
||||||
item->setData(dir.id, kIdRole);
|
item->setData(dir.id, kIdRole);
|
||||||
item->setIcon(dir_icon_);
|
item->setIcon(dir_icon_);
|
||||||
storage_ << std::make_shared<FilesystemMusicStorage>(backend_->source(), dir.path, dir.id);
|
storage_ << make_shared<FilesystemMusicStorage>(backend_->source(), dir.path, dir.id);
|
||||||
appendRow(item);
|
appendRow(item);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionDirectoryModel::DirectoryDeleted(const CollectionDirectory &dir) {
|
void CollectionDirectoryModel::RemoveDirectory(const CollectionDirectory &dir) {
|
||||||
|
|
||||||
|
directories_.remove(dir.id);
|
||||||
|
paths_.removeAll(dir.path);
|
||||||
|
|
||||||
for (int i = 0; i < rowCount(); ++i) {
|
for (int i = 0; i < rowCount(); ++i) {
|
||||||
if (item(i, 0)->data(kIdRole).toInt() == dir.id) {
|
if (item(i, 0)->data(kIdRole).toInt() == dir.id) {
|
||||||
|
@ -68,26 +78,6 @@ void CollectionDirectoryModel::DirectoryDeleted(const CollectionDirectory &dir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionDirectoryModel::AddDirectory(const QString &path) {
|
|
||||||
|
|
||||||
if (!backend_) return;
|
|
||||||
|
|
||||||
backend_->AddDirectory(path);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollectionDirectoryModel::RemoveDirectory(const QModelIndex &idx) {
|
|
||||||
|
|
||||||
if (!backend_ || !idx.isValid()) return;
|
|
||||||
|
|
||||||
CollectionDirectory dir;
|
|
||||||
dir.path = idx.data().toString();
|
|
||||||
dir.id = idx.data(kIdRole).toInt();
|
|
||||||
|
|
||||||
backend_->RemoveDirectory(dir);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant CollectionDirectoryModel::data(const QModelIndex &idx, int role) const {
|
QVariant CollectionDirectoryModel::data(const QModelIndex &idx, int role) const {
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,18 +24,20 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
|
#include "collectiondirectory.h"
|
||||||
|
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
|
||||||
struct CollectionDirectory;
|
|
||||||
class CollectionBackend;
|
class CollectionBackend;
|
||||||
class MusicStorage;
|
class MusicStorage;
|
||||||
|
|
||||||
|
@ -42,26 +45,27 @@ class CollectionDirectoryModel : public QStandardItemModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CollectionDirectoryModel(CollectionBackend *backend, QObject *parent = nullptr);
|
explicit CollectionDirectoryModel(SharedPtr<CollectionBackend> collection_backend, QObject *parent = nullptr);
|
||||||
~CollectionDirectoryModel() override;
|
|
||||||
|
|
||||||
// To be called by GUIs
|
|
||||||
void AddDirectory(const QString &path);
|
|
||||||
void RemoveDirectory(const QModelIndex &idx);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &idx, int role) const override;
|
QVariant data(const QModelIndex &idx, int role) const override;
|
||||||
|
|
||||||
|
SharedPtr<CollectionBackend> backend() const { return backend_; }
|
||||||
|
|
||||||
|
QMap<int, CollectionDirectory> directories() const { return directories_; }
|
||||||
|
QStringList paths() const { return paths_; }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// To be called by the backend
|
void AddDirectory(const CollectionDirectory &directory);
|
||||||
void DirectoryDiscovered(const CollectionDirectory &directories);
|
void RemoveDirectory(const CollectionDirectory &directory);
|
||||||
void DirectoryDeleted(const CollectionDirectory &directories);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kIdRole = Qt::UserRole + 1;
|
static const int kIdRole = Qt::UserRole + 1;
|
||||||
|
|
||||||
QIcon dir_icon_;
|
QIcon dir_icon_;
|
||||||
CollectionBackend *backend_;
|
SharedPtr<CollectionBackend> backend_;
|
||||||
QList<std::shared_ptr<MusicStorage>> storage_;
|
QMap<int, CollectionDirectory> directories_;
|
||||||
|
QStringList paths_;
|
||||||
|
QList<SharedPtr<MusicStorage>> storage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONDIRECTORYMODEL_H
|
#endif // COLLECTIONDIRECTORYMODEL_H
|
||||||
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2021-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "utilities/timeconstants.h"
|
||||||
|
#include "utilities/searchparserutils.h"
|
||||||
|
|
||||||
|
#include "collectionfilter.h"
|
||||||
|
#include "collectionmodel.h"
|
||||||
|
#include "collectionitem.h"
|
||||||
|
|
||||||
|
const QStringList CollectionFilter::Operators = QStringList() << QStringLiteral(":")
|
||||||
|
<< QStringLiteral("=")
|
||||||
|
<< QStringLiteral("==")
|
||||||
|
<< QStringLiteral("<>")
|
||||||
|
<< QStringLiteral("<")
|
||||||
|
<< QStringLiteral("<=")
|
||||||
|
<< QStringLiteral(">")
|
||||||
|
<< QStringLiteral(">=");
|
||||||
|
|
||||||
|
CollectionFilter::CollectionFilter(QObject *parent) : QSortFilterProxyModel(parent) {}
|
||||||
|
|
||||||
|
bool CollectionFilter::filterAcceptsRow(const int source_row, const QModelIndex &source_parent) const {
|
||||||
|
|
||||||
|
CollectionModel *model = qobject_cast<CollectionModel*>(sourceModel());
|
||||||
|
if (!model) return false;
|
||||||
|
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
|
||||||
|
if (!idx.isValid()) return false;
|
||||||
|
CollectionItem *item = model->IndexToItem(idx);
|
||||||
|
if (!item) return false;
|
||||||
|
|
||||||
|
if (item->type == CollectionItem::Type::LoadingIndicator) return true;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
QString filter_text = filterRegularExpression().pattern().remove(QLatin1Char('\\'));
|
||||||
|
#else
|
||||||
|
QString filter_text = filterRegExp().pattern();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (filter_text.isEmpty()) return true;
|
||||||
|
|
||||||
|
filter_text = filter_text.replace(QRegularExpression(QStringLiteral("\\s*:\\s*")), QStringLiteral(":"))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*=\\s*")), QStringLiteral("="))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*==\\s*")), QStringLiteral("=="))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*<>\\s*")), QStringLiteral("<>"))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*<\\s*")), QStringLiteral("<"))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*>\\s*")), QStringLiteral(">"))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*<=\\s*")), QStringLiteral("<="))
|
||||||
|
.replace(QRegularExpression(QStringLiteral("\\s*>=\\s*")), QStringLiteral(">="));
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
const QStringList tokens = filter_text.split(QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts);
|
||||||
|
#else
|
||||||
|
const QStringList tokens = filter_text.split(QRegularExpression(QStringLiteral("\\s+")), QString::SkipEmptyParts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
filter_text.clear();
|
||||||
|
|
||||||
|
FilterList filters;
|
||||||
|
static QRegularExpression operator_regex(QStringLiteral("(=|<[>=]?|>=?|!=)"));
|
||||||
|
for (int i = 0; i < tokens.count(); ++i) {
|
||||||
|
const QString &token = tokens[i];
|
||||||
|
if (token.contains(QLatin1Char(':'))) {
|
||||||
|
QString field = token.section(QLatin1Char(':'), 0, 0).remove(QLatin1Char(':')).trimmed();
|
||||||
|
QString value = token.section(QLatin1Char(':'), 1, -1).remove(QLatin1Char(':')).trimmed();
|
||||||
|
if (field.isEmpty() || value.isEmpty()) continue;
|
||||||
|
if (Song::kTextSearchColumns.contains(field, Qt::CaseInsensitive) && value.count(QLatin1Char('"')) <= 2) {
|
||||||
|
bool quotation_mark_start = false;
|
||||||
|
bool quotation_mark_end = false;
|
||||||
|
if (value.left(1) == QLatin1Char('"')) {
|
||||||
|
value.remove(0, 1);
|
||||||
|
quotation_mark_start = true;
|
||||||
|
if (value.length() >= 1 && value.count(QLatin1Char('"')) == 1) {
|
||||||
|
value = value.section(QLatin1Char(QLatin1Char('"')), 0, 0).remove(QLatin1Char('"')).trimmed();
|
||||||
|
quotation_mark_end = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int y = i + 1; y < tokens.count() && !quotation_mark_end; ++y) {
|
||||||
|
QString next_value = tokens[y];
|
||||||
|
if (!quotation_mark_start && ContainsOperators(next_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (quotation_mark_start && next_value.contains(QLatin1Char('"'))) {
|
||||||
|
next_value = next_value.section(QLatin1Char(QLatin1Char('"')), 0, 0).remove(QLatin1Char('"')).trimmed();
|
||||||
|
quotation_mark_end = true;
|
||||||
|
}
|
||||||
|
value.append(QLatin1Char(' ') + next_value);
|
||||||
|
i = y;
|
||||||
|
}
|
||||||
|
if (!field.isEmpty() && !value.isEmpty()) {
|
||||||
|
filters.insert(field, Filter(field, value));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (token.contains(operator_regex)) {
|
||||||
|
QRegularExpressionMatch re_match = operator_regex.match(token);
|
||||||
|
if (re_match.hasMatch()) {
|
||||||
|
const QString foperator = re_match.captured(0);
|
||||||
|
const QString field = token.section(foperator, 0, 0).remove(foperator).trimmed();
|
||||||
|
const QString value = token.section(foperator, 1, -1).remove(foperator).trimmed();
|
||||||
|
if (value.isEmpty()) continue;
|
||||||
|
if (Song::kNumericalSearchColumns.contains(field, Qt::CaseInsensitive)) {
|
||||||
|
if (Song::kIntSearchColumns.contains(field, Qt::CaseInsensitive)) {
|
||||||
|
bool ok = false;
|
||||||
|
const int value_int = value.toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
filters.insert(field, Filter(field, value_int, foperator));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Song::kUIntSearchColumns.contains(field, Qt::CaseInsensitive)) {
|
||||||
|
bool ok = false;
|
||||||
|
const uint value_uint = value.toUInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
filters.insert(field, Filter(field, value_uint, foperator));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (field.compare(QLatin1String("length"), Qt::CaseInsensitive) == 0) {
|
||||||
|
filters.insert(field, Filter(field, static_cast<qint64>(Utilities::ParseSearchTime(value)) * kNsecPerSec, foperator));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (field.compare(QLatin1String("rating"), Qt::CaseInsensitive) == 0) {
|
||||||
|
filters.insert(field, Filter(field, Utilities::ParseSearchRating(value), foperator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!filter_text.isEmpty()) filter_text.append(QLatin1Char(' '));
|
||||||
|
filter_text += token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter_text.isEmpty() && filters.isEmpty()) return true;
|
||||||
|
|
||||||
|
return ItemMatchesFilters(item, filters, filter_text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::ItemMatchesFilters(CollectionItem *item, const FilterList &filters, const QString &filter_text) {
|
||||||
|
|
||||||
|
if (item->type == CollectionItem::Type::Song &&
|
||||||
|
item->metadata.is_valid() &&
|
||||||
|
ItemMetadataMatchesFilters(item->metadata, filters, filter_text)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CollectionItem *child : std::as_const(item->children)) {
|
||||||
|
if (ItemMatchesFilters(child, filters, filter_text)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::ItemMetadataMatchesFilters(const Song &metadata, const FilterList &filters, const QString &filter_text) {
|
||||||
|
|
||||||
|
for (FilterList::const_iterator it = filters.begin() ; it != filters.end() ; ++it) {
|
||||||
|
const QString &field = it.key();
|
||||||
|
const Filter &filter = it.value();
|
||||||
|
const QVariant &value = filter.value;
|
||||||
|
const QString &foperator = filter.foperator;
|
||||||
|
if (field.isEmpty() || !value.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QVariant data = DataFromField(field, metadata);
|
||||||
|
if (
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
value.metaType() != data.metaType()
|
||||||
|
#else
|
||||||
|
value.type() != data.type()
|
||||||
|
#endif
|
||||||
|
|| !FieldValueMatchesData(value, data, foperator)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter_text.isEmpty() || ItemMetadataMatchesFilterText(metadata, filter_text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::ItemMetadataMatchesFilterText(const Song &metadata, const QString &filter_text) {
|
||||||
|
|
||||||
|
return metadata.effective_albumartist().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.artist().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.album().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.title().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.composer().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.performer().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.grouping().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.genre().contains(filter_text, Qt::CaseInsensitive) ||
|
||||||
|
metadata.comment().contains(filter_text, Qt::CaseInsensitive);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CollectionFilter::DataFromField(const QString &field, const Song &metadata) {
|
||||||
|
|
||||||
|
if (field == QLatin1String("albumartist")) return metadata.effective_albumartist();
|
||||||
|
if (field == QLatin1String("artist")) return metadata.artist();
|
||||||
|
if (field == QLatin1String("album")) return metadata.album();
|
||||||
|
if (field == QLatin1String("title")) return metadata.title();
|
||||||
|
if (field == QLatin1String("composer")) return metadata.composer();
|
||||||
|
if (field == QLatin1String("performer")) return metadata.performer();
|
||||||
|
if (field == QLatin1String("grouping")) return metadata.grouping();
|
||||||
|
if (field == QLatin1String("genre")) return metadata.genre();
|
||||||
|
if (field == QLatin1String("comment")) return metadata.comment();
|
||||||
|
if (field == QLatin1String("track")) return metadata.track();
|
||||||
|
if (field == QLatin1String("year")) return metadata.year();
|
||||||
|
if (field == QLatin1String("length")) return metadata.length_nanosec();
|
||||||
|
if (field == QLatin1String("samplerate")) return metadata.samplerate();
|
||||||
|
if (field == QLatin1String("bitdepth")) return metadata.bitdepth();
|
||||||
|
if (field == QLatin1String("bitrate")) return metadata.bitrate();
|
||||||
|
if (field == QLatin1String("rating")) return metadata.rating();
|
||||||
|
if (field == QLatin1String("playcount")) return metadata.playcount();
|
||||||
|
if (field == QLatin1String("skipcount")) return metadata.skipcount();
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::FieldValueMatchesData(const QVariant &value, const QVariant &data, const QString &foperator) {
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
switch (value.metaType().id()) {
|
||||||
|
#else
|
||||||
|
switch (value.userType()) {
|
||||||
|
#endif
|
||||||
|
case QMetaType::QString:{
|
||||||
|
const QString str_value = value.toString();
|
||||||
|
const QString str_data = data.toString();
|
||||||
|
return str_data.contains(str_value, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
case QMetaType::Int:{
|
||||||
|
return FieldIntValueMatchesData(value.toInt(), foperator, data.toInt());
|
||||||
|
}
|
||||||
|
case QMetaType::UInt:{
|
||||||
|
return FieldUIntValueMatchesData(value.toUInt(), foperator, data.toUInt());
|
||||||
|
}
|
||||||
|
case QMetaType::LongLong:{
|
||||||
|
return FieldLongLongValueMatchesData(value.toLongLong(), foperator, data.toLongLong());
|
||||||
|
}
|
||||||
|
case QMetaType::Float:{
|
||||||
|
return FieldFloatValueMatchesData(value.toFloat(), foperator, data.toFloat());
|
||||||
|
}
|
||||||
|
default:{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool CollectionFilter::FieldNumericalValueMatchesData(const T value, const QString &foperator, const T data) {
|
||||||
|
|
||||||
|
if (foperator == QLatin1Char('=') || foperator == QLatin1String("==")) {
|
||||||
|
return data == value;
|
||||||
|
}
|
||||||
|
if (foperator == QLatin1String("!=") || foperator == QLatin1String("<>")) {
|
||||||
|
return data != value;
|
||||||
|
}
|
||||||
|
if (foperator == QLatin1Char('<')) {
|
||||||
|
return data < value;
|
||||||
|
}
|
||||||
|
if (foperator == QLatin1Char('>')) {
|
||||||
|
return data > value;
|
||||||
|
}
|
||||||
|
if (foperator == QLatin1String(">=")) {
|
||||||
|
return data >= value;
|
||||||
|
}
|
||||||
|
if (foperator == QLatin1String("<=")) {
|
||||||
|
return data <= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::FieldIntValueMatchesData(const int value, const QString &foperator, const int data) {
|
||||||
|
|
||||||
|
return FieldNumericalValueMatchesData(value, foperator, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::FieldUIntValueMatchesData(const uint value, const QString &foperator, const uint data) {
|
||||||
|
|
||||||
|
return FieldNumericalValueMatchesData(value, foperator, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::FieldLongLongValueMatchesData(const qint64 value, const QString &foperator, const qint64 data) {
|
||||||
|
|
||||||
|
return FieldNumericalValueMatchesData(value, foperator, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::FieldFloatValueMatchesData(const float value, const QString &foperator, const float data) {
|
||||||
|
|
||||||
|
return FieldNumericalValueMatchesData(value, foperator, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionFilter::ContainsOperators(const QString &token) {
|
||||||
|
|
||||||
|
for (const QString &foperator : Operators) {
|
||||||
|
if (token.contains(foperator, Qt::CaseInsensitive)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2021-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COLLECTIONFILTER_H
|
||||||
|
#define COLLECTIONFILTER_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
class CollectionItem;
|
||||||
|
|
||||||
|
class CollectionFilter : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CollectionFilter(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(const int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QStringList Operators;
|
||||||
|
struct Filter {
|
||||||
|
public:
|
||||||
|
Filter(const QString &_field = QString(), const QVariant &_value = QVariant(), const QString &_foperator = QString()) : field(_field), value(_value), foperator(_foperator) {}
|
||||||
|
QString field;
|
||||||
|
QVariant value;
|
||||||
|
QString foperator;
|
||||||
|
};
|
||||||
|
using FilterList = QMap<QString, Filter>;
|
||||||
|
static bool ItemMatchesFilters(CollectionItem *item, const FilterList &filters, const QString &filter_text);
|
||||||
|
static bool ItemMetadataMatchesFilters(const Song &metadata, const FilterList &filters, const QString &filter_text);
|
||||||
|
static bool ItemMetadataMatchesFilterText(const Song &metadata, const QString &filter_text);
|
||||||
|
static QVariant DataFromField(const QString &field, const Song &metadata);
|
||||||
|
static bool FieldValueMatchesData(const QVariant &value, const QVariant &data, const QString &foperator);
|
||||||
|
template<typename T>
|
||||||
|
static bool FieldNumericalValueMatchesData(const T value, const QString &foperator, const T data);
|
||||||
|
static bool FieldIntValueMatchesData(const int value, const QString &foperator, const int data);
|
||||||
|
static bool FieldUIntValueMatchesData(const uint value, const QString &foperator, const uint data);
|
||||||
|
static bool FieldLongLongValueMatchesData(const qint64 value, const QString &foperator, const qint64 data);
|
||||||
|
static bool FieldFloatValueMatchesData(const float value, const QString &foperator, const float data);
|
||||||
|
static bool ContainsOperators(const QString &token);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLLECTIONFILTER_H
|
|
@ -29,7 +29,7 @@ CollectionFilterOptions::CollectionFilterOptions() : filter_mode_(FilterMode::Al
|
||||||
bool CollectionFilterOptions::Matches(const Song &song) const {
|
bool CollectionFilterOptions::Matches(const Song &song) const {
|
||||||
|
|
||||||
if (max_age_ != -1) {
|
if (max_age_ != -1) {
|
||||||
const qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
|
const qint64 cutoff = QDateTime::currentSecsSinceEpoch() - max_age_;
|
||||||
if (song.ctime() <= cutoff) return false;
|
if (song.ctime() <= cutoff) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
@ -46,8 +47,10 @@
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
|
#include "collectionfilter.h"
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "savedgroupingmanager.h"
|
#include "savedgroupingmanager.h"
|
||||||
#include "collectionfilterwidget.h"
|
#include "collectionfilterwidget.h"
|
||||||
|
@ -61,6 +64,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
ui_(new Ui_CollectionFilterWidget),
|
ui_(new Ui_CollectionFilterWidget),
|
||||||
model_(nullptr),
|
model_(nullptr),
|
||||||
|
filter_(nullptr),
|
||||||
group_by_dialog_(new GroupByDialog(this)),
|
group_by_dialog_(new GroupByDialog(this)),
|
||||||
groupings_manager_(nullptr),
|
groupings_manager_(nullptr),
|
||||||
filter_age_menu_(nullptr),
|
filter_age_menu_(nullptr),
|
||||||
|
@ -73,26 +77,35 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||||
|
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
|
|
||||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
|
QString available_fields = Song::kTextSearchColumns.join(QLatin1String(", "));
|
||||||
|
available_fields += QLatin1String(", ") + Song::kNumericalSearchColumns.join(QLatin1String(", "));
|
||||||
|
|
||||||
ui_->search_field->setToolTip(
|
ui_->search_field->setToolTip(
|
||||||
QString("<html><head/><body><p>") +
|
QLatin1String("<html><head/><body><p>") +
|
||||||
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
|
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
|
||||||
QString(" ") +
|
QLatin1Char(' ') +
|
||||||
QString("<span style=\"font-weight:600;\">") +
|
QLatin1String("<span style=\"font-weight:600;\">") +
|
||||||
tr("artist") +
|
tr("artist") +
|
||||||
QString(":") +
|
QLatin1String(":</span><span style=\"font-style:italic;\">Strawbs</span> ") +
|
||||||
QString("</span><span style=\"font-style:italic;\">Strawbs</span>") +
|
tr("searches the collection for all artists that contain the word %1. ").arg(QLatin1String("Strawbs")) +
|
||||||
QString(" ") +
|
QLatin1String("</p><p>") +
|
||||||
tr("searches the collection for all artists that contain the word") +
|
tr("Search terms for numerical fields can be prefixed with %1 or %2 to refine the search, e.g.: ")
|
||||||
QString(" Strawbs.") +
|
.arg(QLatin1String(" =, !=, <, >, <="), QLatin1String(">=")) +
|
||||||
QString("</p><p><span style=\"font-weight:600;\">") +
|
QLatin1String("<span style=\"font-weight:600;\">") +
|
||||||
|
tr("rating") +
|
||||||
|
QLatin1String("</span>") +
|
||||||
|
QLatin1String(":>=") +
|
||||||
|
QLatin1String("<span style=\"font-weight:italic;\">4</span>") +
|
||||||
|
|
||||||
|
QLatin1String("</p><p><span style=\"font-weight:600;\">") +
|
||||||
tr("Available fields") +
|
tr("Available fields") +
|
||||||
QString(": ") +
|
QLatin1String(": ") +
|
||||||
"</span><span style=\"font-style:italic;\">" +
|
QLatin1String("</span>") +
|
||||||
|
QLatin1String("<span style=\"font-style:italic;\">") +
|
||||||
available_fields +
|
available_fields +
|
||||||
QString("</span>.") +
|
QLatin1String("</span>.") +
|
||||||
QString("</p></body></html>"));
|
QLatin1String("</p></body></html>")
|
||||||
|
);
|
||||||
|
|
||||||
QObject::connect(ui_->search_field, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
|
QObject::connect(ui_->search_field, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
|
||||||
QObject::connect(filter_delay_, &QTimer::timeout, this, &CollectionFilterWidget::FilterDelayTimeout);
|
QObject::connect(filter_delay_, &QTimer::timeout, this, &CollectionFilterWidget::FilterDelayTimeout);
|
||||||
|
@ -101,7 +114,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||||
filter_delay_->setSingleShot(true);
|
filter_delay_->setSingleShot(true);
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
ui_->options->setIcon(IconLoader::Load("configure"));
|
ui_->options->setIcon(IconLoader::Load(QStringLiteral("configure")));
|
||||||
|
|
||||||
// Filter by age
|
// Filter by age
|
||||||
QActionGroup *filter_age_group = new QActionGroup(this);
|
QActionGroup *filter_age_group = new QActionGroup(this);
|
||||||
|
@ -115,12 +128,12 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||||
filter_age_menu_ = new QMenu(tr("Show"), this);
|
filter_age_menu_ = new QMenu(tr("Show"), this);
|
||||||
filter_age_menu_->addActions(filter_age_group->actions());
|
filter_age_menu_->addActions(filter_age_group->actions());
|
||||||
|
|
||||||
filter_ages_[ui_->filter_age_all] = -1;
|
filter_max_ages_[ui_->filter_age_all] = -1;
|
||||||
filter_ages_[ui_->filter_age_today] = 60 * 60 * 24;
|
filter_max_ages_[ui_->filter_age_today] = 60 * 60 * 24;
|
||||||
filter_ages_[ui_->filter_age_week] = 60 * 60 * 24 * 7;
|
filter_max_ages_[ui_->filter_age_week] = 60 * 60 * 24 * 7;
|
||||||
filter_ages_[ui_->filter_age_month] = 60 * 60 * 24 * 30;
|
filter_max_ages_[ui_->filter_age_month] = 60 * 60 * 24 * 30;
|
||||||
filter_ages_[ui_->filter_age_three_months] = 60 * 60 * 24 * 30 * 3;
|
filter_max_ages_[ui_->filter_age_three_months] = 60 * 60 * 24 * 30 * 3;
|
||||||
filter_ages_[ui_->filter_age_year] = 60 * 60 * 24 * 365;
|
filter_max_ages_[ui_->filter_age_year] = 60 * 60 * 24 * 365;
|
||||||
|
|
||||||
group_by_menu_ = new QMenu(tr("Group by"), this);
|
group_by_menu_ = new QMenu(tr("Group by"), this);
|
||||||
|
|
||||||
|
@ -146,34 +159,35 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||||
|
|
||||||
CollectionFilterWidget::~CollectionFilterWidget() { delete ui_; }
|
CollectionFilterWidget::~CollectionFilterWidget() { delete ui_; }
|
||||||
|
|
||||||
void CollectionFilterWidget::Init(CollectionModel *model) {
|
void CollectionFilterWidget::Init(CollectionModel *model, CollectionFilter *filter) {
|
||||||
|
|
||||||
if (model_) {
|
if (model_) {
|
||||||
QObject::disconnect(model_, nullptr, this, nullptr);
|
QObject::disconnect(model_, nullptr, this, nullptr);
|
||||||
QObject::disconnect(model_, nullptr, group_by_dialog_, nullptr);
|
QObject::disconnect(model_, nullptr, group_by_dialog_, nullptr);
|
||||||
QObject::disconnect(group_by_dialog_, nullptr, model_, nullptr);
|
QObject::disconnect(group_by_dialog_, nullptr, model_, nullptr);
|
||||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
const QList<QAction*> actions = filter_max_ages_.keys();
|
||||||
for (QAction *action : filter_ages) {
|
for (QAction *action : actions) {
|
||||||
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
|
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model_ = model;
|
model_ = model;
|
||||||
|
filter_ = filter;
|
||||||
|
|
||||||
// Connect signals
|
// Connect signals
|
||||||
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_, &GroupByDialog::CollectionGroupingChanged);
|
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_, &GroupByDialog::CollectionGroupingChanged);
|
||||||
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
|
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
|
||||||
QObject::connect(group_by_dialog_, &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
|
QObject::connect(group_by_dialog_, &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
|
||||||
|
|
||||||
QList<QAction*> filter_ages = filter_ages_.keys();
|
const QList<QAction*> actions = filter_max_ages_.keys();
|
||||||
for (QAction *action : filter_ages) {
|
for (QAction *action : actions) {
|
||||||
int age = filter_ages_[action];
|
int filter_max_age = filter_max_ages_[action];
|
||||||
QObject::connect(action, &QAction::triggered, this, [this, age]() { model_->SetFilterAge(age); } );
|
QObject::connect(action, &QAction::triggered, this, [this, filter_max_age]() { model_->SetFilterMaxAge(filter_max_age); } );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
if (!settings_group_.isEmpty()) {
|
if (!settings_group_.isEmpty()) {
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(settings_group_);
|
s.beginGroup(settings_group_);
|
||||||
int version = 0;
|
int version = 0;
|
||||||
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
|
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
|
||||||
|
@ -207,9 +221,13 @@ void CollectionFilterWidget::SetSettingsPrefix(const QString &prefix) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionFilterWidget::setFilter(CollectionFilter *filter) {
|
||||||
|
filter_ = filter;
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionFilterWidget::ReloadSettings() {
|
void CollectionFilterWidget::ReloadSettings() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
||||||
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt();
|
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
@ -221,23 +239,21 @@ void CollectionFilterWidget::ReloadSettings() {
|
||||||
QString CollectionFilterWidget::group_by_version() const {
|
QString CollectionFilterWidget::group_by_version() const {
|
||||||
|
|
||||||
if (settings_prefix_.isEmpty()) {
|
if (settings_prefix_.isEmpty()) {
|
||||||
return "group_by_version";
|
return QStringLiteral("group_by_version");
|
||||||
}
|
|
||||||
else {
|
|
||||||
return QString("%1_group_by_version").arg(settings_prefix_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("%1_group_by_version").arg(settings_prefix_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionFilterWidget::group_by_key() const {
|
QString CollectionFilterWidget::group_by_key() const {
|
||||||
|
|
||||||
if (settings_prefix_.isEmpty()) {
|
if (settings_prefix_.isEmpty()) {
|
||||||
return "group_by";
|
return QStringLiteral("group_by");
|
||||||
}
|
|
||||||
else {
|
|
||||||
return QString("%1_group_by").arg(settings_prefix_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("%1_group_by").arg(settings_prefix_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionFilterWidget::group_by_key(const int number) const { return group_by_key() + QString::number(number); }
|
QString CollectionFilterWidget::group_by_key(const int number) const { return group_by_key() + QString::number(number); }
|
||||||
|
@ -245,12 +261,11 @@ QString CollectionFilterWidget::group_by_key(const int number) const { return gr
|
||||||
QString CollectionFilterWidget::separate_albums_by_grouping_key() const {
|
QString CollectionFilterWidget::separate_albums_by_grouping_key() const {
|
||||||
|
|
||||||
if (settings_prefix_.isEmpty()) {
|
if (settings_prefix_.isEmpty()) {
|
||||||
return "separate_albums_by_grouping";
|
return QStringLiteral("separate_albums_by_grouping");
|
||||||
}
|
|
||||||
else {
|
|
||||||
return QString("%1_separate_albums_by_grouping").arg(settings_prefix_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("%1_separate_albums_by_grouping").arg(settings_prefix_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionFilterWidget::UpdateGroupByActions() {
|
void CollectionFilterWidget::UpdateGroupByActions() {
|
||||||
|
@ -298,13 +313,13 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(const QString &saved_
|
||||||
ret->addAction(sep1);
|
ret->addAction(sep1);
|
||||||
|
|
||||||
// Read saved groupings
|
// Read saved groupings
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(saved_groupings_settings_group);
|
s.beginGroup(saved_groupings_settings_group);
|
||||||
int version = s.value("version").toInt();
|
int version = s.value("version").toInt();
|
||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version") continue;
|
if (saved.at(i) == QLatin1String("version")) continue;
|
||||||
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
QByteArray bytes = s.value(saved.at(i)).toByteArray();
|
||||||
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
QDataStream ds(&bytes, QIODevice::ReadOnly);
|
||||||
CollectionModel::Grouping g;
|
CollectionModel::Grouping g;
|
||||||
|
@ -315,7 +330,7 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(const QString &saved_
|
||||||
else {
|
else {
|
||||||
QStringList saved = s.childKeys();
|
QStringList saved = s.childKeys();
|
||||||
for (int i = 0; i < saved.size(); ++i) {
|
for (int i = 0; i < saved.size(); ++i) {
|
||||||
if (saved.at(i) == "version") continue;
|
if (saved.at(i) == QLatin1String("version")) continue;
|
||||||
s.remove(saved.at(i));
|
s.remove(saved.at(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,17 +368,17 @@ void CollectionFilterWidget::SaveGroupBy() {
|
||||||
|
|
||||||
qLog(Debug) << "Saving current grouping to" << name;
|
qLog(Debug) << "Saving current grouping to" << name;
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
if (settings_group_.isEmpty() || settings_group_ == CollectionSettingsPage::kSettingsGroup) {
|
if (settings_group_.isEmpty() || settings_group_ == QLatin1String(CollectionSettingsPage::kSettingsGroup)) {
|
||||||
s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup);
|
s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
s.beginGroup(QString(SavedGroupingManager::kSavedGroupingsSettingsGroup) + "_" + settings_group_);
|
s.beginGroup(QLatin1String(SavedGroupingManager::kSavedGroupingsSettingsGroup) + QLatin1Char('_') + settings_group_);
|
||||||
}
|
}
|
||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
QDataStream datastream(&buffer, QIODevice::WriteOnly);
|
QDataStream datastream(&buffer, QIODevice::WriteOnly);
|
||||||
datastream << model_->GetGroupBy();
|
datastream << model_->GetGroupBy();
|
||||||
s.setValue("version", "1");
|
s.setValue("version", QStringLiteral("1"));
|
||||||
s.setValue(name, buffer);
|
s.setValue(name, buffer);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
|
@ -417,7 +432,7 @@ void CollectionFilterWidget::GroupByClicked(QAction *action) {
|
||||||
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping) {
|
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping) {
|
||||||
|
|
||||||
if (!settings_group_.isEmpty()) {
|
if (!settings_group_.isEmpty()) {
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(settings_group_);
|
s.beginGroup(settings_group_);
|
||||||
s.setValue(group_by_version(), 1);
|
s.setValue(group_by_version(), 1);
|
||||||
s.setValue(group_by_key(1), static_cast<int>(g[0]));
|
s.setValue(group_by_key(1), static_cast<int>(g[0]));
|
||||||
|
@ -438,7 +453,8 @@ void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Groupin
|
||||||
UpdateGroupByActions();
|
UpdateGroupByActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QAction *action : group_by_group_->actions()) {
|
const QList<QAction*> actions = group_by_group_->actions();
|
||||||
|
for (QAction *action : actions) {
|
||||||
if (action->property("group_by").isNull()) continue;
|
if (action->property("group_by").isNull()) continue;
|
||||||
|
|
||||||
if (g == action->property("group_by").value<CollectionModel::Grouping>()) {
|
if (g == action->property("group_by").value<CollectionModel::Grouping>()) {
|
||||||
|
@ -448,7 +464,6 @@ void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Groupin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the advanced action
|
// Check the advanced action
|
||||||
QList<QAction*> actions = group_by_group_->actions();
|
|
||||||
QAction *action = actions.last();
|
QAction *action = actions.last();
|
||||||
action->setChecked(true);
|
action->setChecked(true);
|
||||||
|
|
||||||
|
@ -500,6 +515,9 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
|
||||||
ui_->search_field->clear();
|
ui_->search_field->clear();
|
||||||
e->accept();
|
e->accept();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget::keyReleaseEvent(e);
|
QWidget::keyReleaseEvent(e);
|
||||||
|
@ -508,9 +526,6 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
|
||||||
|
|
||||||
void CollectionFilterWidget::FilterTextChanged(const QString &text) {
|
void CollectionFilterWidget::FilterTextChanged(const QString &text) {
|
||||||
|
|
||||||
// Searching with one or two characters can be very expensive on the database even with FTS,
|
|
||||||
// so if there are a large number of songs in the database introduce a small delay before actually filtering the model,
|
|
||||||
// so if the user is typing the first few characters of something it will be quicker.
|
|
||||||
const bool delay = (delay_behaviour_ == DelayBehaviour::AlwaysDelayed) || (delay_behaviour_ == DelayBehaviour::DelayedOnLargeLibraries && !text.isEmpty() && text.length() < 3 && model_->total_song_count() >= 100000);
|
const bool delay = (delay_behaviour_ == DelayBehaviour::AlwaysDelayed) || (delay_behaviour_ == DelayBehaviour::DelayedOnLargeLibraries && !text.isEmpty() && text.length() < 3 && model_->total_song_count() >= 100000);
|
||||||
|
|
||||||
if (delay) {
|
if (delay) {
|
||||||
|
@ -525,9 +540,8 @@ void CollectionFilterWidget::FilterTextChanged(const QString &text) {
|
||||||
|
|
||||||
void CollectionFilterWidget::FilterDelayTimeout() {
|
void CollectionFilterWidget::FilterDelayTimeout() {
|
||||||
|
|
||||||
emit Filter(ui_->search_field->text());
|
|
||||||
if (filter_applies_to_model_) {
|
if (filter_applies_to_model_) {
|
||||||
model_->SetFilterText(ui_->search_field->text());
|
filter_->setFilterFixedString(ui_->search_field->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "collectionquery.h"
|
|
||||||
#include "collectionqueryoptions.h"
|
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
@ -43,6 +41,7 @@ class QKeyEvent;
|
||||||
|
|
||||||
class GroupByDialog;
|
class GroupByDialog;
|
||||||
class SavedGroupingManager;
|
class SavedGroupingManager;
|
||||||
|
class CollectionFilter;
|
||||||
class Ui_CollectionFilterWidget;
|
class Ui_CollectionFilterWidget;
|
||||||
|
|
||||||
class CollectionFilterWidget : public QWidget {
|
class CollectionFilterWidget : public QWidget {
|
||||||
|
@ -60,7 +59,9 @@ class CollectionFilterWidget : public QWidget {
|
||||||
AlwaysDelayed,
|
AlwaysDelayed,
|
||||||
};
|
};
|
||||||
|
|
||||||
void Init(CollectionModel *model);
|
void Init(CollectionModel *model, CollectionFilter *filter);
|
||||||
|
|
||||||
|
void setFilter(CollectionFilter *filter);
|
||||||
|
|
||||||
static QActionGroup *CreateGroupByActions(const QString &saved_groupings_settings_group, QObject *parent);
|
static QActionGroup *CreateGroupByActions(const QString &saved_groupings_settings_group, QObject *parent);
|
||||||
|
|
||||||
|
@ -96,7 +97,6 @@ class CollectionFilterWidget : public QWidget {
|
||||||
void UpPressed();
|
void UpPressed();
|
||||||
void DownPressed();
|
void DownPressed();
|
||||||
void ReturnPressed();
|
void ReturnPressed();
|
||||||
void Filter(const QString &text);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyReleaseEvent(QKeyEvent *e) override;
|
void keyReleaseEvent(QKeyEvent *e) override;
|
||||||
|
@ -117,6 +117,7 @@ class CollectionFilterWidget : public QWidget {
|
||||||
private:
|
private:
|
||||||
Ui_CollectionFilterWidget *ui_;
|
Ui_CollectionFilterWidget *ui_;
|
||||||
CollectionModel *model_;
|
CollectionModel *model_;
|
||||||
|
CollectionFilter *filter_;
|
||||||
|
|
||||||
GroupByDialog *group_by_dialog_;
|
GroupByDialog *group_by_dialog_;
|
||||||
SavedGroupingManager *groupings_manager_;
|
SavedGroupingManager *groupings_manager_;
|
||||||
|
@ -125,7 +126,7 @@ class CollectionFilterWidget : public QWidget {
|
||||||
QMenu *group_by_menu_;
|
QMenu *group_by_menu_;
|
||||||
QMenu *collection_menu_;
|
QMenu *collection_menu_;
|
||||||
QActionGroup *group_by_group_;
|
QActionGroup *group_by_group_;
|
||||||
QHash<QAction*, int> filter_ages_;
|
QHash<QAction*, int> filter_max_ages_;
|
||||||
|
|
||||||
QTimer *filter_delay_;
|
QTimer *filter_delay_;
|
||||||
|
|
||||||
|
@ -135,7 +136,6 @@ class CollectionFilterWidget : public QWidget {
|
||||||
QString settings_group_;
|
QString settings_group_;
|
||||||
QString saved_groupings_settings_group_;
|
QString saved_groupings_settings_group_;
|
||||||
QString settings_prefix_;
|
QString settings_prefix_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COLLECTIONFILTERWIDGET_H
|
#endif // COLLECTIONFILTERWIDGET_H
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="options">
|
<widget class="QToolButton" name="options">
|
||||||
|
<property name="accessibleName">
|
||||||
|
<string>MenuPopupToolButton</string>
|
||||||
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16</width>
|
<width>16</width>
|
||||||
|
|
|
@ -29,24 +29,27 @@
|
||||||
|
|
||||||
class CollectionItem : public SimpleTreeItem<CollectionItem> {
|
class CollectionItem : public SimpleTreeItem<CollectionItem> {
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum class Type {
|
||||||
Type_Root,
|
Root,
|
||||||
Type_Divider,
|
Divider,
|
||||||
Type_Container,
|
Container,
|
||||||
Type_Song,
|
Song,
|
||||||
Type_LoadingIndicator,
|
LoadingIndicator,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model)
|
explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model)
|
||||||
: SimpleTreeItem<CollectionItem>(Type_Root, _model),
|
: SimpleTreeItem<CollectionItem>(_model),
|
||||||
|
type(Type::Root),
|
||||||
container_level(-1),
|
container_level(-1),
|
||||||
compilation_artist_node_(nullptr) {}
|
compilation_artist_node_(nullptr) {}
|
||||||
|
|
||||||
explicit CollectionItem(Type _type, CollectionItem *_parent = nullptr)
|
explicit CollectionItem(const Type _type, CollectionItem *_parent = nullptr)
|
||||||
: SimpleTreeItem<CollectionItem>(_type, _parent),
|
: SimpleTreeItem<CollectionItem>(_parent),
|
||||||
|
type(_type),
|
||||||
container_level(-1),
|
container_level(-1),
|
||||||
compilation_artist_node_(nullptr) {}
|
compilation_artist_node_(nullptr) {}
|
||||||
|
|
||||||
|
Type type;
|
||||||
int container_level;
|
int container_level;
|
||||||
Song metadata;
|
Song metadata;
|
||||||
CollectionItem *compilation_artist_node_;
|
CollectionItem *compilation_artist_node_;
|
||||||
|
@ -55,4 +58,6 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
|
||||||
Q_DISABLE_COPY(CollectionItem)
|
Q_DISABLE_COPY(CollectionItem)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(CollectionItem::Type)
|
||||||
|
|
||||||
#endif // COLLECTIONITEM_H
|
#endif // COLLECTIONITEM_H
|
||||||
|
|
|
@ -127,10 +127,10 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
|
||||||
|
|
||||||
QString text = displayText(idx.data(), QLocale::system());
|
QString text = displayText(idx.data(), QLocale::system());
|
||||||
|
|
||||||
if (text.isEmpty() || !event) return false;
|
if (text.isEmpty()) return false;
|
||||||
|
|
||||||
switch (event->type()) {
|
switch (event->type()) {
|
||||||
case QEvent::ToolTip: {
|
case QEvent::ToolTip:{
|
||||||
|
|
||||||
QSize real_text = sizeHint(option, idx);
|
QSize real_text = sizeHint(option, idx);
|
||||||
QRect displayed_text = view->visualRect(idx);
|
QRect displayed_text = view->visualRect(idx);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -44,45 +42,47 @@
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "core/simpletreemodel.h"
|
#include "core/simpletreemodel.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
#include "covermanager/albumcoverloader.h"
|
|
||||||
#include "covermanager/albumcoverloaderoptions.h"
|
#include "covermanager/albumcoverloaderoptions.h"
|
||||||
|
#include "covermanager/albumcoverloaderresult.h"
|
||||||
|
#include "collectionmodelupdate.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionquery.h"
|
|
||||||
#include "collectionqueryoptions.h"
|
|
||||||
#include "collectionitem.h"
|
#include "collectionitem.h"
|
||||||
|
|
||||||
class QSettings;
|
class QTimer;
|
||||||
|
class Settings;
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
class CollectionBackend;
|
class CollectionBackend;
|
||||||
class CollectionDirectoryModel;
|
class CollectionDirectoryModel;
|
||||||
|
class CollectionFilter;
|
||||||
|
|
||||||
class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CollectionModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr);
|
explicit CollectionModel(SharedPtr<CollectionBackend> backend, Application *app, QObject *parent = nullptr);
|
||||||
~CollectionModel() override;
|
~CollectionModel() override;
|
||||||
|
|
||||||
static const int kPrettyCoverSize;
|
static const int kPrettyCoverSize;
|
||||||
static const char *kPixmapDiskCacheDir;
|
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
Role_Type = Qt::UserRole + 1,
|
Role_Type = Qt::UserRole + 1,
|
||||||
Role_ContainerType,
|
Role_ContainerType,
|
||||||
Role_SortText,
|
Role_SortText,
|
||||||
Role_Key,
|
Role_ContainerKey,
|
||||||
Role_Artist,
|
Role_Artist,
|
||||||
Role_IsDivider,
|
Role_IsDivider,
|
||||||
Role_Editable,
|
Role_Editable,
|
||||||
LastRole
|
LastRole
|
||||||
};
|
};
|
||||||
|
|
||||||
// These values get saved in QSettings - don't change them
|
// These values get saved in Settings - don't change them
|
||||||
enum class GroupBy {
|
enum class GroupBy {
|
||||||
None = 0,
|
None = 0,
|
||||||
AlbumArtist = 1,
|
AlbumArtist = 1,
|
||||||
|
@ -125,167 +125,176 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||||
bool operator!=(const Grouping other) const { return !(*this == other); }
|
bool operator!=(const Grouping other) const { return !(*this == other); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct QueryResult {
|
struct Options {
|
||||||
QueryResult() : create_va(false) {}
|
Options() : group_by(GroupBy::AlbumArtist, GroupBy::AlbumDisc, GroupBy::None),
|
||||||
|
show_dividers(true),
|
||||||
|
show_pretty_covers(true),
|
||||||
|
show_various_artists(true),
|
||||||
|
sort_skips_articles(true),
|
||||||
|
separate_albums_by_grouping(false) {}
|
||||||
|
|
||||||
SqlRowList rows;
|
Grouping group_by;
|
||||||
bool create_va;
|
bool show_dividers;
|
||||||
|
bool show_pretty_covers;
|
||||||
|
bool show_various_artists;
|
||||||
|
bool sort_skips_articles;
|
||||||
|
bool separate_albums_by_grouping;
|
||||||
|
CollectionFilterOptions filter_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
CollectionBackend *backend() const { return backend_; }
|
CollectionFilter *filter() const { return filter_; }
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
void ReloadSettings();
|
||||||
|
|
||||||
CollectionDirectoryModel *directory_model() const { return dir_model_; }
|
CollectionDirectoryModel *directory_model() const { return dir_model_; }
|
||||||
|
|
||||||
// Call before Init()
|
|
||||||
void set_show_various_artists(const bool show_various_artists) { show_various_artists_ = show_various_artists; }
|
|
||||||
|
|
||||||
// Get information about the collection
|
|
||||||
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
|
|
||||||
SongList GetChildSongs(const QModelIndex &idx) const;
|
|
||||||
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
|
||||||
|
|
||||||
// Might be accurate
|
|
||||||
int total_song_count() const { return total_song_count_; }
|
int total_song_count() const { return total_song_count_; }
|
||||||
int total_artist_count() const { return total_artist_count_; }
|
int total_artist_count() const { return total_artist_count_; }
|
||||||
int total_album_count() const { return total_album_count_; }
|
int total_album_count() const { return total_album_count_; }
|
||||||
|
|
||||||
// QAbstractItemModel
|
|
||||||
QVariant data(const QModelIndex &idx, const int role = Qt::DisplayRole) const override;
|
|
||||||
Qt::ItemFlags flags(const QModelIndex &idx) const override;
|
|
||||||
QStringList mimeTypes() const override;
|
|
||||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
|
||||||
bool canFetchMore(const QModelIndex &parent) const override;
|
|
||||||
|
|
||||||
// Whether or not to use album cover art, if it exists, in the collection view
|
|
||||||
void set_pretty_covers(const bool use_pretty_covers);
|
|
||||||
bool use_pretty_covers() const { return use_pretty_covers_; }
|
|
||||||
|
|
||||||
// Whether or not to show letters heading in the collection view
|
|
||||||
void set_show_dividers(const bool show_dividers);
|
|
||||||
|
|
||||||
// Reload settings.
|
|
||||||
void ReloadSettings();
|
|
||||||
|
|
||||||
// Utility functions for manipulating text
|
|
||||||
static QString TextOrUnknown(const QString &text);
|
|
||||||
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);
|
|
||||||
static QString SortTextForSong(const Song &song);
|
|
||||||
static QString SortTextForYear(const int year);
|
|
||||||
static QString SortTextForBitrate(const int bitrate);
|
|
||||||
|
|
||||||
quint64 icon_cache_disk_size() { return sIconCache->cacheSize(); }
|
quint64 icon_cache_disk_size() { return sIconCache->cacheSize(); }
|
||||||
|
|
||||||
|
const CollectionModel::Grouping GetGroupBy() const { return options_current_.group_by; }
|
||||||
|
void SetGroupBy(const CollectionModel::Grouping g, const std::optional<bool> separate_albums_by_grouping = std::optional<bool>());
|
||||||
|
|
||||||
static bool IsArtistGroupBy(const GroupBy group_by) {
|
static bool IsArtistGroupBy(const GroupBy group_by) {
|
||||||
return group_by == CollectionModel::GroupBy::Artist || group_by == CollectionModel::GroupBy::AlbumArtist;
|
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; }
|
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]; }
|
QMap<QString, CollectionItem*> container_nodes(const int i) { return container_nodes_[i]; }
|
||||||
QList<CollectionItem*> song_nodes() const { return song_nodes_.values(); }
|
QList<CollectionItem*> song_nodes() const { return song_nodes_.values(); }
|
||||||
int divider_nodes_count() const { return divider_nodes_.count(); }
|
int divider_nodes_count() const { return divider_nodes_.count(); }
|
||||||
|
|
||||||
|
// QAbstractItemModel
|
||||||
|
QVariant data(const QModelIndex &idx, const int role = Qt::DisplayRole) const override;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &idx) const override;
|
||||||
|
QStringList mimeTypes() const override;
|
||||||
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
|
// Utility functions for manipulating text
|
||||||
|
static QString DisplayText(const GroupBy group_by, const Song &song);
|
||||||
|
static QString TextOrUnknown(const QString &text);
|
||||||
|
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 PrettyFormat(const Song &song);
|
||||||
|
QString SortText(const GroupBy group_by, const int container_level, const Song &song, const bool sort_skips_articles);
|
||||||
|
static QString SortText(QString text);
|
||||||
|
static QString SortTextForNumber(const int number);
|
||||||
|
static QString SortTextForArtist(QString artist, const bool skip_articles);
|
||||||
|
static QString SortTextForSong(const Song &song);
|
||||||
|
static QString SortTextForYear(const int year);
|
||||||
|
static QString SortTextForBitrate(const int bitrate);
|
||||||
|
static bool IsSongTitleDataChanged(const Song &song1, const Song &song2);
|
||||||
|
QString ContainerKey(const GroupBy group_by, const Song &song) const;
|
||||||
|
|
||||||
|
// Get information about the collection
|
||||||
|
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
|
||||||
|
SongList GetChildSongs(const QModelIndex &idx) const;
|
||||||
|
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
||||||
|
|
||||||
void ExpandAll(CollectionItem *item = nullptr) const;
|
void ExpandAll(CollectionItem *item = nullptr) const;
|
||||||
|
|
||||||
const CollectionModel::Grouping GetGroupBy() const { return group_by_; }
|
|
||||||
void SetGroupBy(const CollectionModel::Grouping g, const std::optional<bool> separate_albums_by_grouping = std::optional<bool>());
|
|
||||||
|
|
||||||
static QString ContainerKey(const GroupBy group_by, const bool separate_albums_by_grouping, const Song &song);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TotalSongCountUpdated(const int count);
|
void TotalSongCountUpdated(const int count);
|
||||||
void TotalArtistCountUpdated(const int count);
|
void TotalArtistCountUpdated(const int count);
|
||||||
void TotalAlbumCountUpdated(const int count);
|
void TotalAlbumCountUpdated(const int count);
|
||||||
void GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping);
|
void GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping);
|
||||||
|
void SongsAdded(const SongList &songs);
|
||||||
|
void SongsRemoved(const SongList &songs);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetFilterMode(CollectionFilterOptions::FilterMode filter_mode);
|
void SetFilterMode(const CollectionFilterOptions::FilterMode filter_mode);
|
||||||
void SetFilterAge(const int filter_age);
|
void SetFilterMaxAge(const int filter_max_age);
|
||||||
void SetFilterText(const QString &filter_text);
|
|
||||||
|
|
||||||
void Init(const bool async = true);
|
void AddReAddOrUpdate(const SongList &songs);
|
||||||
void Reset();
|
void RemoveSongs(const SongList &songs);
|
||||||
void ResetAsync();
|
|
||||||
|
|
||||||
void SongsDiscovered(const SongList &songs);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void LazyPopulate(CollectionItem *item) override { LazyPopulate(item, true); }
|
|
||||||
void LazyPopulate(CollectionItem *parent, const bool signal);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
// From CollectionBackend
|
|
||||||
void SongsDeleted(const SongList &songs);
|
|
||||||
void SongsSlightlyChanged(const SongList &songs);
|
|
||||||
void TotalSongCountUpdatedSlot(const int count);
|
|
||||||
void TotalArtistCountUpdatedSlot(const int count);
|
|
||||||
void TotalAlbumCountUpdatedSlot(const int count);
|
|
||||||
static void ClearDiskCache();
|
|
||||||
|
|
||||||
// Called after ResetAsync
|
|
||||||
void ResetAsyncQueryFinished();
|
|
||||||
|
|
||||||
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Provides some optimizations for loading the list of items in the root.
|
void Clear();
|
||||||
// This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread.
|
|
||||||
CollectionQueryOptions PrepareQuery(CollectionItem *parent);
|
|
||||||
QueryResult RunQuery(const CollectionFilterOptions &filter_options = CollectionFilterOptions(), const CollectionQueryOptions &query_options = CollectionQueryOptions());
|
|
||||||
void PostQuery(CollectionItem *parent, const QueryResult &result, const bool signal);
|
|
||||||
|
|
||||||
bool HasCompilations(const QSqlDatabase &db, const CollectionFilterOptions &filter_options, const CollectionQueryOptions &query_options);
|
|
||||||
|
|
||||||
void BeginReset();
|
void BeginReset();
|
||||||
|
void EndReset();
|
||||||
|
|
||||||
// Functions for working with queries and creating items.
|
QVariant data(const CollectionItem *item, const int role) const;
|
||||||
// When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items.
|
|
||||||
// Filters are added for each parent item, restricting the songs returned to a particular album or artist for example.
|
|
||||||
static void SetQueryColumnSpec(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQueryOptions *query_options);
|
|
||||||
static void AddQueryWhere(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQueryOptions *query_options);
|
|
||||||
|
|
||||||
// Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend.
|
void ScheduleUpdate(const CollectionModelUpdate::Type type, const SongList &songs);
|
||||||
CollectionItem *ItemFromQuery(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level);
|
void ScheduleAddSongs(const SongList &songs);
|
||||||
CollectionItem *ItemFromSong(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level);
|
void ScheduleUpdateSongs(const SongList &songs);
|
||||||
|
void ScheduleRemoveSongs(const SongList &songs);
|
||||||
|
|
||||||
// The "Various Artists" node is an annoying special case.
|
void AddReAddOrUpdateSongsInternal(const SongList &songs);
|
||||||
CollectionItem *CreateCompilationArtistNode(const bool signal, CollectionItem *parent);
|
void AddSongsInternal(const SongList &songs);
|
||||||
|
void UpdateSongsInternal(const SongList &songs);
|
||||||
|
void RemoveSongsInternal(const SongList &songs);
|
||||||
|
|
||||||
// Helpers for ItemFromQuery and ItemFromSong
|
void CreateDividerItem(const QString ÷r_key, const QString &display_text, CollectionItem *parent);
|
||||||
CollectionItem *InitItem(const GroupBy group_by, const bool signal, CollectionItem *parent, const int container_level);
|
CollectionItem *CreateContainerItem(const GroupBy group_by, const int container_level, const QString &container_key, const Song &song, CollectionItem *parent);
|
||||||
void FinishItem(const GroupBy group_by, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item);
|
void CreateSongItem(const Song &song, CollectionItem *parent);
|
||||||
|
void SetSongItemData(CollectionItem *item, const Song &song);
|
||||||
|
CollectionItem *CreateCompilationArtistNode(CollectionItem *parent);
|
||||||
|
|
||||||
static QString DividerKey(const GroupBy group_by, CollectionItem *item);
|
void LoadSongsFromSqlAsync();
|
||||||
|
SongList LoadSongsFromSql(const CollectionFilterOptions &filter_options = CollectionFilterOptions());
|
||||||
|
|
||||||
|
static QString DividerKey(const GroupBy group_by, const Song &song, const QString &sort_text);
|
||||||
static QString DividerDisplayText(const GroupBy group_by, const QString &key);
|
static QString DividerDisplayText(const GroupBy group_by, const QString &key);
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; }
|
static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; }
|
||||||
QString AlbumIconPixmapCacheKey(const QModelIndex &idx) const;
|
QString AlbumIconPixmapCacheKey(const QModelIndex &idx) const;
|
||||||
QUrl AlbumIconPixmapDiskCacheKey(const QString &cache_key) const;
|
static QUrl AlbumIconPixmapDiskCacheKey(const QString &cache_key);
|
||||||
QVariant AlbumIcon(const QModelIndex &idx);
|
QVariant AlbumIcon(const QModelIndex &idx);
|
||||||
QVariant data(const CollectionItem *item, const int role) const;
|
void ClearItemPixmapCache(CollectionItem *item);
|
||||||
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
||||||
static qint64 MaximumCacheSize(QSettings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Reload();
|
||||||
|
void ScheduleReset();
|
||||||
|
void ProcessUpdate();
|
||||||
|
void LoadSongsFromSqlAsyncFinished();
|
||||||
|
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
|
||||||
|
|
||||||
|
// From CollectionBackend
|
||||||
|
void TotalSongCountUpdatedSlot(const int count);
|
||||||
|
void TotalArtistCountUpdatedSlot(const int count);
|
||||||
|
void TotalAlbumCountUpdatedSlot(const int count);
|
||||||
|
|
||||||
|
static void ClearDiskCache();
|
||||||
|
|
||||||
|
void RowsInserted(const QModelIndex &parent, const int first, const int last);
|
||||||
|
void RowsRemoved(const QModelIndex &parent, const int first, const int last);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CollectionBackend *backend_;
|
static QNetworkDiskCache *sIconCache;
|
||||||
|
SharedPtr<CollectionBackend> backend_;
|
||||||
Application *app_;
|
Application *app_;
|
||||||
CollectionDirectoryModel *dir_model_;
|
CollectionDirectoryModel *dir_model_;
|
||||||
bool show_various_artists_;
|
CollectionFilter *filter_;
|
||||||
|
QTimer *timer_reload_;
|
||||||
|
QTimer *timer_update_;
|
||||||
|
|
||||||
|
QPixmap pixmap_no_cover_;
|
||||||
|
QIcon icon_artist_;
|
||||||
|
|
||||||
|
Options options_current_;
|
||||||
|
Options options_active_;
|
||||||
|
|
||||||
|
bool use_disk_cache_;
|
||||||
|
AlbumCoverLoaderOptions::Types cover_types_;
|
||||||
|
|
||||||
int total_song_count_;
|
int total_song_count_;
|
||||||
int total_artist_count_;
|
int total_artist_count_;
|
||||||
int total_album_count_;
|
int total_album_count_;
|
||||||
|
|
||||||
CollectionFilterOptions filter_options_;
|
bool loading_;
|
||||||
Grouping group_by_;
|
|
||||||
bool separate_albums_by_grouping_;
|
QQueue<CollectionModelUpdate> updates_;
|
||||||
|
|
||||||
// Keyed on database ID
|
// Keyed on database ID
|
||||||
QMap<int, CollectionItem*> song_nodes_;
|
QMap<int, CollectionItem*> song_nodes_;
|
||||||
|
@ -296,22 +305,6 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||||
// Keyed on a letter, a year, a century, etc.
|
// Keyed on a letter, a year, a century, etc.
|
||||||
QMap<QString, CollectionItem*> divider_nodes_;
|
QMap<QString, CollectionItem*> divider_nodes_;
|
||||||
|
|
||||||
QIcon artist_icon_;
|
|
||||||
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_;
|
|
||||||
|
|
||||||
static QNetworkDiskCache *sIconCache;
|
|
||||||
|
|
||||||
int init_task_id_;
|
|
||||||
|
|
||||||
bool use_pretty_covers_;
|
|
||||||
bool show_dividers_;
|
|
||||||
bool use_disk_cache_;
|
|
||||||
bool use_lazy_loading_;
|
|
||||||
|
|
||||||
AlbumCoverLoaderOptions::Types cover_types_;
|
|
||||||
|
|
||||||
using ItemAndCacheKey = QPair<CollectionItem*, QString>;
|
using ItemAndCacheKey = QPair<CollectionItem*, QString>;
|
||||||
QMap<quint64, ItemAndCacheKey> pending_art_;
|
QMap<quint64, ItemAndCacheKey> pending_art_;
|
||||||
QSet<QString> pending_cache_keys_;
|
QSet<QString> pending_cache_keys_;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "collectionmodelupdate.h"
|
||||||
|
|
||||||
|
CollectionModelUpdate::CollectionModelUpdate(const Type &_type, const SongList &_songs)
|
||||||
|
: type(_type), songs(_songs) {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Strawberry Music Player
|
||||||
|
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
*
|
||||||
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Strawberry is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COLLECTIONMODELUPDATE_H
|
||||||
|
#define COLLECTIONMODELUPDATE_H
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
|
||||||
|
class CollectionModelUpdate {
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
AddReAddOrUpdate,
|
||||||
|
Add,
|
||||||
|
Update,
|
||||||
|
Remove,
|
||||||
|
};
|
||||||
|
explicit CollectionModelUpdate(const Type &_type, const SongList &_songs);
|
||||||
|
Type type;
|
||||||
|
SongList songs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLLECTIONMODELUPDATE_H
|
|
@ -2,7 +2,7 @@
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,96 +27,34 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QStringBuilder>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "core/sqlquery.h"
|
#include "core/sqlquery.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionqueryoptions.h"
|
#include "utilities/searchparserutils.h"
|
||||||
|
|
||||||
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const CollectionFilterOptions &filter_options)
|
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const CollectionFilterOptions &filter_options)
|
||||||
: QSqlQuery(db),
|
: SqlQuery(db),
|
||||||
songs_table_(songs_table),
|
songs_table_(songs_table),
|
||||||
fts_table_(fts_table),
|
|
||||||
include_unavailable_(false),
|
include_unavailable_(false),
|
||||||
join_with_fts_(false),
|
|
||||||
duplicates_only_(false),
|
duplicates_only_(false),
|
||||||
limit_(-1) {
|
limit_(-1) {
|
||||||
|
|
||||||
if (!filter_options.filter_text().isEmpty()) {
|
|
||||||
// We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS5:
|
|
||||||
// 1) Append * to all tokens.
|
|
||||||
// 2) Prefix "fts" to column names.
|
|
||||||
// 3) Remove colons which don't correspond to column names.
|
|
||||||
|
|
||||||
// Split on whitespace
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QStringList tokens(filter_options.filter_text().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts));
|
|
||||||
#else
|
|
||||||
QStringList tokens(filter_options.filter_text().split(QRegularExpression("\\s+"), QString::SkipEmptyParts));
|
|
||||||
#endif
|
|
||||||
QString query;
|
|
||||||
for (QString token : tokens) {
|
|
||||||
token.remove('(');
|
|
||||||
token.remove(')');
|
|
||||||
token.remove('"');
|
|
||||||
token.replace('-', ' ');
|
|
||||||
|
|
||||||
if (token.contains(':')) {
|
|
||||||
// Only prefix fts if the token is a valid column name.
|
|
||||||
if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0), Qt::CaseInsensitive)) {
|
|
||||||
// Account for multiple colons.
|
|
||||||
QString columntoken = token.section(':', 0, 0, QString::SectionIncludeTrailingSep);
|
|
||||||
QString subtoken = token.section(':', 1, -1);
|
|
||||||
subtoken.replace(":", " ");
|
|
||||||
subtoken = subtoken.trimmed();
|
|
||||||
if (!subtoken.isEmpty()) {
|
|
||||||
if (!query.isEmpty()) query.append(" ");
|
|
||||||
query += "fts" + columntoken + "\"" + subtoken + "\"*";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
token.replace(":", " ");
|
|
||||||
token = token.trimmed();
|
|
||||||
if (!query.isEmpty()) query.append(" ");
|
|
||||||
query += "\"" + token + "\"*";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!query.isEmpty()) query.append(" ");
|
|
||||||
query += "\"" + token + "\"*";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!query.isEmpty()) {
|
|
||||||
where_clauses_ << "fts.%fts_table_noprefix MATCH ?";
|
|
||||||
bound_values_ << query;
|
|
||||||
join_with_fts_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter_options.max_age() != -1) {
|
if (filter_options.max_age() != -1) {
|
||||||
qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - filter_options.max_age();
|
qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - filter_options.max_age();
|
||||||
|
|
||||||
where_clauses_ << "ctime > ?";
|
where_clauses_ << QStringLiteral("ctime > ?");
|
||||||
bound_values_ << cutoff;
|
bound_values_ << cutoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Currently you cannot use any FilterMode other than All and FTS at the same time.
|
|
||||||
// Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
|
|
||||||
// The query takes about 20 seconds on my machine then. Why?
|
|
||||||
// Untagged mode could work with additional filtering but I'm disabling it just to be consistent
|
|
||||||
// this way filtering is available only in the All mode.
|
|
||||||
// Remember though that when you fix the Duplicates + FTS cooperation, enable the filtering in both Duplicates and Untagged modes.
|
|
||||||
duplicates_only_ = filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Duplicates;
|
duplicates_only_ = filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Duplicates;
|
||||||
|
|
||||||
if (filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Untagged) {
|
if (filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Untagged) {
|
||||||
where_clauses_ << "(artist = '' OR album = '' OR title ='')";
|
where_clauses_ << QStringLiteral("(artist = '' OR album = '' OR title ='')");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -124,16 +62,16 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
|
||||||
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
||||||
|
|
||||||
// Ignore 'literal' for IN
|
// Ignore 'literal' for IN
|
||||||
if (op.compare("IN", Qt::CaseInsensitive) == 0) {
|
if (op.compare(QLatin1String("IN"), Qt::CaseInsensitive) == 0) {
|
||||||
QStringList values = value.toStringList();
|
QStringList values = value.toStringList();
|
||||||
QStringList final_values;
|
QStringList final_values;
|
||||||
final_values.reserve(values.count());
|
final_values.reserve(values.count());
|
||||||
for (const QString &single_value : values) {
|
for (const QString &single_value : values) {
|
||||||
final_values.append("?");
|
final_values.append(QStringLiteral("?"));
|
||||||
bound_values_ << single_value;
|
bound_values_ << single_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
where_clauses_ << QString("%1 IN (" + final_values.join(",") + ")").arg(column);
|
where_clauses_ << QStringLiteral("%1 IN (%2)").arg(column, final_values.join(QLatin1Char(',')));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
||||||
|
@ -142,7 +80,7 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
|
||||||
#else
|
#else
|
||||||
if (value.type() == QVariant::Int) {
|
if (value.type() == QVariant::Int) {
|
||||||
#endif
|
#endif
|
||||||
where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString());
|
where_clauses_ << QStringLiteral("%1 %2 %3").arg(column, op, value.toString());
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
@ -151,36 +89,26 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
|
||||||
value.type() == QVariant::String
|
value.type() == QVariant::String
|
||||||
#endif
|
#endif
|
||||||
&& value.toString().isNull()) {
|
&& value.toString().isNull()) {
|
||||||
where_clauses_ << QString("%1 %2 ?").arg(column, op);
|
where_clauses_ << QStringLiteral("%1 %2 ?").arg(column, op);
|
||||||
bound_values_ << QString("");
|
bound_values_ << QLatin1String("");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
where_clauses_ << QString("%1 %2 ?").arg(column, op);
|
where_clauses_ << QStringLiteral("%1 %2 ?").arg(column, op);
|
||||||
bound_values_ << value;
|
bound_values_ << value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionQuery::AddWhereArtist(const QVariant &value) {
|
|
||||||
|
|
||||||
where_clauses_ << QString("((artist = ? AND albumartist = '') OR albumartist = ?)");
|
|
||||||
bound_values_ << value;
|
|
||||||
bound_values_ << value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollectionQuery::AddCompilationRequirement(const bool compilation) {
|
void CollectionQuery::AddCompilationRequirement(const bool compilation) {
|
||||||
// The unary + is added to prevent sqlite from using the index idx_comp_artist.
|
// The unary + is added to prevent sqlite from using the index idx_comp_artist.
|
||||||
// When joining with fts, sqlite 3.8 has a tendency to use this index and thereby nesting the tables in an order which gives very poor performance
|
where_clauses_ << QStringLiteral("+compilation_effective = %1").arg(compilation ? 1 : 0);
|
||||||
|
|
||||||
where_clauses_ << QString("+compilation_effective = %1").arg(compilation ? 1 : 0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionQuery::GetInnerQuery() const {
|
QString CollectionQuery::GetInnerQuery() const {
|
||||||
return duplicates_only_
|
return duplicates_only_
|
||||||
? QString(" INNER JOIN (select * from duplicated_songs) dsongs "
|
? QStringLiteral(" INNER JOIN (select * from duplicated_songs) dsongs "
|
||||||
"ON (%songs_table.artist = dsongs.dup_artist "
|
"ON (%songs_table.artist = dsongs.dup_artist "
|
||||||
"AND %songs_table.album = dsongs.dup_album "
|
"AND %songs_table.album = dsongs.dup_album "
|
||||||
"AND %songs_table.title = dsongs.dup_title) ")
|
"AND %songs_table.title = dsongs.dup_title) ")
|
||||||
|
@ -189,31 +117,22 @@ QString CollectionQuery::GetInnerQuery() const {
|
||||||
|
|
||||||
bool CollectionQuery::Exec() {
|
bool CollectionQuery::Exec() {
|
||||||
|
|
||||||
QString sql;
|
QString sql = QStringLiteral("SELECT %1 FROM %2 %3").arg(column_spec_, songs_table_, GetInnerQuery());
|
||||||
|
|
||||||
if (join_with_fts_) {
|
|
||||||
sql = QString("SELECT %1 FROM %2 INNER JOIN %3 AS fts ON %2.ROWID = fts.ROWID").arg(column_spec_, songs_table_, fts_table_);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sql = QString("SELECT %1 FROM %2 %3").arg(column_spec_, songs_table_, GetInnerQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList where_clauses(where_clauses_);
|
QStringList where_clauses(where_clauses_);
|
||||||
if (!include_unavailable_) {
|
if (!include_unavailable_) {
|
||||||
where_clauses << "unavailable = 0";
|
where_clauses << QStringLiteral("unavailable = 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!where_clauses.isEmpty()) sql += " WHERE " + where_clauses.join(" AND ");
|
if (!where_clauses.isEmpty()) sql += QLatin1String(" WHERE ") + where_clauses.join(QLatin1String(" AND "));
|
||||||
|
|
||||||
if (!order_by_.isEmpty()) sql += " ORDER BY " + order_by_;
|
if (!order_by_.isEmpty()) sql += QLatin1String(" ORDER BY ") + order_by_;
|
||||||
|
|
||||||
if (limit_ != -1) sql += " LIMIT " + QString::number(limit_);
|
if (limit_ != -1) sql += QLatin1String(" LIMIT ") + QString::number(limit_);
|
||||||
|
|
||||||
sql.replace("%songs_table", songs_table_);
|
sql.replace(QLatin1String("%songs_table"), songs_table_);
|
||||||
sql.replace("%fts_table_noprefix", fts_table_.section('.', -1, -1));
|
|
||||||
sql.replace("%fts_table", fts_table_);
|
|
||||||
|
|
||||||
QSqlQuery::prepare(sql);
|
if (!QSqlQuery::prepare(sql)) return false;
|
||||||
|
|
||||||
// Bind values
|
// Bind values
|
||||||
for (const QVariant &value : bound_values_) {
|
for (const QVariant &value : bound_values_) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -29,20 +29,20 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
|
||||||
|
#include "core/sqlquery.h"
|
||||||
|
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.h"
|
||||||
#include "collectionqueryoptions.h"
|
|
||||||
|
|
||||||
class CollectionQuery : public QSqlQuery {
|
class CollectionQuery : public SqlQuery {
|
||||||
public:
|
public:
|
||||||
explicit CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const CollectionFilterOptions &filter_options = CollectionFilterOptions());
|
explicit CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const CollectionFilterOptions &filter_options = CollectionFilterOptions());
|
||||||
|
|
||||||
QVariant Value(const int column) const;
|
QVariant Value(const int column) const;
|
||||||
QVariant value(const int column) const { return Value(column); }
|
QVariant value(const int column) const { return Value(column); }
|
||||||
|
|
||||||
bool Exec();
|
bool Exec();
|
||||||
bool exec() { return QSqlQuery::exec(); }
|
bool exec() { return SqlQuery::exec(); }
|
||||||
|
|
||||||
bool Next();
|
bool Next();
|
||||||
|
|
||||||
|
@ -51,7 +51,6 @@ class CollectionQuery : public QSqlQuery {
|
||||||
QStringList where_clauses() const { return where_clauses_; }
|
QStringList where_clauses() const { return where_clauses_; }
|
||||||
QVariantList bound_values() const { return bound_values_; }
|
QVariantList bound_values() const { return bound_values_; }
|
||||||
bool include_unavailable() const { return include_unavailable_; }
|
bool include_unavailable() const { return include_unavailable_; }
|
||||||
bool join_with_fts() const { return join_with_fts_; }
|
|
||||||
bool duplicates_only() const { return duplicates_only_; }
|
bool duplicates_only() const { return duplicates_only_; }
|
||||||
int limit() const { return limit_; }
|
int limit() const { return limit_; }
|
||||||
|
|
||||||
|
@ -62,10 +61,10 @@ class CollectionQuery : public QSqlQuery {
|
||||||
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
|
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
|
||||||
|
|
||||||
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
||||||
|
|
||||||
// Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
|
// Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
|
||||||
// Please note that IN operator expects a QStringList as value.
|
// Please note that IN operator expects a QStringList as value.
|
||||||
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
void AddWhere(const QString &column, const QVariant &value, const QString &op = QStringLiteral("="));
|
||||||
void AddWhereArtist(const QVariant &value);
|
|
||||||
|
|
||||||
void SetBoundValues(const QVariantList &bound_values) { bound_values_ = bound_values; }
|
void SetBoundValues(const QVariantList &bound_values) { bound_values_ = bound_values; }
|
||||||
void SetDuplicatesOnly(const bool duplicates_only) { duplicates_only_ = duplicates_only; }
|
void SetDuplicatesOnly(const bool duplicates_only) { duplicates_only_ = duplicates_only; }
|
||||||
|
@ -78,7 +77,6 @@ class CollectionQuery : public QSqlQuery {
|
||||||
|
|
||||||
QSqlDatabase db_;
|
QSqlDatabase db_;
|
||||||
QString songs_table_;
|
QString songs_table_;
|
||||||
QString fts_table_;
|
|
||||||
|
|
||||||
QString column_spec_;
|
QString column_spec_;
|
||||||
QString order_by_;
|
QString order_by_;
|
||||||
|
@ -86,7 +84,6 @@ class CollectionQuery : public QSqlQuery {
|
||||||
QVariantList bound_values_;
|
QVariantList bound_values_;
|
||||||
|
|
||||||
bool include_unavailable_;
|
bool include_unavailable_;
|
||||||
bool join_with_fts_;
|
|
||||||
bool duplicates_only_;
|
bool duplicates_only_;
|
||||||
int limit_;
|
int limit_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "collectionqueryoptions.h"
|
#include "collectionqueryoptions.h"
|
||||||
#include "collectionfilteroptions.h"
|
|
||||||
|
|
||||||
CollectionQueryOptions::CollectionQueryOptions()
|
CollectionQueryOptions::CollectionQueryOptions()
|
||||||
: compilation_requirement_(CollectionQueryOptions::CompilationRequirement::None),
|
: compilation_requirement_(CollectionQueryOptions::CompilationRequirement::None),
|
||||||
|
|
|
@ -30,7 +30,7 @@ class CollectionQueryOptions {
|
||||||
explicit CollectionQueryOptions();
|
explicit CollectionQueryOptions();
|
||||||
|
|
||||||
struct Where {
|
struct Where {
|
||||||
explicit Where(const QString _column = QString(), const QVariant _value = QString(), const QString _op = QString()) : column(_column), value(_value), op(_op) {}
|
explicit Where(const QString &_column = QString(), const QVariant &_value = QString(), const QString &_op = QString()) : column(_column), value(_value), op(_op) {}
|
||||||
QString column;
|
QString column;
|
||||||
QVariant value;
|
QVariant value;
|
||||||
QString op;
|
QString op;
|
||||||
|
@ -51,7 +51,7 @@ class CollectionQueryOptions {
|
||||||
void set_query_have_compilations(const bool query_have_compilations) { query_have_compilations_ = query_have_compilations; }
|
void set_query_have_compilations(const bool query_have_compilations) { query_have_compilations_ = query_have_compilations; }
|
||||||
|
|
||||||
QList<Where> where_clauses() const { return where_clauses_; }
|
QList<Where> where_clauses() const { return where_clauses_; }
|
||||||
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
void AddWhere(const QString &column, const QVariant &value, const QString &op = QStringLiteral("="));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString column_spec_;
|
QString column_spec_;
|
||||||
|
|
|
@ -19,10 +19,11 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
#include "collectiontask.h"
|
#include "collectiontask.h"
|
||||||
|
|
||||||
CollectionTask::CollectionTask(TaskManager *task_manager, const QString &message) : task_manager_(task_manager), task_id_(-1) {
|
CollectionTask::CollectionTask(SharedPtr<TaskManager> task_manager, const QString &message) : task_manager_(task_manager), task_id_(-1) {
|
||||||
|
|
||||||
if (task_manager_) task_id_ = task_manager_->StartTask(message);
|
if (task_manager_) task_id_ = task_manager_->StartTask(message);
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,17 @@
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/shared_ptr.h"
|
||||||
|
|
||||||
class TaskManager;
|
class TaskManager;
|
||||||
|
|
||||||
class CollectionTask {
|
class CollectionTask {
|
||||||
public:
|
public:
|
||||||
explicit CollectionTask(TaskManager *task_manager, const QString &message);
|
explicit CollectionTask(SharedPtr<TaskManager> task_manager, const QString &message);
|
||||||
~CollectionTask();
|
~CollectionTask();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TaskManager *task_manager_;
|
SharedPtr<TaskManager> task_manager_;
|
||||||
int task_id_;
|
int task_id_;
|
||||||
|
|
||||||
Q_DISABLE_COPY(CollectionTask)
|
Q_DISABLE_COPY(CollectionTask)
|
||||||
|
|