Compare commits
403 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c87b56adcb | ||
|
|
8ddd309d5d | ||
|
|
8c8acbb546 | ||
|
|
fe30f27af3 | ||
|
|
d5d2eaba8a | ||
|
|
e8f64bfe8f | ||
|
|
79b03993b0 | ||
|
|
e3b8f9cb8c | ||
|
|
819463a865 | ||
|
|
c69777ca39 | ||
|
|
40f3e828aa | ||
|
|
dc8520ebec | ||
|
|
7ffbedf0dd | ||
|
|
826fad1ad4 | ||
|
|
1c4d3aebad | ||
|
|
ff6e93fc15 | ||
|
|
17e88bb97d | ||
|
|
d3dd26c596 | ||
|
|
c2a7509e0e | ||
|
|
079040b721 | ||
|
|
b80d239820 | ||
|
|
c3ff43dac2 | ||
|
|
4ac51afd8f | ||
|
|
26eab51698 | ||
|
|
388dcf2a1f | ||
|
|
9bfd14d067 | ||
|
|
061e0562d1 | ||
|
|
2ba20b013d | ||
|
|
dc36f87a6e | ||
|
|
e82ecb48b8 | ||
|
|
c56a134179 | ||
|
|
11028456ad | ||
|
|
81fcb02974 | ||
|
|
5ef4976c53 | ||
|
|
160356072f | ||
|
|
c10df5b634 | ||
|
|
70fdd5b0b3 | ||
|
|
6c1ec51fab | ||
|
|
66b1c22174 | ||
|
|
97138fd6b9 | ||
|
|
03c69be421 | ||
|
|
3a9a952cb0 | ||
|
|
3bd0331aa3 | ||
|
|
3ecf224d91 | ||
|
|
37743606a2 | ||
|
|
58587f4a9a | ||
|
|
464fde1851 | ||
|
|
2639498642 | ||
|
|
49f074737c | ||
|
|
3d53a8b434 | ||
|
|
e260433c7a | ||
|
|
88ef8bff0b | ||
|
|
fbdac36f6f | ||
|
|
da3876bd83 | ||
|
|
d303e700ae | ||
|
|
92a1173b9e | ||
|
|
9f7ebb1ac7 | ||
|
|
1a8690e1f2 | ||
|
|
6543e4c5da | ||
|
|
dd904fe3c2 | ||
|
|
c14cc6bf0b | ||
|
|
95c265ffd3 | ||
|
|
31c1ae68df | ||
|
|
f2eb0c3b6b | ||
|
|
32be33847c | ||
|
|
3100b0c044 | ||
|
|
f4ec3ab379 | ||
|
|
cdd7faa9bb | ||
|
|
e7b35aeaf7 | ||
|
|
696256eb5b | ||
|
|
8ad560ce0e | ||
|
|
1c71506f62 | ||
|
|
8bea6ec5b0 | ||
|
|
e8144487ee | ||
|
|
41d9d15dda | ||
|
|
124b97c024 | ||
|
|
98e0b45403 | ||
|
|
1f2b8d8bf6 | ||
|
|
8327751b91 | ||
|
|
6417f89596 | ||
|
|
2e53656f44 | ||
|
|
822cf0ad07 | ||
|
|
67f04a81b3 | ||
|
|
9232ad0125 | ||
|
|
0de87b3e1e | ||
|
|
74b8cd6156 | ||
|
|
ac959387fe | ||
|
|
ffd8ce9281 | ||
|
|
d8052b295f | ||
|
|
625929133c | ||
|
|
79c28e7e1d | ||
|
|
01f4a79f07 | ||
|
|
47c5a2215e | ||
|
|
bb6e38630f | ||
|
|
85acf69d4f | ||
|
|
69ba56ebef | ||
|
|
527a61f909 | ||
|
|
a15ebcde24 | ||
|
|
e80629cc40 | ||
|
|
d956ae9726 | ||
|
|
0435d6d7b9 | ||
|
|
59752e6fe9 | ||
|
|
d3ee0bfdf4 | ||
|
|
463df825f0 | ||
|
|
480559ef0b | ||
|
|
2a4fd346f9 | ||
|
|
6200fed224 | ||
|
|
e1b4585dc7 | ||
|
|
c5eb72fa9f | ||
|
|
d2e19ef4c3 | ||
|
|
4509c43b81 | ||
|
|
e2ffe716e7 | ||
|
|
0d9ededea9 | ||
|
|
2edc4369d2 | ||
|
|
32baa95500 | ||
|
|
ad9f3ce078 | ||
|
|
c1f66b1885 | ||
|
|
e0be15cf01 | ||
|
|
93660bfc81 | ||
|
|
6446942e73 | ||
|
|
0038cf8c4e | ||
|
|
7f177aef08 | ||
|
|
a7a42ea5ec | ||
|
|
14cddfd42f | ||
|
|
ae0ce65674 | ||
|
|
9c9926d5a7 | ||
|
|
95a3c41baa | ||
|
|
f2518baef9 | ||
|
|
4be9265546 | ||
|
|
9f9c46e370 | ||
|
|
5816d0bb12 | ||
|
|
70c2b99771 | ||
|
|
6177d4a2c4 | ||
|
|
05f012e590 | ||
|
|
cc0506490f | ||
|
|
06114c9835 | ||
|
|
2518e4d47d | ||
|
|
ceea805196 | ||
|
|
ae7e515945 | ||
|
|
b275f91a58 | ||
|
|
b8ef96028c | ||
|
|
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 |
575
.github/workflows/build.yml
vendored
133
.gitignore
vendored
@@ -1,120 +1,17 @@
|
|||||||
# This file is used to ignore files which are generated
|
/build
|
||||||
# ----------------------------------------------------------------------------
|
/bin
|
||||||
|
/CMakeLists.txt.user
|
||||||
# Build
|
/.kdev4
|
||||||
build/
|
/strawberry.kdev4
|
||||||
bin/
|
/.vscode
|
||||||
|
/.code-workspace
|
||||||
# CMake
|
/.sublime-workspace
|
||||||
CMakeLists.txt.user
|
/.idea
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles
|
|
||||||
CMakeScripts
|
|
||||||
Makefile*
|
|
||||||
Testing
|
|
||||||
cmake_install.cmake
|
|
||||||
install_manifest.txt
|
|
||||||
compile_commands.json
|
|
||||||
CTestTestfile.cmake
|
|
||||||
_deps
|
|
||||||
|
|
||||||
# Prerequisites
|
|
||||||
*.d
|
|
||||||
|
|
||||||
# Compiled Object files
|
|
||||||
*.slo
|
|
||||||
*.lo
|
|
||||||
*.o
|
|
||||||
*.obj
|
|
||||||
|
|
||||||
# Precompiled Headers
|
|
||||||
*.gch
|
|
||||||
*.pch
|
|
||||||
|
|
||||||
# Compiled Dynamic libraries
|
|
||||||
*.so
|
|
||||||
*.so.*
|
|
||||||
*.dylib
|
|
||||||
*.dll
|
|
||||||
|
|
||||||
# Fortran module files
|
|
||||||
*.mod
|
|
||||||
*.smod
|
|
||||||
|
|
||||||
# Compiled Static libraries
|
|
||||||
*.lai
|
|
||||||
*.la
|
|
||||||
*.a
|
|
||||||
*.lib
|
|
||||||
|
|
||||||
# Executables
|
|
||||||
*.exe
|
|
||||||
*.out
|
|
||||||
*.app
|
|
||||||
|
|
||||||
# Dump files
|
|
||||||
*.core
|
|
||||||
*.stackdump
|
|
||||||
|
|
||||||
# Qt
|
|
||||||
*build-*
|
|
||||||
moc_*.cpp
|
|
||||||
moc_*.h
|
|
||||||
qrc_*.cpp
|
|
||||||
ui_*.h
|
|
||||||
*.moc
|
|
||||||
*.qm
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*~
|
|
||||||
*.autosave
|
|
||||||
*.orig
|
|
||||||
*.rej
|
|
||||||
.*.kate-swp
|
|
||||||
.swp.*
|
|
||||||
.*.swp
|
|
||||||
*.flc
|
|
||||||
|
|
||||||
# Directory files
|
|
||||||
.directory
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# MinGW generated files
|
|
||||||
*.Debug
|
|
||||||
*.Release
|
|
||||||
|
|
||||||
# Package files
|
|
||||||
*.spec
|
|
||||||
*.nsi
|
|
||||||
*.plist
|
|
||||||
|
|
||||||
# Stuff in dist
|
|
||||||
maketarball.sh
|
|
||||||
changelog
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
translations.pot
|
|
||||||
zanata.xml
|
|
||||||
.zanata-cache/
|
|
||||||
|
|
||||||
# QtCreator
|
|
||||||
CMakeLists.txt.user*
|
|
||||||
*.pro.user
|
|
||||||
*.pro.user.*
|
|
||||||
*creator.user*
|
|
||||||
target_wrapper.*
|
|
||||||
compile_commands.json
|
|
||||||
|
|
||||||
*.kdev4
|
|
||||||
*.vscode
|
|
||||||
*.code-workspace
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# MSVC
|
|
||||||
CMakeSettings.json
|
|
||||||
/.vs
|
/.vs
|
||||||
/out
|
/out
|
||||||
|
/CMakeSettings.json
|
||||||
# CLion
|
/dist/scripts/maketarball.sh
|
||||||
/.idea
|
/dist/unix/strawberry.spec
|
||||||
|
/dist/windows/strawberry.nsi
|
||||||
|
/debian/control
|
||||||
|
/debian/changelog
|
||||||
|
|||||||
6
3rdparty/getopt/getopt.cpp
vendored
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,23 +99,17 @@ 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(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(Backtrace)
|
find_package(Backtrace)
|
||||||
if(Backtrace_FOUND)
|
if(Backtrace_FOUND)
|
||||||
set(HAVE_BACKTRACE ON)
|
set(HAVE_BACKTRACE ON)
|
||||||
endif()
|
endif()
|
||||||
find_package(Boost REQUIRED)
|
find_package(Boost CONFIG)
|
||||||
if(USE_ICU)
|
if(NOT Boost_FOUND)
|
||||||
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
if(ICU_FOUND)
|
|
||||||
set(HAVE_ICU ON)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
find_package(Iconv)
|
|
||||||
endif()
|
endif()
|
||||||
|
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
||||||
find_package(Protobuf CONFIG)
|
find_package(Protobuf CONFIG)
|
||||||
if(NOT Protobuf_FOUND)
|
if(NOT Protobuf_FOUND)
|
||||||
find_package(Protobuf REQUIRED)
|
find_package(Protobuf REQUIRED)
|
||||||
@@ -266,36 +260,36 @@ 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()
|
||||||
|
set(HAVE_TAGLIB ON)
|
||||||
|
else()
|
||||||
|
set(HAVE_TAGLIB OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# TAGPARSER
|
# TAGPARSER
|
||||||
if(USE_TAGPARSER)
|
if(USE_TAGPARSER)
|
||||||
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
||||||
|
set(HAVE_TAGPARSER ON)
|
||||||
|
else()
|
||||||
|
set(HAVE_TAGPARSER OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
||||||
|
|
||||||
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
if(NOT HAVE_TAGLIB AND NOT HAVE_TAGPARSER)
|
||||||
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -305,18 +299,13 @@ if(QT_VERSION_MAJOR EQUAL 5)
|
|||||||
else()
|
else()
|
||||||
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication-qt${QT_VERSION_MAJOR}")
|
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication-qt${QT_VERSION_MAJOR}")
|
||||||
endif()
|
endif()
|
||||||
find_package(${KDSINGLEAPPLICATION_NAME})
|
find_package(${KDSINGLEAPPLICATION_NAME} 1.1.0)
|
||||||
if(TARGET KDAB::kdsingleapplication)
|
if(TARGET KDAB::kdsingleapplication)
|
||||||
if(QT_VERSION_MAJOR EQUAL 5)
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication_VERSION}")
|
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication_VERSION}")
|
||||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication-qt6_VERSION}")
|
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication-qt6_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
if(KDSINGLEAPPLICATION_VERSION VERSION_GREATER_EQUAL 1.0.95)
|
|
||||||
set(HAVE_KDSINGLEAPPLICATION_OPTIONS ON)
|
|
||||||
else()
|
|
||||||
set(HAVE_KDSINGLEAPPLICATION_OPTIONS OFF)
|
|
||||||
endif()
|
|
||||||
message(STATUS "Using system KDSingleApplication (Version ${KDSINGLEAPPLICATION_VERSION})")
|
message(STATUS "Using system KDSingleApplication (Version ${KDSINGLEAPPLICATION_VERSION})")
|
||||||
set(SINGLEAPPLICATION_LIBRARIES KDAB::kdsingleapplication)
|
set(SINGLEAPPLICATION_LIBRARIES KDAB::kdsingleapplication)
|
||||||
else()
|
else()
|
||||||
@@ -450,6 +439,7 @@ 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"
|
||||||
@@ -474,9 +464,11 @@ if(NOT CMAKE_CROSSCOMPILING)
|
|||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql)
|
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql)
|
||||||
check_cxx_source_runs("
|
check_cxx_source_runs("
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
int main() {
|
int main(int argc, char *argv[]) {
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");
|
QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");
|
||||||
db.setDatabaseName(\":memory:\");
|
db.setDatabaseName(\":memory:\");
|
||||||
if (!db.open()) { return 1; }
|
if (!db.open()) { return 1; }
|
||||||
@@ -487,25 +479,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
|
||||||
@@ -519,6 +492,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)
|
||||||
@@ -564,15 +539,11 @@ 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()
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
if(HAVE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
||||||
message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.")
|
message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.")
|
||||||
endif()
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
97
Changelog
@@ -2,6 +2,103 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
|
Unreleased:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed Tidal Open API cover provider to only login when needed instead of on startup.
|
||||||
|
* Fixed KDE added keyboard accelerator characters (ampersands) appearing in sidebar (#1400, #1389, #1476).
|
||||||
|
* Fixed KDE added keyboard accelerator characters (ampersands) appearing when editing playlist name (#1499).
|
||||||
|
* Fixed collection "Search for this" adding prefix without value (#1510).
|
||||||
|
* (macOS) Fixed missing Spotify.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Improved volume adjustment and track seeking using touchpad (#1498).
|
||||||
|
|
||||||
|
Version 1.1.1 (2024.07.22):
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed compilation songs being split into different albums when using album grouping.
|
||||||
|
* Fixed adding playlist columns not working when stretch mode is disabled (#1085).
|
||||||
|
* Fixed resetting playlist columns.
|
||||||
|
* Fixed adding songs to playlist adding all songs instead of filtered songs.
|
||||||
|
* Fixed collection filter matching entire text instead of individual words.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Use same code for collection and playlist filter search.
|
||||||
|
|
||||||
|
Version 1.1.0 (2024.07.14):
|
||||||
|
|
||||||
|
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).
|
||||||
|
* Fixed open in file manager feature not handling missing XDG_DATA_DIRS variable.
|
||||||
|
* Fixed collection pixmap disk cache and moodbar cache with newer Qt versions.
|
||||||
|
* Fixed reading common metadata CUE's with multiple files (#1463).
|
||||||
|
* Fixed playlist header stretch mode to only resize the right column of the column being resized.
|
||||||
|
* Fixed adding columns to playlist header not working when not using stretch mode (#1085).
|
||||||
|
* Fixed severe memory leak (!) in context album cover fading (#1464).
|
||||||
|
* Separate albums by different artist in album groupings (#1276).
|
||||||
|
* Removed -new-window parameter from dolphin command for open in file manager (#1412).
|
||||||
|
* Only use playbin3 with GStreamer 1.24 and higher, not with GStreamer 1.22 or lower.
|
||||||
|
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
|
||||||
|
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
|
||||||
|
* (Windows) Fixed update window blocking sponsor window on startup.
|
||||||
|
|
||||||
|
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 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).
|
||||||
|
* Save volume to settings when adjusting (#1272).
|
||||||
|
* Resolve song from collection using track with Cue in XSPF (#1181).
|
||||||
|
* Added background image to sidebar.
|
||||||
|
* Read metadata from RIFF WAV files (#1424).
|
||||||
|
* Use original path instead of canonical path when adding directories to the collection.
|
||||||
|
* Only apply added/removed collection directories when settings are saved.
|
||||||
|
* Detect and handle different text encodings when reading CUE files (#1429).
|
||||||
|
* The collection has been rewritten and improved (model/filter/search) (#392).
|
||||||
|
* Improve error messages from tag reader.
|
||||||
|
* (Unix) Add experimental GStreamer pipewire support.
|
||||||
|
* (Windows) Add experimental exclusive mode for WASAPI.
|
||||||
|
* (Windows MSVC) Added experimental ASIO support.
|
||||||
|
* (Windows MSVC) Add back WASAPI2.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Letras lyrics provider.
|
||||||
|
* Open Tidal API (openapi.tidal.com) cover provider.
|
||||||
|
* Turbine analyzer.
|
||||||
|
* WaveRubber analyzer.
|
||||||
|
* Spotify streaming support.
|
||||||
|
|
||||||
|
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):
|
Version 1.0.22 (2023.12.09):
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|||||||
24
README.md
@@ -1,4 +1,4 @@
|
|||||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||||
=======================
|
=======================
|
||||||
[](https://github.com/sponsors/jonaski)
|
[](https://github.com/sponsors/jonaski)
|
||||||
[](https://patreon.com/jonaskvinge)
|
[](https://patreon.com/jonaskvinge)
|
||||||
@@ -19,7 +19,7 @@ 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
|
* Translations: https://crowdin.com/project/strawberrymusicplayer/
|
||||||
|
|
||||||
### :bangbang: Opening an issue
|
### :bangbang: Opening an issue
|
||||||
|
|
||||||
@@ -29,12 +29,12 @@ Resources:
|
|||||||
* 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/
|
* 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. [Ko-fi](https://ko-fi.com/jonaskvinge)
|
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
|
||||||
4. [PayPal](https://paypal.me/jonaskvinge)
|
4. [PayPal](https://paypal.me/jonaskvinge)
|
||||||
@@ -54,18 +54,18 @@ Funding developers is a way to contribute to open source projects you appreciate
|
|||||||
* 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 [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/), [elyrics.net](https://www.elyrics.net/) and [lyricsmode.com](https://www.lyricsmode.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.
|
||||||
|
|
||||||
**macOS releases are currently limited to sponsors. This is because macOS releases require a developer account, Apple hardware and maintaining all libraries strawberry depends on. If you are sponsoring strawberry, e-mail support@strawberrymusicplayer.org for access to downloads.**
|
**Access to macOS and Windows releases are currently restricted to sponsors, a 5 USD monthly sponsorship is required. You can sponsor strawberry through <a href="https://www.patreon.com/jonaskvinge">Patreon</a> for direct access to new releases. If you are sponsoring through GitHub, Ko-fi or PayPal, please e-mail support AT strawberrymusicplayer.org for access to downloads.**
|
||||||
|
|
||||||
### :heavy_exclamation_mark: Requirements
|
### :heavy_exclamation_mark: Requirements
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ 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/)
|
||||||
@@ -97,7 +97,7 @@ Optional dependencies:
|
|||||||
|
|
||||||
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:
|
||||||
|
|
||||||
@@ -118,6 +118,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
|
||||||
|
|
||||||
[](https://repology.org/metapackage/strawberry/versions)
|
[](https://repology.org/metapackage/strawberry/versions)
|
||||||
|
|||||||
@@ -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 ${GETTEXT_XGETTEXT_EXECUTABLE} ${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}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,27 +1,15 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||||
set(STRAWBERRY_VERSION_MINOR 0)
|
set(STRAWBERRY_VERSION_MINOR 1)
|
||||||
set(STRAWBERRY_VERSION_PATCH 22)
|
set(STRAWBERRY_VERSION_PATCH 1)
|
||||||
#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}")
|
||||||
|
|
||||||
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
|
if(FORCE_GIT_REVISION)
|
||||||
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
|
set(GIT_REVISION ${FORCE_GIT_REVISION})
|
||||||
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
|
elseif(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
set(STRAWBERRY_VERSION_RPM_R "1")
|
|
||||||
set(STRAWBERRY_VERSION_PAC_V "${majorminorpatch}")
|
|
||||||
set(STRAWBERRY_VERSION_PAC_R "1")
|
|
||||||
|
|
||||||
if(STRAWBERRY_VERSION_PRERELEASE)
|
|
||||||
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_DISPLAY} ${STRAWBERRY_VERSION_PRERELEASE}")
|
|
||||||
set(STRAWBERRY_VERSION_RPM_R "0.${STRAWBERRY_VERSION_PRERELEASE}")
|
|
||||||
set(STRAWBERRY_VERSION_PACKAGE "${STRAWBERRY_VERSION_PACKAGE}${STRAWBERRY_VERSION_PRERELEASE}")
|
|
||||||
endif(STRAWBERRY_VERSION_PRERELEASE)
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -53,10 +41,6 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
|||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(FORCE_GIT_REVISION)
|
|
||||||
set(GIT_REVISION ${FORCE_GIT_REVISION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(GIT_REVISION)
|
if(GIT_REVISION)
|
||||||
|
|
||||||
string(REGEX REPLACE "^(.+)-([0-9]+)-(g[a-f0-9]+)$" "\\1;\\2;\\3" GIT_PARTS ${GIT_REVISION})
|
string(REGEX REPLACE "^(.+)-([0-9]+)-(g[a-f0-9]+)$" "\\1;\\2;\\3" GIT_PARTS ${GIT_REVISION})
|
||||||
@@ -78,15 +62,23 @@ if(GIT_REVISION)
|
|||||||
|
|
||||||
set(STRAWBERRY_VERSION_DISPLAY "${GIT_REVISION}")
|
set(STRAWBERRY_VERSION_DISPLAY "${GIT_REVISION}")
|
||||||
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
||||||
set(STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
|
string(REPLACE "-" "~" STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
|
||||||
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
||||||
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}.r${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
|
||||||
set(STRAWBERRY_VERSION_PAC_R "1")
|
|
||||||
|
|
||||||
|
else()
|
||||||
|
if(STRAWBERRY_VERSION_PRERELEASE)
|
||||||
|
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}-${STRAWBERRY_VERSION_PRERELEASE}")
|
||||||
|
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}~${STRAWBERRY_VERSION_PRERELEASE}")
|
||||||
|
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}${STRAWBERRY_VERSION_PRERELEASE}")
|
||||||
|
else()
|
||||||
|
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
|
||||||
|
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
|
||||||
|
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
|
||||||
|
endif()
|
||||||
|
set(STRAWBERRY_VERSION_RPM_R "1")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Strawberry Version:")
|
message(STATUS "Strawberry Version:")
|
||||||
message(STATUS "Display: ${STRAWBERRY_VERSION_DISPLAY}")
|
message(STATUS "Display: ${STRAWBERRY_VERSION_DISPLAY}")
|
||||||
message(STATUS "Package: ${STRAWBERRY_VERSION_PACKAGE}")
|
message(STATUS "Package: ${STRAWBERRY_VERSION_PACKAGE}")
|
||||||
message(STATUS "RPM: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
|
message(STATUS "RPM: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
|
||||||
message(STATUS "PAC: ${STRAWBERRY_VERSION_PAC_V}-${STRAWBERRY_VERSION_PAC_R}")
|
|
||||||
|
|||||||
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
files:
|
||||||
|
- source: /src/translations/translations.pot
|
||||||
|
translation: /src/translations/%locale_with_underscore%.po
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
<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-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>
|
||||||
@@ -42,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>
|
||||||
|
|||||||
BIN
data/icons/128x128/spotify.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
data/icons/22x22/spotify.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/icons/32x32/spotify.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
data/icons/48x48/spotify.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
data/icons/64x64/spotify.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
data/icons/full/spotify.png
Executable file
|
After Width: | Height: | Size: 16 KiB |
BIN
data/pictures/sidebar-background.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
@@ -94,9 +94,4 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
|
|||||||
|
|
||||||
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
|
||||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
||||||
|
|||||||
19
data/schema/schema-19.sql
Normal file
@@ -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;
|
||||||
244
data/schema/schema-20.sql
Normal file
@@ -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 (18);
|
INSERT INTO schema_version (version) VALUES (20);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS directories (
|
CREATE TABLE IF NOT EXISTS directories (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
@@ -422,6 +422,249 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
|
|||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||||
|
|
||||||
title TEXT,
|
title TEXT,
|
||||||
@@ -796,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;
|
||||||
}
|
}
|
||||||
|
|||||||
2
debian/control.in
vendored
@@ -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 Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.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
|
||||||
|
|||||||
5
dist/CMakeLists.txt
vendored
@@ -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)
|
||||||
|
|
||||||
|
|||||||
2
dist/macos/Info.plist.in
vendored
@@ -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>
|
||||||
|
|||||||
10
dist/macos/macgstcopy.sh
vendored
@@ -62,6 +62,7 @@ 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
|
install_name_tool -add_rpath "@loader_path/../Frameworks" "${bundledir}/Contents/PlugIns/$(basename ${GST_PLUGIN_SCANNER})" || exit 1
|
||||||
|
|
||||||
gst_plugins="
|
gst_plugins="
|
||||||
|
libgstadaptivedemux2
|
||||||
libgstaes
|
libgstaes
|
||||||
libgstaiff
|
libgstaiff
|
||||||
libgstapetag
|
libgstapetag
|
||||||
@@ -70,16 +71,14 @@ libgstasf
|
|||||||
libgstasfmux
|
libgstasfmux
|
||||||
libgstaudioconvert
|
libgstaudioconvert
|
||||||
libgstaudiofx
|
libgstaudiofx
|
||||||
libgstaudiomixer
|
|
||||||
libgstaudioparsers
|
libgstaudioparsers
|
||||||
libgstaudiorate
|
|
||||||
libgstaudioresample
|
libgstaudioresample
|
||||||
libgstaudiotestsrc
|
|
||||||
libgstautodetect
|
libgstautodetect
|
||||||
libgstbs2b
|
libgstbs2b
|
||||||
libgstcdio
|
libgstcdio
|
||||||
libgstcoreelements
|
libgstcoreelements
|
||||||
libgstdash
|
libgstdash
|
||||||
|
libgstdsd
|
||||||
libgstequalizer
|
libgstequalizer
|
||||||
libgstfaac
|
libgstfaac
|
||||||
libgstfaad
|
libgstfaad
|
||||||
@@ -92,6 +91,10 @@ libgstid3demux
|
|||||||
libgstid3tag
|
libgstid3tag
|
||||||
libgstisomp4
|
libgstisomp4
|
||||||
libgstlame
|
libgstlame
|
||||||
|
libgstmpegpsdemux
|
||||||
|
libgstmpegpsmux
|
||||||
|
libgstmpegtsdemux
|
||||||
|
libgstmpegtsmux
|
||||||
libgstlibav
|
libgstlibav
|
||||||
libgstmpg123
|
libgstmpg123
|
||||||
libgstmusepack
|
libgstmusepack
|
||||||
@@ -108,6 +111,7 @@ libgstrtsp
|
|||||||
libgstsoup
|
libgstsoup
|
||||||
libgstspectrum
|
libgstspectrum
|
||||||
libgstspeex
|
libgstspeex
|
||||||
|
libgstspotify
|
||||||
libgsttaglib
|
libgsttaglib
|
||||||
libgsttcp
|
libgsttcp
|
||||||
libgsttwolame
|
libgsttwolame
|
||||||
|
|||||||
@@ -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 Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.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,9 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.1.1" date="2024-07-22"/>
|
||||||
|
<release version="1.1.0" date="2024-07-14"/>
|
||||||
|
<release version="1.0.23" date="2024-01-11"/>
|
||||||
<release version="1.0.22" date="2023-12-09"/>
|
<release version="1.0.22" date="2023-12-09"/>
|
||||||
<release version="1.0.21" date="2023-10-21"/>
|
<release version="1.0.21" date="2023-10-21"/>
|
||||||
<release version="1.0.20" date="2023-09-24"/>
|
<release version="1.0.20" date="2023-09-24"/>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
2
dist/unix/strawberry.spec.in
vendored
@@ -99,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 Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.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
|
||||||
|
|||||||
103
dist/windows/strawberry.nsi.in
vendored
@@ -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"
|
||||||
@@ -257,7 +261,6 @@ Section "Strawberry" Strawberry
|
|||||||
File "libFLAC-12.dll"
|
File "libFLAC-12.dll"
|
||||||
File "libbrotlicommon.dll"
|
File "libbrotlicommon.dll"
|
||||||
File "libbrotlidec.dll"
|
File "libbrotlidec.dll"
|
||||||
File "libbrotlienc.dll"
|
|
||||||
File "libbs2b-0.dll"
|
File "libbs2b-0.dll"
|
||||||
File "libbz2.dll"
|
File "libbz2.dll"
|
||||||
File "libchromaprint.dll"
|
File "libchromaprint.dll"
|
||||||
@@ -282,8 +285,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"
|
||||||
@@ -328,7 +333,6 @@ 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 "zlib1.dll"
|
File "zlib1.dll"
|
||||||
|
|
||||||
@@ -422,8 +426,10 @@ Section "Strawberry" Strawberry
|
|||||||
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"
|
||||||
@@ -465,7 +471,6 @@ Section "Strawberry" Strawberry
|
|||||||
File "libiconv.dll"
|
File "libiconv.dll"
|
||||||
File "libpng16.dll"
|
File "libpng16.dll"
|
||||||
File "libspeex.dll"
|
File "libspeex.dll"
|
||||||
File "libxml2.dll"
|
|
||||||
File "pcre2-8.dll"
|
File "pcre2-8.dll"
|
||||||
File "pcre2-16.dll"
|
File "pcre2-16.dll"
|
||||||
File "twolame.dll"
|
File "twolame.dll"
|
||||||
@@ -476,7 +481,6 @@ Section "Strawberry" Strawberry
|
|||||||
File "libiconvd.dll"
|
File "libiconvd.dll"
|
||||||
File "libpng16d.dll"
|
File "libpng16d.dll"
|
||||||
File "libspeexd.dll"
|
File "libspeexd.dll"
|
||||||
File "libxml2d.dll"
|
|
||||||
File "pcre2-8d.dll"
|
File "pcre2-8d.dll"
|
||||||
File "pcre2-16d.dll"
|
File "pcre2-16d.dll"
|
||||||
File "twolamed.dll"
|
File "twolamed.dll"
|
||||||
@@ -493,7 +497,7 @@ Section "Strawberry" Strawberry
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
File "icudt74.dll"
|
File "icudt75.dll"
|
||||||
File "libfftw3-3.dll"
|
File "libfftw3-3.dll"
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
File "libprotobufd.dll"
|
File "libprotobufd.dll"
|
||||||
@@ -501,8 +505,9 @@ Section "Strawberry" Strawberry
|
|||||||
File "libprotobuf.dll"
|
File "libprotobuf.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
File "icuin74d.dll"
|
File "icuin75d.dll"
|
||||||
File "icuuc74d.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"
|
||||||
@@ -510,8 +515,9 @@ Section "Strawberry" Strawberry
|
|||||||
File "Qt6Sqld.dll"
|
File "Qt6Sqld.dll"
|
||||||
File "Qt6Widgetsd.dll"
|
File "Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
File "icuin74.dll"
|
File "icuin75.dll"
|
||||||
File "icuuc74.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"
|
||||||
@@ -584,9 +590,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
|
||||||
|
|
||||||
@@ -627,6 +633,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"
|
||||||
@@ -635,16 +642,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"
|
||||||
@@ -659,6 +664,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"
|
||||||
@@ -681,6 +690,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"
|
||||||
@@ -688,24 +698,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"
|
||||||
@@ -720,6 +730,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"
|
||||||
@@ -742,12 +756,15 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
|
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
|
||||||
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"
|
||||||
; Disable wasapi2 until issue (https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2870) is fixed.
|
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"
|
||||||
File "/oname=gstxingmux.dll" "gstreamer-plugins\gstxingmux.dll"
|
File "/oname=gstxingmux.dll" "gstreamer-plugins\gstxingmux.dll"
|
||||||
|
!ifdef arch_x64
|
||||||
|
File "/oname=gstspotify.dll" "gstreamer-plugins\gstspotify.dll"
|
||||||
|
!endif
|
||||||
!endif ; MSVC
|
!endif ; MSVC
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
@@ -814,7 +831,6 @@ Section "Uninstall"
|
|||||||
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"
|
||||||
Delete "$INSTDIR\libbrotlienc.dll"
|
|
||||||
Delete "$INSTDIR\libbs2b-0.dll"
|
Delete "$INSTDIR\libbs2b-0.dll"
|
||||||
Delete "$INSTDIR\libbz2.dll"
|
Delete "$INSTDIR\libbz2.dll"
|
||||||
Delete "$INSTDIR\libchromaprint.dll"
|
Delete "$INSTDIR\libchromaprint.dll"
|
||||||
@@ -839,8 +855,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"
|
||||||
@@ -885,7 +903,6 @@ 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\zlib1.dll"
|
Delete "$INSTDIR\zlib1.dll"
|
||||||
|
|
||||||
@@ -979,8 +996,10 @@ Section "Uninstall"
|
|||||||
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"
|
||||||
@@ -1022,7 +1041,6 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libiconv.dll"
|
Delete "$INSTDIR\libiconv.dll"
|
||||||
Delete "$INSTDIR\libpng16.dll"
|
Delete "$INSTDIR\libpng16.dll"
|
||||||
Delete "$INSTDIR\libspeex.dll"
|
Delete "$INSTDIR\libspeex.dll"
|
||||||
Delete "$INSTDIR\libxml2.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"
|
||||||
@@ -1033,7 +1051,6 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libiconvd.dll"
|
Delete "$INSTDIR\libiconvd.dll"
|
||||||
Delete "$INSTDIR\libpng16d.dll"
|
Delete "$INSTDIR\libpng16d.dll"
|
||||||
Delete "$INSTDIR\libspeexd.dll"
|
Delete "$INSTDIR\libspeexd.dll"
|
||||||
Delete "$INSTDIR\libxml2d.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"
|
||||||
@@ -1049,7 +1066,7 @@ Section "Uninstall"
|
|||||||
|
|
||||||
; Common files
|
; Common files
|
||||||
|
|
||||||
Delete "$INSTDIR\icudt74.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"
|
||||||
@@ -1057,8 +1074,9 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libprotobuf.dll"
|
Delete "$INSTDIR\libprotobuf.dll"
|
||||||
!endif
|
!endif
|
||||||
!ifdef msvc && debug
|
!ifdef msvc && debug
|
||||||
Delete "$INSTDIR\icuin74d.dll"
|
Delete "$INSTDIR\icuin75d.dll"
|
||||||
Delete "$INSTDIR\icuuc74d.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"
|
||||||
@@ -1066,8 +1084,9 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\Qt6Sqld.dll"
|
Delete "$INSTDIR\Qt6Sqld.dll"
|
||||||
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
Delete "$INSTDIR\Qt6Widgetsd.dll"
|
||||||
!else
|
!else
|
||||||
Delete "$INSTDIR\icuin74.dll"
|
Delete "$INSTDIR\icuin75.dll"
|
||||||
Delete "$INSTDIR\icuuc74.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"
|
||||||
@@ -1095,7 +1114,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"
|
||||||
@@ -1104,7 +1123,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"
|
||||||
@@ -1116,6 +1135,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"
|
||||||
@@ -1124,16 +1144,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"
|
||||||
@@ -1148,6 +1166,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"
|
||||||
@@ -1170,6 +1192,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"
|
||||||
@@ -1179,24 +1202,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"
|
||||||
@@ -1211,6 +1234,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"
|
||||||
@@ -1234,10 +1261,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"
|
||||||
|
!ifdef arch_x64
|
||||||
|
Delete "$INSTDIR\gstreamer-plugins\gstspotify.dll"
|
||||||
|
!endif
|
||||||
!endif ; msvc
|
!endif ; msvc
|
||||||
|
|
||||||
Delete "$INSTDIR\Uninstall.exe"
|
Delete "$INSTDIR\Uninstall.exe"
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -67,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>
|
||||||
@@ -135,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;
|
||||||
}
|
}
|
||||||
@@ -157,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();
|
||||||
@@ -193,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;
|
||||||
@@ -212,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 {
|
||||||
@@ -225,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);
|
||||||
}
|
}
|
||||||
@@ -237,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);
|
||||||
}
|
}
|
||||||
@@ -259,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)) {
|
||||||
@@ -272,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;
|
||||||
@@ -284,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();
|
||||||
|
|
||||||
@@ -310,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;
|
||||||
@@ -326,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);
|
||||||
@@ -392,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
|
||||||
|
|||||||
@@ -120,13 +120,13 @@ template<typename MT>
|
|||||||
void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
|
void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
std::string data = message.SerializeAsString();
|
const std::string data = message.SerializeAsString();
|
||||||
WriteMessage(QByteArray(data.data(), data.size()));
|
WriteMessage(QByteArray(data.data(), data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MT>
|
template<typename MT>
|
||||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
|
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
|
||||||
std::string data = message.SerializeAsString();
|
const std::string data = message.SerializeAsString();
|
||||||
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@@ -171,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -243,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)
|
#if defined(Q_OS_MACOS)
|
||||||
search_path << QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../PlugIns");
|
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;
|
||||||
@@ -294,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;
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ endif()
|
|||||||
|
|
||||||
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
|
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
list(APPEND SOURCES tagreadertaglib.cpp tagreadergme.cpp)
|
list(APPEND SOURCES tagreadertaglib.cpp tagreadergme.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
list(APPEND SOURCES tagreadertagparser.cpp)
|
list(APPEND SOURCES tagreadertagparser.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -24,11 +24,11 @@ link_directories(
|
|||||||
${PROTOBUF_LIBRARY_DIRS}
|
${PROTOBUF_LIBRARY_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -56,12 +56,12 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
|||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
)
|
)
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
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
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
@@ -33,13 +34,38 @@
|
|||||||
TagReaderBase::TagReaderBase() = default;
|
TagReaderBase::TagReaderBase() = default;
|
||||||
TagReaderBase::~TagReaderBase() = default;
|
TagReaderBase::~TagReaderBase() = default;
|
||||||
|
|
||||||
|
QString TagReaderBase::ErrorString(const Result &result) {
|
||||||
|
|
||||||
|
switch (result.error_code) {
|
||||||
|
case Result::ErrorCode::Success:
|
||||||
|
return QObject::tr("Success");
|
||||||
|
case Result::ErrorCode::Unsupported:
|
||||||
|
return QObject::tr("File is unsupported");
|
||||||
|
case Result::ErrorCode::FilenameMissing:
|
||||||
|
return QObject::tr("Filename is missing");
|
||||||
|
case Result::ErrorCode::FileDoesNotExist:
|
||||||
|
return QObject::tr("File does not exist");
|
||||||
|
case Result::ErrorCode::FileOpenError:
|
||||||
|
return QObject::tr("File could not be opened");
|
||||||
|
case Result::ErrorCode::FileParseError:
|
||||||
|
return QObject::tr("Could not parse file");
|
||||||
|
case Result::ErrorCode::FileSaveError:
|
||||||
|
return QObject::tr("Could save file");
|
||||||
|
case Result::ErrorCode::CustomError:
|
||||||
|
return result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::tr("Unknown error");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
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,25 +74,24 @@ 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request) {
|
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::WriteFileRequest &request) {
|
||||||
|
|
||||||
if (!request.has_save_cover() || !request.save_cover()) {
|
if (!request.has_save_cover() || !request.save_cover()) {
|
||||||
return Cover();
|
return Cover();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
|
||||||
QString cover_filename;
|
QString cover_filename;
|
||||||
if (request.has_cover_filename()) {
|
if (request.has_cover_filename()) {
|
||||||
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
|
cover_filename = QString::fromStdString(request.cover_filename());
|
||||||
}
|
}
|
||||||
QByteArray cover_data;
|
QByteArray cover_data;
|
||||||
if (request.has_cover_data()) {
|
if (request.has_cover_data()) {
|
||||||
@@ -74,19 +99,18 @@ 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request) {
|
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::SaveEmbeddedArtRequest &request) {
|
||||||
|
|
||||||
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
|
||||||
QString cover_filename;
|
QString cover_filename;
|
||||||
if (request.has_cover_filename()) {
|
if (request.has_cover_filename()) {
|
||||||
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
|
cover_filename = QString::fromStdString(request.cover_filename());
|
||||||
}
|
}
|
||||||
QByteArray cover_data;
|
QByteArray cover_data;
|
||||||
if (request.has_cover_data()) {
|
if (request.has_cover_data()) {
|
||||||
@@ -94,7 +118,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 +142,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();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
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
|
||||||
@@ -34,7 +34,25 @@
|
|||||||
class TagReaderBase {
|
class TagReaderBase {
|
||||||
public:
|
public:
|
||||||
explicit TagReaderBase();
|
explicit TagReaderBase();
|
||||||
~TagReaderBase();
|
virtual ~TagReaderBase();
|
||||||
|
|
||||||
|
class Result {
|
||||||
|
public:
|
||||||
|
enum class ErrorCode {
|
||||||
|
Success,
|
||||||
|
Unsupported,
|
||||||
|
FilenameMissing,
|
||||||
|
FileDoesNotExist,
|
||||||
|
FileOpenError,
|
||||||
|
FileParseError,
|
||||||
|
FileSaveError,
|
||||||
|
CustomError,
|
||||||
|
};
|
||||||
|
Result(const ErrorCode _error_code, const QString &_error = QString()) : error_code(_error_code), error(_error) {}
|
||||||
|
ErrorCode error_code;
|
||||||
|
QString error;
|
||||||
|
bool success() const { return error_code == ErrorCode::Success; }
|
||||||
|
};
|
||||||
|
|
||||||
class Cover {
|
class Cover {
|
||||||
public:
|
public:
|
||||||
@@ -44,22 +62,25 @@ class TagReaderBase {
|
|||||||
QString error;
|
QString error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static QString ErrorString(const Result &result);
|
||||||
|
|
||||||
virtual bool IsMediaFile(const QString &filename) const = 0;
|
virtual bool IsMediaFile(const QString &filename) const = 0;
|
||||||
|
|
||||||
virtual bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
virtual Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
||||||
virtual bool SaveFile(const spb::tagreader::SaveFileRequest &request) const = 0;
|
virtual Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const = 0;
|
||||||
|
|
||||||
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
|
virtual Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const = 0;
|
||||||
virtual bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const = 0;
|
virtual Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const = 0;
|
||||||
|
|
||||||
virtual bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
virtual Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const = 0;
|
||||||
virtual bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
virtual Result SaveSongRatingToFile(const QString &filename, const float rating) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
static float ConvertPOPMRating(const int POPM_rating);
|
static float ConvertPOPMRating(const int POPM_rating);
|
||||||
static int ConvertToPOPMRating(const float rating);
|
static int ConvertToPOPMRating(const float rating);
|
||||||
|
|
||||||
static Cover LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request);
|
static Cover LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::WriteFileRequest &request);
|
||||||
static Cover LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request);
|
static Cover LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::SaveEmbeddedArtRequest &request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Cover LoadCoverFromRequest(const QString &song_filename, const QString &cover_filename, QByteArray cover_data, QString cover_mime_type);
|
static Cover LoadCoverFromRequest(const QString &song_filename, const QString &cover_filename, QByteArray cover_data, QString cover_mime_type);
|
||||||
|
|||||||
@@ -19,8 +19,7 @@
|
|||||||
|
|
||||||
#include "tagreadergme.h"
|
#include "tagreadergme.h"
|
||||||
|
|
||||||
#include <tag.h>
|
#include <taglib/apefile.h>
|
||||||
#include <apefile.h>
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -35,22 +34,23 @@
|
|||||||
#include "tagreaderbase.h"
|
#include "tagreaderbase.h"
|
||||||
#include "tagreadertaglib.h"
|
#include "tagreadertaglib.h"
|
||||||
|
|
||||||
bool GME::IsSupportedFormat(const QFileInfo &file_info) {
|
#undef TStringToQString
|
||||||
return file_info.exists() && (file_info.completeSuffix().endsWith("spc", Qt::CaseInsensitive) || file_info.completeSuffix().endsWith("vgm"), Qt::CaseInsensitive);
|
#undef QStringToTString
|
||||||
|
|
||||||
|
bool GME::IsSupportedFormat(const QFileInfo &fileinfo) {
|
||||||
|
return fileinfo.exists() && (fileinfo.completeSuffix().endsWith(QLatin1String("spc"), Qt::CaseInsensitive) || fileinfo.completeSuffix().endsWith(QLatin1String("vgm")), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GME::ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
TagReaderBase::Result GME::ReadFile(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||||
|
|
||||||
if (file_info.completeSuffix().endsWith("spc"), Qt::CaseInsensitive) {
|
if (fileinfo.completeSuffix().endsWith(QLatin1String("spc")), Qt::CaseInsensitive) {
|
||||||
SPC::Read(file_info, song_info);
|
return SPC::Read(fileinfo, song);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (file_info.completeSuffix().endsWith("vgm", Qt::CaseInsensitive)) {
|
if (fileinfo.completeSuffix().endsWith(QLatin1String("vgm"), Qt::CaseInsensitive)) {
|
||||||
VGM::Read(file_info, song_info);
|
return VGM::Read(fileinfo, song);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return TagReaderBase::Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,15 +67,19 @@ quint32 GME::UnpackBytes32(const char *const bytes, size_t length) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
TagReaderBase::Result GME::SPC::Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||||
|
|
||||||
QFile file(file_info.filePath());
|
QFile file(fileinfo.filePath());
|
||||||
if (!file.open(QIODevice::ReadOnly)) return;
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
return TagReaderBase::Result(TagReaderBase::Result::ErrorCode::FileOpenError, file.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Reading tags from SPC file" << file_info.fileName();
|
qLog(Debug) << "Reading tags from SPC file" << fileinfo.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 TagReaderBase::Result::ErrorCode::Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
@@ -88,13 +92,13 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
|||||||
const bool has_id6 = id6_status.length() >= 1 && id6_status[0] == static_cast<char>(xID6_STATUS::ON);
|
const bool has_id6 = id6_status.length() >= 1 && id6_status[0] == static_cast<char>(xID6_STATUS::ON);
|
||||||
|
|
||||||
file.seek(SONG_TITLE_OFFSET);
|
file.seek(SONG_TITLE_OFFSET);
|
||||||
song_info->set_title(QString::fromLatin1(file.read(32)).toStdString());
|
song->set_title(QString::fromLatin1(file.read(32)).toStdString());
|
||||||
|
|
||||||
file.seek(GAME_TITLE_OFFSET);
|
file.seek(GAME_TITLE_OFFSET);
|
||||||
song_info->set_album(QString::fromLatin1(file.read(32)).toStdString());
|
song->set_album(QString::fromLatin1(file.read(32)).toStdString());
|
||||||
|
|
||||||
file.seek(ARTIST_OFFSET);
|
file.seek(ARTIST_OFFSET);
|
||||||
song_info->set_artist(QString::fromLatin1(file.read(32)).toStdString());
|
song->set_artist(QString::fromLatin1(file.read(32)).toStdString());
|
||||||
|
|
||||||
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);
|
||||||
@@ -107,7 +111,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length_in_sec < 0x1FFF) {
|
if (length_in_sec < 0x1FFF) {
|
||||||
song_info->set_length_nanosec(length_in_sec * kNsecPerSec);
|
song->set_length_nanosec(length_in_sec * kNsecPerSec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +129,13 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
|||||||
// 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];
|
||||||
// This should be the size remaining for entire ID6 block, but it seems that most files treat this as the size of the remaining header space...
|
// This should be the size remaining for entire ID6 block, but it seems that most files treat this as the size of the remaining header space...
|
||||||
|
|
||||||
qLog(Debug) << file_info.fileName() << "has ID6 tag.";
|
qLog(Debug) << fileinfo.fileName() << "has ID6 tag.";
|
||||||
|
|
||||||
while ((file.pos()) + 4 < XID6_OFFSET + xid6_size) {
|
while ((file.pos()) + 4 < XID6_OFFSET + xid6_size) {
|
||||||
QByteArray arr = file.read(4);
|
QByteArray arr = file.read(4);
|
||||||
@@ -152,21 +156,25 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
|||||||
// an APETAG entry at the bottom of the file instead of writing into the xid6 tagging space.
|
// an APETAG entry at the bottom of the file instead of writing into the xid6 tagging space.
|
||||||
// This is where a lot of the extra data for a file is stored, such as genre or replaygain data.
|
// This is where a lot of the extra data for a file is stored, such as genre or replaygain data.
|
||||||
// This data is currently supported by TagLib, so we will simply use that for the remaining values.
|
// This data is currently supported by TagLib, so we will simply use that for the remaining values.
|
||||||
TagLib::APE::File ape(file_info.filePath().toStdString().data());
|
TagLib::APE::File ape(fileinfo.filePath().toStdString().data());
|
||||||
if (ape.hasAPETag()) {
|
if (ape.hasAPETag()) {
|
||||||
TagLib::Tag *tag = ape.tag();
|
TagLib::Tag *tag = ape.tag();
|
||||||
if (!tag) return;
|
if (!tag) {
|
||||||
|
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||||
|
}
|
||||||
|
|
||||||
TagReaderTagLib::TStringToStdString(tag->artist(), song_info->mutable_artist());
|
TagReaderTagLib::AssignTagLibStringToStdString(tag->artist(), song->mutable_artist());
|
||||||
TagReaderTagLib::TStringToStdString(tag->album(), song_info->mutable_album());
|
TagReaderTagLib::AssignTagLibStringToStdString(tag->album(), song->mutable_album());
|
||||||
TagReaderTagLib::TStringToStdString(tag->title(), song_info->mutable_title());
|
TagReaderTagLib::AssignTagLibStringToStdString(tag->title(), song->mutable_title());
|
||||||
TagReaderTagLib::TStringToStdString(tag->genre(), song_info->mutable_genre());
|
TagReaderTagLib::AssignTagLibStringToStdString(tag->genre(), song->mutable_genre());
|
||||||
song_info->set_track(static_cast<std::int32_t>(tag->track()));
|
song->set_track(static_cast<std::int32_t>(tag->track()));
|
||||||
song_info->set_year(static_cast<std::int32_t>(tag->year()));
|
song->set_year(static_cast<std::int32_t>(tag->year()));
|
||||||
}
|
}
|
||||||
|
|
||||||
song_info->set_valid(true);
|
song->set_valid(true);
|
||||||
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_SPC);
|
song->set_filetype(spb::tagreader::SongMetadata_FileType_SPC);
|
||||||
|
|
||||||
|
return TagReaderBase::Result::ErrorCode::Success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,18 +196,24 @@ quint64 GME::SPC::ConvertSPCStringToNum(const QByteArray &arr) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
TagReaderBase::Result GME::VGM::Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||||
|
|
||||||
QFile file(file_info.filePath());
|
QFile file(fileinfo.filePath());
|
||||||
if (!file.open(QIODevice::ReadOnly)) return;
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
return TagReaderBase::Result(TagReaderBase::Result::ErrorCode::FileOpenError, file.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Reading tags from VGM file" << file_info.fileName();
|
qLog(Debug) << "Reading tags from VGM file" << fileinfo.filePath();
|
||||||
|
|
||||||
if (!file.read(4).startsWith(QString("Vgm ").toLatin1())) return;
|
if (!file.read(4).startsWith(QStringLiteral("Vgm ").toLatin1())) {
|
||||||
|
return TagReaderBase::Result::ErrorCode::Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
file.seek(GD3_TAG_PTR);
|
file.seek(GD3_TAG_PTR);
|
||||||
QByteArray gd3_head = file.read(4);
|
QByteArray gd3_head = file.read(4);
|
||||||
if (gd3_head.size() < 4) return;
|
if (gd3_head.size() < 4) {
|
||||||
|
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||||
|
}
|
||||||
|
|
||||||
quint64 pt = GME::UnpackBytes32(gd3_head.constData(), gd3_head.size());
|
quint64 pt = GME::UnpackBytes32(gd3_head.constData(), gd3_head.size());
|
||||||
|
|
||||||
@@ -209,7 +223,9 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
|||||||
QByteArray loop_count_bytes = file.read(4);
|
QByteArray loop_count_bytes = file.read(4);
|
||||||
quint64 length = 0;
|
quint64 length = 0;
|
||||||
|
|
||||||
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) return;
|
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) {
|
||||||
|
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||||
|
}
|
||||||
|
|
||||||
file.seek(static_cast<qint64>(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);
|
||||||
@@ -226,19 +242,23 @@ 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 TagReaderBase::Result::ErrorCode::FileParseError;
|
||||||
|
}
|
||||||
|
|
||||||
// VGM standard dictates string tag data exist in specific order.
|
// VGM standard dictates string tag data exist in specific order.
|
||||||
// Order alternates between English and Japanese version of data.
|
// Order alternates between English and Japanese version of data.
|
||||||
// Read GD3 tag standard for more details.
|
// Read GD3 tag standard for more details.
|
||||||
song_info->set_title(strings[0].toStdString());
|
song->set_title(strings[0].toStdString());
|
||||||
song_info->set_album(strings[2].toStdString());
|
song->set_album(strings[2].toStdString());
|
||||||
song_info->set_artist(strings[6].toStdString());
|
song->set_artist(strings[6].toStdString());
|
||||||
song_info->set_year(strings[8].left(4).toInt());
|
song->set_year(strings[8].left(4).toInt());
|
||||||
song_info->set_length_nanosec(length * kNsecPerMsec);
|
song->set_length_nanosec(length * kNsecPerMsec);
|
||||||
song_info->set_valid(true);
|
song->set_valid(true);
|
||||||
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_VGM);
|
song->set_filetype(spb::tagreader::SongMetadata_FileType_VGM);
|
||||||
|
|
||||||
|
return TagReaderBase::Result::ErrorCode::Success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,34 +287,63 @@ 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);
|
||||||
return GME::IsSupportedFormat(fileinfo);
|
return GME::IsSupportedFormat(fileinfo);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderGME::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
TagReaderBase::Result TagReaderGME::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
QFileInfo fileinfo(filename);
|
QFileInfo fileinfo(filename);
|
||||||
return GME::ReadFile(fileinfo, song);
|
return GME::ReadFile(fileinfo, song);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderGME::SaveFile(const spb::tagreader::SaveFileRequest&) const {
|
TagReaderBase::Result TagReaderGME::WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const {
|
||||||
return false;
|
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(request);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TagReaderGME::LoadEmbeddedArt(const QString&) const {
|
TagReaderBase::Result TagReaderGME::LoadEmbeddedArt(const QString &filename, QByteArray &data) const {
|
||||||
return QByteArray();
|
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(data);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderGME::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest&) const {
|
TagReaderBase::Result TagReaderGME::SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||||
return false;
|
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(request);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderGME::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const {
|
TagReaderBase::Result TagReaderGME::SaveSongPlaycountToFile(const QString &filename, const uint playcount) const {
|
||||||
return false;
|
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(playcount);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderGME::SaveSongRatingToFile(const QString&, const spb::tagreader::SongMetadata&) const {
|
TagReaderBase::Result TagReaderGME::SaveSongRatingToFile(const QString &filename, const float rating) const {
|
||||||
return false;
|
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(rating);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
#ifndef TAGREADERGME_H
|
#ifndef TAGREADERGME_H
|
||||||
#define TAGREADERGME_H
|
#define TAGREADERGME_H
|
||||||
|
|
||||||
#include <taglib/tstring.h>
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -29,10 +27,9 @@
|
|||||||
#include "tagreaderbase.h"
|
#include "tagreaderbase.h"
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
|
|
||||||
namespace GME {
|
namespace GME {
|
||||||
bool IsSupportedFormat(const QFileInfo &file_info);
|
bool IsSupportedFormat(const QFileInfo &fileinfo);
|
||||||
bool ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
TagReaderBase::Result ReadFile(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||||
|
|
||||||
uint32_t UnpackBytes32(const char *const bytes, size_t length);
|
uint32_t UnpackBytes32(const char *const bytes, size_t length);
|
||||||
|
|
||||||
@@ -72,7 +69,7 @@ enum class xID6_TYPE {
|
|||||||
Integer = 0x4
|
Integer = 0x4
|
||||||
};
|
};
|
||||||
|
|
||||||
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
TagReaderBase::Result Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||||
qint16 GetNextMemAddressAlign32bit(qint16 input);
|
qint16 GetNextMemAddressAlign32bit(qint16 input);
|
||||||
quint64 ConvertSPCStringToNum(const QByteArray &arr);
|
quint64 ConvertSPCStringToNum(const QByteArray &arr);
|
||||||
} // namespace SPC
|
} // namespace SPC
|
||||||
@@ -88,7 +85,7 @@ constexpr int LOOP_SAMPLE_COUNT = 0x20;
|
|||||||
constexpr int SAMPLE_TIMEBASE = 44100;
|
constexpr int SAMPLE_TIMEBASE = 44100;
|
||||||
constexpr int GST_GME_LOOP_TIME_MS = 8000;
|
constexpr int GST_GME_LOOP_TIME_MS = 8000;
|
||||||
|
|
||||||
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
TagReaderBase::Result Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||||
// Takes in two QByteArrays, expected to be 4 bytes long. Desired length is returned via output parameter out_length. Returns false on error.
|
// Takes in two QByteArrays, expected to be 4 bytes long. Desired length is returned via output parameter out_length. Returns false on error.
|
||||||
bool GetPlaybackLength(const QByteArray &sample_count_bytes, const QByteArray &loop_count_bytes, quint64 &out_length);
|
bool GetPlaybackLength(const QByteArray &sample_count_bytes, const QByteArray &loop_count_bytes, quint64 &out_length);
|
||||||
|
|
||||||
@@ -102,18 +99,17 @@ class TagReaderGME : public TagReaderBase {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TagReaderGME();
|
explicit TagReaderGME();
|
||||||
~TagReaderGME();
|
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const override;
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||||
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const override;
|
||||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||||
|
|
||||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TAGREADERGME_H
|
#endif // TAGREADERGME_H
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ message IsMediaFileRequest {
|
|||||||
|
|
||||||
message IsMediaFileResponse {
|
message IsMediaFileResponse {
|
||||||
optional bool success = 1;
|
optional bool success = 1;
|
||||||
optional string error = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ReadFileRequest {
|
message ReadFileRequest {
|
||||||
@@ -105,11 +104,12 @@ message ReadFileRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message ReadFileResponse {
|
message ReadFileResponse {
|
||||||
optional SongMetadata metadata = 1;
|
optional bool success = 1;
|
||||||
optional string error = 2;
|
optional SongMetadata metadata = 2;
|
||||||
|
optional string error = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SaveFileRequest {
|
message WriteFileRequest {
|
||||||
optional string filename = 1;
|
optional string filename = 1;
|
||||||
optional bool save_tags = 2;
|
optional bool save_tags = 2;
|
||||||
optional bool save_playcount = 3;
|
optional bool save_playcount = 3;
|
||||||
@@ -121,7 +121,7 @@ message SaveFileRequest {
|
|||||||
optional string cover_mime_type = 9;
|
optional string cover_mime_type = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SaveFileResponse {
|
message WriteFileResponse {
|
||||||
optional bool success = 1;
|
optional bool success = 1;
|
||||||
optional string error = 2;
|
optional string error = 2;
|
||||||
}
|
}
|
||||||
@@ -131,8 +131,9 @@ message LoadEmbeddedArtRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message LoadEmbeddedArtResponse {
|
message LoadEmbeddedArtResponse {
|
||||||
optional bytes data = 1;
|
optional bool success = 1;
|
||||||
optional string error = 2;
|
optional bytes data = 2;
|
||||||
|
optional string error = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SaveEmbeddedArtRequest {
|
message SaveEmbeddedArtRequest {
|
||||||
@@ -149,7 +150,7 @@ message SaveEmbeddedArtResponse {
|
|||||||
|
|
||||||
message SaveSongPlaycountToFileRequest {
|
message SaveSongPlaycountToFileRequest {
|
||||||
optional string filename = 1;
|
optional string filename = 1;
|
||||||
optional SongMetadata metadata = 2;
|
optional uint32 playcount = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SaveSongPlaycountToFileResponse {
|
message SaveSongPlaycountToFileResponse {
|
||||||
@@ -159,7 +160,7 @@ message SaveSongPlaycountToFileResponse {
|
|||||||
|
|
||||||
message SaveSongRatingToFileRequest {
|
message SaveSongRatingToFileRequest {
|
||||||
optional string filename = 1;
|
optional string filename = 1;
|
||||||
optional SongMetadata metadata = 2;
|
optional float rating = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SaveSongRatingToFileResponse {
|
message SaveSongRatingToFileResponse {
|
||||||
@@ -173,8 +174,8 @@ message Message {
|
|||||||
optional ReadFileRequest read_file_request = 2;
|
optional ReadFileRequest read_file_request = 2;
|
||||||
optional ReadFileResponse read_file_response = 3;
|
optional ReadFileResponse read_file_response = 3;
|
||||||
|
|
||||||
optional SaveFileRequest save_file_request = 4;
|
optional WriteFileRequest write_file_request = 4;
|
||||||
optional SaveFileResponse save_file_response = 5;
|
optional WriteFileResponse write_file_response = 5;
|
||||||
|
|
||||||
optional IsMediaFileRequest is_media_file_request = 6;
|
optional IsMediaFileRequest is_media_file_request = 6;
|
||||||
optional IsMediaFileResponse is_media_file_response = 7;
|
optional IsMediaFileResponse is_media_file_response = 7;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
Copyright 2013, David Sansome <me@davidsansome.com>
|
Copyright 2013, 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
|
||||||
@@ -37,10 +37,15 @@
|
|||||||
#include <taglib/asffile.h>
|
#include <taglib/asffile.h>
|
||||||
#include <taglib/id3v2tag.h>
|
#include <taglib/id3v2tag.h>
|
||||||
#include <taglib/popularimeterframe.h>
|
#include <taglib/popularimeterframe.h>
|
||||||
|
#include <taglib/mp4tag.h>
|
||||||
|
#include <taglib/asftag.h>
|
||||||
|
|
||||||
#include "tagreaderbase.h"
|
#include "tagreaderbase.h"
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
|
#undef TStringToQString
|
||||||
|
#undef QStringToTString
|
||||||
|
|
||||||
class FileRefFactory;
|
class FileRefFactory;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -50,55 +55,85 @@ class FileRefFactory;
|
|||||||
class TagReaderTagLib : public TagReaderBase {
|
class TagReaderTagLib : public TagReaderBase {
|
||||||
public:
|
public:
|
||||||
explicit TagReaderTagLib();
|
explicit TagReaderTagLib();
|
||||||
~TagReaderTagLib();
|
~TagReaderTagLib() override;
|
||||||
|
|
||||||
|
static inline TagLib::String StdStringToTagLibString(const std::string &s) {
|
||||||
|
return TagLib::String(s.c_str(), TagLib::String::UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string TagLibStringToStdString(const TagLib::String &s) {
|
||||||
|
return std::string(s.toCString(true), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TagLib::String QStringToTagLibString(const QString &s) {
|
||||||
|
return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString TagLibStringToQString(const TagLib::String &s) {
|
||||||
|
return QString::fromUtf8((s).toCString(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void AssignTagLibStringToStdString(const TagLib::String &tstr, std::string *output) {
|
||||||
|
|
||||||
|
const QString qstr = TagLibStringToQString(tstr).trimmed();
|
||||||
|
const QByteArray data = qstr.toUtf8();
|
||||||
|
output->assign(data.constData(), data.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const override;
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||||
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const override;
|
||||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||||
|
|
||||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||||
|
|
||||||
static void TStringToStdString(const TagLib::String &tag, std::string *output);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
void ParseID3v2Tags(TagLib::ID3v2::Tag *tag, 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 ParseVorbisComments(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
|
void ParseAPETags(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 ParseMP4Tags(TagLib::MP4::Tag *tag, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
void SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void ParseASFTags(TagLib::ASF::Tag *tag, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
|
||||||
|
void ParseASFAttribute(const TagLib::ASF::AttributeListMap &attributes_map, const char *attribute, std::string *str) const;
|
||||||
|
|
||||||
|
void SetID3v2Tag(TagLib::ID3v2::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;
|
||||||
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
void SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
void SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
|
|
||||||
|
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment, const spb::tagreader::SongMetadata &song) const;
|
||||||
|
void SetAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||||
|
void SetASFTag(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||||
|
void SetAsfAttribute(TagLib::ASF::Tag *tag, const char *attribute, const std::string &value) const;
|
||||||
|
void SetAsfAttribute(TagLib::ASF::Tag *tag, const char *attribute, const int value) const;
|
||||||
|
|
||||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||||
|
|
||||||
static TagLib::ID3v2::PopularimeterFrame *GetPOPMFrameFromTag(TagLib::ID3v2::Tag *tag);
|
static TagLib::ID3v2::PopularimeterFrame *GetPOPMFrameFromTag(TagLib::ID3v2::Tag *tag);
|
||||||
|
|
||||||
void SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
|
void SetPlaycount(TagLib::Ogg::XiphComment *vorbis_comment, const uint playcount) const;
|
||||||
void SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetPlaycount(TagLib::APE::Tag *tag, const uint playcount) const;
|
||||||
void SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetPlaycount(TagLib::ID3v2::Tag *tag, const uint playcount) const;
|
||||||
void SetPlaycount(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetPlaycount(TagLib::MP4::Tag *tag, const uint playcount) const;
|
||||||
void SetPlaycount(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetPlaycount(TagLib::ASF::Tag *tag, const uint playcount) const;
|
||||||
|
|
||||||
void SetRating(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
|
void SetRating(TagLib::Ogg::XiphComment *vorbis_comment, const float rating) const;
|
||||||
void SetRating(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetRating(TagLib::APE::Tag *tag, const float rating) const;
|
||||||
void SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetRating(TagLib::ID3v2::Tag *tag, const float rating) const;
|
||||||
void SetRating(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetRating(TagLib::MP4::Tag *tag, const float rating) const;
|
||||||
void SetRating(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SetRating(TagLib::ASF::Tag *tag, const float rating) const;
|
||||||
|
|
||||||
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 *vorbis_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 *vorbis_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:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
Copyright 2021-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
|
||||||
@@ -46,14 +46,12 @@
|
|||||||
|
|
||||||
TagReaderTagParser::TagReaderTagParser() = default;
|
TagReaderTagParser::TagReaderTagParser() = default;
|
||||||
|
|
||||||
TagReaderTagParser::~TagReaderTagParser() = default;
|
|
||||||
|
|
||||||
bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
||||||
|
|
||||||
qLog(Debug) << "Checking for valid file" << filename;
|
qLog(Debug) << "Checking for valid file" << filename;
|
||||||
|
|
||||||
QFileInfo fileinfo(filename);
|
QFileInfo fileinfo(filename);
|
||||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
|
if (!fileinfo.exists() || fileinfo.suffix().compare(QLatin1String("bak"), Qt::CaseInsensitive) == 0) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TagParser::MediaFileInfo taginfo;
|
TagParser::MediaFileInfo taginfo;
|
||||||
@@ -94,13 +92,13 @@ bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
TagReaderBase::Result TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
qLog(Debug) << "Reading tags from" << filename;
|
qLog(Debug) << "Reading tags from" << filename;
|
||||||
|
|
||||||
const QFileInfo fileinfo(filename);
|
const QFileInfo fileinfo(filename);
|
||||||
|
|
||||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
|
if (!fileinfo.exists() || fileinfo.suffix().compare(QLatin1String("bak"), Qt::CaseInsensitive) == 0) return Result::ErrorCode::FileParseError;
|
||||||
|
|
||||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||||
const QByteArray basefilename = fileinfo.fileName().toUtf8();
|
const QByteArray basefilename = fileinfo.fileName().toUtf8();
|
||||||
@@ -134,19 +132,19 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
|||||||
taginfo.parseContainerFormat(diag, progress);
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTracks(diag, progress);
|
taginfo.parseTracks(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTags(diag, progress);
|
taginfo.parseTags(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const TagParser::DiagMessage &msg : diag) {
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
@@ -205,7 +203,7 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
|||||||
|
|
||||||
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
|
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::Unsupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||||
@@ -246,21 +244,20 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
|||||||
|
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
|
|
||||||
return true;
|
return Result::ErrorCode::Success;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(...) {
|
catch(...) {
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request) const {
|
TagReaderBase::Result TagReaderTagParser::WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const {
|
||||||
|
|
||||||
if (request.filename().empty()) return false;
|
if (request.filename().empty()) return Result::ErrorCode::FilenameMissing;
|
||||||
|
|
||||||
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
|
const spb::tagreader::SongMetadata &song = request.metadata();
|
||||||
const spb::tagreader::SongMetadata song = request.metadata();
|
|
||||||
const bool save_tags = request.has_save_tags() && request.save_tags();
|
const bool save_tags = request.has_save_tags() && request.save_tags();
|
||||||
const bool save_playcount = request.has_save_playcount() && request.save_playcount();
|
const bool save_playcount = request.has_save_playcount() && request.save_playcount();
|
||||||
const bool save_rating = request.has_save_rating() && request.save_rating();
|
const bool save_rating = request.has_save_rating() && request.save_rating();
|
||||||
@@ -268,21 +265,21 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
|||||||
|
|
||||||
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 QByteArray cover_data = LoadCoverDataFromRequest(request);
|
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TagParser::MediaFileInfo taginfo;
|
TagParser::MediaFileInfo taginfo;
|
||||||
@@ -298,19 +295,19 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
|||||||
taginfo.parseContainerFormat(diag, progress);
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTracks(diag, progress);
|
taginfo.parseTracks(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTags(diag, progress);
|
taginfo.parseTags(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taginfo.tags().size() <= 0) {
|
if (taginfo.tags().size() <= 0) {
|
||||||
@@ -335,13 +332,13 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
|||||||
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
|
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
|
||||||
}
|
}
|
||||||
if (save_playcount) {
|
if (save_playcount) {
|
||||||
SaveSongPlaycountToFile(tag, song);
|
SaveSongPlaycountToFile(tag, song.playcount());
|
||||||
}
|
}
|
||||||
if (save_rating) {
|
if (save_rating) {
|
||||||
SaveSongRatingToFile(tag, song);
|
SaveSongRatingToFile(tag, song.rating());
|
||||||
}
|
}
|
||||||
if (save_cover) {
|
if (save_cover) {
|
||||||
SaveEmbeddedArt(tag, cover_data);
|
SaveEmbeddedArt(tag, cover.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,17 +349,17 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
|||||||
qLog(Debug) << QString::fromStdString(msg.message());
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return Result::ErrorCode::Success;
|
||||||
}
|
}
|
||||||
catch(...) {}
|
catch(...) {}
|
||||||
|
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
TagReaderBase::Result TagReaderTagParser::LoadEmbeddedArt(const QString &filename, QByteArray &data) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return QByteArray();
|
if (filename.isEmpty()) return Result::ErrorCode::FilenameMissing;
|
||||||
|
|
||||||
qLog(Debug) << "Loading art from" << filename;
|
qLog(Debug) << "Loading art from" << filename;
|
||||||
|
|
||||||
@@ -383,20 +380,20 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
|||||||
taginfo.parseContainerFormat(diag, progress);
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return QByteArray();
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTags(diag, progress);
|
taginfo.parseTags(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return QByteArray();
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||||
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
||||||
QByteArray data(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
|
data = QByteArray(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return data;
|
return Result::ErrorCode::Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +406,7 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
|||||||
}
|
}
|
||||||
catch(...) {}
|
catch(...) {}
|
||||||
|
|
||||||
return QByteArray();
|
return Result::ErrorCode::FileParseError;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,15 +416,13 @@ void TagReaderTagParser::SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
TagReaderBase::Result TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||||
|
|
||||||
if (request.filename().empty()) return false;
|
if (request.filename().empty()) return Result::ErrorCode::FilenameMissing;
|
||||||
|
|
||||||
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
|
|
||||||
|
|
||||||
qLog(Debug) << "Saving art to" << filename;
|
qLog(Debug) << "Saving art to" << filename;
|
||||||
|
|
||||||
const QByteArray cover_data = LoadCoverDataFromRequest(request);
|
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -446,13 +441,13 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
|||||||
taginfo.parseContainerFormat(diag, progress);
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTags(diag, progress);
|
taginfo.parseTags(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taginfo.tags().size() <= 0) {
|
if (taginfo.tags().size() <= 0) {
|
||||||
@@ -460,7 +455,7 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||||
SaveEmbeddedArt(tag, cover_data);
|
SaveEmbeddedArt(tag, cover.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.applyChanges(diag, progress);
|
taginfo.applyChanges(diag, progress);
|
||||||
@@ -470,28 +465,40 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
|||||||
qLog(Debug) << QString::fromStdString(msg.message());
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return Result::ErrorCode::Success;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(...) {}
|
catch(...) {}
|
||||||
|
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReaderTagParser::SaveSongPlaycountToFile(TagParser::Tag*, const spb::tagreader::SongMetadata&) const {}
|
void TagReaderTagParser::SaveSongPlaycountToFile(TagParser::Tag *tag, const uint playcount) const {
|
||||||
|
|
||||||
bool TagReaderTagParser::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const { return false; }
|
Q_UNUSED(tag);
|
||||||
|
Q_UNUSED(playcount);
|
||||||
void TagReaderTagParser::SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
|
||||||
|
|
||||||
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(ConvertToPOPMRating(song.rating())));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
TagReaderBase::Result TagReaderTagParser::SaveSongPlaycountToFile(const QString &filename, const uint playcount) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return false;
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(playcount);
|
||||||
|
|
||||||
|
return Result::ErrorCode::Unsupported;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TagReaderTagParser::SaveSongRatingToFile(TagParser::Tag *tag, const float rating) const {
|
||||||
|
|
||||||
|
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(ConvertToPOPMRating(rating)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TagReaderBase::Result TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const float rating) const {
|
||||||
|
|
||||||
|
if (filename.isEmpty()) return Result::ErrorCode::FilenameMissing;
|
||||||
|
|
||||||
qLog(Debug) << "Saving song rating to" << filename;
|
qLog(Debug) << "Saving song rating to" << filename;
|
||||||
|
|
||||||
@@ -509,19 +516,19 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
|||||||
taginfo.parseContainerFormat(diag, progress);
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTracks(diag, progress);
|
taginfo.parseTracks(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.parseTags(diag, progress);
|
taginfo.parseTags(diag, progress);
|
||||||
if (progress.isAborted()) {
|
if (progress.isAborted()) {
|
||||||
taginfo.close();
|
taginfo.close();
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taginfo.tags().size() <= 0) {
|
if (taginfo.tags().size() <= 0) {
|
||||||
@@ -529,7 +536,7 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||||
SaveSongRatingToFile(tag, song);
|
SaveSongRatingToFile(tag, rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
taginfo.applyChanges(diag, progress);
|
taginfo.applyChanges(diag, progress);
|
||||||
@@ -539,10 +546,10 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
|||||||
qLog(Debug) << QString::fromStdString(msg.message());
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return Result::ErrorCode::Success;
|
||||||
}
|
}
|
||||||
catch(...) {}
|
catch(...) {}
|
||||||
|
|
||||||
return false;
|
return Result::ErrorCode::FileParseError;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
Copyright 2021-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
|
||||||
@@ -37,22 +37,21 @@
|
|||||||
class TagReaderTagParser : public TagReaderBase {
|
class TagReaderTagParser : public TagReaderBase {
|
||||||
public:
|
public:
|
||||||
explicit TagReaderTagParser();
|
explicit TagReaderTagParser();
|
||||||
~TagReaderTagParser();
|
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const override;
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||||
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const override;
|
||||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||||
|
|
||||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SaveSongPlaycountToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SaveSongPlaycountToFile(TagParser::Tag *tag, const uint playcount) const;
|
||||||
void SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
void SaveSongRatingToFile(TagParser::Tag *tag, const float rating) const;
|
||||||
void SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const;
|
void SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ qt_wrap_cpp(MOC ${HEADERS})
|
|||||||
|
|
||||||
link_directories(${GLIB_LIBRARY_DIRS})
|
link_directories(${GLIB_LIBRARY_DIRS})
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -39,12 +39,12 @@ target_link_libraries(strawberry-tagreader PRIVATE
|
|||||||
libstrawberry-tagreader
|
libstrawberry-tagreader
|
||||||
)
|
)
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
Copyright 2011, 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
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@@ -27,20 +28,37 @@
|
|||||||
|
|
||||||
#include "tagreaderworker.h"
|
#include "tagreaderworker.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGLIB
|
||||||
|
# include "tagreadertaglib.h"
|
||||||
|
# include "tagreadergme.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGPARSER
|
||||||
|
# include "tagreadertagparser.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using std::make_shared;
|
||||||
|
using std::shared_ptr;
|
||||||
|
|
||||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
||||||
: AbstractMessageHandler<spb::tagreader::Message>(socket, parent) {}
|
: AbstractMessageHandler<spb::tagreader::Message>(socket, parent) {
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGLIB
|
||||||
|
tagreaders_ << make_shared<TagReaderTagLib>();
|
||||||
|
tagreaders_ << make_shared<TagReaderGME>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGPARSER
|
||||||
|
tagreaders_ << make_shared<TagReaderTagParser>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
|
void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
|
||||||
|
|
||||||
spb::tagreader::Message reply;
|
spb::tagreader::Message reply;
|
||||||
|
|
||||||
bool success = HandleMessage(message, reply, &tag_reader_);
|
HandleMessage(message, reply);
|
||||||
if (!success) {
|
|
||||||
#if defined(USE_TAGLIB)
|
|
||||||
HandleMessage(message, reply, &tag_reader_gme_);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SendReply(message, &reply);
|
SendReply(message, &reply);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -53,48 +71,123 @@ void TagReaderWorker::DeviceClosed() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase *reader) {
|
void TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply) {
|
||||||
|
|
||||||
if (message.has_is_media_file_request()) {
|
for (shared_ptr<TagReaderBase> reader : tagreaders_) {
|
||||||
const QString filename = QString::fromUtf8(message.is_media_file_request().filename().data(), static_cast<qint64>(message.is_media_file_request().filename().size()));
|
|
||||||
bool success = reader->IsMediaFile(filename);
|
|
||||||
reply.mutable_is_media_file_response()->set_success(success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
else 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())));
|
|
||||||
bool success = reader->ReadFile(filename, reply.mutable_read_file_response()->mutable_metadata());
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
else if (message.has_save_file_request()) {
|
|
||||||
bool success = reader->SaveFile(message.save_file_request());
|
|
||||||
reply.mutable_save_file_response()->set_success(success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
else 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())));
|
|
||||||
QByteArray data = reader->LoadEmbeddedArt(filename);
|
|
||||||
reply.mutable_load_embedded_art_response()->set_data(data.constData(), data.size());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (message.has_save_embedded_art_request()) {
|
|
||||||
bool success = reader->SaveEmbeddedArt(message.save_embedded_art_request());
|
|
||||||
reply.mutable_save_embedded_art_response()->set_success(success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
else 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())));
|
|
||||||
bool success = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().metadata());
|
|
||||||
reply.mutable_save_song_playcount_to_file_response()->set_success(success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
else 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()));
|
|
||||||
bool success = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().metadata());
|
|
||||||
reply.mutable_save_song_rating_to_file_response()->set_success(success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
if (message.has_is_media_file_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.is_media_file_request().filename());
|
||||||
|
const bool success = reader->IsMediaFile(filename);
|
||||||
|
reply.mutable_is_media_file_response()->set_success(success);
|
||||||
|
if (success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_read_file_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.read_file_request().filename());
|
||||||
|
spb::tagreader::ReadFileResponse *response = reply.mutable_read_file_response();
|
||||||
|
const TagReaderBase::Result result = reader->ReadFile(filename, response->mutable_metadata());
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_write_file_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.write_file_request().filename());
|
||||||
|
const TagReaderBase::Result result = reader->WriteFile(filename, message.write_file_request());
|
||||||
|
spb::tagreader::WriteFileResponse *response = reply.mutable_write_file_response();
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_load_embedded_art_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.load_embedded_art_request().filename());
|
||||||
|
QByteArray data;
|
||||||
|
const TagReaderBase::Result result = reader->LoadEmbeddedArt(filename, data);
|
||||||
|
spb::tagreader::LoadEmbeddedArtResponse *response = reply.mutable_load_embedded_art_response();
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
response->set_data(data.toStdString());
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_save_embedded_art_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.save_embedded_art_request().filename());
|
||||||
|
const TagReaderBase::Result result = reader->SaveEmbeddedArt(filename, message.save_embedded_art_request());
|
||||||
|
spb::tagreader::SaveEmbeddedArtResponse *response = reply.mutable_save_embedded_art_response();
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_save_song_playcount_to_file_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.save_song_playcount_to_file_request().filename());
|
||||||
|
const TagReaderBase::Result result = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().playcount());
|
||||||
|
spb::tagreader::SaveSongPlaycountToFileResponse *response = reply.mutable_save_song_playcount_to_file_response();
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.has_save_song_rating_to_file_request()) {
|
||||||
|
const QString filename = QString::fromStdString(message.save_song_rating_to_file_request().filename());
|
||||||
|
const TagReaderBase::Result result = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().rating());
|
||||||
|
spb::tagreader::SaveSongRatingToFileResponse *response = reply.mutable_save_song_rating_to_file_response();
|
||||||
|
response->set_success(result.success());
|
||||||
|
if (result.success()) {
|
||||||
|
if (response->has_error()) {
|
||||||
|
response->clear_error();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!response->has_error()) {
|
||||||
|
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* This file is part of Strawberry.
|
/* This file is part of Strawberry.
|
||||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
Copyright 2011, 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
|
||||||
@@ -21,19 +21,19 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
#include "core/messagehandler.h"
|
#include "core/messagehandler.h"
|
||||||
#if defined(USE_TAGLIB)
|
|
||||||
# include "tagreadertaglib.h"
|
|
||||||
# include "tagreadergme.h"
|
|
||||||
#elif defined(USE_TAGPARSER)
|
|
||||||
# include "tagreadertagparser.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
|
class TagReaderBase;
|
||||||
|
|
||||||
|
using std::shared_ptr;
|
||||||
|
|
||||||
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -46,15 +46,9 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
|||||||
void DeviceClosed() override;
|
void DeviceClosed() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Handle message using specific TagReaderBase implementation. Returns true on successful message handle.
|
void HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply);
|
||||||
bool HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase* reader);
|
|
||||||
|
|
||||||
#if defined(USE_TAGLIB)
|
QList<shared_ptr<TagReaderBase>> tagreaders_;
|
||||||
TagReaderTagLib tag_reader_;
|
|
||||||
TagReaderGME tag_reader_gme_;
|
|
||||||
#elif defined(USE_TAGPARSER)
|
|
||||||
TagReaderTagParser tag_reader_;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TAGREADERWORKER_H
|
#endif // TAGREADERWORKER_H
|
||||||
|
|||||||
@@ -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,7 +58,10 @@ 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
|
||||||
|
|
||||||
|
filterparser/filterparser.cpp
|
||||||
|
filterparser/filtertree.cpp
|
||||||
|
|
||||||
engine/enginebase.cpp
|
engine/enginebase.cpp
|
||||||
engine/enginedevice.cpp
|
engine/enginedevice.cpp
|
||||||
@@ -70,8 +74,10 @@ set(SOURCES
|
|||||||
analyzer/analyzercontainer.cpp
|
analyzer/analyzercontainer.cpp
|
||||||
analyzer/blockanalyzer.cpp
|
analyzer/blockanalyzer.cpp
|
||||||
analyzer/boomanalyzer.cpp
|
analyzer/boomanalyzer.cpp
|
||||||
|
analyzer/turbineanalyzer.cpp
|
||||||
|
analyzer/sonogramanalyzer.cpp
|
||||||
|
analyzer/waverubberanalyzer.cpp
|
||||||
analyzer/rainbowanalyzer.cpp
|
analyzer/rainbowanalyzer.cpp
|
||||||
analyzer/sonogram.cpp
|
|
||||||
|
|
||||||
equalizer/equalizer.cpp
|
equalizer/equalizer.cpp
|
||||||
equalizer/equalizerslider.cpp
|
equalizer/equalizerslider.cpp
|
||||||
@@ -89,19 +95,19 @@ 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/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
|
||||||
playlist/playlistcontainer.cpp
|
playlist/playlistcontainer.cpp
|
||||||
playlist/playlistdelegates.cpp
|
playlist/playlistdelegates.cpp
|
||||||
playlist/playlistfilter.cpp
|
playlist/playlistfilter.cpp
|
||||||
playlist/playlistfilterparser.cpp
|
|
||||||
playlist/playlistheader.cpp
|
playlist/playlistheader.cpp
|
||||||
playlist/playlistitem.cpp
|
playlist/playlistitem.cpp
|
||||||
playlist/playlistlistcontainer.cpp
|
playlist/playlistlistcontainer.cpp
|
||||||
@@ -170,7 +176,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
|
||||||
@@ -188,7 +194,7 @@ set(SOURCES
|
|||||||
lyrics/songlyricscomlyricsprovider.cpp
|
lyrics/songlyricscomlyricsprovider.cpp
|
||||||
lyrics/azlyricscomlyricsprovider.cpp
|
lyrics/azlyricscomlyricsprovider.cpp
|
||||||
lyrics/elyricsnetlyricsprovider.cpp
|
lyrics/elyricsnetlyricsprovider.cpp
|
||||||
lyrics/lyricsmodecomlyricsprovider.cpp
|
lyrics/letraslyricsprovider.cpp
|
||||||
|
|
||||||
providers/musixmatchprovider.cpp
|
providers/musixmatchprovider.cpp
|
||||||
|
|
||||||
@@ -196,6 +202,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
|
||||||
@@ -223,6 +230,8 @@ set(SOURCES
|
|||||||
widgets/busyindicator.cpp
|
widgets/busyindicator.cpp
|
||||||
widgets/clickablelabel.cpp
|
widgets/clickablelabel.cpp
|
||||||
widgets/fancytabwidget.cpp
|
widgets/fancytabwidget.cpp
|
||||||
|
widgets/fancytabbar.cpp
|
||||||
|
widgets/fancytabdata.cpp
|
||||||
widgets/favoritewidget.cpp
|
widgets/favoritewidget.cpp
|
||||||
widgets/fileview.cpp
|
widgets/fileview.cpp
|
||||||
widgets/fileviewlist.cpp
|
widgets/fileviewlist.cpp
|
||||||
@@ -249,19 +258,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
|
||||||
@@ -306,6 +314,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
|
||||||
@@ -316,6 +325,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
|
||||||
@@ -324,8 +334,10 @@ set(HEADERS
|
|||||||
analyzer/analyzercontainer.h
|
analyzer/analyzercontainer.h
|
||||||
analyzer/blockanalyzer.h
|
analyzer/blockanalyzer.h
|
||||||
analyzer/boomanalyzer.h
|
analyzer/boomanalyzer.h
|
||||||
|
analyzer/turbineanalyzer.h
|
||||||
|
analyzer/sonogramanalyzer.h
|
||||||
|
analyzer/waverubberanalyzer.h
|
||||||
analyzer/rainbowanalyzer.h
|
analyzer/rainbowanalyzer.h
|
||||||
analyzer/sonogram.h
|
|
||||||
|
|
||||||
equalizer/equalizer.h
|
equalizer/equalizer.h
|
||||||
equalizer/equalizerslider.h
|
equalizer/equalizerslider.h
|
||||||
@@ -342,6 +354,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
|
||||||
|
|
||||||
@@ -415,7 +428,7 @@ 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
|
||||||
@@ -431,12 +444,13 @@ set(HEADERS
|
|||||||
lyrics/songlyricscomlyricsprovider.h
|
lyrics/songlyricscomlyricsprovider.h
|
||||||
lyrics/azlyricscomlyricsprovider.h
|
lyrics/azlyricscomlyricsprovider.h
|
||||||
lyrics/elyricsnetlyricsprovider.h
|
lyrics/elyricsnetlyricsprovider.h
|
||||||
lyrics/lyricsmodecomlyricsprovider.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
|
||||||
@@ -464,6 +478,8 @@ set(HEADERS
|
|||||||
widgets/busyindicator.h
|
widgets/busyindicator.h
|
||||||
widgets/clickablelabel.h
|
widgets/clickablelabel.h
|
||||||
widgets/fancytabwidget.h
|
widgets/fancytabwidget.h
|
||||||
|
widgets/fancytabbar.h
|
||||||
|
widgets/fancytabdata.h
|
||||||
widgets/favoritewidget.h
|
widgets/favoritewidget.h
|
||||||
widgets/fileview.h
|
widgets/fileview.h
|
||||||
widgets/fileviewlist.h
|
widgets/fileviewlist.h
|
||||||
@@ -486,22 +502,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
|
||||||
@@ -592,9 +608,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
|
||||||
|
|
||||||
@@ -850,7 +866,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
|
||||||
@@ -896,6 +912,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
|
||||||
@@ -971,7 +1006,7 @@ if(HAVE_TRANSLATIONS)
|
|||||||
|
|
||||||
add_pot(POT
|
add_pot(POT
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
|
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.pot
|
${CMAKE_CURRENT_BINARY_DIR}/translations/translations.pot
|
||||||
${SOURCES}
|
${SOURCES}
|
||||||
${MOC}
|
${MOC}
|
||||||
${UIC}
|
${UIC}
|
||||||
@@ -988,14 +1023,9 @@ link_directories(
|
|||||||
${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()
|
||||||
@@ -1051,11 +1081,11 @@ if(HAVE_LIBMTP)
|
|||||||
link_directories(${LIBMTP_LIBRARY_DIRS})
|
link_directories(${LIBMTP_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
if(HAVE_TAGLIB)
|
||||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
if(HAVE_TAGPARSER)
|
||||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -1079,6 +1109,7 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
|
|||||||
${GOBJECT_INCLUDE_DIRS}
|
${GOBJECT_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)
|
||||||
@@ -1101,6 +1132,7 @@ target_link_libraries(strawberry_lib PUBLIC
|
|||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${GOBJECT_LIBRARIES}
|
${GOBJECT_LIBRARIES}
|
||||||
${SQLITE_LIBRARIES}
|
${SQLITE_LIBRARIES}
|
||||||
|
${ICU_LIBRARIES}
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
@@ -1121,17 +1153,6 @@ if(HAVE_X11_GLOBALSHORTCUTS AND HAVE_X11EXTRAS)
|
|||||||
target_link_libraries(strawberry_lib PUBLIC Qt${QT_VERSION_MAJOR}::X11Extras)
|
target_link_libraries(strawberry_lib PUBLIC Qt${QT_VERSION_MAJOR}::X11Extras)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAVE_ICU)
|
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ICU_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${ICU_LIBRARIES})
|
|
||||||
else()
|
|
||||||
if(FREEBSD AND NOT Iconv_LIBRARIES)
|
|
||||||
set(Iconv_LIBRARIES iconv)
|
|
||||||
endif()
|
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${Iconv_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${Iconv_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_ALSA)
|
if(HAVE_ALSA)
|
||||||
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS})
|
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS})
|
||||||
target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES})
|
target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES})
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,14 @@
|
|||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
#include "blockanalyzer.h"
|
#include "blockanalyzer.h"
|
||||||
#include "boomanalyzer.h"
|
#include "boomanalyzer.h"
|
||||||
|
#include "turbineanalyzer.h"
|
||||||
|
#include "sonogramanalyzer.h"
|
||||||
|
#include "waverubberanalyzer.h"
|
||||||
#include "rainbowanalyzer.h"
|
#include "rainbowanalyzer.h"
|
||||||
#include "sonogram.h"
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/shared_ptr.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;
|
||||||
@@ -52,10 +55,12 @@ const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
|
|||||||
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
|
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
|
||||||
|
|
||||||
// Framerates
|
// Framerates
|
||||||
const int AnalyzerContainer::kLowFramerate = 20;
|
namespace {
|
||||||
const int AnalyzerContainer::kMediumFramerate = 25;
|
constexpr int kLowFramerate = 20;
|
||||||
const int AnalyzerContainer::kHighFramerate = 30;
|
constexpr int kMediumFramerate = 25;
|
||||||
const int AnalyzerContainer::kSuperHighFramerate = 60;
|
constexpr int kHighFramerate = 30;
|
||||||
|
constexpr int kSuperHighFramerate = 60;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
@@ -84,9 +89,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
|||||||
|
|
||||||
AddAnalyzerType<BlockAnalyzer>();
|
AddAnalyzerType<BlockAnalyzer>();
|
||||||
AddAnalyzerType<BoomAnalyzer>();
|
AddAnalyzerType<BoomAnalyzer>();
|
||||||
AddAnalyzerType<NyanCatAnalyzer>();
|
AddAnalyzerType<TurbineAnalyzer>();
|
||||||
|
AddAnalyzerType<SonogramAnalyzer>();
|
||||||
|
AddAnalyzerType<WaveRubberAnalyzer>();
|
||||||
AddAnalyzerType<RainbowDashAnalyzer>();
|
AddAnalyzerType<RainbowDashAnalyzer>();
|
||||||
AddAnalyzerType<Sonogram>();
|
AddAnalyzerType<NyanCatAnalyzer>();
|
||||||
|
|
||||||
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);
|
||||||
@@ -178,9 +185,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();
|
||||||
|
|
||||||
@@ -191,12 +198,16 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!current_analyzer_) {
|
||||||
|
ChangeAnalyzer(0);
|
||||||
|
actions_[0]->setChecked(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Framerate
|
// Framerate
|
||||||
@@ -215,7 +226,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();
|
||||||
@@ -224,9 +235,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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class AnalyzerContainer : public QWidget {
|
|||||||
explicit AnalyzerContainer(QWidget *parent);
|
explicit AnalyzerContainer(QWidget *parent);
|
||||||
|
|
||||||
void SetEngine(SharedPtr<EngineBase> engine);
|
void SetEngine(SharedPtr<EngineBase> engine);
|
||||||
void SetActions(QAction *visualisation);
|
|
||||||
|
|
||||||
static const char *kSettingsGroup;
|
static const char *kSettingsGroup;
|
||||||
static const char *kSettingsFramerate;
|
static const char *kSettingsFramerate;
|
||||||
@@ -55,7 +54,7 @@ class AnalyzerContainer : public QWidget {
|
|||||||
void WheelEvent(const int delta);
|
void WheelEvent(const int delta);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mouseReleaseEvent(QMouseEvent*) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
void wheelEvent(QWheelEvent *e) override;
|
void wheelEvent(QWheelEvent *e) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -65,11 +64,6 @@ class AnalyzerContainer : public QWidget {
|
|||||||
void ShowPopupMenu();
|
void ShowPopupMenu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kLowFramerate;
|
|
||||||
static const int kMediumFramerate;
|
|
||||||
static const int kHighFramerate;
|
|
||||||
static const int kSuperHighFramerate;
|
|
||||||
|
|
||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
void SaveFramerate(const int framerate);
|
void SaveFramerate(const int framerate);
|
||||||
|
|||||||
@@ -36,12 +36,14 @@
|
|||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
|
|
||||||
const int BlockAnalyzer::kHeight = 2;
|
namespace {
|
||||||
const int BlockAnalyzer::kWidth = 4;
|
constexpr int kHeight = 2;
|
||||||
const int BlockAnalyzer::kMinRows = 3; // arbitrary
|
constexpr int kWidth = 4;
|
||||||
const int BlockAnalyzer::kMinColumns = 32; // arbitrary
|
constexpr int kMinRows = 3; // arbitrary
|
||||||
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
|
constexpr int kMinColumns = 32; // arbitrary
|
||||||
const int BlockAnalyzer::kFadeSize = 90;
|
constexpr int kMaxColumns = 256; // must be 2**n
|
||||||
|
constexpr int kFadeSize = 90;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||||
|
|
||||||
@@ -136,7 +138,7 @@ void BlockAnalyzer::transform(Scope &s) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
// y = 2 3 2 1 0 2
|
// y = 2 3 2 1 0 2
|
||||||
// . . . . # .
|
// . . . . # .
|
||||||
@@ -266,12 +268,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 +287,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 +296,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 +312,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,18 +43,11 @@ class BlockAnalyzer : public AnalyzerBase {
|
|||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit BlockAnalyzer(QWidget*);
|
Q_INVOKABLE explicit BlockAnalyzer(QWidget*);
|
||||||
|
|
||||||
static const int kHeight;
|
|
||||||
static const int kWidth;
|
|
||||||
static const int kMinRows;
|
|
||||||
static const int kMinColumns;
|
|
||||||
static const int kMaxColumns;
|
|
||||||
static const int kFadeSize;
|
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transform(Scope&) override;
|
void transform(Scope&) override;
|
||||||
void analyze(QPainter &p, const Scope&, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
void resizeEvent(QResizeEvent*) override;
|
void resizeEvent(QResizeEvent*) override;
|
||||||
virtual void paletteChange(const QPalette&);
|
virtual void paletteChange(const QPalette&);
|
||||||
void framerateChanged() override;
|
void framerateChanged() override;
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class BoomAnalyzer : public AnalyzerBase {
|
|||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
void transform(Scope &s) override;
|
void transform(Scope &s) override;
|
||||||
void analyze(QPainter &p, const Scope&, const bool new_frame) override;
|
void analyze(QPainter &p, const Scope &scope, const bool new_frame) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void changeK_barHeight(int);
|
void changeK_barHeight(int);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -41,18 +41,21 @@
|
|||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
|
const char *NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
||||||
|
const char *RainbowDashAnalyzer::kName = "Rainbow Dash";
|
||||||
|
|
||||||
|
RainbowAnalyzer::RainbowType RainbowAnalyzer::rainbowtype;
|
||||||
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
|
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
|
||||||
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
|
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
|
||||||
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
|
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
|
||||||
const int RainbowAnalyzer::kRainbowHeight[] = { 21, 16 };
|
|
||||||
const int RainbowAnalyzer::kRainbowOverlap[] = { 13, 15 };
|
|
||||||
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
|
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
|
||||||
|
|
||||||
const char *NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
namespace {
|
||||||
const char *RainbowDashAnalyzer::kName = "Rainbow Dash";
|
constexpr int kFrameIntervalMs = 150;
|
||||||
const float RainbowAnalyzer::kPixelScale = 0.02F;
|
constexpr int kRainbowHeight[] = { 21, 16 };
|
||||||
|
constexpr int kRainbowOverlap[] = { 13, 15 };
|
||||||
RainbowAnalyzer::RainbowType RainbowAnalyzer::rainbowtype;
|
constexpr float kPixelScale = 0.02F;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
||||||
: AnalyzerBase(parent, 9),
|
: AnalyzerBase(parent, 9),
|
||||||
@@ -65,8 +68,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) {
|
||||||
@@ -106,7 +109,7 @@ void RainbowAnalyzer::resizeEvent(QResizeEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
// Discard the second half of the transform
|
// Discard the second half of the transform
|
||||||
const int scope_size = static_cast<int>(s.size() / 2);
|
const int scope_size = static_cast<int>(s.size() / 2);
|
||||||
|
|||||||
@@ -49,44 +49,37 @@ class RainbowAnalyzer : public AnalyzerBase {
|
|||||||
Dash = 1
|
Dash = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
|
explicit RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transform(Scope&) override;
|
void transform(Scope &s) override;
|
||||||
void analyze(QPainter &p, const Scope&, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
|
|
||||||
void timerEvent(QTimerEvent *e) override;
|
void timerEvent(QTimerEvent *e) override;
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const int kRainbowBands = 6;
|
||||||
|
static const int kHistorySize = 128;
|
||||||
|
static RainbowType rainbowtype;
|
||||||
static const int kHeight[];
|
static const int kHeight[];
|
||||||
static const int kWidth[];
|
static const int kWidth[];
|
||||||
static const int kFrameCount[];
|
static const int kFrameCount[];
|
||||||
static const int kRainbowHeight[];
|
|
||||||
static const int kRainbowOverlap[];
|
|
||||||
static const int kSleepingHeight[];
|
static const int kSleepingHeight[];
|
||||||
|
|
||||||
static const int kHistorySize = 128;
|
inline QRect SourceRect(const RainbowType _rainbowtype) const {
|
||||||
static const int kRainbowBands = 6;
|
|
||||||
static const float kPixelScale;
|
|
||||||
|
|
||||||
static const int kFrameIntervalMs = 150;
|
|
||||||
|
|
||||||
static RainbowType rainbowtype;
|
|
||||||
|
|
||||||
inline QRect SourceRect(RainbowType _rainbowtype) const {
|
|
||||||
return QRect(0, kHeight[_rainbowtype] * frame_, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
return QRect(0, kHeight[_rainbowtype] * frame_, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect SleepingSourceRect(RainbowType _rainbowtype) const {
|
inline QRect SleepingSourceRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(0, kHeight[_rainbowtype] * kFrameCount[_rainbowtype], kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
return QRect(0, kHeight[_rainbowtype] * kFrameCount[_rainbowtype], kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect DestRect(RainbowType _rainbowtype) const {
|
inline QRect DestRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(width() - kWidth[_rainbowtype], (height() - kHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
return QRect(width() - kWidth[_rainbowtype], (height() - kHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect SleepingDestRect(RainbowType _rainbowtype) const {
|
inline QRect SleepingDestRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(width() - kWidth[_rainbowtype], (height() - kSleepingHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
return QRect(width() - kWidth[_rainbowtype], (height() - kSleepingHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
#include "sonogram.h"
|
#include "sonogramanalyzer.h"
|
||||||
|
|
||||||
const char *Sonogram::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
const char *SonogramAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
||||||
|
|
||||||
Sonogram::Sonogram(QWidget *parent)
|
SonogramAnalyzer::SonogramAnalyzer(QWidget *parent)
|
||||||
: AnalyzerBase(parent, 9) {}
|
: AnalyzerBase(parent, 9) {}
|
||||||
|
|
||||||
void Sonogram::resizeEvent(QResizeEvent *e) {
|
void SonogramAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
Q_UNUSED(e)
|
Q_UNUSED(e)
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ void Sonogram::resizeEvent(QResizeEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void SonogramAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
@@ -81,7 +81,7 @@ void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::transform(Scope &scope) {
|
void SonogramAnalyzer::transform(Scope &scope) {
|
||||||
|
|
||||||
fht_->power2(scope.data());
|
fht_->power2(scope.data());
|
||||||
fht_->scale(scope.data(), 1.0 / 256);
|
fht_->scale(scope.data(), 1.0 / 256);
|
||||||
@@ -89,6 +89,6 @@ void Sonogram::transform(Scope &scope) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::demo(QPainter &p) {
|
void SonogramAnalyzer::demo(QPainter &p) {
|
||||||
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||||
}
|
}
|
||||||
@@ -21,24 +21,25 @@
|
|||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SONOGRAM_H
|
#ifndef SONOGRAMANALYZER_H
|
||||||
#define SONOGRAM_H
|
#define SONOGRAMANALYZER_H
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
class Sonogram : public AnalyzerBase {
|
class SonogramAnalyzer : public AnalyzerBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit Sonogram(QWidget *parent);
|
Q_INVOKABLE explicit SonogramAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void analyze(QPainter &p, const Scope &s, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
void transform(Scope &scope) override;
|
void transform(Scope &scope) override;
|
||||||
void demo(QPainter &p) override;
|
void demo(QPainter &p) override;
|
||||||
|
|
||||||
@@ -46,4 +47,4 @@ class Sonogram : public AnalyzerBase {
|
|||||||
QPixmap canvas_;
|
QPixmap canvas_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SONOGRAM_H
|
#endif // SONOGRAMANALYZER_H
|
||||||
100
src/analyzer/turbineanalyzer.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
This file was part of Clementine.
|
||||||
|
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
|
||||||
|
Copyright 2003, Max Howell <max.howell@methylblue.com>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||||
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Clementine 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.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "turbineanalyzer.h"
|
||||||
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
|
const char *TurbineAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Turbine");
|
||||||
|
|
||||||
|
TurbineAnalyzer::TurbineAnalyzer(QWidget *parent) : BoomAnalyzer(parent) {}
|
||||||
|
|
||||||
|
void TurbineAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame) {
|
||||||
|
|
||||||
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint hd2 = height() / 2;
|
||||||
|
const uint kMaxHeight = hd2 - 1;
|
||||||
|
|
||||||
|
QPainter canvas_painter(&canvas_);
|
||||||
|
canvas_.fill(palette().color(QPalette::Window));
|
||||||
|
|
||||||
|
AnalyzerBase::interpolate(scope, scope_);
|
||||||
|
|
||||||
|
for (uint i = 0, x = 0, y = 0; i < static_cast<uint>(bands_); ++i, x += kColumnWidth + 1) {
|
||||||
|
float h = static_cast<float>(std::min(log10(scope_[i] * 256.0) * F_ * 0.5, kMaxHeight * 1.0));
|
||||||
|
|
||||||
|
if (h > bar_height_[i]) {
|
||||||
|
bar_height_[i] = h;
|
||||||
|
if (h > peak_height_[i]) {
|
||||||
|
peak_height_[i] = h;
|
||||||
|
peak_speed_[i] = 0.01;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto peak_handling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (bar_height_[i] > 0.0) {
|
||||||
|
bar_height_[i] -= K_barHeight_; // 1.4
|
||||||
|
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
peak_handling:
|
||||||
|
if (peak_height_[i] > 0.0) {
|
||||||
|
peak_height_[i] -= peak_speed_[i];
|
||||||
|
peak_speed_[i] *= F_peakSpeed_; // 1.12
|
||||||
|
peak_height_[i] = std::max(0.0, std::max(bar_height_[i], peak_height_[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y = hd2 - static_cast<uint>(bar_height_[i]);
|
||||||
|
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(y), barPixmap_, 0, static_cast<int>(y), -1, -1);
|
||||||
|
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(hd2), barPixmap_, 0, static_cast<int>(bar_height_[i]), -1, -1);
|
||||||
|
|
||||||
|
canvas_painter.setPen(fg_);
|
||||||
|
if (bar_height_[i] > 0) {
|
||||||
|
canvas_painter.drawRect(static_cast<int>(x), static_cast<int>(y), kColumnWidth - 1, static_cast<int>(bar_height_[i]) * 2 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint x2 = x + kColumnWidth - 1;
|
||||||
|
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||||
|
y = hd2 - static_cast<uint>(peak_height_[i]);
|
||||||
|
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
|
||||||
|
y = hd2 + static_cast<uint>(peak_height_[i]);
|
||||||
|
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
|
||||||
|
}
|
||||||
41
src/analyzer/turbineanalyzer.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
This file was part of Clementine.
|
||||||
|
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Clementine 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.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TURBINEANALYZER_H
|
||||||
|
#define TURBINEANALYZER_H
|
||||||
|
|
||||||
|
#include "boomanalyzer.h"
|
||||||
|
|
||||||
|
class QPainter;
|
||||||
|
|
||||||
|
class TurbineAnalyzer : public BoomAnalyzer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE explicit TurbineAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
|
void analyze(QPainter &p, const Scope &scope, const bool new_frame);
|
||||||
|
|
||||||
|
static const char *kName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TURBINEANALYZER_H
|
||||||
92
src/analyzer/waverubberanalyzer.cpp
Normal file
@@ -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 "waverubberanalyzer.h"
|
||||||
|
|
||||||
|
const char *WaveRubberAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "WaveRubber");
|
||||||
|
|
||||||
|
WaveRubberAnalyzer::WaveRubberAnalyzer(QWidget *parent)
|
||||||
|
: AnalyzerBase(parent, 9) {}
|
||||||
|
|
||||||
|
void WaveRubberAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
|
Q_UNUSED(e)
|
||||||
|
|
||||||
|
canvas_ = QPixmap(size());
|
||||||
|
canvas_.fill(palette().color(QPalette::AlternateBase));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveRubberAnalyzer::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 WaveRubberAnalyzer::transform(Scope &s) {
|
||||||
|
// No need transformation for waveform analyzer
|
||||||
|
Q_UNUSED(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveRubberAnalyzer::demo(QPainter &p) {
|
||||||
|
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||||
|
}
|
||||||
41
src/analyzer/waverubberanalyzer.h
Normal file
@@ -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 WaveRubberAnalyzer : public AnalyzerBase {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE explicit WaveRubberAnalyzer(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_;
|
||||||
|
};
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#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"
|
||||||
@@ -48,7 +49,6 @@
|
|||||||
using std::make_shared;
|
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";
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ SCollection::SCollection(Application *app, QObject *parent)
|
|||||||
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);
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ void SCollection::Init() {
|
|||||||
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);
|
||||||
@@ -179,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();
|
||||||
@@ -206,8 +206,8 @@ void SCollection::SyncPlaycountAndRatingToFiles() {
|
|||||||
const qint64 nb_songs = songs.size();
|
const qint64 nb_songs = songs.size();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
TagReaderClient::Instance()->UpdateSongPlaycountBlocking(song);
|
(void)TagReaderClient::Instance()->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount());
|
||||||
TagReaderClient::Instance()->UpdateSongRatingBlocking(song);
|
(void)TagReaderClient::Instance()->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating());
|
||||||
app_->task_manager()->SetTaskProgress(task_id, ++i, nb_songs);
|
app_->task_manager()->SetTaskProgress(task_id, ++i, nb_songs);
|
||||||
}
|
}
|
||||||
app_->task_manager()->SetTaskFinished(task_id);
|
app_->task_manager()->SetTaskFinished(task_id);
|
||||||
@@ -217,7 +217,7 @@ void SCollection::SyncPlaycountAndRatingToFiles() {
|
|||||||
void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_tags) {
|
void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_tags) {
|
||||||
|
|
||||||
if (save_tags || save_playcounts_to_files_) {
|
if (save_tags || save_playcounts_to_files_) {
|
||||||
app_->tag_reader_client()->UpdateSongsPlaycount(songs);
|
app_->tag_reader_client()->SaveSongsPlaycount(songs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_t
|
|||||||
void SCollection::SongsRatingChanged(const SongList &songs, const bool save_tags) {
|
void SCollection::SongsRatingChanged(const SongList &songs, const bool save_tags) {
|
||||||
|
|
||||||
if (save_tags || save_ratings_to_files_) {
|
if (save_tags || save_ratings_to_files_) {
|
||||||
app_->tag_reader_client()->UpdateSongsRating(songs);
|
app_->tag_reader_client()->SaveSongsRating(songs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -80,12 +80,13 @@ 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 SharedPtr<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;
|
||||||
|
|
||||||
@@ -130,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 {
|
||||||
@@ -144,7 +146,8 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
|
|
||||||
~CollectionBackend();
|
~CollectionBackend();
|
||||||
|
|
||||||
void Init(SharedPtr<Database> db, SharedPtr<TaskManager> task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
|
void 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();
|
||||||
@@ -156,10 +159,11 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
SharedPtr<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;
|
||||||
|
|
||||||
@@ -203,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);
|
||||||
@@ -233,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);
|
||||||
@@ -248,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);
|
||||||
@@ -268,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();
|
||||||
@@ -297,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);
|
||||||
@@ -315,7 +325,6 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
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
|
||||||
@@ -41,15 +42,18 @@ using std::make_shared;
|
|||||||
|
|
||||||
CollectionDirectoryModel::CollectionDirectoryModel(SharedPtr<CollectionBackend> backend, QObject *parent)
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &dir) {
|
void CollectionDirectoryModel::AddDirectory(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);
|
||||||
@@ -59,7 +63,10 @@ void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &di
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -71,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
|
||||||
@@ -26,15 +27,17 @@
|
|||||||
#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 "core/shared_ptr.h"
|
||||||
|
#include "collectiondirectory.h"
|
||||||
|
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
|
||||||
struct CollectionDirectory;
|
|
||||||
class CollectionBackend;
|
class CollectionBackend;
|
||||||
class MusicStorage;
|
class MusicStorage;
|
||||||
|
|
||||||
@@ -44,22 +47,24 @@ class CollectionDirectoryModel : public QStandardItemModel {
|
|||||||
public:
|
public:
|
||||||
explicit CollectionDirectoryModel(SharedPtr<CollectionBackend> collection_backend, QObject *parent = nullptr);
|
explicit CollectionDirectoryModel(SharedPtr<CollectionBackend> collection_backend, QObject *parent = nullptr);
|
||||||
|
|
||||||
// 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_;
|
||||||
SharedPtr<CollectionBackend> backend_;
|
SharedPtr<CollectionBackend> backend_;
|
||||||
|
QMap<int, CollectionDirectory> directories_;
|
||||||
|
QStringList paths_;
|
||||||
QList<SharedPtr<MusicStorage>> storage_;
|
QList<SharedPtr<MusicStorage>> storage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
136
src/collection/collectionfilter.cpp
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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 <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "filterparser/filterparser.h"
|
||||||
|
#include "filterparser/filtertree.h"
|
||||||
|
#include "playlist/songmimedata.h"
|
||||||
|
#include "playlist/playlistmanager.h"
|
||||||
|
#include "collectionbackend.h"
|
||||||
|
#include "collectionfilter.h"
|
||||||
|
#include "collectionmodel.h"
|
||||||
|
#include "collectionitem.h"
|
||||||
|
|
||||||
|
CollectionFilter::CollectionFilter(QObject *parent) : QSortFilterProxyModel(parent), query_hash_(0) {
|
||||||
|
|
||||||
|
setSortLocaleAware(true);
|
||||||
|
setDynamicSortFilter(true);
|
||||||
|
setRecursiveFilteringEnabled(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (filter_string_.isEmpty()) return true;
|
||||||
|
|
||||||
|
if (item->type != CollectionItem::Type::Song) {
|
||||||
|
return item->type == CollectionItem::Type::LoadingIndicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
const size_t hash = qHash(filter_string_);
|
||||||
|
#else
|
||||||
|
const uint hash = qHash(filter_string_);
|
||||||
|
#endif
|
||||||
|
if (hash != query_hash_) {
|
||||||
|
FilterParser p(filter_string_);
|
||||||
|
filter_tree_.reset(p.parse());
|
||||||
|
query_hash_ = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item->metadata.is_valid() && filter_tree_->accept(item->metadata);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionFilter::SetFilterString(const QString &filter_string) {
|
||||||
|
|
||||||
|
filter_string_ = filter_string;
|
||||||
|
setFilterFixedString(filter_string);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QMimeData *CollectionFilter::mimeData(const QModelIndexList &indexes) const {
|
||||||
|
|
||||||
|
if (indexes.isEmpty()) return nullptr;
|
||||||
|
|
||||||
|
CollectionModel *collection_model = qobject_cast<CollectionModel*>(sourceModel());
|
||||||
|
SongMimeData *data = new SongMimeData;
|
||||||
|
data->backend = collection_model->backend();
|
||||||
|
|
||||||
|
QSet<int> song_ids;
|
||||||
|
QList<QUrl> urls;
|
||||||
|
for (const QModelIndex &idx : indexes) {
|
||||||
|
const QModelIndex source_index = mapToSource(idx);
|
||||||
|
CollectionItem *item = collection_model->IndexToItem(source_index);
|
||||||
|
GetChildSongs(item, song_ids, urls, data->songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
data->setUrls(urls);
|
||||||
|
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionFilter::GetChildSongs(CollectionItem *item, QSet<int> &song_ids, QList<QUrl> &urls, SongList &songs) const {
|
||||||
|
|
||||||
|
CollectionModel *collection_model = qobject_cast<CollectionModel*>(sourceModel());
|
||||||
|
|
||||||
|
switch (item->type) {
|
||||||
|
case CollectionItem::Type::Container:{
|
||||||
|
QList<CollectionItem*> children = item->children;
|
||||||
|
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, collection_model, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
for (CollectionItem *child : children) {
|
||||||
|
GetChildSongs(child, song_ids, urls, songs);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CollectionItem::Type::Song:{
|
||||||
|
const QModelIndex idx = collection_model->ItemToIndex(item);
|
||||||
|
if (filterAcceptsRow(idx.row(), idx.parent())) {
|
||||||
|
urls << item->metadata.url();
|
||||||
|
if (!song_ids.contains(item->metadata.id())) {
|
||||||
|
song_ids.insert(item->metadata.id());
|
||||||
|
songs << item->metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
src/collection/collectionfilter.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QSortFilterProxyModel>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QList>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "filterparser/filtertree.h"
|
||||||
|
|
||||||
|
class CollectionItem;
|
||||||
|
|
||||||
|
class CollectionFilter : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CollectionFilter(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void SetFilterString(const QString &filter_string);
|
||||||
|
QString filter_string() const { return filter_string_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(const int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GetChildSongs(CollectionItem *item, QSet<int> &song_ids, QList<QUrl> &urls, SongList &songs) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QScopedPointer<FilterTree> filter_tree_;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
mutable size_t query_hash_;
|
||||||
|
#else
|
||||||
|
mutable uint query_hash_;
|
||||||
|
#endif
|
||||||
|
QString filter_string_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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,12 @@
|
|||||||
#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 "filterparser/filterparser.h"
|
||||||
#include "savedgroupingmanager.h"
|
#include "savedgroupingmanager.h"
|
||||||
#include "collectionfilterwidget.h"
|
#include "collectionfilterwidget.h"
|
||||||
#include "groupbydialog.h"
|
#include "groupbydialog.h"
|
||||||
@@ -56,60 +61,37 @@
|
|||||||
#include "settings/collectionsettingspage.h"
|
#include "settings/collectionsettingspage.h"
|
||||||
#include "settings/appearancesettingspage.h"
|
#include "settings/appearancesettingspage.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr int kFilterDelay = 500; // msec
|
||||||
|
}
|
||||||
|
|
||||||
CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
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),
|
||||||
group_by_menu_(nullptr),
|
group_by_menu_(nullptr),
|
||||||
collection_menu_(nullptr),
|
collection_menu_(nullptr),
|
||||||
group_by_group_(nullptr),
|
group_by_group_(nullptr),
|
||||||
filter_delay_(new QTimer(this)),
|
timer_filter_delay_(new QTimer(this)),
|
||||||
filter_applies_to_model_(true),
|
filter_applies_to_model_(true),
|
||||||
delay_behaviour_(DelayBehaviour::DelayedOnLargeLibraries) {
|
delay_behaviour_(DelayBehaviour::DelayedOnLargeLibraries) {
|
||||||
|
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
|
|
||||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
|
ui_->search_field->setToolTip(FilterParser::ToolTip());
|
||||||
available_fields += QString(", ") + Song::kNumericalColumns.join(", ");
|
|
||||||
|
|
||||||
ui_->search_field->setToolTip(
|
|
||||||
QString("<html><head/><body><p>") +
|
|
||||||
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
|
|
||||||
QString(" ") +
|
|
||||||
QString("<span style=\"font-weight:600;\">") +
|
|
||||||
tr("artist") +
|
|
||||||
QString(":</span><span style=\"font-style:italic;\">Strawbs</span> ") +
|
|
||||||
tr("searches the collection for all artists that contain the word %1. ").arg("Strawbs") +
|
|
||||||
QString("</p><p>") +
|
|
||||||
tr("Search terms for numerical fields can be prefixed with %1 or %2 to refine the search, e.g.: ")
|
|
||||||
.arg(" =, !=, <, >, <=", ">=") +
|
|
||||||
QString("<span style=\"font-weight:600;\">") +
|
|
||||||
tr("rating") +
|
|
||||||
QString("</span>") +
|
|
||||||
QString(":>=") +
|
|
||||||
QString("<span style=\"font-weight:italic;\">4</span>") +
|
|
||||||
|
|
||||||
QString("</p><p><span style=\"font-weight:600;\">") +
|
|
||||||
tr("Available fields") +
|
|
||||||
QString(": ") +
|
|
||||||
QString("</span>") +
|
|
||||||
QString("<span style=\"font-style:italic;\">") +
|
|
||||||
available_fields +
|
|
||||||
QString("</span>.") +
|
|
||||||
QString("</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(timer_filter_delay_, &QTimer::timeout, this, &CollectionFilterWidget::FilterDelayTimeout);
|
||||||
|
|
||||||
filter_delay_->setInterval(kFilterDelay);
|
timer_filter_delay_->setInterval(kFilterDelay);
|
||||||
filter_delay_->setSingleShot(true);
|
timer_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);
|
||||||
@@ -123,12 +105,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);
|
||||||
|
|
||||||
@@ -154,34 +136,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();
|
||||||
@@ -215,9 +198,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();
|
||||||
@@ -229,23 +216,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); }
|
||||||
@@ -253,12 +238,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() {
|
||||||
@@ -306,13 +290,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;
|
||||||
@@ -323,7 +307,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,17 +345,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();
|
||||||
|
|
||||||
@@ -425,7 +409,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]));
|
||||||
@@ -446,7 +430,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>()) {
|
||||||
@@ -456,7 +441,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);
|
||||||
|
|
||||||
@@ -508,6 +492,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);
|
||||||
@@ -516,16 +503,13 @@ 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) {
|
||||||
filter_delay_->start();
|
timer_filter_delay_->start();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
filter_delay_->stop();
|
timer_filter_delay_->stop();
|
||||||
FilterDelayTimeout();
|
FilterDelayTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,9 +517,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_->SetFilterString(ui_->search_field->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,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 {
|
||||||
@@ -50,15 +51,15 @@ class CollectionFilterWidget : public QWidget {
|
|||||||
explicit CollectionFilterWidget(QWidget *parent = nullptr);
|
explicit CollectionFilterWidget(QWidget *parent = nullptr);
|
||||||
~CollectionFilterWidget() override;
|
~CollectionFilterWidget() override;
|
||||||
|
|
||||||
static const int kFilterDelay = 500; // msec
|
|
||||||
|
|
||||||
enum class DelayBehaviour {
|
enum class DelayBehaviour {
|
||||||
AlwaysInstant,
|
AlwaysInstant,
|
||||||
DelayedOnLargeLibraries,
|
DelayedOnLargeLibraries,
|
||||||
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);
|
||||||
|
|
||||||
@@ -94,7 +95,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;
|
||||||
@@ -115,6 +115,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_;
|
||||||
@@ -123,9 +124,9 @@ 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 *timer_filter_delay_;
|
||||||
|
|
||||||
bool filter_applies_to_model_;
|
bool filter_applies_to_model_;
|
||||||
DelayBehaviour delay_behaviour_;
|
DelayBehaviour delay_behaviour_;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
|
|||||||
if (text.isEmpty()) 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,6 +42,7 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
#include "core/shared_ptr.h"
|
#include "core/shared_ptr.h"
|
||||||
#include "core/simpletreemodel.h"
|
#include "core/simpletreemodel.h"
|
||||||
@@ -51,15 +50,17 @@
|
|||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
#include "covermanager/albumcoverloaderoptions.h"
|
#include "covermanager/albumcoverloaderoptions.h"
|
||||||
#include "covermanager/albumcoverloaderresult.h"
|
#include "covermanager/albumcoverloaderresult.h"
|
||||||
|
#include "collectionmodelupdate.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.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
|
||||||
@@ -69,20 +70,19 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
|||||||
~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;
|
||||||
};
|
};
|
||||||
|
|
||||||
SharedPtr<CollectionBackend> backend() const { return backend_; }
|
SharedPtr<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(); }
|
||||||
|
|
||||||
void ExpandAll(CollectionItem *item = nullptr) const;
|
// 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;
|
||||||
|
|
||||||
const CollectionModel::Grouping GetGroupBy() const { return group_by_; }
|
// Utility functions for manipulating text
|
||||||
void SetGroupBy(const CollectionModel::Grouping g, const std::optional<bool> separate_albums_by_grouping = std::optional<bool>());
|
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, bool &has_unique_album_identifier) const;
|
||||||
|
|
||||||
static QString ContainerKey(const GroupBy group_by, const bool separate_albums_by_grouping, const Song &song);
|
// 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;
|
||||||
|
|
||||||
|
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
||||||
|
|
||||||
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;
|
static qint64 MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default);
|
||||||
static qint64 MaximumCacheSize(QSettings *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:
|
||||||
|
static QNetworkDiskCache *sIconCache;
|
||||||
SharedPtr<CollectionBackend> backend_;
|
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_;
|
||||||
|
|||||||
23
src/collection/collectionmodelupdate.cpp
Normal file
@@ -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) {}
|
||||||
@@ -17,17 +17,22 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QVariant>
|
#ifndef COLLECTIONMODELUPDATE_H
|
||||||
#include <QString>
|
#define COLLECTIONMODELUPDATE_H
|
||||||
|
|
||||||
#include "collectionqueryoptions.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
CollectionQueryOptions::CollectionQueryOptions()
|
class CollectionModelUpdate {
|
||||||
: compilation_requirement_(CollectionQueryOptions::CompilationRequirement::None),
|
public:
|
||||||
query_have_compilations_(false) {}
|
enum class Type {
|
||||||
|
AddReAddOrUpdate,
|
||||||
|
Add,
|
||||||
|
Update,
|
||||||
|
Remove,
|
||||||
|
};
|
||||||
|
explicit CollectionModelUpdate(const Type &_type, const SongList &_songs);
|
||||||
|
Type type;
|
||||||
|
SongList songs;
|
||||||
|
};
|
||||||
|
|
||||||
void CollectionQueryOptions::AddWhere(const QString &column, const QVariant &value, const QString &op) {
|
#endif // COLLECTIONMODELUPDATE_H
|
||||||
|
|
||||||
where_clauses_ << Where(column, value, op);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -41,7 +41,11 @@ QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
|
|||||||
|
|
||||||
void CollectionPlaylistItem::Reload() {
|
void CollectionPlaylistItem::Reload() {
|
||||||
|
|
||||||
TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||||
|
if (!result.success()) {
|
||||||
|
qLog(Error) << "Could not reload file" << song_.url() << result.error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
UpdateTemporaryMetadata(song_);
|
UpdateTemporaryMetadata(song_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,141 +27,50 @@
|
|||||||
#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/sqlquery.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "collectionfilteroptions.h"
|
#include "collectionfilteroptions.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
|
|
||||||
QString filter_text = filter_options.filter_text().replace(QRegularExpression(":\\s+"), ":");
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QStringList tokens(filter_text.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts));
|
|
||||||
#else
|
|
||||||
QStringList tokens(filter_text.split(QRegularExpression("\\s+"), QString::SkipEmptyParts));
|
|
||||||
#endif
|
|
||||||
QString query;
|
|
||||||
for (QString token : tokens) {
|
|
||||||
token.remove('(')
|
|
||||||
.remove(')')
|
|
||||||
.remove('"')
|
|
||||||
.replace('-', ' ');
|
|
||||||
|
|
||||||
if (token.contains(':')) {
|
|
||||||
const QString columntoken = token.section(':', 0, 0);
|
|
||||||
QString subtoken = token.section(':', 1, -1).replace(":", " ").trimmed();
|
|
||||||
if (subtoken.isEmpty()) continue;
|
|
||||||
if (Song::kFtsColumns.contains("fts" + columntoken, Qt::CaseInsensitive)) {
|
|
||||||
if (!query.isEmpty()) query.append(" ");
|
|
||||||
query += "fts" + columntoken + ":\"" + subtoken + "\"*";
|
|
||||||
}
|
|
||||||
else if (Song::kNumericalColumns.contains(columntoken, Qt::CaseInsensitive)) {
|
|
||||||
QString comparator = RemoveSqlOperator(subtoken);
|
|
||||||
if (columntoken.compare("rating", Qt::CaseInsensitive) == 0) {
|
|
||||||
AddWhereRating(subtoken, comparator);
|
|
||||||
}
|
|
||||||
else if (columntoken.compare("length", Qt::CaseInsensitive) == 0) {
|
|
||||||
// Time is saved in nanoseconds, so add 9 0's
|
|
||||||
QString parsedTime = QString::number(Utilities::ParseSearchTime(subtoken)) + "000000000";
|
|
||||||
AddWhere(columntoken, parsedTime, comparator);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AddWhere(columntoken, subtoken, comparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Not a valid filter, remove
|
|
||||||
else {
|
|
||||||
token = token.replace(":", " ").trimmed();
|
|
||||||
if (!token.isEmpty()) {
|
|
||||||
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 ='')");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CollectionQuery::RemoveSqlOperator(QString &token) {
|
|
||||||
|
|
||||||
QString op = "=";
|
|
||||||
static QRegularExpression rxOp("^(=|<[>=]?|>=?|!=)");
|
|
||||||
QRegularExpressionMatch match = rxOp.match(token);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
op = match.captured(0);
|
|
||||||
}
|
|
||||||
token.remove(rxOp);
|
|
||||||
|
|
||||||
if (op == "!=") {
|
|
||||||
op = "<>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return op;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
@@ -170,7 +79,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)
|
||||||
@@ -179,67 +88,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::AddWhereRating(const QVariant &value, const QString &op) {
|
|
||||||
|
|
||||||
float parsed_rating = Utilities::ParseSearchRating(value.toString());
|
|
||||||
|
|
||||||
// You can't query the database for a float, due to float precision errors,
|
|
||||||
// So we have to use a certain tolerance, so that the searched value is definetly included.
|
|
||||||
const float tolerance = 0.001F;
|
|
||||||
if (op == "<") {
|
|
||||||
AddWhere("rating", parsed_rating-tolerance, "<");
|
|
||||||
}
|
|
||||||
else if (op == ">") {
|
|
||||||
AddWhere("rating", parsed_rating+tolerance, ">");
|
|
||||||
}
|
|
||||||
else if (op == "<=") {
|
|
||||||
AddWhere("rating", parsed_rating+tolerance, "<=");
|
|
||||||
}
|
|
||||||
else if (op == ">=") {
|
|
||||||
AddWhere("rating", parsed_rating-tolerance, ">=");
|
|
||||||
}
|
|
||||||
else if (op == "<>") {
|
|
||||||
where_clauses_ << QString("(rating<? OR rating>?)");
|
|
||||||
bound_values_ << parsed_rating - tolerance;
|
|
||||||
bound_values_ << parsed_rating + tolerance;
|
|
||||||
}
|
|
||||||
else /* (op == "=") */ {
|
|
||||||
AddWhere("rating", parsed_rating+tolerance, "<");
|
|
||||||
AddWhere("rating", parsed_rating-tolerance, ">");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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) ")
|
||||||
@@ -248,29 +116,20 @@ 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_);
|
|
||||||
|
|
||||||
if (!QSqlQuery::prepare(sql)) return false;
|
if (!QSqlQuery::prepare(sql)) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -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,19 +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"
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -50,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,14 +62,9 @@ class CollectionQuery : public QSqlQuery {
|
|||||||
|
|
||||||
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
void SetWhereClauses(const QStringList &where_clauses) { where_clauses_ = where_clauses; }
|
||||||
|
|
||||||
// Removes = < > <= >= <> from the beginning of the input string and returns the operator
|
|
||||||
// If the input String has no operator, returns "="
|
|
||||||
QString RemoveSqlOperator(QString &token);
|
|
||||||
// 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 AddWhereRating(const QVariant &value, const QString &op = "=");
|
|
||||||
|
|
||||||
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; }
|
||||||
@@ -82,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_;
|
||||||
@@ -90,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_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 COLLECTIONQUERYOPTIONS_H
|
|
||||||
#define COLLECTIONQUERYOPTIONS_H
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class CollectionQueryOptions {
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit CollectionQueryOptions();
|
|
||||||
|
|
||||||
struct Where {
|
|
||||||
explicit Where(const QString &_column = QString(), const QVariant &_value = QString(), const QString &_op = QString()) : column(_column), value(_value), op(_op) {}
|
|
||||||
QString column;
|
|
||||||
QVariant value;
|
|
||||||
QString op;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CompilationRequirement {
|
|
||||||
None,
|
|
||||||
On,
|
|
||||||
Off
|
|
||||||
};
|
|
||||||
|
|
||||||
QString column_spec() const { return column_spec_; }
|
|
||||||
CompilationRequirement compilation_requirement() const { return compilation_requirement_; }
|
|
||||||
bool query_have_compilations() const { return query_have_compilations_; }
|
|
||||||
|
|
||||||
void set_column_spec(const QString &column_spec) { column_spec_ = column_spec; }
|
|
||||||
void set_compilation_requirement(const CompilationRequirement compilation_requirement) { compilation_requirement_ = compilation_requirement; }
|
|
||||||
void set_query_have_compilations(const bool query_have_compilations) { query_have_compilations_ = query_have_compilations; }
|
|
||||||
|
|
||||||
QList<Where> where_clauses() const { return where_clauses_; }
|
|
||||||
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString column_spec_;
|
|
||||||
CompilationRequirement compilation_requirement_;
|
|
||||||
bool query_have_compilations_;
|
|
||||||
QList<Where> where_clauses_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // COLLECTIONQUERYOPTIONS_H
|
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
#include "core/mimedata.h"
|
#include "core/mimedata.h"
|
||||||
#include "core/musicstorage.h"
|
#include "core/musicstorage.h"
|
||||||
#include "core/deletefiles.h"
|
#include "core/deletefiles.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "utilities/filemanagerutils.h"
|
#include "utilities/filemanagerutils.h"
|
||||||
#include "collection.h"
|
#include "collection.h"
|
||||||
#include "collectionbackend.h"
|
#include "collectionbackend.h"
|
||||||
@@ -80,7 +82,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
total_song_count_(-1),
|
total_song_count_(-1),
|
||||||
total_artist_count_(-1),
|
total_artist_count_(-1),
|
||||||
total_album_count_(-1),
|
total_album_count_(-1),
|
||||||
nomusic_(":/pictures/nomusic.png"),
|
nomusic_(QStringLiteral(":/pictures/nomusic.png")),
|
||||||
context_menu_(nullptr),
|
context_menu_(nullptr),
|
||||||
action_load_(nullptr),
|
action_load_(nullptr),
|
||||||
action_add_to_playlist_(nullptr),
|
action_add_to_playlist_(nullptr),
|
||||||
@@ -88,6 +90,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
action_add_to_playlist_enqueue_next_(nullptr),
|
action_add_to_playlist_enqueue_next_(nullptr),
|
||||||
action_open_in_new_playlist_(nullptr),
|
action_open_in_new_playlist_(nullptr),
|
||||||
action_organize_(nullptr),
|
action_organize_(nullptr),
|
||||||
|
action_search_for_this_(nullptr),
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
action_copy_to_device_(nullptr),
|
action_copy_to_device_(nullptr),
|
||||||
#endif
|
#endif
|
||||||
@@ -109,7 +112,7 @@ CollectionView::CollectionView(QWidget *parent)
|
|||||||
setDragDropMode(QAbstractItemView::DragOnly);
|
setDragDropMode(QAbstractItemView::DragOnly);
|
||||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
setStyleSheet("QTreeView::item{padding-top:1px;}");
|
setStyleSheet(QStringLiteral("QTreeView::item{padding-top:1px;}"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +120,14 @@ CollectionView::~CollectionView() = default;
|
|||||||
|
|
||||||
void CollectionView::SaveFocus() {
|
void CollectionView::SaveFocus() {
|
||||||
|
|
||||||
QModelIndex current = currentIndex();
|
const QModelIndex current = currentIndex();
|
||||||
QVariant type = model()->data(current, CollectionModel::Role_Type);
|
const QVariant role_type = model()->data(current, CollectionModel::Role_Type);
|
||||||
if (!type.isValid() || (type.toInt() != CollectionItem::Type_Song && type.toInt() != CollectionItem::Type_Container && type.toInt() != CollectionItem::Type_Divider)) {
|
if (!role_type.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CollectionItem::Type item_type = role_type.value<CollectionItem::Type>();
|
||||||
|
if (item_type != CollectionItem::Type::Song && item_type != CollectionItem::Type::Container && item_type != CollectionItem::Type::Divider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +135,8 @@ void CollectionView::SaveFocus() {
|
|||||||
last_selected_song_ = Song();
|
last_selected_song_ = Song();
|
||||||
last_selected_container_ = QString();
|
last_selected_container_ = QString();
|
||||||
|
|
||||||
switch (type.toInt()) {
|
switch (item_type) {
|
||||||
case CollectionItem::Type_Song: {
|
case CollectionItem::Type::Song:{
|
||||||
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
||||||
SongList songs = app_->collection_model()->GetChildSongs(index);
|
SongList songs = app_->collection_model()->GetChildSongs(index);
|
||||||
if (!songs.isEmpty()) {
|
if (!songs.isEmpty()) {
|
||||||
@@ -137,8 +145,8 @@ void CollectionView::SaveFocus() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CollectionItem::Type_Container:
|
case CollectionItem::Type::Container:
|
||||||
case CollectionItem::Type_Divider: {
|
case CollectionItem::Type::Divider:{
|
||||||
QString text = model()->data(current, CollectionModel::Role_SortText).toString();
|
QString text = model()->data(current, CollectionModel::Role_SortText).toString();
|
||||||
last_selected_container_ = text;
|
last_selected_container_ = text;
|
||||||
break;
|
break;
|
||||||
@@ -154,9 +162,14 @@ void CollectionView::SaveFocus() {
|
|||||||
|
|
||||||
void CollectionView::SaveContainerPath(const QModelIndex &child) {
|
void CollectionView::SaveContainerPath(const QModelIndex &child) {
|
||||||
|
|
||||||
QModelIndex current = model()->parent(child);
|
const QModelIndex current = model()->parent(child);
|
||||||
QVariant type = model()->data(current, CollectionModel::Role_Type);
|
const QVariant role_type = model()->data(current, CollectionModel::Role_Type);
|
||||||
if (!type.isValid() || (type.toInt() != CollectionItem::Type_Container && type.toInt() != CollectionItem::Type_Divider)) {
|
if (!role_type.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CollectionItem::Type item_type = role_type.value<CollectionItem::Type>();
|
||||||
|
if (item_type != CollectionItem::Type::Container && item_type != CollectionItem::Type::Divider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,12 +193,17 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
|
|||||||
if (model()->canFetchMore(parent)) {
|
if (model()->canFetchMore(parent)) {
|
||||||
model()->fetchMore(parent);
|
model()->fetchMore(parent);
|
||||||
}
|
}
|
||||||
int rows = model()->rowCount(parent);
|
const int rows = model()->rowCount(parent);
|
||||||
for (int i = 0; i < rows; i++) {
|
for (int i = 0; i < rows; i++) {
|
||||||
QModelIndex current = model()->index(i, 0, parent);
|
QModelIndex current = model()->index(i, 0, parent);
|
||||||
QVariant type = model()->data(current, CollectionModel::Role_Type);
|
const QVariant role_type = model()->data(current, CollectionModel::Role_Type);
|
||||||
switch (type.toInt()) {
|
if (!role_type.isValid()) return false;
|
||||||
case CollectionItem::Type_Song:
|
const CollectionItem::Type item_type = role_type.value<CollectionItem::Type>();
|
||||||
|
switch (item_type) {
|
||||||
|
case CollectionItem::Type::Root:
|
||||||
|
case CollectionItem::Type::LoadingIndicator:
|
||||||
|
break;
|
||||||
|
case CollectionItem::Type::Song:
|
||||||
if (!last_selected_song_.url().isEmpty()) {
|
if (!last_selected_song_.url().isEmpty()) {
|
||||||
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
||||||
const SongList songs = app_->collection_model()->GetChildSongs(index);
|
const SongList songs = app_->collection_model()->GetChildSongs(index);
|
||||||
@@ -196,8 +214,8 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CollectionItem::Type_Container:
|
case CollectionItem::Type::Container:
|
||||||
case CollectionItem::Type_Divider: {
|
case CollectionItem::Type::Divider:{
|
||||||
QString text = model()->data(current, CollectionModel::Role_SortText).toString();
|
QString text = model()->data(current, CollectionModel::Role_SortText).toString();
|
||||||
if (!last_selected_container_.isEmpty() && last_selected_container_ == text) {
|
if (!last_selected_container_.isEmpty() && last_selected_container_ == text) {
|
||||||
expand(current);
|
expand(current);
|
||||||
@@ -224,18 +242,10 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
|
|||||||
|
|
||||||
void CollectionView::ReloadSettings() {
|
void CollectionView::ReloadSettings() {
|
||||||
|
|
||||||
QSettings settings;
|
Settings settings;
|
||||||
|
|
||||||
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||||
SetAutoOpen(settings.value("auto_open", false).toBool());
|
SetAutoOpen(settings.value("auto_open", false).toBool());
|
||||||
|
|
||||||
if (app_) {
|
|
||||||
app_->collection_model()->set_pretty_covers(settings.value("pretty_covers", true).toBool());
|
|
||||||
app_->collection_model()->set_show_dividers(settings.value("show_dividers", true).toBool());
|
|
||||||
}
|
|
||||||
|
|
||||||
delete_files_ = settings.value("delete_files", false).toBool();
|
delete_files_ = settings.value("delete_files", false).toBool();
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -343,29 +353,51 @@ void CollectionView::mouseReleaseEvent(QMouseEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionView::keyPressEvent(QKeyEvent *e) {
|
||||||
|
|
||||||
|
switch (e->key()) {
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
case Qt::Key_Return:
|
||||||
|
if (currentIndex().isValid()) {
|
||||||
|
AddToPlaylist();
|
||||||
|
}
|
||||||
|
e->accept();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoExpandingTreeView::keyPressEvent(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
|
|
||||||
if (!context_menu_) {
|
if (!context_menu_) {
|
||||||
context_menu_ = new QMenu(this);
|
context_menu_ = new QMenu(this);
|
||||||
action_add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, &CollectionView::AddToPlaylist);
|
action_add_to_playlist_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("media-playback-start")), tr("Append to current playlist"), this, &CollectionView::AddToPlaylist);
|
||||||
action_load_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, &CollectionView::Load);
|
action_load_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("media-playback-start")), tr("Replace current playlist"), this, &CollectionView::Load);
|
||||||
action_open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, &CollectionView::OpenInNewPlaylist);
|
action_open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("document-new")), tr("Open in new playlist"), this, &CollectionView::OpenInNewPlaylist);
|
||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
action_add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, &CollectionView::AddToPlaylistEnqueue);
|
action_add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("go-next")), tr("Queue track"), this, &CollectionView::AddToPlaylistEnqueue);
|
||||||
action_add_to_playlist_enqueue_next_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue to play next"), this, &CollectionView::AddToPlaylistEnqueueNext);
|
action_add_to_playlist_enqueue_next_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("go-next")), tr("Queue to play next"), this, &CollectionView::AddToPlaylistEnqueueNext);
|
||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
action_organize_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organize files..."), this, &CollectionView::Organize);
|
|
||||||
|
action_search_for_this_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("edit-find")), tr("Search for this"), this, &CollectionView::SearchForThis);
|
||||||
|
|
||||||
|
context_menu_->addSeparator();
|
||||||
|
action_organize_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("edit-copy")), tr("Organize files..."), this, &CollectionView::Organize);
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
action_copy_to_device_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("device")), tr("Copy to device..."), this, &CollectionView::CopyToDevice);
|
||||||
#endif
|
#endif
|
||||||
action_delete_files_ = context_menu_->addAction(IconLoader::Load("edit-delete"), tr("Delete from disk..."), this, &CollectionView::Delete);
|
action_delete_files_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("edit-delete")), tr("Delete from disk..."), this, &CollectionView::Delete);
|
||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
action_edit_track_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit track information..."), this, &CollectionView::EditTracks);
|
action_edit_track_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("edit-rename")), tr("Edit track information..."), this, &CollectionView::EditTracks);
|
||||||
action_edit_tracks_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit tracks information..."), this, &CollectionView::EditTracks);
|
action_edit_tracks_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("edit-rename")), tr("Edit tracks information..."), this, &CollectionView::EditTracks);
|
||||||
action_show_in_browser_ = context_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, &CollectionView::ShowInBrowser);
|
action_show_in_browser_ = context_menu_->addAction(IconLoader::Load(QStringLiteral("document-open-folder")), tr("Show in file browser..."), this, &CollectionView::ShowInBrowser);
|
||||||
|
|
||||||
context_menu_->addSeparator();
|
context_menu_->addSeparator();
|
||||||
|
|
||||||
@@ -391,7 +423,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
|||||||
|
|
||||||
context_menu_index_ = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(context_menu_index_);
|
context_menu_index_ = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(context_menu_index_);
|
||||||
|
|
||||||
QModelIndexList selected_indexes = qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(selectionModel()->selection()).indexes();
|
const QModelIndexList selected_indexes = qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(selectionModel()->selection()).indexes();
|
||||||
|
|
||||||
int regular_elements = 0;
|
int regular_elements = 0;
|
||||||
int regular_editable = 0;
|
int regular_editable = 0;
|
||||||
@@ -462,7 +494,8 @@ void CollectionView::SetShowInVarious(const bool on) {
|
|||||||
// We put through "Various Artists" changes one album at a time,
|
// We put through "Various Artists" changes one album at a time,
|
||||||
// to make sure the old album node gets removed (due to all children removed), before the new one gets added
|
// to make sure the old album node gets removed (due to all children removed), before the new one gets added
|
||||||
QMultiMap<QString, QString> albums;
|
QMultiMap<QString, QString> albums;
|
||||||
for (const Song &song : GetSelectedSongs()) {
|
const SongList songs = GetSelectedSongs();
|
||||||
|
for (const Song &song : songs) {
|
||||||
if (albums.find(song.album(), song.artist()) == albums.end())
|
if (albums.find(song.album(), song.artist()) == albums.end())
|
||||||
albums.insert(song.album(), song.artist());
|
albums.insert(song.album(), song.artist());
|
||||||
}
|
}
|
||||||
@@ -472,7 +505,7 @@ void CollectionView::SetShowInVarious(const bool on) {
|
|||||||
if (on && albums.keys().count() == 1) {
|
if (on && albums.keys().count() == 1) {
|
||||||
const QStringList albums_list = albums.keys();
|
const QStringList albums_list = albums.keys();
|
||||||
const QString album = albums_list.first();
|
const QString album = albums_list.first();
|
||||||
SongList all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
|
const SongList all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
|
||||||
QSet<QString> other_artists;
|
QSet<QString> other_artists;
|
||||||
for (const Song &s : all_of_album) {
|
for (const Song &s : all_of_album) {
|
||||||
if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) {
|
if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) {
|
||||||
@@ -489,9 +522,9 @@ void CollectionView::SetShowInVarious(const bool on) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||||
QSet<QString> albums_set = QSet<QString>(albums.keyBegin(), albums.keyEnd());
|
const QSet<QString> albums_set = QSet<QString>(albums.keyBegin(), albums.keyEnd());
|
||||||
#else
|
#else
|
||||||
QSet<QString> albums_set = QSet<QString>::fromList(albums.keys());
|
const QSet<QString> albums_set = QSet<QString>::fromList(albums.keys());
|
||||||
#endif
|
#endif
|
||||||
for (const QString &album : albums_set) {
|
for (const QString &album : albums_set) {
|
||||||
app_->collection_backend()->ForceCompilation(album, albums.values(album), on);
|
app_->collection_backend()->ForceCompilation(album, albums.values(album), on);
|
||||||
@@ -545,6 +578,102 @@ void CollectionView::OpenInNewPlaylist() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionView::SearchForThis() {
|
||||||
|
|
||||||
|
const QModelIndex current = currentIndex();
|
||||||
|
const QVariant role_type = model()->data(current, CollectionModel::Role_Type);
|
||||||
|
if (!role_type.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CollectionItem::Type item_type = role_type.value<CollectionItem::Type>();
|
||||||
|
if (item_type != CollectionItem::Type::Song && item_type != CollectionItem::Type::Container && item_type != CollectionItem::Type::Divider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString search;
|
||||||
|
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
||||||
|
|
||||||
|
switch (item_type) {
|
||||||
|
case CollectionItem::Type::Song:{
|
||||||
|
SongList songs = app_->collection_model()->GetChildSongs(index);
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
last_selected_song_ = songs.last();
|
||||||
|
}
|
||||||
|
search = QStringLiteral("title:\"%1\"").arg(last_selected_song_.title());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CollectionItem::Type::Divider:{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CollectionItem::Type::Container:{
|
||||||
|
CollectionItem *item = app_->collection_model()->IndexToItem(index);
|
||||||
|
const CollectionModel::GroupBy group_by = app_->collection_model()->GetGroupBy()[item->container_level];
|
||||||
|
while (!item->children.isEmpty()) {
|
||||||
|
item = item->children.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (group_by) {
|
||||||
|
case CollectionModel::GroupBy::AlbumArtist:
|
||||||
|
search = QStringLiteral("albumartist:\"%1\"").arg(item->metadata.effective_albumartist());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Artist:
|
||||||
|
search = QStringLiteral("artist:\"%1\"").arg(item->metadata.artist());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Album:
|
||||||
|
case CollectionModel::GroupBy::AlbumDisc:
|
||||||
|
search = QStringLiteral("album:\"%1\"").arg(item->metadata.album());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::YearAlbum:
|
||||||
|
case CollectionModel::GroupBy::YearAlbumDisc:
|
||||||
|
search = QStringLiteral("year:%1 album:\"%2\"").arg(item->metadata.year()).arg(item->metadata.album());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::OriginalYearAlbum:
|
||||||
|
case CollectionModel::GroupBy::OriginalYearAlbumDisc:
|
||||||
|
search = QStringLiteral("year:%1 album:\"%2\"").arg(item->metadata.effective_originalyear()).arg(item->metadata.album());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Year:
|
||||||
|
search = QStringLiteral("year:%1").arg(item->metadata.year());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::OriginalYear:
|
||||||
|
search = QStringLiteral("year:%1").arg(item->metadata.effective_originalyear());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Genre:
|
||||||
|
search = QStringLiteral("genre:\"%1\"").arg(item->metadata.genre());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Composer:
|
||||||
|
search = QStringLiteral("composer:\"%1\"").arg(item->metadata.composer());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Performer:
|
||||||
|
search = QStringLiteral("performer:\"%1\"").arg(item->metadata.performer());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Grouping:
|
||||||
|
search = QStringLiteral("grouping:\"%1\"").arg(item->metadata.grouping());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Samplerate:
|
||||||
|
search = QStringLiteral("samplerate:%1").arg(item->metadata.samplerate());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Bitdepth:
|
||||||
|
search = QStringLiteral("bitdepth:%1").arg(item->metadata.bitdepth());
|
||||||
|
break;
|
||||||
|
case CollectionModel::GroupBy::Bitrate:
|
||||||
|
search = QStringLiteral("bitrate:%1").arg(item->metadata.bitrate());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
search = model()->data(current, Qt::DisplayRole).toString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_->ShowInCollection(search);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionView::keyboardSearch(const QString &search) {
|
void CollectionView::keyboardSearch(const QString &search) {
|
||||||
|
|
||||||
is_in_keyboard_search_ = true;
|
is_in_keyboard_search_ = true;
|
||||||
@@ -631,8 +760,11 @@ void CollectionView::FilterReturnPressed() {
|
|||||||
if (!currentIndex().isValid()) {
|
if (!currentIndex().isValid()) {
|
||||||
// Pick the first thing that isn't a divider
|
// Pick the first thing that isn't a divider
|
||||||
for (int row = 0; row < model()->rowCount(); ++row) {
|
for (int row = 0; row < model()->rowCount(); ++row) {
|
||||||
QModelIndex idx(model()->index(row, 0));
|
const QModelIndex idx = model()->index(row, 0);
|
||||||
if (idx.data(CollectionModel::Role_Type) != CollectionItem::Type_Divider) {
|
const QVariant role_type = idx.data(CollectionModel::Role_Type);
|
||||||
|
if (!role_type.isValid()) continue;
|
||||||
|
const CollectionItem::Type item_type = role_type.value<CollectionItem::Type>();
|
||||||
|
if (item_type != CollectionItem::Type::Divider) {
|
||||||
setCurrentIndex(idx);
|
setCurrentIndex(idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -646,7 +778,7 @@ void CollectionView::FilterReturnPressed() {
|
|||||||
|
|
||||||
void CollectionView::ShowInBrowser() const {
|
void CollectionView::ShowInBrowser() const {
|
||||||
|
|
||||||
SongList songs = GetSelectedSongs();
|
const SongList songs = GetSelectedSongs();
|
||||||
QList<QUrl> urls;
|
QList<QUrl> urls;
|
||||||
urls.reserve(songs.count());
|
urls.reserve(songs.count());
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
@@ -671,7 +803,7 @@ void CollectionView::Delete() {
|
|||||||
|
|
||||||
if (!delete_files_) return;
|
if (!delete_files_) return;
|
||||||
|
|
||||||
SongList selected_songs = GetSelectedSongs();
|
const SongList selected_songs = GetSelectedSongs();
|
||||||
|
|
||||||
SongList songs;
|
SongList songs;
|
||||||
QStringList files;
|
QStringList files;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
protected:
|
protected:
|
||||||
// QWidget
|
// QWidget
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
void AddToPlaylistEnqueue();
|
void AddToPlaylistEnqueue();
|
||||||
void AddToPlaylistEnqueueNext();
|
void AddToPlaylistEnqueueNext();
|
||||||
void OpenInNewPlaylist();
|
void OpenInNewPlaylist();
|
||||||
|
void SearchForThis();
|
||||||
void Organize();
|
void Organize();
|
||||||
void CopyToDevice();
|
void CopyToDevice();
|
||||||
void EditTracks();
|
void EditTracks();
|
||||||
@@ -136,6 +138,8 @@ class CollectionView : public AutoExpandingTreeView {
|
|||||||
QAction *action_add_to_playlist_enqueue_next_;
|
QAction *action_add_to_playlist_enqueue_next_;
|
||||||
QAction *action_open_in_new_playlist_;
|
QAction *action_open_in_new_playlist_;
|
||||||
QAction *action_organize_;
|
QAction *action_organize_;
|
||||||
|
QAction *action_search_for_this_;
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
QAction *action_copy_to_device_;
|
QAction *action_copy_to_device_;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/tagreaderclient.h"
|
#include "core/tagreaderclient.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "utilities/imageutils.h"
|
#include "utilities/imageutils.h"
|
||||||
#include "utilities/timeconstants.h"
|
#include "utilities/timeconstants.h"
|
||||||
#include "collectiondirectory.h"
|
#include "collectiondirectory.h"
|
||||||
@@ -70,8 +71,8 @@
|
|||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
QStringList CollectionWatcher::sValidImages = QStringList() << "jpg" << "png" << "gif" << "jpeg";
|
QStringList CollectionWatcher::sValidImages = QStringList() << QStringLiteral("jpg") << QStringLiteral("png") << QStringLiteral("gif") << QStringLiteral("jpeg");
|
||||||
QStringList CollectionWatcher::kIgnoredExtensions = QStringList() << "tmp" << "tar" << "gz" << "bz2" << "xz" << "tbz" << "tgz" << "z" << "zip" << "rar";
|
const QStringList CollectionWatcher::kIgnoredExtensions = QStringList() << QStringLiteral("tmp") << QStringLiteral("tar") << QStringLiteral("gz") << QStringLiteral("bz2") << QStringLiteral("xz") << QStringLiteral("tbz") << QStringLiteral("tgz") << QStringLiteral("z") << QStringLiteral("zip") << QStringLiteral("rar");
|
||||||
|
|
||||||
CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
@@ -105,7 +106,7 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
|||||||
periodic_scan_timer_->setInterval(86400 * kMsecPerSec);
|
periodic_scan_timer_->setInterval(86400 * kMsecPerSec);
|
||||||
periodic_scan_timer_->setSingleShot(false);
|
periodic_scan_timer_->setSingleShot(false);
|
||||||
|
|
||||||
QStringList image_formats = ImageUtils::SupportedImageFormats();
|
const QStringList image_formats = ImageUtils::SupportedImageFormats();
|
||||||
for (const QString &format : image_formats) {
|
for (const QString &format : image_formats) {
|
||||||
if (!sValidImages.contains(format)) {
|
if (!sValidImages.contains(format)) {
|
||||||
sValidImages.append(format);
|
sValidImages.append(format);
|
||||||
@@ -150,11 +151,11 @@ void CollectionWatcher::ReloadSettingsAsync() {
|
|||||||
void CollectionWatcher::ReloadSettings() {
|
void CollectionWatcher::ReloadSettings() {
|
||||||
|
|
||||||
const bool was_monitoring_before = monitor_;
|
const bool was_monitoring_before = monitor_;
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||||
scan_on_startup_ = s.value("startup_scan", true).toBool();
|
scan_on_startup_ = s.value("startup_scan", true).toBool();
|
||||||
monitor_ = s.value("monitor", true).toBool();
|
monitor_ = s.value("monitor", true).toBool();
|
||||||
QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList();
|
const QStringList filters = s.value("cover_art_patterns", QStringList() << QStringLiteral("front") << QStringLiteral("cover")).toStringList();
|
||||||
if (source_ == Song::Source::Collection) {
|
if (source_ == Song::Source::Collection) {
|
||||||
song_tracking_ = s.value("song_tracking", false).toBool();
|
song_tracking_ = s.value("song_tracking", false).toBool();
|
||||||
song_ebur128_loudness_analysis_ = s.value("song_ebur128_loudness_analysis", false).toBool();
|
song_ebur128_loudness_analysis_ = s.value("song_ebur128_loudness_analysis", false).toBool();
|
||||||
@@ -182,7 +183,7 @@ void CollectionWatcher::ReloadSettings() {
|
|||||||
else if (monitor_ && !was_monitoring_before) {
|
else if (monitor_ && !was_monitoring_before) {
|
||||||
// Add all directories to all QFileSystemWatchers again
|
// Add all directories to all QFileSystemWatchers again
|
||||||
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
for (const CollectionDirectory &dir : std::as_const(watched_dirs_)) {
|
||||||
CollectionSubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
|
const CollectionSubdirectoryList subdirs = backend_->SubdirsInDirectory(dir.id);
|
||||||
for (const CollectionSubdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
AddWatch(dir, subdir.path);
|
AddWatch(dir, subdir.path);
|
||||||
}
|
}
|
||||||
@@ -192,7 +193,7 @@ void CollectionWatcher::ReloadSettings() {
|
|||||||
if (monitor_ && scan_on_startup_ && mark_songs_unavailable_ && !periodic_scan_timer_->isActive()) {
|
if (monitor_ && scan_on_startup_ && mark_songs_unavailable_ && !periodic_scan_timer_->isActive()) {
|
||||||
periodic_scan_timer_->start();
|
periodic_scan_timer_->start();
|
||||||
}
|
}
|
||||||
else if (!mark_songs_unavailable_ && periodic_scan_timer_->isActive()) {
|
else if ((!monitor_ || !scan_on_startup_ || !mark_songs_unavailable_) && periodic_scan_timer_->isActive()) {
|
||||||
periodic_scan_timer_->stop();
|
periodic_scan_timer_->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +288,7 @@ void CollectionWatcher::ScanTransaction::CommitNewOrUpdatedSongs() {
|
|||||||
touched_subdirs.clear();
|
touched_subdirs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const CollectionSubdirectory &subdir : deleted_subdirs) {
|
for (const CollectionSubdirectory &subdir : std::as_const(deleted_subdirs)) {
|
||||||
if (watcher_->watched_dirs_.contains(dir_)) {
|
if (watcher_->watched_dirs_.contains(dir_)) {
|
||||||
watcher_->RemoveWatch(watcher_->watched_dirs_[dir_], subdir);
|
watcher_->RemoveWatch(watcher_->watched_dirs_[dir_], subdir);
|
||||||
}
|
}
|
||||||
@@ -296,7 +297,7 @@ void CollectionWatcher::ScanTransaction::CommitNewOrUpdatedSongs() {
|
|||||||
|
|
||||||
if (watcher_->monitor_) {
|
if (watcher_->monitor_) {
|
||||||
// Watch the new subdirectories
|
// Watch the new subdirectories
|
||||||
for (const CollectionSubdirectory &subdir : new_subdirs) {
|
for (const CollectionSubdirectory &subdir : std::as_const(new_subdirs)) {
|
||||||
if (watcher_->watched_dirs_.contains(dir_)) {
|
if (watcher_->watched_dirs_.contains(dir_)) {
|
||||||
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
|
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
|
||||||
}
|
}
|
||||||
@@ -316,7 +317,7 @@ SongList CollectionWatcher::ScanTransaction::FindSongsInSubdirectory(const QStri
|
|||||||
if (cached_songs_dirty_) {
|
if (cached_songs_dirty_) {
|
||||||
const SongList songs = watcher_->backend_->FindSongsInDirectory(dir_);
|
const SongList songs = watcher_->backend_->FindSongsInDirectory(dir_);
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
const QString p = song.url().toLocalFile().section('/', 0, -2);
|
const QString p = song.url().toLocalFile().section(QLatin1Char('/'), 0, -2);
|
||||||
cached_songs_.insert(p, song);
|
cached_songs_.insert(p, song);
|
||||||
}
|
}
|
||||||
cached_songs_dirty_ = false;
|
cached_songs_dirty_ = false;
|
||||||
@@ -325,7 +326,8 @@ SongList CollectionWatcher::ScanTransaction::FindSongsInSubdirectory(const QStri
|
|||||||
if (cached_songs_.contains(path)) {
|
if (cached_songs_.contains(path)) {
|
||||||
return cached_songs_.values(path);
|
return cached_songs_.values(path);
|
||||||
}
|
}
|
||||||
else return SongList();
|
|
||||||
|
return SongList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +336,7 @@ bool CollectionWatcher::ScanTransaction::HasSongsWithMissingFingerprint(const QS
|
|||||||
if (cached_songs_missing_fingerprint_dirty_) {
|
if (cached_songs_missing_fingerprint_dirty_) {
|
||||||
const SongList songs = watcher_->backend_->SongsWithMissingFingerprint(dir_);
|
const SongList songs = watcher_->backend_->SongsWithMissingFingerprint(dir_);
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
const QString p = song.url().toLocalFile().section('/', 0, -2);
|
const QString p = song.url().toLocalFile().section(QLatin1Char('/'), 0, -2);
|
||||||
cached_songs_missing_fingerprint_.insert(p, song);
|
cached_songs_missing_fingerprint_.insert(p, song);
|
||||||
}
|
}
|
||||||
cached_songs_missing_fingerprint_dirty_ = false;
|
cached_songs_missing_fingerprint_dirty_ = false;
|
||||||
@@ -349,7 +351,7 @@ bool CollectionWatcher::ScanTransaction::HasSongsWithMissingLoudnessCharacterist
|
|||||||
if (cached_songs_missing_loudness_characteristics_dirty_) {
|
if (cached_songs_missing_loudness_characteristics_dirty_) {
|
||||||
const SongList songs = watcher_->backend_->SongsWithMissingLoudnessCharacteristics(dir_);
|
const SongList songs = watcher_->backend_->SongsWithMissingLoudnessCharacteristics(dir_);
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
const QString p = song.url().toLocalFile().section('/', 0, -2);
|
const QString p = song.url().toLocalFile().section(QLatin1Char('/'), 0, -2);
|
||||||
cached_songs_missing_loudness_characteristics_.insert(p, song);
|
cached_songs_missing_loudness_characteristics_.insert(p, song);
|
||||||
}
|
}
|
||||||
cached_songs_missing_loudness_characteristics_dirty_ = false;
|
cached_songs_missing_loudness_characteristics_dirty_ = false;
|
||||||
@@ -383,7 +385,7 @@ CollectionSubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdi
|
|||||||
}
|
}
|
||||||
|
|
||||||
CollectionSubdirectoryList ret;
|
CollectionSubdirectoryList ret;
|
||||||
for (const CollectionSubdirectory &subdir : known_subdirs_) {
|
for (const CollectionSubdirectory &subdir : std::as_const(known_subdirs_)) {
|
||||||
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
|
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
|
||||||
ret << subdir;
|
ret << subdir;
|
||||||
}
|
}
|
||||||
@@ -416,25 +418,29 @@ void CollectionWatcher::AddDirectory(const CollectionDirectory &dir, const Colle
|
|||||||
transaction.SetKnownSubdirs(subdirs);
|
transaction.SetKnownSubdirs(subdirs);
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
ScanSubdirectory(dir.path, CollectionSubdirectory(), files_count, &transaction);
|
ScanSubdirectory(dir.path, CollectionSubdirectory(), files_count, &transaction);
|
||||||
last_scan_time_ = QDateTime::currentDateTime().toSecsSinceEpoch();
|
last_scan_time_ = QDateTime::currentSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We can do an incremental scan - looking at the mtimes of each subdirectory and only rescan if the directory has changed.
|
if (monitor_) {
|
||||||
ScanTransaction transaction(this, dir.id, true, false, mark_songs_unavailable_);
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
QMap<QString, quint64> subdir_files_count;
|
AddWatch(dir, subdir.path);
|
||||||
const quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
}
|
||||||
transaction.SetKnownSubdirs(subdirs);
|
}
|
||||||
transaction.AddToProgressMax(files_count);
|
if (scan_on_startup_) {
|
||||||
for (const CollectionSubdirectory &subdir : subdirs) {
|
// We can do an incremental scan - looking at the mtimes of each subdirectory and only rescan if the directory has changed.
|
||||||
if (stop_requested_ || abort_requested_) break;
|
ScanTransaction transaction(this, dir.id, true, false, mark_songs_unavailable_);
|
||||||
|
QMap<QString, quint64> subdir_files_count;
|
||||||
if (scan_on_startup_) ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
const quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
||||||
|
transaction.SetKnownSubdirs(subdirs);
|
||||||
if (monitor_) AddWatch(dir, subdir.path);
|
transaction.AddToProgressMax(files_count);
|
||||||
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
|
if (stop_requested_ || abort_requested_) break;
|
||||||
|
ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
||||||
|
}
|
||||||
|
if (!stop_requested_ && !abort_requested_) {
|
||||||
|
last_scan_time_ = QDateTime::currentSecsSinceEpoch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_scan_time_ = QDateTime::currentDateTime().toSecsSinceEpoch();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit CompilationsNeedUpdating();
|
emit CompilationsNeedUpdating();
|
||||||
@@ -480,7 +486,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
|
|
||||||
// If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist anymore.
|
// If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist anymore.
|
||||||
// If one has been removed, "rescan" it to get the deleted songs
|
// If one has been removed, "rescan" it to get the deleted songs
|
||||||
CollectionSubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
|
const CollectionSubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
|
||||||
for (const CollectionSubdirectory &prev_subdir : previous_subdirs) {
|
for (const CollectionSubdirectory &prev_subdir : previous_subdirs) {
|
||||||
if (!QFile::exists(prev_subdir.path) && prev_subdir.path != path) {
|
if (!QFile::exists(prev_subdir.path) && prev_subdir.path != path) {
|
||||||
ScanSubdirectory(prev_subdir.path, prev_subdir, 0, t, true);
|
ScanSubdirectory(prev_subdir.path, prev_subdir, 0, t, true);
|
||||||
@@ -510,7 +516,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
else {
|
else {
|
||||||
QString ext_part(ExtensionPart(child));
|
QString ext_part(ExtensionPart(child));
|
||||||
QString dir_part(DirectoryPart(child));
|
QString dir_part(DirectoryPart(child));
|
||||||
if (kIgnoredExtensions.contains(child_info.suffix(), Qt::CaseInsensitive) || child_info.baseName() == "qt_temp") {
|
if (kIgnoredExtensions.contains(child_info.suffix(), Qt::CaseInsensitive) || child_info.baseName() == QLatin1String("qt_temp")) {
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
}
|
}
|
||||||
else if (sValidImages.contains(ext_part)) {
|
else if (sValidImages.contains(ext_part)) {
|
||||||
@@ -612,7 +618,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
Chromaprinter chromaprinter(file);
|
Chromaprinter chromaprinter(file);
|
||||||
fingerprint = chromaprinter.CreateFingerprint();
|
fingerprint = chromaprinter.CreateFingerprint();
|
||||||
if (fingerprint.isEmpty()) {
|
if (fingerprint.isEmpty()) {
|
||||||
fingerprint = "NONE";
|
fingerprint = QLatin1String("NONE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -627,6 +633,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
|
|
||||||
// Nothing has changed - mark the song available without re-scanning
|
// Nothing has changed - mark the song available without re-scanning
|
||||||
else if (matching_song.unavailable()) {
|
else if (matching_song.unavailable()) {
|
||||||
|
qLog(Debug) << "Unavailable song" << file << "restored.";
|
||||||
t->readded_songs << matching_songs;
|
t->readded_songs << matching_songs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,11 +645,11 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
Chromaprinter chromaprinter(file);
|
Chromaprinter chromaprinter(file);
|
||||||
fingerprint = chromaprinter.CreateFingerprint();
|
fingerprint = chromaprinter.CreateFingerprint();
|
||||||
if (fingerprint.isEmpty()) {
|
if (fingerprint.isEmpty()) {
|
||||||
fingerprint = "NONE";
|
fingerprint = QLatin1String("NONE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (song_tracking_ && !fingerprint.isEmpty() && fingerprint != "NONE" && FindSongsByFingerprint(file, fingerprint, &matching_songs)) {
|
if (song_tracking_ && !fingerprint.isEmpty() && fingerprint != QLatin1String("NONE") && FindSongsByFingerprint(file, fingerprint, &matching_songs)) {
|
||||||
|
|
||||||
// The song is in the database and still on disk.
|
// The song is in the database and still on disk.
|
||||||
// Check the mtime to see if it's been changed since it was added.
|
// Check the mtime to see if it's been changed since it was added.
|
||||||
@@ -656,7 +663,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
|
|
||||||
// Make sure the songs aren't deleted, as they still exist elsewhere with a different file path.
|
// Make sure the songs aren't deleted, as they still exist elsewhere with a different file path.
|
||||||
bool matching_songs_has_cue = false;
|
bool matching_songs_has_cue = false;
|
||||||
for (const Song &matching_song : matching_songs) {
|
for (const Song &matching_song : std::as_const(matching_songs)) {
|
||||||
QString matching_filename = matching_song.url().toLocalFile();
|
QString matching_filename = matching_song.url().toLocalFile();
|
||||||
if (!t->files_changed_path_.contains(matching_filename)) {
|
if (!t->files_changed_path_.contains(matching_filename)) {
|
||||||
t->files_changed_path_ << matching_filename;
|
t->files_changed_path_ << matching_filename;
|
||||||
@@ -689,7 +696,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
}
|
}
|
||||||
else { // The song is on disk but not in the DB
|
else { // The song is on disk but not in the DB
|
||||||
|
|
||||||
SongList songs = ScanNewFile(file, path, fingerprint, new_cue, &cues_processed);
|
const SongList songs = ScanNewFile(file, path, fingerprint, new_cue, &cues_processed);
|
||||||
if (songs.isEmpty()) {
|
if (songs.isEmpty()) {
|
||||||
t->AddToProgress(1);
|
t->AddToProgress(1);
|
||||||
continue;
|
continue;
|
||||||
@@ -711,7 +718,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for deleted songs
|
// Look for deleted songs
|
||||||
for (const Song &song : songs_in_db) {
|
for (const Song &song : std::as_const(songs_in_db)) {
|
||||||
QString file = song.url().toLocalFile();
|
QString file = song.url().toLocalFile();
|
||||||
if (!song.unavailable() && !files_on_disk.contains(file) && !t->files_changed_path_.contains(file)) {
|
if (!song.unavailable() && !files_on_disk.contains(file) && !t->files_changed_path_.contains(file)) {
|
||||||
qLog(Debug) << "Song deleted from disk:" << file;
|
qLog(Debug) << "Song deleted from disk:" << file;
|
||||||
@@ -737,7 +744,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recurse into the new subdirs that we found
|
// Recurse into the new subdirs that we found
|
||||||
for (const CollectionSubdirectory &my_new_subdir : my_new_subdirs) {
|
for (const CollectionSubdirectory &my_new_subdir : std::as_const(my_new_subdirs)) {
|
||||||
if (stop_requested_ || abort_requested_) return;
|
if (stop_requested_ || abort_requested_) return;
|
||||||
ScanSubdirectory(my_new_subdir.path, my_new_subdir, 0, t, true);
|
ScanSubdirectory(my_new_subdir.path, my_new_subdir, 0, t, true);
|
||||||
}
|
}
|
||||||
@@ -794,6 +801,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file,
|
|||||||
t->deleted_songs << old_cue;
|
t->deleted_songs << old_cue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
|
void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
|
||||||
@@ -814,8 +822,8 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Song song_on_disk(source_);
|
Song song_on_disk(source_);
|
||||||
TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
|
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
|
||||||
if (song_on_disk.is_valid()) {
|
if (result.success() && song_on_disk.is_valid()) {
|
||||||
song_on_disk.set_source(source_);
|
song_on_disk.set_source(source_);
|
||||||
song_on_disk.set_directory_id(t->dir());
|
song_on_disk.set_directory_id(t->dir());
|
||||||
song_on_disk.set_id(matching_song.id());
|
song_on_disk.set_id(matching_song.id());
|
||||||
@@ -867,8 +875,8 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
|
|||||||
}
|
}
|
||||||
else { // It's a normal media file
|
else { // It's a normal media file
|
||||||
Song song(source_);
|
Song song(source_);
|
||||||
TagReaderClient::Instance()->ReadFileBlocking(file, &song);
|
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(file, &song);
|
||||||
if (song.is_valid()) {
|
if (result.success() && song.is_valid()) {
|
||||||
song.set_source(source_);
|
song.set_source(source_);
|
||||||
PerformEBUR128Analysis(song);
|
PerformEBUR128Analysis(song);
|
||||||
song.set_fingerprint(fingerprint);
|
song.set_fingerprint(fingerprint);
|
||||||
@@ -886,55 +894,55 @@ void CollectionWatcher::AddChangedSong(const QString &file, const Song &matching
|
|||||||
QStringList changes;
|
QStringList changes;
|
||||||
|
|
||||||
if (matching_song.unavailable()) {
|
if (matching_song.unavailable()) {
|
||||||
qLog(Debug) << "unavailable song" << file << "restored.";
|
qLog(Debug) << "Unavailable song" << file << "restored.";
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (matching_song.url() != new_song.url()) {
|
if (matching_song.url() != new_song.url()) {
|
||||||
changes << "file path";
|
changes << QStringLiteral("file path");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (matching_song.fingerprint() != new_song.fingerprint()) {
|
if (matching_song.fingerprint() != new_song.fingerprint()) {
|
||||||
changes << "fingerprint";
|
changes << QStringLiteral("fingerprint");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsMetadataEqual(new_song)) {
|
if (!matching_song.IsMetadataEqual(new_song)) {
|
||||||
changes << "metadata";
|
changes << QStringLiteral("metadata");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsPlayStatisticsEqual(new_song)) {
|
if (!matching_song.IsPlayStatisticsEqual(new_song)) {
|
||||||
changes << "play statistics";
|
changes << QStringLiteral("play statistics");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsRatingEqual(new_song)) {
|
if (!matching_song.IsRatingEqual(new_song)) {
|
||||||
changes << "rating";
|
changes << QStringLiteral("rating");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsArtEqual(new_song)) {
|
if (!matching_song.IsArtEqual(new_song)) {
|
||||||
changes << "album art";
|
changes << QStringLiteral("album art");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsAcoustIdEqual(new_song)) {
|
if (!matching_song.IsAcoustIdEqual(new_song)) {
|
||||||
changes << "acoustid";
|
changes << QStringLiteral("acoustid");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsMusicBrainzEqual(new_song)) {
|
if (!matching_song.IsMusicBrainzEqual(new_song)) {
|
||||||
changes << "musicbrainz";
|
changes << QStringLiteral("musicbrainz");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (!matching_song.IsEBUR128Equal(new_song)) {
|
if (!matching_song.IsEBUR128Equal(new_song)) {
|
||||||
changes << "ebur128 loudness characteristics";
|
changes << QStringLiteral("ebur128 loudness characteristics");
|
||||||
notify_new = true;
|
notify_new = true;
|
||||||
}
|
}
|
||||||
if (matching_song.mtime() != new_song.mtime()) {
|
if (matching_song.mtime() != new_song.mtime()) {
|
||||||
changes << "mtime";
|
changes << QStringLiteral("mtime");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.isEmpty()) {
|
if (changes.isEmpty()) {
|
||||||
qLog(Debug) << "Song" << file << "unchanged.";
|
qLog(Debug) << "Song" << file << "unchanged.";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Debug) << "Song" << file << changes.join(", ") << "changed.";
|
qLog(Debug) << "Song" << file << changes.join(QLatin1String(", ")) << "changed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -989,7 +997,7 @@ void CollectionWatcher::AddWatch(const CollectionDirectory &dir, const QString &
|
|||||||
|
|
||||||
void CollectionWatcher::RemoveWatch(const CollectionDirectory &dir, const CollectionSubdirectory &subdir) {
|
void CollectionWatcher::RemoveWatch(const CollectionDirectory &dir, const CollectionSubdirectory &subdir) {
|
||||||
|
|
||||||
QStringList subdir_paths = subdir_mapping_.keys(dir);
|
const QStringList subdir_paths = subdir_mapping_.keys(dir);
|
||||||
for (const QString &subdir_path : subdir_paths) {
|
for (const QString &subdir_path : subdir_paths) {
|
||||||
if (subdir_path != subdir.path) continue;
|
if (subdir_path != subdir.path) continue;
|
||||||
fs_watcher_->RemovePath(subdir_path);
|
fs_watcher_->RemovePath(subdir_path);
|
||||||
@@ -1005,7 +1013,7 @@ void CollectionWatcher::RemoveDirectory(const CollectionDirectory &dir) {
|
|||||||
watched_dirs_.remove(dir.id);
|
watched_dirs_.remove(dir.id);
|
||||||
|
|
||||||
// Stop watching the directory's subdirectories
|
// Stop watching the directory's subdirectories
|
||||||
QStringList subdir_paths = subdir_mapping_.keys(dir);
|
const QStringList subdir_paths = subdir_mapping_.keys(dir);
|
||||||
for (const QString &subdir_path : subdir_paths) {
|
for (const QString &subdir_path : subdir_paths) {
|
||||||
fs_watcher_->RemovePath(subdir_path);
|
fs_watcher_->RemovePath(subdir_path);
|
||||||
subdir_mapping_.remove(subdir_path);
|
subdir_mapping_.remove(subdir_path);
|
||||||
@@ -1027,7 +1035,7 @@ bool CollectionWatcher::FindSongsByPath(const SongList &songs, const QString &pa
|
|||||||
|
|
||||||
bool CollectionWatcher::FindSongsByFingerprint(const QString &file, const QString &fingerprint, SongList *out) {
|
bool CollectionWatcher::FindSongsByFingerprint(const QString &file, const QString &fingerprint, SongList *out) {
|
||||||
|
|
||||||
SongList songs = backend_->GetSongsByFingerprint(fingerprint);
|
const SongList songs = backend_->GetSongsByFingerprint(fingerprint);
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
QString filename = song.url().toLocalFile();
|
QString filename = song.url().toLocalFile();
|
||||||
QFileInfo info(filename);
|
QFileInfo info(filename);
|
||||||
@@ -1076,19 +1084,21 @@ void CollectionWatcher::DirectoryChanged(const QString &subdir) {
|
|||||||
|
|
||||||
void CollectionWatcher::RescanPathsNow() {
|
void CollectionWatcher::RescanPathsNow() {
|
||||||
|
|
||||||
QList<int> dirs = rescan_queue_.keys();
|
const QList<int> dirs = rescan_queue_.keys();
|
||||||
for (const int dir : dirs) {
|
for (const int dir : dirs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
ScanTransaction transaction(this, dir, false, false, mark_songs_unavailable_);
|
ScanTransaction transaction(this, dir, false, false, mark_songs_unavailable_);
|
||||||
|
|
||||||
|
const QStringList paths = rescan_queue_[dir];
|
||||||
|
|
||||||
QMap<QString, quint64> subdir_files_count;
|
QMap<QString, quint64> subdir_files_count;
|
||||||
for (const QString &path : rescan_queue_[dir]) {
|
for (const QString &path : paths) {
|
||||||
quint64 files_count = FilesCountForPath(&transaction, path);
|
quint64 files_count = FilesCountForPath(&transaction, path);
|
||||||
subdir_files_count[path] = files_count;
|
subdir_files_count[path] = files_count;
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QString &path : rescan_queue_[dir]) {
|
for (const QString &path : paths) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
CollectionSubdirectory subdir;
|
CollectionSubdirectory subdir;
|
||||||
subdir.directory_id = dir;
|
subdir.directory_id = dir;
|
||||||
@@ -1111,7 +1121,7 @@ QString CollectionWatcher::PickBestArt(const QStringList &art_automatic_list) {
|
|||||||
|
|
||||||
QStringList filtered;
|
QStringList filtered;
|
||||||
|
|
||||||
for (const QString &filter_text : best_art_filters_) {
|
for (const QString &filter_text : std::as_const(best_art_filters_)) {
|
||||||
// The images in the images list are represented by a full path, so we need to isolate just the filename
|
// The images in the images list are represented by a full path, so we need to isolate just the filename
|
||||||
for (const QString &art_automatic : art_automatic_list) {
|
for (const QString &art_automatic : art_automatic_list) {
|
||||||
QFileInfo fileinfo(art_automatic);
|
QFileInfo fileinfo(art_automatic);
|
||||||
@@ -1133,7 +1143,7 @@ QString CollectionWatcher::PickBestArt(const QStringList &art_automatic_list) {
|
|||||||
int biggest_size = 0;
|
int biggest_size = 0;
|
||||||
QString biggest_path;
|
QString biggest_path;
|
||||||
|
|
||||||
for (const QString &path : filtered) {
|
for (const QString &path : std::as_const(filtered)) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
|
|
||||||
QImage image(path);
|
QImage image(path);
|
||||||
@@ -1196,7 +1206,7 @@ void CollectionWatcher::FullScanAsync() {
|
|||||||
|
|
||||||
void CollectionWatcher::IncrementalScanCheck() {
|
void CollectionWatcher::IncrementalScanCheck() {
|
||||||
|
|
||||||
qint64 duration = QDateTime::currentDateTime().toSecsSinceEpoch() - last_scan_time_;
|
qint64 duration = QDateTime::currentSecsSinceEpoch() - last_scan_time_;
|
||||||
if (duration >= 86400) {
|
if (duration >= 86400) {
|
||||||
qLog(Debug) << "Performing periodic incremental scan.";
|
qLog(Debug) << "Performing periodic incremental scan.";
|
||||||
IncrementalScanNow();
|
IncrementalScanNow();
|
||||||
@@ -1217,7 +1227,7 @@ void CollectionWatcher::PerformScan(const bool incremental, const bool ignore_mt
|
|||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
|
|
||||||
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes, mark_songs_unavailable_);
|
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes, mark_songs_unavailable_);
|
||||||
CollectionSubdirectoryList subdirs(transaction.GetAllSubdirs());
|
CollectionSubdirectoryList subdirs = transaction.GetAllSubdirs();
|
||||||
|
|
||||||
if (subdirs.isEmpty()) {
|
if (subdirs.isEmpty()) {
|
||||||
qLog(Debug) << "Collection directory wasn't in subdir list.";
|
qLog(Debug) << "Collection directory wasn't in subdir list.";
|
||||||
@@ -1231,14 +1241,14 @@ void CollectionWatcher::PerformScan(const bool incremental, const bool ignore_mt
|
|||||||
quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
quint64 files_count = FilesCountForSubdirs(&transaction, subdirs, subdir_files_count);
|
||||||
transaction.AddToProgressMax(files_count);
|
transaction.AddToProgressMax(files_count);
|
||||||
|
|
||||||
for (const CollectionSubdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : std::as_const(subdirs)) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
ScanSubdirectory(subdir.path, subdir, subdir_files_count[subdir.path], &transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
last_scan_time_ = QDateTime::currentDateTime().toSecsSinceEpoch();
|
last_scan_time_ = QDateTime::currentSecsSinceEpoch();
|
||||||
|
|
||||||
emit CompilationsNeedUpdating();
|
emit CompilationsNeedUpdating();
|
||||||
|
|
||||||
@@ -1307,10 +1317,10 @@ void CollectionWatcher::RescanSongs(const SongList &songs) {
|
|||||||
QStringList scanned_paths;
|
QStringList scanned_paths;
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
const QString song_path = song.url().toLocalFile().section('/', 0, -2);
|
const QString song_path = song.url().toLocalFile().section(QLatin1Char('/'), 0, -2);
|
||||||
if (scanned_paths.contains(song_path)) continue;
|
if (scanned_paths.contains(song_path)) continue;
|
||||||
ScanTransaction transaction(this, song.directory_id(), false, true, mark_songs_unavailable_);
|
ScanTransaction transaction(this, song.directory_id(), false, true, mark_songs_unavailable_);
|
||||||
CollectionSubdirectoryList subdirs(transaction.GetAllSubdirs());
|
const CollectionSubdirectoryList subdirs = transaction.GetAllSubdirs();
|
||||||
for (const CollectionSubdirectory &subdir : subdirs) {
|
for (const CollectionSubdirectory &subdir : subdirs) {
|
||||||
if (stop_requested_ || abort_requested_) break;
|
if (stop_requested_ || abort_requested_) break;
|
||||||
if (subdir.path != song_path) continue;
|
if (subdir.path != song_path) continue;
|
||||||
|
|||||||
@@ -245,21 +245,21 @@ class CollectionWatcher : public QObject {
|
|||||||
CueParser *cue_parser_;
|
CueParser *cue_parser_;
|
||||||
|
|
||||||
static QStringList sValidImages;
|
static QStringList sValidImages;
|
||||||
static QStringList kIgnoredExtensions;
|
static const QStringList kIgnoredExtensions;
|
||||||
|
|
||||||
qint64 last_scan_time_;
|
qint64 last_scan_time_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) {
|
inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) {
|
||||||
return fileName.contains('.') ? fileName.section('.', 0, -2) : "";
|
return fileName.contains(QLatin1Char('.')) ? fileName.section(QLatin1Char('.'), 0, -2) : QLatin1String("");
|
||||||
}
|
}
|
||||||
// Thanks Amarok
|
// Thanks Amarok
|
||||||
inline QString CollectionWatcher::ExtensionPart(const QString &fileName) {
|
inline QString CollectionWatcher::ExtensionPart(const QString &fileName) {
|
||||||
return fileName.contains( '.' ) ? fileName.mid( fileName.lastIndexOf('.') + 1 ).toLower() : "";
|
return fileName.contains(QLatin1Char('.')) ? fileName.mid(fileName.lastIndexOf(QLatin1Char('.')) + 1).toLower() : QLatin1String("");
|
||||||
}
|
}
|
||||||
inline QString CollectionWatcher::DirectoryPart(const QString &fileName) {
|
inline QString CollectionWatcher::DirectoryPart(const QString &fileName) {
|
||||||
return fileName.section('/', 0, -2);
|
return fileName.section(QLatin1Char('/'), 0, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // COLLECTIONWATCHER_H
|
#endif // COLLECTIONWATCHER_H
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QItemSelectionModel>
|
#include <QItemSelectionModel>
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/iconloader.h"
|
#include "core/iconloader.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "settings/collectionsettingspage.h"
|
#include "settings/collectionsettingspage.h"
|
||||||
#include "collectionmodel.h"
|
#include "collectionmodel.h"
|
||||||
#include "savedgroupingmanager.h"
|
#include "savedgroupingmanager.h"
|
||||||
@@ -56,7 +59,7 @@ SavedGroupingManager::SavedGroupingManager(const QString &saved_groupings_settin
|
|||||||
model_->setHorizontalHeaderItem(2, new QStandardItem(tr("Second Level")));
|
model_->setHorizontalHeaderItem(2, new QStandardItem(tr("Second Level")));
|
||||||
model_->setHorizontalHeaderItem(3, new QStandardItem(tr("Third Level")));
|
model_->setHorizontalHeaderItem(3, new QStandardItem(tr("Third Level")));
|
||||||
ui_->list->setModel(model_);
|
ui_->list->setModel(model_);
|
||||||
ui_->remove->setIcon(IconLoader::Load("edit-delete"));
|
ui_->remove->setIcon(IconLoader::Load(QStringLiteral("edit-delete")));
|
||||||
ui_->remove->setEnabled(false);
|
ui_->remove->setEnabled(false);
|
||||||
|
|
||||||
ui_->remove->setShortcut(QKeySequence::Delete);
|
ui_->remove->setShortcut(QKeySequence::Delete);
|
||||||
@@ -72,80 +75,79 @@ SavedGroupingManager::~SavedGroupingManager() {
|
|||||||
|
|
||||||
QString SavedGroupingManager::GetSavedGroupingsSettingsGroup(const QString &settings_group) {
|
QString SavedGroupingManager::GetSavedGroupingsSettingsGroup(const QString &settings_group) {
|
||||||
|
|
||||||
if (settings_group.isEmpty() || settings_group == CollectionSettingsPage::kSettingsGroup) {
|
if (settings_group.isEmpty() || settings_group == QLatin1String(CollectionSettingsPage::kSettingsGroup)) {
|
||||||
return kSavedGroupingsSettingsGroup;
|
return QLatin1String(kSavedGroupingsSettingsGroup);
|
||||||
}
|
|
||||||
else {
|
|
||||||
return QString(kSavedGroupingsSettingsGroup) + "_" + settings_group;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QLatin1String(kSavedGroupingsSettingsGroup) + QLatin1Char('_') + settings_group;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy g) {
|
QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy g) {
|
||||||
|
|
||||||
switch (g) {
|
switch (g) {
|
||||||
case CollectionModel::GroupBy::None:
|
case CollectionModel::GroupBy::None:
|
||||||
case CollectionModel::GroupBy::GroupByCount: {
|
case CollectionModel::GroupBy::GroupByCount:{
|
||||||
return tr("None");
|
return tr("None");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::AlbumArtist: {
|
case CollectionModel::GroupBy::AlbumArtist:{
|
||||||
return tr("Album artist");
|
return tr("Album artist");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Artist: {
|
case CollectionModel::GroupBy::Artist:{
|
||||||
return tr("Artist");
|
return tr("Artist");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Album: {
|
case CollectionModel::GroupBy::Album:{
|
||||||
return tr("Album");
|
return tr("Album");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::AlbumDisc: {
|
case CollectionModel::GroupBy::AlbumDisc:{
|
||||||
return tr("Album - Disc");
|
return tr("Album - Disc");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::YearAlbum: {
|
case CollectionModel::GroupBy::YearAlbum:{
|
||||||
return tr("Year - Album");
|
return tr("Year - Album");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::YearAlbumDisc: {
|
case CollectionModel::GroupBy::YearAlbumDisc:{
|
||||||
return tr("Year - Album - Disc");
|
return tr("Year - Album - Disc");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::OriginalYearAlbum: {
|
case CollectionModel::GroupBy::OriginalYearAlbum:{
|
||||||
return tr("Original year - Album");
|
return tr("Original year - Album");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::OriginalYearAlbumDisc: {
|
case CollectionModel::GroupBy::OriginalYearAlbumDisc:{
|
||||||
return tr("Original year - Album - Disc");
|
return tr("Original year - Album - Disc");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Disc: {
|
case CollectionModel::GroupBy::Disc:{
|
||||||
return tr("Disc");
|
return tr("Disc");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Year: {
|
case CollectionModel::GroupBy::Year:{
|
||||||
return tr("Year");
|
return tr("Year");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::OriginalYear: {
|
case CollectionModel::GroupBy::OriginalYear:{
|
||||||
return tr("Original year");
|
return tr("Original year");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Genre: {
|
case CollectionModel::GroupBy::Genre:{
|
||||||
return tr("Genre");
|
return tr("Genre");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Composer: {
|
case CollectionModel::GroupBy::Composer:{
|
||||||
return tr("Composer");
|
return tr("Composer");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Performer: {
|
case CollectionModel::GroupBy::Performer:{
|
||||||
return tr("Performer");
|
return tr("Performer");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Grouping: {
|
case CollectionModel::GroupBy::Grouping:{
|
||||||
return tr("Grouping");
|
return tr("Grouping");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::FileType: {
|
case CollectionModel::GroupBy::FileType:{
|
||||||
return tr("File type");
|
return tr("File type");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Format: {
|
case CollectionModel::GroupBy::Format:{
|
||||||
return tr("Format");
|
return tr("Format");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Samplerate: {
|
case CollectionModel::GroupBy::Samplerate:{
|
||||||
return tr("Sample rate");
|
return tr("Sample rate");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Bitdepth: {
|
case CollectionModel::GroupBy::Bitdepth:{
|
||||||
return tr("Bit depth");
|
return tr("Bit depth");
|
||||||
}
|
}
|
||||||
case CollectionModel::GroupBy::Bitrate: {
|
case CollectionModel::GroupBy::Bitrate:{
|
||||||
return tr("Bitrate");
|
return tr("Bitrate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,13 +159,13 @@ QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy g)
|
|||||||
void SavedGroupingManager::UpdateModel() {
|
void SavedGroupingManager::UpdateModel() {
|
||||||
|
|
||||||
model_->setRowCount(0); // don't use clear, it deletes headers
|
model_->setRowCount(0); // don't use clear, it deletes headers
|
||||||
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;
|
||||||
@@ -181,7 +183,7 @@ void SavedGroupingManager::UpdateModel() {
|
|||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,9 +194,10 @@ void SavedGroupingManager::UpdateModel() {
|
|||||||
void SavedGroupingManager::Remove() {
|
void SavedGroupingManager::Remove() {
|
||||||
|
|
||||||
if (ui_->list->selectionModel()->hasSelection()) {
|
if (ui_->list->selectionModel()->hasSelection()) {
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(saved_groupings_settings_group_);
|
s.beginGroup(saved_groupings_settings_group_);
|
||||||
for (const QModelIndex &idx : ui_->list->selectionModel()->selectedRows()) {
|
const QModelIndexList indexes = ui_->list->selectionModel()->selectedRows();
|
||||||
|
for (const QModelIndex &idx : indexes) {
|
||||||
if (idx.isValid()) {
|
if (idx.isValid()) {
|
||||||
qLog(Debug) << "Remove saved grouping: " << model_->item(idx.row(), 0)->text();
|
qLog(Debug) << "Remove saved grouping: " << model_->item(idx.row(), 0)->text();
|
||||||
s.remove(model_->item(idx.row(), 0)->text());
|
s.remove(model_->item(idx.row(), 0)->text());
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#cmakedefine HAVE_MUSICBRAINZ
|
#cmakedefine HAVE_MUSICBRAINZ
|
||||||
#cmakedefine HAVE_GLOBALSHORTCUTS
|
#cmakedefine HAVE_GLOBALSHORTCUTS
|
||||||
#cmakedefine HAVE_X11_GLOBALSHORTCUTS
|
#cmakedefine HAVE_X11_GLOBALSHORTCUTS
|
||||||
#cmakedefine HAVE_ICU
|
|
||||||
|
|
||||||
#cmakedefine USE_INSTALL_PREFIX
|
#cmakedefine USE_INSTALL_PREFIX
|
||||||
|
|
||||||
@@ -30,6 +29,7 @@
|
|||||||
|
|
||||||
#cmakedefine HAVE_SUBSONIC
|
#cmakedefine HAVE_SUBSONIC
|
||||||
#cmakedefine HAVE_TIDAL
|
#cmakedefine HAVE_TIDAL
|
||||||
|
#cmakedefine HAVE_SPOTIFY
|
||||||
#cmakedefine HAVE_QOBUZ
|
#cmakedefine HAVE_QOBUZ
|
||||||
|
|
||||||
#cmakedefine HAVE_MOODBAR
|
#cmakedefine HAVE_MOODBAR
|
||||||
@@ -37,8 +37,10 @@
|
|||||||
#cmakedefine HAVE_KEYSYMDEF_H
|
#cmakedefine HAVE_KEYSYMDEF_H
|
||||||
#cmakedefine HAVE_XF86KEYSYM_H
|
#cmakedefine HAVE_XF86KEYSYM_H
|
||||||
|
|
||||||
|
#cmakedefine HAVE_TAGLIB
|
||||||
#cmakedefine HAVE_TAGLIB_DSFFILE
|
#cmakedefine HAVE_TAGLIB_DSFFILE
|
||||||
#cmakedefine HAVE_TAGLIB_DSDIFFFILE
|
#cmakedefine HAVE_TAGLIB_DSDIFFFILE
|
||||||
|
#cmakedefine HAVE_TAGPARSER
|
||||||
|
|
||||||
#cmakedefine USE_BUNDLE
|
#cmakedefine USE_BUNDLE
|
||||||
|
|
||||||
@@ -51,13 +53,8 @@
|
|||||||
|
|
||||||
#cmakedefine ENABLE_WIN32_CONSOLE
|
#cmakedefine ENABLE_WIN32_CONSOLE
|
||||||
|
|
||||||
#cmakedefine USE_TAGLIB
|
|
||||||
#cmakedefine USE_TAGPARSER
|
|
||||||
|
|
||||||
#cmakedefine HAVE_QX11APPLICATION
|
#cmakedefine HAVE_QX11APPLICATION
|
||||||
|
|
||||||
#cmakedefine HAVE_EBUR128
|
#cmakedefine HAVE_EBUR128
|
||||||
|
|
||||||
#cmakedefine HAVE_KDSINGLEAPPLICATION_OPTIONS
|
|
||||||
|
|
||||||
#endif // CONFIG_H_IN
|
#endif // CONFIG_H_IN
|
||||||
|
|||||||
@@ -47,7 +47,9 @@
|
|||||||
using std::make_unique;
|
using std::make_unique;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
|
||||||
const int ContextAlbum::kFadeTimeLineMs = 1000;
|
namespace {
|
||||||
|
constexpr int kFadeTimeLineMs = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
ContextAlbum::ContextAlbum(QWidget *parent)
|
ContextAlbum::ContextAlbum(QWidget *parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
@@ -56,12 +58,12 @@ ContextAlbum::ContextAlbum(QWidget *parent)
|
|||||||
album_cover_choice_controller_(nullptr),
|
album_cover_choice_controller_(nullptr),
|
||||||
downloading_covers_(false),
|
downloading_covers_(false),
|
||||||
timeline_fade_(new QTimeLine(kFadeTimeLineMs, this)),
|
timeline_fade_(new QTimeLine(kFadeTimeLineMs, this)),
|
||||||
image_strawberry_(":/pictures/strawberry.png"),
|
image_strawberry_(QStringLiteral(":/pictures/strawberry.png")),
|
||||||
image_original_(image_strawberry_),
|
image_original_(image_strawberry_),
|
||||||
pixmap_current_opacity_(1.0),
|
pixmap_current_opacity_(1.0),
|
||||||
desired_height_(width()) {
|
desired_height_(width()) {
|
||||||
|
|
||||||
setObjectName("context-widget-album");
|
setObjectName(QStringLiteral("context-widget-album"));
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
|
||||||
@@ -139,11 +141,7 @@ void ContextAlbum::UpdateWidth(const int new_width) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContextAlbum::SetImage(QImage image) {
|
void ContextAlbum::SetImage(const QImage &image) {
|
||||||
|
|
||||||
if (image.isNull()) {
|
|
||||||
image = image_strawberry_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloading_covers_) {
|
if (downloading_covers_) {
|
||||||
downloading_covers_ = false;
|
downloading_covers_ = false;
|
||||||
@@ -154,14 +152,20 @@ void ContextAlbum::SetImage(QImage image) {
|
|||||||
QPixmap pixmap_previous = pixmap_current_;
|
QPixmap pixmap_previous = pixmap_current_;
|
||||||
qreal opacity_previous = pixmap_current_opacity_;
|
qreal opacity_previous = pixmap_current_opacity_;
|
||||||
|
|
||||||
image_original_ = image;
|
if (image.isNull()) {
|
||||||
|
image_original_ = image_strawberry_;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
image_original_ = image;
|
||||||
|
}
|
||||||
|
|
||||||
pixmap_current_opacity_ = 0.0;
|
pixmap_current_opacity_ = 0.0;
|
||||||
ScaleCover();
|
ScaleCover();
|
||||||
|
|
||||||
if (!pixmap_previous.isNull()) {
|
if (!pixmap_previous.isNull()) {
|
||||||
SharedPtr<PreviousCover> previous_cover = make_shared<PreviousCover>();
|
SharedPtr<PreviousCover> previous_cover = make_shared<PreviousCover>();
|
||||||
previous_cover->image = image_previous;
|
previous_cover->image = image_previous;
|
||||||
previous_cover->pixmap = pixmap_previous;
|
previous_cover->pixmap = pixmap_previous;
|
||||||
previous_cover->opacity = opacity_previous;
|
previous_cover->opacity = opacity_previous;
|
||||||
previous_cover->timeline.reset(new QTimeLine(kFadeTimeLineMs), [](QTimeLine *timeline) { timeline->deleteLater(); });
|
previous_cover->timeline.reset(new QTimeLine(kFadeTimeLineMs), [](QTimeLine *timeline) { timeline->deleteLater(); });
|
||||||
previous_cover->timeline->setDirection(QTimeLine::Backward);
|
previous_cover->timeline->setDirection(QTimeLine::Backward);
|
||||||
@@ -231,6 +235,7 @@ void ContextAlbum::FadePreviousCover(SharedPtr<PreviousCover> previous_cover) {
|
|||||||
|
|
||||||
void ContextAlbum::FadePreviousCoverFinished(SharedPtr<PreviousCover> previous_cover) {
|
void ContextAlbum::FadePreviousCoverFinished(SharedPtr<PreviousCover> previous_cover) {
|
||||||
|
|
||||||
|
previous_cover->timeline.reset();
|
||||||
previous_covers_.removeAll(previous_cover);
|
previous_covers_.removeAll(previous_cover);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -266,7 +271,7 @@ void ContextAlbum::SearchCoverInProgress() {
|
|||||||
downloading_covers_ = true;
|
downloading_covers_ = true;
|
||||||
|
|
||||||
// Show a spinner animation
|
// Show a spinner animation
|
||||||
spinner_animation_ = make_unique<QMovie>(":/pictures/spinner.gif", QByteArray(), this);
|
spinner_animation_ = make_unique<QMovie>(QStringLiteral(":/pictures/spinner.gif"), QByteArray(), this);
|
||||||
QObject::connect(&*spinner_animation_, &QMovie::updated, this, &ContextAlbum::Update);
|
QObject::connect(&*spinner_animation_, &QMovie::updated, this, &ContextAlbum::Update);
|
||||||
spinner_animation_->start();
|
spinner_animation_->start();
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class ContextAlbum : public QWidget {
|
|||||||
explicit ContextAlbum(QWidget *parent = nullptr);
|
explicit ContextAlbum(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void Init(ContextView *context_view, AlbumCoverChoiceController *album_cover_choice_controller);
|
void Init(ContextView *context_view, AlbumCoverChoiceController *album_cover_choice_controller);
|
||||||
void SetImage(QImage image = QImage());
|
void SetImage(const QImage &image = QImage());
|
||||||
void UpdateWidth(const int width);
|
void UpdateWidth(const int width);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -63,7 +63,7 @@ class ContextAlbum : public QWidget {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
struct PreviousCover {
|
struct PreviousCover {
|
||||||
PreviousCover() : opacity(0.0) {}
|
explicit PreviousCover() : opacity(0.0) {}
|
||||||
QImage image;
|
QImage image;
|
||||||
QPixmap pixmap;
|
QPixmap pixmap;
|
||||||
qreal opacity;
|
qreal opacity;
|
||||||
@@ -77,7 +77,6 @@ class ContextAlbum : public QWidget {
|
|||||||
void DrawPreviousCovers(QPainter *p);
|
void DrawPreviousCovers(QPainter *p);
|
||||||
void ScaleCover();
|
void ScaleCover();
|
||||||
void ScalePreviousCovers();
|
void ScalePreviousCovers();
|
||||||
void GetCoverAutomatically();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void FadeStopFinished();
|
void FadeStopFinished();
|
||||||
@@ -93,9 +92,6 @@ class ContextAlbum : public QWidget {
|
|||||||
public slots:
|
public slots:
|
||||||
void SearchCoverInProgress();
|
void SearchCoverInProgress();
|
||||||
|
|
||||||
private:
|
|
||||||
static const int kFadeTimeLineMs;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMenu *menu_;
|
QMenu *menu_;
|
||||||
ContextView *context_view_;
|
ContextView *context_view_;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/player.h"
|
#include "core/player.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "utilities/strutils.h"
|
#include "utilities/strutils.h"
|
||||||
#include "utilities/timeutils.h"
|
#include "utilities/timeutils.h"
|
||||||
#include "widgets/resizabletextedit.h"
|
#include "widgets/resizabletextedit.h"
|
||||||
@@ -64,7 +65,9 @@
|
|||||||
#include "contextview.h"
|
#include "contextview.h"
|
||||||
#include "contextalbum.h"
|
#include "contextalbum.h"
|
||||||
|
|
||||||
const int ContextView::kWidgetSpacing = 50;
|
namespace {
|
||||||
|
constexpr int kWidgetSpacing = 50;
|
||||||
|
}
|
||||||
|
|
||||||
ContextView::ContextView(QWidget *parent)
|
ContextView::ContextView(QWidget *parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
@@ -112,25 +115,25 @@ ContextView::ContextView(QWidget *parent)
|
|||||||
|
|
||||||
setLayout(layout_container_);
|
setLayout(layout_container_);
|
||||||
|
|
||||||
layout_container_->setObjectName("context-layout-container");
|
layout_container_->setObjectName(QStringLiteral("context-layout-container"));
|
||||||
layout_container_->setContentsMargins(0, 0, 0, 0);
|
layout_container_->setContentsMargins(0, 0, 0, 0);
|
||||||
layout_container_->addWidget(scrollarea_);
|
layout_container_->addWidget(scrollarea_);
|
||||||
|
|
||||||
scrollarea_->setObjectName("context-scrollarea");
|
scrollarea_->setObjectName(QStringLiteral("context-scrollarea"));
|
||||||
scrollarea_->setWidgetResizable(true);
|
scrollarea_->setWidgetResizable(true);
|
||||||
scrollarea_->setWidget(widget_scrollarea_);
|
scrollarea_->setWidget(widget_scrollarea_);
|
||||||
scrollarea_->setContentsMargins(0, 0, 0, 0);
|
scrollarea_->setContentsMargins(0, 0, 0, 0);
|
||||||
scrollarea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
scrollarea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
scrollarea_->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
scrollarea_->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
|
||||||
widget_scrollarea_->setObjectName("context-widget-scrollarea");
|
widget_scrollarea_->setObjectName(QStringLiteral("context-widget-scrollarea"));
|
||||||
widget_scrollarea_->setLayout(layout_scrollarea_);
|
widget_scrollarea_->setLayout(layout_scrollarea_);
|
||||||
widget_scrollarea_->setContentsMargins(0, 0, 0, 0);
|
widget_scrollarea_->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
textedit_top_->setReadOnly(true);
|
textedit_top_->setReadOnly(true);
|
||||||
textedit_top_->setFrameShape(QFrame::NoFrame);
|
textedit_top_->setFrameShape(QFrame::NoFrame);
|
||||||
|
|
||||||
layout_scrollarea_->setObjectName("context-layout-scrollarea");
|
layout_scrollarea_->setObjectName(QStringLiteral("context-layout-scrollarea"));
|
||||||
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
|
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
|
||||||
layout_scrollarea_->addWidget(textedit_top_);
|
layout_scrollarea_->addWidget(textedit_top_);
|
||||||
layout_scrollarea_->addWidget(widget_album_);
|
layout_scrollarea_->addWidget(widget_album_);
|
||||||
@@ -291,20 +294,20 @@ void ContextView::ReloadSettings() {
|
|||||||
|
|
||||||
QString default_font;
|
QString default_font;
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
if (QFontDatabase::families().contains(ContextSettingsPage::kDefaultFontFamily)) {
|
if (QFontDatabase::families().contains(QLatin1String(ContextSettingsPage::kDefaultFontFamily))) {
|
||||||
#else
|
#else
|
||||||
if (QFontDatabase().families().contains(ContextSettingsPage::kDefaultFontFamily)) {
|
if (QFontDatabase().families().contains(QLatin1String(ContextSettingsPage::kDefaultFontFamily))) {
|
||||||
#endif
|
#endif
|
||||||
default_font = ContextSettingsPage::kDefaultFontFamily;
|
default_font = QLatin1String(ContextSettingsPage::kDefaultFontFamily);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
default_font = font().family();
|
default_font = font().family();
|
||||||
}
|
}
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
||||||
title_fmt_ = s.value(ContextSettingsPage::kSettingsTitleFmt, "%title% - %artist%").toString();
|
title_fmt_ = s.value(ContextSettingsPage::kSettingsTitleFmt, QStringLiteral("%title% - %artist%")).toString();
|
||||||
summary_fmt_ = s.value(ContextSettingsPage::kSettingsSummaryFmt, "%album%").toString();
|
summary_fmt_ = s.value(ContextSettingsPage::kSettingsSummaryFmt, QStringLiteral("%album%")).toString();
|
||||||
action_show_album_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], true).toBool());
|
action_show_album_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], true).toBool());
|
||||||
action_show_data_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], false).toBool());
|
action_show_data_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], false).toBool());
|
||||||
action_show_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], true).toBool());
|
action_show_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], true).toBool());
|
||||||
@@ -390,7 +393,7 @@ void ContextView::FadeStopFinished() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ContextView::SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def) {
|
void ContextView::SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def) {
|
||||||
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
|
label->setText(value <= 0 ? def : (QString::number(value) + QLatin1Char(' ') + suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContextView::UpdateNoSong() {
|
void ContextView::UpdateNoSong() {
|
||||||
@@ -409,15 +412,15 @@ void ContextView::NoSong() {
|
|||||||
QString html;
|
QString html;
|
||||||
if (collectionview_->TotalSongs() == 1) html += tr("%1 song").arg(collectionview_->TotalSongs());
|
if (collectionview_->TotalSongs() == 1) html += tr("%1 song").arg(collectionview_->TotalSongs());
|
||||||
else html += tr("%1 songs").arg(collectionview_->TotalSongs());
|
else html += tr("%1 songs").arg(collectionview_->TotalSongs());
|
||||||
html += "<br />";
|
html += QLatin1String("<br />");
|
||||||
|
|
||||||
if (collectionview_->TotalArtists() == 1) html += tr("%1 artist").arg(collectionview_->TotalArtists());
|
if (collectionview_->TotalArtists() == 1) html += tr("%1 artist").arg(collectionview_->TotalArtists());
|
||||||
else html += tr("%1 artists").arg(collectionview_->TotalArtists());
|
else html += tr("%1 artists").arg(collectionview_->TotalArtists());
|
||||||
html += "<br />";
|
html += QLatin1String("<br />");
|
||||||
|
|
||||||
if (collectionview_->TotalAlbums() == 1) html += tr("%1 album").arg(collectionview_->TotalAlbums());
|
if (collectionview_->TotalAlbums() == 1) html += tr("%1 album").arg(collectionview_->TotalAlbums());
|
||||||
else html += tr("%1 albums").arg(collectionview_->TotalAlbums());
|
else html += tr("%1 albums").arg(collectionview_->TotalAlbums());
|
||||||
html += "<br />";
|
html += QLatin1String("<br />");
|
||||||
|
|
||||||
label_stop_summary_->setFont(font_normal_);
|
label_stop_summary_->setFont(font_normal_);
|
||||||
label_stop_summary_->setText(html);
|
label_stop_summary_->setText(html);
|
||||||
@@ -438,7 +441,7 @@ void ContextView::UpdateFonts() {
|
|||||||
void ContextView::SetSong() {
|
void ContextView::SetSong() {
|
||||||
|
|
||||||
textedit_top_->setFont(font_headline_);
|
textedit_top_->setFont(font_headline_);
|
||||||
textedit_top_->SetText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song_playing_, "<br />", true)));
|
textedit_top_->SetText(QStringLiteral("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, QStringLiteral("<br />"), true), Utilities::ReplaceMessage(summary_fmt_, song_playing_, QStringLiteral("<br />"), true)));
|
||||||
|
|
||||||
label_stop_summary_->clear();
|
label_stop_summary_->clear();
|
||||||
|
|
||||||
@@ -474,7 +477,7 @@ void ContextView::SetSong() {
|
|||||||
else {
|
else {
|
||||||
label_samplerate_title_->show();
|
label_samplerate_title_->show();
|
||||||
label_samplerate_->show();
|
label_samplerate_->show();
|
||||||
SetLabelText(label_samplerate_, song_playing_.samplerate(), "Hz");
|
SetLabelText(label_samplerate_, song_playing_.samplerate(), QStringLiteral("Hz"));
|
||||||
}
|
}
|
||||||
if (song_playing_.bitdepth() <= 0) {
|
if (song_playing_.bitdepth() <= 0) {
|
||||||
label_bitdepth_title_->hide();
|
label_bitdepth_title_->hide();
|
||||||
@@ -484,7 +487,7 @@ void ContextView::SetSong() {
|
|||||||
else {
|
else {
|
||||||
label_bitdepth_title_->show();
|
label_bitdepth_title_->show();
|
||||||
label_bitdepth_->show();
|
label_bitdepth_->show();
|
||||||
SetLabelText(label_bitdepth_, song_playing_.bitdepth(), "Bit");
|
SetLabelText(label_bitdepth_, song_playing_.bitdepth(), QStringLiteral("Bit"));
|
||||||
}
|
}
|
||||||
if (song_playing_.bitrate() <= 0) {
|
if (song_playing_.bitrate() <= 0) {
|
||||||
label_bitrate_title_->hide();
|
label_bitrate_title_->hide();
|
||||||
@@ -546,7 +549,10 @@ void ContextView::SetSong() {
|
|||||||
|
|
||||||
void ContextView::UpdateSong(const Song &song) {
|
void ContextView::UpdateSong(const Song &song) {
|
||||||
|
|
||||||
textedit_top_->SetText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song, "<br />", true)));
|
const QString top_text = QStringLiteral("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song, QStringLiteral("<br />"), true), Utilities::ReplaceMessage(summary_fmt_, song, QStringLiteral("<br />"), true));
|
||||||
|
if (top_text != textedit_top_->Text()) {
|
||||||
|
textedit_top_->SetText(top_text);
|
||||||
|
}
|
||||||
|
|
||||||
if (action_show_data_->isChecked()) {
|
if (action_show_data_->isChecked()) {
|
||||||
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());
|
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());
|
||||||
@@ -571,7 +577,7 @@ void ContextView::UpdateSong(const Song &song) {
|
|||||||
else {
|
else {
|
||||||
label_samplerate_title_->show();
|
label_samplerate_title_->show();
|
||||||
label_samplerate_->show();
|
label_samplerate_->show();
|
||||||
SetLabelText(label_samplerate_, song.samplerate(), "Hz");
|
SetLabelText(label_samplerate_, song.samplerate(), QStringLiteral("Hz"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (song.bitdepth() != song_playing_.bitdepth()) {
|
if (song.bitdepth() != song_playing_.bitdepth()) {
|
||||||
@@ -583,7 +589,7 @@ void ContextView::UpdateSong(const Song &song) {
|
|||||||
else {
|
else {
|
||||||
label_bitdepth_title_->show();
|
label_bitdepth_title_->show();
|
||||||
label_bitdepth_->show();
|
label_bitdepth_->show();
|
||||||
SetLabelText(label_bitdepth_, song.bitdepth(), "Bit");
|
SetLabelText(label_bitdepth_, song.bitdepth(), QStringLiteral("Bit"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (song.bitrate() != song_playing_.bitrate()) {
|
if (song.bitrate() != song_playing_.bitrate()) {
|
||||||
@@ -631,7 +637,12 @@ void ContextView::UpdateLyrics(const quint64 id, const QString &provider, const
|
|||||||
|
|
||||||
if (static_cast<qint64>(id) != lyrics_id_) return;
|
if (static_cast<qint64>(id) != lyrics_id_) return;
|
||||||
|
|
||||||
lyrics_ = lyrics + "\n\n(Lyrics from " + provider + ")\n";
|
if (lyrics.isEmpty()) {
|
||||||
|
lyrics_ = QLatin1String("No lyrics found.\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lyrics_ = lyrics + QLatin1String("\n\n(Lyrics from ") + provider + QLatin1String(")\n");
|
||||||
|
}
|
||||||
lyrics_id_ = -1;
|
lyrics_id_ = -1;
|
||||||
|
|
||||||
if (action_show_lyrics_->isChecked() && !lyrics_.isEmpty()) {
|
if (action_show_lyrics_->isChecked() && !lyrics_.isEmpty()) {
|
||||||
@@ -687,7 +698,7 @@ void ContextView::AlbumCoverLoaded(const Song &song, const QImage &image) {
|
|||||||
|
|
||||||
void ContextView::ActionShowAlbum() {
|
void ContextView::ActionShowAlbum() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
||||||
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], action_show_album_->isChecked());
|
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], action_show_album_->isChecked());
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -697,7 +708,7 @@ void ContextView::ActionShowAlbum() {
|
|||||||
|
|
||||||
void ContextView::ActionShowData() {
|
void ContextView::ActionShowData() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
||||||
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], action_show_data_->isChecked());
|
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], action_show_data_->isChecked());
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -707,7 +718,7 @@ void ContextView::ActionShowData() {
|
|||||||
|
|
||||||
void ContextView::ActionShowLyrics() {
|
void ContextView::ActionShowLyrics() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
||||||
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], action_show_lyrics_->isChecked());
|
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], action_show_lyrics_->isChecked());
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -720,7 +731,7 @@ void ContextView::ActionShowLyrics() {
|
|||||||
|
|
||||||
void ContextView::ActionSearchLyrics() {
|
void ContextView::ActionSearchLyrics() {
|
||||||
|
|
||||||
QSettings s;
|
Settings s;
|
||||||
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
s.beginGroup(ContextSettingsPage::kSettingsGroup);
|
||||||
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SEARCH_LYRICS)], action_search_lyrics_->isChecked());
|
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SEARCH_LYRICS)], action_search_lyrics_->isChecked());
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|||||||
@@ -101,8 +101,6 @@ class ContextView : public QWidget {
|
|||||||
void AlbumCoverLoaded(const Song &song, const QImage &image);
|
void AlbumCoverLoaded(const Song &song, const QImage &image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kWidgetSpacing;
|
|
||||||
|
|
||||||
Application *app_;
|
Application *app_;
|
||||||
CollectionView *collectionview_;
|
CollectionView *collectionview_;
|
||||||
AlbumCoverChoiceController *album_cover_choice_controller_;
|
AlbumCoverChoiceController *album_cover_choice_controller_;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
#include "covermanager/musicbrainzcoverprovider.h"
|
#include "covermanager/musicbrainzcoverprovider.h"
|
||||||
#include "covermanager/deezercoverprovider.h"
|
#include "covermanager/deezercoverprovider.h"
|
||||||
#include "covermanager/musixmatchcoverprovider.h"
|
#include "covermanager/musixmatchcoverprovider.h"
|
||||||
#include "covermanager/spotifycoverprovider.h"
|
#include "covermanager/opentidalcoverprovider.h"
|
||||||
|
|
||||||
#include "lyrics/lyricsproviders.h"
|
#include "lyrics/lyricsproviders.h"
|
||||||
#include "lyrics/geniuslyricsprovider.h"
|
#include "lyrics/geniuslyricsprovider.h"
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
#include "lyrics/songlyricscomlyricsprovider.h"
|
#include "lyrics/songlyricscomlyricsprovider.h"
|
||||||
#include "lyrics/azlyricscomlyricsprovider.h"
|
#include "lyrics/azlyricscomlyricsprovider.h"
|
||||||
#include "lyrics/elyricsnetlyricsprovider.h"
|
#include "lyrics/elyricsnetlyricsprovider.h"
|
||||||
#include "lyrics/lyricsmodecomlyricsprovider.h"
|
#include "lyrics/letraslyricsprovider.h"
|
||||||
|
|
||||||
#include "scrobbler/audioscrobbler.h"
|
#include "scrobbler/audioscrobbler.h"
|
||||||
#include "scrobbler/lastfmscrobbler.h"
|
#include "scrobbler/lastfmscrobbler.h"
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
# include "scrobbler/subsonicscrobbler.h"
|
# include "scrobbler/subsonicscrobbler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "internet/internetservices.h"
|
#include "streaming/streamingservices.h"
|
||||||
|
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
# include "subsonic/subsonicservice.h"
|
# include "subsonic/subsonicservice.h"
|
||||||
@@ -89,6 +89,11 @@
|
|||||||
# include "covermanager/tidalcoverprovider.h"
|
# include "covermanager/tidalcoverprovider.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
# include "spotify/spotifyservice.h"
|
||||||
|
# include "covermanager/spotifycoverprovider.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_QOBUZ
|
#ifdef HAVE_QOBUZ
|
||||||
# include "qobuz/qobuzservice.h"
|
# include "qobuz/qobuzservice.h"
|
||||||
# include "covermanager/qobuzcoverprovider.h"
|
# include "covermanager/qobuzcoverprovider.h"
|
||||||
@@ -142,10 +147,13 @@ class ApplicationImpl {
|
|||||||
cover_providers->AddProvider(new DiscogsCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new DiscogsCoverProvider(app, app->network()));
|
||||||
cover_providers->AddProvider(new DeezerCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new DeezerCoverProvider(app, app->network()));
|
||||||
cover_providers->AddProvider(new MusixmatchCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new MusixmatchCoverProvider(app, app->network()));
|
||||||
cover_providers->AddProvider(new SpotifyCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new OpenTidalCoverProvider(app, app->network()));
|
||||||
#ifdef HAVE_TIDAL
|
#ifdef HAVE_TIDAL
|
||||||
cover_providers->AddProvider(new TidalCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new TidalCoverProvider(app, app->network()));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
cover_providers->AddProvider(new SpotifyCoverProvider(app, app->network()));
|
||||||
|
#endif
|
||||||
#ifdef HAVE_QOBUZ
|
#ifdef HAVE_QOBUZ
|
||||||
cover_providers->AddProvider(new QobuzCoverProvider(app, app->network()));
|
cover_providers->AddProvider(new QobuzCoverProvider(app, app->network()));
|
||||||
#endif
|
#endif
|
||||||
@@ -169,22 +177,25 @@ class ApplicationImpl {
|
|||||||
lyrics_providers->AddProvider(new SongLyricsComLyricsProvider(app->network()));
|
lyrics_providers->AddProvider(new SongLyricsComLyricsProvider(app->network()));
|
||||||
lyrics_providers->AddProvider(new AzLyricsComLyricsProvider(app->network()));
|
lyrics_providers->AddProvider(new AzLyricsComLyricsProvider(app->network()));
|
||||||
lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(app->network()));
|
lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(app->network()));
|
||||||
lyrics_providers->AddProvider(new LyricsModeComLyricsProvider(app->network()));
|
lyrics_providers->AddProvider(new LetrasLyricsProvider(app->network()));
|
||||||
lyrics_providers->ReloadSettings();
|
lyrics_providers->ReloadSettings();
|
||||||
return lyrics_providers;
|
return lyrics_providers;
|
||||||
}),
|
}),
|
||||||
internet_services_([app]() {
|
streaming_services_([app]() {
|
||||||
InternetServices *internet_services = new InternetServices();
|
StreamingServices *streaming_services = new StreamingServices();
|
||||||
#ifdef HAVE_SUBSONIC
|
#ifdef HAVE_SUBSONIC
|
||||||
internet_services->AddService(make_shared<SubsonicService>(app));
|
streaming_services->AddService(make_shared<SubsonicService>(app));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_TIDAL
|
#ifdef HAVE_TIDAL
|
||||||
internet_services->AddService(make_shared<TidalService>(app));
|
streaming_services->AddService(make_shared<TidalService>(app));
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
streaming_services->AddService(make_shared<SpotifyService>(app));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_QOBUZ
|
#ifdef HAVE_QOBUZ
|
||||||
internet_services->AddService(make_shared<QobuzService>(app));
|
streaming_services->AddService(make_shared<QobuzService>(app));
|
||||||
#endif
|
#endif
|
||||||
return internet_services;
|
return streaming_services;
|
||||||
}),
|
}),
|
||||||
radio_services_([app]() { return new RadioServices(app); }),
|
radio_services_([app]() { return new RadioServices(app); }),
|
||||||
scrobbler_([app]() {
|
scrobbler_([app]() {
|
||||||
@@ -220,7 +231,7 @@ class ApplicationImpl {
|
|||||||
Lazy<AlbumCoverLoader> album_cover_loader_;
|
Lazy<AlbumCoverLoader> album_cover_loader_;
|
||||||
Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_;
|
Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_;
|
||||||
Lazy<LyricsProviders> lyrics_providers_;
|
Lazy<LyricsProviders> lyrics_providers_;
|
||||||
Lazy<InternetServices> internet_services_;
|
Lazy<StreamingServices> streaming_services_;
|
||||||
Lazy<RadioServices> radio_services_;
|
Lazy<RadioServices> radio_services_;
|
||||||
Lazy<AudioScrobbler> scrobbler_;
|
Lazy<AudioScrobbler> scrobbler_;
|
||||||
#ifdef HAVE_MOODBAR
|
#ifdef HAVE_MOODBAR
|
||||||
@@ -285,7 +296,7 @@ void Application::Exit() {
|
|||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
<< &*device_manager()
|
<< &*device_manager()
|
||||||
#endif
|
#endif
|
||||||
<< &*internet_services()
|
<< &*streaming_services()
|
||||||
<< &*radio_services()->radio_backend();
|
<< &*radio_services()->radio_backend();
|
||||||
|
|
||||||
QObject::connect(&*tag_reader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived);
|
QObject::connect(&*tag_reader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived);
|
||||||
@@ -305,8 +316,8 @@ void Application::Exit() {
|
|||||||
device_manager()->Exit();
|
device_manager()->Exit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QObject::connect(&*internet_services(), &InternetServices::ExitFinished, this, &Application::ExitReceived);
|
QObject::connect(&*streaming_services(), &StreamingServices::ExitFinished, this, &Application::ExitReceived);
|
||||||
internet_services()->Exit();
|
streaming_services()->Exit();
|
||||||
|
|
||||||
QObject::connect(&*radio_services()->radio_backend(), &RadioBackend::ExitFinished, this, &Application::ExitReceived);
|
QObject::connect(&*radio_services()->radio_backend(), &RadioBackend::ExitFinished, this, &Application::ExitReceived);
|
||||||
radio_services()->radio_backend()->ExitAsync();
|
radio_services()->radio_backend()->ExitAsync();
|
||||||
@@ -351,7 +362,7 @@ SharedPtr<CurrentAlbumCoverLoader> Application::current_albumcover_loader() cons
|
|||||||
SharedPtr<LyricsProviders> Application::lyrics_providers() const { return p_->lyrics_providers_.ptr(); }
|
SharedPtr<LyricsProviders> Application::lyrics_providers() const { return p_->lyrics_providers_.ptr(); }
|
||||||
SharedPtr<PlaylistBackend> Application::playlist_backend() const { return p_->playlist_backend_.ptr(); }
|
SharedPtr<PlaylistBackend> Application::playlist_backend() const { return p_->playlist_backend_.ptr(); }
|
||||||
SharedPtr<PlaylistManager> Application::playlist_manager() const { return p_->playlist_manager_.ptr(); }
|
SharedPtr<PlaylistManager> Application::playlist_manager() const { return p_->playlist_manager_.ptr(); }
|
||||||
SharedPtr<InternetServices> Application::internet_services() const { return p_->internet_services_.ptr(); }
|
SharedPtr<StreamingServices> Application::streaming_services() const { return p_->streaming_services_.ptr(); }
|
||||||
SharedPtr<RadioServices> Application::radio_services() const { return p_->radio_services_.ptr(); }
|
SharedPtr<RadioServices> Application::radio_services() const { return p_->radio_services_.ptr(); }
|
||||||
SharedPtr<AudioScrobbler> Application::scrobbler() const { return p_->scrobbler_.ptr(); }
|
SharedPtr<AudioScrobbler> Application::scrobbler() const { return p_->scrobbler_.ptr(); }
|
||||||
SharedPtr<LastFMImport> Application::lastfm_import() const { return p_->lastfm_import_.ptr(); }
|
SharedPtr<LastFMImport> Application::lastfm_import() const { return p_->lastfm_import_.ptr(); }
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class CoverProviders;
|
|||||||
class LyricsProviders;
|
class LyricsProviders;
|
||||||
class AudioScrobbler;
|
class AudioScrobbler;
|
||||||
class LastFMImport;
|
class LastFMImport;
|
||||||
class InternetServices;
|
class StreamingServices;
|
||||||
class RadioServices;
|
class RadioServices;
|
||||||
#ifdef HAVE_MOODBAR
|
#ifdef HAVE_MOODBAR
|
||||||
class MoodbarController;
|
class MoodbarController;
|
||||||
@@ -97,7 +97,7 @@ class Application : public QObject {
|
|||||||
|
|
||||||
SharedPtr<AudioScrobbler> scrobbler() const;
|
SharedPtr<AudioScrobbler> scrobbler() const;
|
||||||
|
|
||||||
SharedPtr<InternetServices> internet_services() const;
|
SharedPtr<StreamingServices> streaming_services() const;
|
||||||
SharedPtr<RadioServices> radio_services() const;
|
SharedPtr<RadioServices> radio_services() const;
|
||||||
|
|
||||||
#ifdef HAVE_MOODBAR
|
#ifdef HAVE_MOODBAR
|
||||||
|
|||||||