Compare commits
661 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3faf18251 | ||
|
|
ddcc296af4 | ||
|
|
ee765bc210 | ||
|
|
630c67f7d0 | ||
|
|
e78bb1b29c | ||
|
|
db312a7380 | ||
|
|
48b4cbe1b9 | ||
|
|
edb1d4c747 | ||
|
|
128935b55e | ||
|
|
d5a0d67c7b | ||
|
|
a86640fdee | ||
|
|
03291b27d1 | ||
|
|
acf81f116f | ||
|
|
6564c405c1 | ||
|
|
1bd586268c | ||
|
|
497952611d | ||
|
|
9149d1baa3 | ||
|
|
66c60ac5c7 | ||
|
|
971fad4560 | ||
|
|
8ed1ce4103 | ||
|
|
3112c34d11 | ||
|
|
7ebf3cecc6 | ||
|
|
660fa99f9c | ||
|
|
a997e53d8b | ||
|
|
1d1dd583e2 | ||
|
|
d490b29265 | ||
|
|
e8ca64f16b | ||
|
|
380aa7d884 | ||
|
|
0a31a94ee8 | ||
|
|
e1d50ce28f | ||
|
|
38cc39f23d | ||
|
|
8110035d71 | ||
|
|
9af409d6d6 | ||
|
|
8646829853 | ||
|
|
b9e87813b1 | ||
|
|
138e032746 | ||
|
|
d94c6b5aba | ||
|
|
7c03a39316 | ||
|
|
c482e264f4 | ||
|
|
3a9a1f0a94 | ||
|
|
5b5f728f49 | ||
|
|
179aac6b35 | ||
|
|
25de9126ca | ||
|
|
87be5662ad | ||
|
|
053a59e3f4 | ||
|
|
250857b3d4 | ||
|
|
dc288c584c | ||
|
|
fc02543f15 | ||
|
|
0808db706f | ||
|
|
1e649fc565 | ||
|
|
c523e959d7 | ||
|
|
1422f988c1 | ||
|
|
e86252b934 | ||
|
|
e9c59b5c31 | ||
|
|
f7bddee0ce | ||
|
|
d56b76c9e3 | ||
|
|
a41353ed03 | ||
|
|
7ea12cf2f9 | ||
|
|
5bc5170112 | ||
|
|
946ed0c0b5 | ||
|
|
fcd4e5aca2 | ||
|
|
6ab6e6d3a8 | ||
|
|
0893d01b4a | ||
|
|
bf04c45ebf | ||
|
|
2786a1a97e | ||
|
|
cd9a4f1aa9 | ||
|
|
91e5cafe76 | ||
|
|
47754951f0 | ||
|
|
fbef8ef32e | ||
|
|
abd5bf3158 | ||
|
|
fd024368a3 | ||
|
|
cf4856a0c9 | ||
|
|
1b791c82fc | ||
|
|
3a27a3e868 | ||
|
|
7e6c16b287 | ||
|
|
f877639ed7 | ||
|
|
846309d4dd | ||
|
|
f2239ddfdf | ||
|
|
742d455b0e | ||
|
|
20b08a78e7 | ||
|
|
042da74955 | ||
|
|
4dcae4ce21 | ||
|
|
fa0f07f553 | ||
|
|
39792d76cf | ||
|
|
d181ee9101 | ||
|
|
335ba1a5b0 | ||
|
|
4934a360f1 | ||
|
|
0559a4c6f2 | ||
|
|
fc07919a75 | ||
|
|
d7661edf67 | ||
|
|
1c91693294 | ||
|
|
3fc3cbc6d5 | ||
|
|
11b5895e69 | ||
|
|
b4c614edbf | ||
|
|
deddaed04a | ||
|
|
a155e503f4 | ||
|
|
c0663bc19f | ||
|
|
7ffa51b83d | ||
|
|
6d397b9988 | ||
|
|
571a7fa26b | ||
|
|
b3b5a38c3a | ||
|
|
a4b115f89b | ||
|
|
3d672bb145 | ||
|
|
15b656b753 | ||
|
|
f5785db163 | ||
|
|
1ff1bf3292 | ||
|
|
b062febea0 | ||
|
|
35301dc79e | ||
|
|
30c336726b | ||
|
|
3821680817 | ||
|
|
74242ea24f | ||
|
|
73c7024e11 | ||
|
|
bbdec92dc6 | ||
|
|
b4c289101c | ||
|
|
722d0797f6 | ||
|
|
deb27d5b55 | ||
|
|
9cc4ffdf6e | ||
|
|
c76d63d1d9 | ||
|
|
21bb4f33ad | ||
|
|
2897b881d6 | ||
|
|
e9b89d0929 | ||
|
|
e801254b2e | ||
|
|
6f49918ee9 | ||
|
|
0ae7c18f1f | ||
|
|
c9c3fb396a | ||
|
|
91db4f1934 | ||
|
|
9bbed6e95c | ||
|
|
748bc27b25 | ||
|
|
f42708e8bc | ||
|
|
160e4570a2 | ||
|
|
6272965143 | ||
|
|
1e9613bf7f | ||
|
|
a061dac298 | ||
|
|
914dee8571 | ||
|
|
47e2905edf | ||
|
|
ee6675aee0 | ||
|
|
62e0d9fe64 | ||
|
|
a174c142c1 | ||
|
|
95afc5fdec | ||
|
|
04d69f66c0 | ||
|
|
0347141edd | ||
|
|
1d6baae6e0 | ||
|
|
fb0f48f08a | ||
|
|
76e5e03d31 | ||
|
|
4cab743634 | ||
|
|
7c10ec97b7 | ||
|
|
8718a16889 | ||
|
|
75a0b924c3 | ||
|
|
83a90e0c05 | ||
|
|
e5eadd1315 | ||
|
|
f0142d90d4 | ||
|
|
cabd6e6e9d | ||
|
|
4804a05736 | ||
|
|
c258e5a3af | ||
|
|
e8492940a5 | ||
|
|
4bccb1ab47 | ||
|
|
b6d219e232 | ||
|
|
23ee17594d | ||
|
|
d9d39d8e25 | ||
|
|
224d5d46c1 | ||
|
|
09e0059930 | ||
|
|
0ddff2b087 | ||
|
|
2e6a29eacc | ||
|
|
ad2fb82aa9 | ||
|
|
a3f91c11e8 | ||
|
|
a50c978ce3 | ||
|
|
27d6f881cd | ||
|
|
944cd020af | ||
|
|
bbe5d64b99 | ||
|
|
f7b36ac4c7 | ||
|
|
1d555ca17e | ||
|
|
f91b6c3468 | ||
|
|
abe6eeb350 | ||
|
|
64f90a7912 | ||
|
|
5733966843 | ||
|
|
eb1344fcec | ||
|
|
c5fb29f00e | ||
|
|
63135b9c54 | ||
|
|
f7c666584e | ||
|
|
6834324de2 | ||
|
|
3a0d59e66f | ||
|
|
ffd2e2188a | ||
|
|
0e8d5bdc5d | ||
|
|
14806f6614 | ||
|
|
00ece83b9d | ||
|
|
8197ae2a2d | ||
|
|
5fe658bb16 | ||
|
|
617179f0c6 | ||
|
|
95ac85f642 | ||
|
|
ca8877ad47 | ||
|
|
6d8f31048c | ||
|
|
ac859eb576 | ||
|
|
2dfa171b6a | ||
|
|
6f72e3e2ea | ||
|
|
c2b73ae963 | ||
|
|
6c50077409 | ||
|
|
06746449a1 | ||
|
|
da7b8edf51 | ||
|
|
6e29b41f23 | ||
|
|
912bb069af | ||
|
|
6b2d7a67d8 | ||
|
|
dbb8ec0290 | ||
|
|
60b32760f2 | ||
|
|
73a40bcb49 | ||
|
|
0e258a5a32 | ||
|
|
7ca65c81d8 | ||
|
|
2ad1a60e59 | ||
|
|
cf17ff4478 | ||
|
|
fffc3aac68 | ||
|
|
295ac3c458 | ||
|
|
b6693a71f9 | ||
|
|
5b21118a8c | ||
|
|
0235b19801 | ||
|
|
7426399aa2 | ||
|
|
6861b0d668 | ||
|
|
c30fb0d38c | ||
|
|
e45521c6c0 | ||
|
|
d11fe8d4fc | ||
|
|
8e83e63e3d | ||
|
|
c8fd0ac4b3 | ||
|
|
d78419eb33 | ||
|
|
e44a3d013d | ||
|
|
aeb0d05017 | ||
|
|
62702e4b3d | ||
|
|
5146cdfa2f | ||
|
|
246e7018c3 | ||
|
|
24286dbe9d | ||
|
|
2f442dfbe1 | ||
|
|
b2fb01ee9c | ||
|
|
45e0a9a4ef | ||
|
|
675b7b4bf4 | ||
|
|
be7a35443e | ||
|
|
9918615fcd | ||
|
|
4b72ef77c1 | ||
|
|
e427c61fbb | ||
|
|
c78e8937d5 | ||
|
|
5d63f7a93d | ||
|
|
46551ada7f | ||
|
|
83f17a37b1 | ||
|
|
9e23e0a623 | ||
|
|
4a53d4f043 | ||
|
|
0fd61945c7 | ||
|
|
e3624eed30 | ||
|
|
1923c8be0c | ||
|
|
a3e37fbfe2 | ||
|
|
318c3bb422 | ||
|
|
db96e24028 | ||
|
|
cc2cce66df | ||
|
|
7afeabd288 | ||
|
|
eb43a812d6 | ||
|
|
12cbcdb6f4 | ||
|
|
a2e35e30dc | ||
|
|
b6ff7e6b47 | ||
|
|
6dba40c6bb | ||
|
|
896da46422 | ||
|
|
7c07f5eb2a | ||
|
|
298cff37de | ||
|
|
8add802fe9 | ||
|
|
6d080a0d59 | ||
|
|
f0ae1051ee | ||
|
|
1ad0ffeaa6 | ||
|
|
45b44d012d | ||
|
|
cb6cbb9ee5 | ||
|
|
013a0d9cb0 | ||
|
|
8abb3ea225 | ||
|
|
74a5233b5d | ||
|
|
cd695a4522 | ||
|
|
eec4b2cc0e | ||
|
|
616875d0d2 | ||
|
|
eb749bd76f | ||
|
|
7d1e404efd | ||
|
|
4a01a578d1 | ||
|
|
393a2e0ea0 | ||
|
|
8e21decb8d | ||
|
|
22bfbab832 | ||
|
|
28d0cc8795 | ||
|
|
51f2383a07 | ||
|
|
a6426c6eba | ||
|
|
0631da6c8e | ||
|
|
3960239cfe | ||
|
|
b16e48af6f | ||
|
|
4d3950565a | ||
|
|
8d24bc50c9 | ||
|
|
458efe9168 | ||
|
|
ae940a8681 | ||
|
|
d76b223b7c | ||
|
|
7243d5f7cb | ||
|
|
387f74f66a | ||
|
|
61ffb7d97a | ||
|
|
a553693f34 | ||
|
|
cdcfd64ec4 | ||
|
|
7f4302ba20 | ||
|
|
0d94f2d376 | ||
|
|
c1eb20a20b | ||
|
|
634db67685 | ||
|
|
86648258a3 | ||
|
|
287f0a3739 | ||
|
|
8f42df209a | ||
|
|
390fd64a74 | ||
|
|
48ee471def | ||
|
|
e862afcb78 | ||
|
|
872da05ff6 | ||
|
|
d09e2daf00 | ||
|
|
e2d5b44b0a | ||
|
|
52d42ef2a8 | ||
|
|
b6abc34461 | ||
|
|
ae6a50626d | ||
|
|
8f9dbfee2c | ||
|
|
c71ccda967 | ||
|
|
0f284f2e1d | ||
|
|
45ac80dd8c | ||
|
|
09cdba4b3d | ||
|
|
d94ee8863c | ||
|
|
cc8ced6430 | ||
|
|
cdb144e6a6 | ||
|
|
58fb8643c6 | ||
|
|
b81cfa8acb | ||
|
|
938ee20f1f | ||
|
|
d02dc54c1b | ||
|
|
8680a54ae4 | ||
|
|
30001be0ee | ||
|
|
4614cb5ec1 | ||
|
|
e390f3a399 | ||
|
|
e22d463d11 | ||
|
|
5877aa822c | ||
|
|
a2915913bb | ||
|
|
a8b40747b2 | ||
|
|
b63030d302 | ||
|
|
4457e416db | ||
|
|
c7c1a8ede1 | ||
|
|
fd1e9d7fb0 | ||
|
|
59a6d2317b | ||
|
|
531c171542 | ||
|
|
0c743452b0 | ||
|
|
4893d3da1f | ||
|
|
5cdc24bfb0 | ||
|
|
523cdca449 | ||
|
|
09537d73da | ||
|
|
99c0c0b3b1 | ||
|
|
45bc353341 | ||
|
|
b2fc41a911 | ||
|
|
ebefe8b6d2 | ||
|
|
9e3508134b | ||
|
|
3166ca2127 | ||
|
|
204508478f | ||
|
|
51ce674c97 | ||
|
|
6fcde9fe5f | ||
|
|
c688e3431d | ||
|
|
230376c7f3 | ||
|
|
91d0a2cd0c | ||
|
|
1ffd010e4a | ||
|
|
ae366e141f | ||
|
|
eb869c6e97 | ||
|
|
b559f7cd16 | ||
|
|
375d32f08e | ||
|
|
89d6b7cec0 | ||
|
|
fdf96e8342 | ||
|
|
c5d73d7b09 | ||
|
|
34b4cc2d9e | ||
|
|
40ed17e609 | ||
|
|
673d76af45 | ||
|
|
c13fb6f9d5 | ||
|
|
424b1e7d1f | ||
|
|
e80c9c4101 | ||
|
|
f75c3633e9 | ||
|
|
d535cd8276 | ||
|
|
c13f6ab2af | ||
|
|
d2a30bfb78 | ||
|
|
e5b17092b4 | ||
|
|
466cb4c78b | ||
|
|
9722e1ddc4 | ||
|
|
e3a9b0b943 | ||
|
|
d668a8aee2 | ||
|
|
75dc0aafcf | ||
|
|
41b94233c6 | ||
|
|
52cff01b9c | ||
|
|
372fc6603d | ||
|
|
9221797c9d | ||
|
|
7501131558 | ||
|
|
bba7be99a3 | ||
|
|
abb95534d0 | ||
|
|
0b7b4c12e4 | ||
|
|
4056f00169 | ||
|
|
10303cb9c0 | ||
|
|
e3587d369e | ||
|
|
2a048502cc | ||
|
|
cac01fbde9 | ||
|
|
2d2ce191ec | ||
|
|
6380cb8458 | ||
|
|
48e0e6af71 | ||
|
|
b756bccc7a | ||
|
|
ae8eed7a67 | ||
|
|
ae4882bec3 | ||
|
|
1379741859 | ||
|
|
b45c7ace78 | ||
|
|
3843d9f55b | ||
|
|
f3422cb2fe | ||
|
|
73692797dc | ||
|
|
31dd910289 | ||
|
|
da51580299 | ||
|
|
9db148b1ec | ||
|
|
091b1b8209 | ||
|
|
900a4071ed | ||
|
|
df4b2f7938 | ||
|
|
803f44d9bc | ||
|
|
4b67aee020 | ||
|
|
71dc47d6c9 | ||
|
|
9cb305fb0d | ||
|
|
1672130486 | ||
|
|
8e135e3e79 | ||
|
|
aa1a4f6ea5 | ||
|
|
ba34cf5258 | ||
|
|
647089d2a8 | ||
|
|
5211508eb4 | ||
|
|
e6f05ae465 | ||
|
|
a9193f9b76 | ||
|
|
e373a17cd3 | ||
|
|
ebab9b7e4a | ||
|
|
6de0399807 | ||
|
|
5cc7bb80f6 | ||
|
|
6e0bd9b3f8 | ||
|
|
d1943f72d3 | ||
|
|
81709873bd | ||
|
|
f6bb7cd8ed | ||
|
|
d1c19e431c | ||
|
|
9ab2dde8ab | ||
|
|
71559bb125 | ||
|
|
ae4c95262c | ||
|
|
dbbf07c9c1 | ||
|
|
ab8cd619d5 | ||
|
|
c30ad2819d | ||
|
|
311e91797a | ||
|
|
52be4df355 | ||
|
|
0364e81050 | ||
|
|
2d49b71bc9 | ||
|
|
a18a4bdf31 | ||
|
|
d3acbe8288 | ||
|
|
22afcbcbb6 | ||
|
|
495c6bc21c | ||
|
|
cfd1fe59f3 | ||
|
|
c46cf5bc84 | ||
|
|
a8742557bd | ||
|
|
3bea58cf56 | ||
|
|
5aaa5231b8 | ||
|
|
82d10dd7cb | ||
|
|
841065fb91 | ||
|
|
c7146ef138 | ||
|
|
08f32d1de6 | ||
|
|
4c3f86aa4d | ||
|
|
445cf22333 | ||
|
|
4919de647a | ||
|
|
77fae99528 | ||
|
|
10bd4b40b9 | ||
|
|
9d8e6bb253 | ||
|
|
49c71ecfad | ||
|
|
d18834b415 | ||
|
|
e52cda193e | ||
|
|
0d5edd35ea | ||
|
|
f3f51c3d9d | ||
|
|
1431916183 | ||
|
|
ae48008803 | ||
|
|
5664813931 | ||
|
|
3948af80b8 | ||
|
|
343d6f9aff | ||
|
|
51b379502f | ||
|
|
82142751de | ||
|
|
4e5755f218 | ||
|
|
2f4f29517e | ||
|
|
e8a073651f | ||
|
|
d23da7a612 | ||
|
|
1ae4938da3 | ||
|
|
b5e27d4d69 | ||
|
|
d74fc8d1ce | ||
|
|
c5d9a41967 | ||
|
|
7e3042c4f4 | ||
|
|
1291dadbd6 | ||
|
|
f645099a39 | ||
|
|
8f32038891 | ||
|
|
a6fb1dcdf9 | ||
|
|
f01b469f3f | ||
|
|
47884919c8 | ||
|
|
653a35496d | ||
|
|
9b14df6b27 | ||
|
|
09813f3c5a | ||
|
|
7d76b7e4c2 | ||
|
|
47b5dea95c | ||
|
|
b0df63f1e8 | ||
|
|
3c4209b676 | ||
|
|
d51b9a8e0e | ||
|
|
3b56125bd2 | ||
|
|
6e69e39007 | ||
|
|
97208cb329 | ||
|
|
414a4a97fb | ||
|
|
2a809f96c4 | ||
|
|
17799b03f3 | ||
|
|
efc55fc648 | ||
|
|
22bd41211a | ||
|
|
4cb0171bd0 | ||
|
|
ce6c5af72c | ||
|
|
171575256c | ||
|
|
d3664dcf78 | ||
|
|
0788981783 | ||
|
|
aeee7c02d5 | ||
|
|
6e49a50461 | ||
|
|
fbc99827ab | ||
|
|
3b134320c4 | ||
|
|
c315e5016d | ||
|
|
7aebd6ed57 | ||
|
|
1a8ca06495 | ||
|
|
a27ae7e4a6 | ||
|
|
dd0ab897aa | ||
|
|
00ad92fb6d | ||
|
|
f84128ecbd | ||
|
|
ba89e0f4e3 | ||
|
|
832d36a4d4 | ||
|
|
0b437b3bfb | ||
|
|
7b50ec4630 | ||
|
|
4ddb13abac | ||
|
|
d2ac081177 | ||
|
|
9692fbf15b | ||
|
|
be966488e8 | ||
|
|
0ce613264f | ||
|
|
34634d776e | ||
|
|
673ded3819 | ||
|
|
b9a94ad3ae | ||
|
|
3f80b330cc | ||
|
|
01632d538c | ||
|
|
b0a9b1cd09 | ||
|
|
1f772081fd | ||
|
|
4ae54dbaad | ||
|
|
e47f4ff731 | ||
|
|
465369d79e | ||
|
|
15ddf6ff20 | ||
|
|
16a753bd95 | ||
|
|
c15103636c | ||
|
|
8a5f82ee7d | ||
|
|
5ec33ec821 | ||
|
|
ab7d383cf1 | ||
|
|
184e9a5c93 | ||
|
|
c4f5363cde | ||
|
|
002882cebf | ||
|
|
1cb3ec0c7b | ||
|
|
6bf325c6f6 | ||
|
|
09c7ff9e8b | ||
|
|
c2a94b61bf | ||
|
|
1db16232de | ||
|
|
3da681a6b1 | ||
|
|
a79b3e7852 | ||
|
|
b1099e6974 | ||
|
|
4f6e06131c | ||
|
|
19f69e9e6c | ||
|
|
01481da773 | ||
|
|
3e8f7e1cf1 | ||
|
|
5da69646f2 | ||
|
|
3cac01583b | ||
|
|
d16a26605e | ||
|
|
a4f692c788 | ||
|
|
9f01206c57 | ||
|
|
d34fc551ed | ||
|
|
7aa5f0d258 | ||
|
|
276a34bb66 | ||
|
|
0d820eda12 | ||
|
|
1991c1b677 | ||
|
|
459404e3f0 | ||
|
|
badc623a3c | ||
|
|
8e39f92cb7 | ||
|
|
3ff4885973 | ||
|
|
f9d45f7657 | ||
|
|
789ff9df5c | ||
|
|
a2064ed16b | ||
|
|
b3d06c0868 | ||
|
|
db1a6b3e38 | ||
|
|
8390237cc4 | ||
|
|
9967eae7bb | ||
|
|
b3be7d1c6f | ||
|
|
33ccb5dbb2 | ||
|
|
5b90c0d695 | ||
|
|
472a660239 | ||
|
|
ab67536d9a | ||
|
|
ee85fb3aec | ||
|
|
214b6f4358 | ||
|
|
af0d092054 | ||
|
|
b07903c3e9 | ||
|
|
ffa4c6bf09 | ||
|
|
b4125fa56c | ||
|
|
2c72302087 | ||
|
|
0fa52bc64f | ||
|
|
f55a80b15a | ||
|
|
0735483321 | ||
|
|
53fc2c7c21 | ||
|
|
f22133c3c5 | ||
|
|
2d5a6d6583 | ||
|
|
dc4adf2836 | ||
|
|
dd6e254e4f | ||
|
|
4c028c1659 | ||
|
|
d332a6777a | ||
|
|
378251f229 | ||
|
|
b6b9b903ed | ||
|
|
143d68cfd5 | ||
|
|
a31eac1426 | ||
|
|
797196f7fc | ||
|
|
c9d0bc81dd | ||
|
|
b5448ff607 | ||
|
|
5ebd363d5d | ||
|
|
1d439e673e | ||
|
|
0b7b7656b2 | ||
|
|
eb270df835 | ||
|
|
ff73dd2183 | ||
|
|
e043a03eb6 | ||
|
|
3cb4e8e373 | ||
|
|
7e6de528b4 | ||
|
|
df901c30ef | ||
|
|
13856b33ec | ||
|
|
a3a1c6f4c8 | ||
|
|
638998a861 | ||
|
|
6e2ec89a05 | ||
|
|
af67de8aa6 | ||
|
|
425dac478e | ||
|
|
b15c4ecd10 | ||
|
|
d7f88cf3a4 | ||
|
|
5b7fbcd9b8 | ||
|
|
7af64b0782 | ||
|
|
b84c70e811 | ||
|
|
f5b245c72d | ||
|
|
4307183817 | ||
|
|
aeb32783d6 | ||
|
|
3927b3bf27 | ||
|
|
da9d2f9417 | ||
|
|
1cec48e8f8 | ||
|
|
f1105393da | ||
|
|
dc7047e3c2 | ||
|
|
08b2945623 | ||
|
|
cbcc223150 | ||
|
|
9830f21e4a | ||
|
|
5f49567bf7 | ||
|
|
6154ae7342 | ||
|
|
978f3a3682 | ||
|
|
1f0961d574 | ||
|
|
a101252701 | ||
|
|
c96c29b1e3 | ||
|
|
3b0fc180ff | ||
|
|
9b8bfdf33c | ||
|
|
4328831fcd | ||
|
|
e5b3df41e9 | ||
|
|
cf5259e218 | ||
|
|
f24b6a520c | ||
|
|
4140163ab2 | ||
|
|
7afde0e93f | ||
|
|
d27a571882 | ||
|
|
1c70e3be25 | ||
|
|
1819f64467 | ||
|
|
9852e588c1 | ||
|
|
71a1ea481b | ||
|
|
9e32f0d778 | ||
|
|
4478174dc2 | ||
|
|
07553476d4 | ||
|
|
1773283456 | ||
|
|
221ab51d90 | ||
|
|
43e0dd922b | ||
|
|
b3262652c3 | ||
|
|
0cfa2b8c20 |
@@ -80,7 +80,6 @@ commands:
|
|||||||
libpulse-devel
|
libpulse-devel
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
gstreamer-plugins-base-devel
|
gstreamer-plugins-base-devel
|
||||||
libxine-devel
|
|
||||||
vlc-devel
|
vlc-devel
|
||||||
libQt5Core-devel
|
libQt5Core-devel
|
||||||
libQt5Gui-devel
|
libQt5Gui-devel
|
||||||
@@ -96,9 +95,7 @@ commands:
|
|||||||
libqt5-linguist-devel
|
libqt5-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
@@ -150,8 +147,6 @@ commands:
|
|||||||
taglib-devel
|
taglib-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libusbmuxd-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
fftw-devel
|
fftw-devel
|
||||||
@@ -160,141 +155,6 @@ commands:
|
|||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
|
|
||||||
|
|
||||||
install_centos_dependencies:
|
|
||||||
description: Install CentOS dependencies
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Install epel-release
|
|
||||||
command: dnf install -y epel-release
|
|
||||||
- run:
|
|
||||||
name: Install epel-release-latest-8.noarch.rpm
|
|
||||||
command: dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
|
|
||||||
- run:
|
|
||||||
name: Install config-manager
|
|
||||||
command: dnf install -y 'dnf-command(config-manager)'
|
|
||||||
- run:
|
|
||||||
name: PowerTools
|
|
||||||
command: dnf config-manager --set-enabled PowerTools
|
|
||||||
- run:
|
|
||||||
name: DNF Clean All
|
|
||||||
command: dnf clean all
|
|
||||||
- run:
|
|
||||||
name: Update packages
|
|
||||||
command: dnf update -y
|
|
||||||
- run:
|
|
||||||
name: Install CentOS dependencies
|
|
||||||
command: >
|
|
||||||
dnf install -y
|
|
||||||
glibc
|
|
||||||
gcc-c++
|
|
||||||
make
|
|
||||||
libtool
|
|
||||||
cmake3
|
|
||||||
rpmdevtools
|
|
||||||
redhat-lsb-core
|
|
||||||
git
|
|
||||||
man
|
|
||||||
tar
|
|
||||||
gettext
|
|
||||||
boost-devel
|
|
||||||
fuse-devel
|
|
||||||
dbus-devel
|
|
||||||
libnotify-devel
|
|
||||||
gnutls-devel
|
|
||||||
sqlite-devel
|
|
||||||
protobuf-devel
|
|
||||||
protobuf-compiler
|
|
||||||
alsa-lib-devel
|
|
||||||
pulseaudio-libs-devel
|
|
||||||
qt5-devel
|
|
||||||
qt5-qtbase-devel
|
|
||||||
qt5-qtx11extras-devel
|
|
||||||
qt5-qttools-devel
|
|
||||||
fftw-devel
|
|
||||||
libchromaprint-devel
|
|
||||||
libcdio-devel
|
|
||||||
libgpod-devel
|
|
||||||
libplist-devel
|
|
||||||
libusbmuxd-devel
|
|
||||||
libmtp-devel
|
|
||||||
libjpeg-devel
|
|
||||||
cairo-devel
|
|
||||||
dbus-x11
|
|
||||||
xorg-x11-server-Xvfb
|
|
||||||
xorg-x11-xauth
|
|
||||||
vim-common
|
|
||||||
desktop-file-utils
|
|
||||||
libappstream-glib
|
|
||||||
appstream-data
|
|
||||||
hicolor-icon-theme
|
|
||||||
python3-pip
|
|
||||||
python3-devel
|
|
||||||
gstreamer1-devel
|
|
||||||
gstreamer1-plugins-base-devel
|
|
||||||
|
|
||||||
|
|
||||||
install_mageia_dependencies:
|
|
||||||
description: Install Mageia dependencies
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Update packages
|
|
||||||
command: urpmi.update -a
|
|
||||||
- run:
|
|
||||||
name: Configure auto update
|
|
||||||
command: urpmi --auto-update
|
|
||||||
- run:
|
|
||||||
name: Install dependencies
|
|
||||||
command: >
|
|
||||||
urpmi --force
|
|
||||||
urpmi-debuginfo-install
|
|
||||||
git
|
|
||||||
tar
|
|
||||||
rpmdevtools
|
|
||||||
make
|
|
||||||
cmake
|
|
||||||
glibc
|
|
||||||
binutils
|
|
||||||
gcc-c++
|
|
||||||
man
|
|
||||||
gettext
|
|
||||||
notification-daemon
|
|
||||||
dbus-devel
|
|
||||||
libgnutls-devel
|
|
||||||
lib64boost-devel
|
|
||||||
lib64protobuf-devel
|
|
||||||
protobuf-compiler
|
|
||||||
lib64sqlite3-devel
|
|
||||||
lib64alsa2-devel
|
|
||||||
lib64pulseaudio-devel
|
|
||||||
lib64notify-devel
|
|
||||||
lib64qt5core-devel
|
|
||||||
lib64qt5gui-devel
|
|
||||||
lib64qt5widgets-devel
|
|
||||||
lib64qt5network-devel
|
|
||||||
lib64qt5concurrent-devel
|
|
||||||
lib64qt5sql-devel
|
|
||||||
lib64qt5dbus-devel
|
|
||||||
lib64qt5x11extras-devel
|
|
||||||
lib64qt5help-devel
|
|
||||||
libqt5test-devel
|
|
||||||
lib64gstreamer1.0-devel
|
|
||||||
lib64gstreamer-plugins-base1.0-devel
|
|
||||||
lib64cdio-devel
|
|
||||||
lib64gpod-devel
|
|
||||||
lib64plist-devel
|
|
||||||
lib64usbmuxd-devel
|
|
||||||
lib64mtp-devel
|
|
||||||
lib64raw1394-devel
|
|
||||||
lib64chromaprint-devel
|
|
||||||
libfftw-devel
|
|
||||||
desktop-file-utils
|
|
||||||
appstream-util
|
|
||||||
libappstream-glib8
|
|
||||||
hicolor-icon-theme
|
|
||||||
qt5ct
|
|
||||||
lib64mesaegl1
|
|
||||||
|
|
||||||
|
|
||||||
install_debian_dependencies:
|
install_debian_dependencies:
|
||||||
description: Install Debian dependencies
|
description: Install Debian dependencies
|
||||||
steps:
|
steps:
|
||||||
@@ -303,6 +163,7 @@ commands:
|
|||||||
command: >
|
command: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -336,9 +197,6 @@ commands:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
|
|
||||||
|
|
||||||
install_ubuntu_dependencies:
|
install_ubuntu_dependencies:
|
||||||
@@ -354,6 +212,7 @@ commands:
|
|||||||
command: >
|
command: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -390,16 +249,13 @@ commands:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build_source:
|
build_source:
|
||||||
docker:
|
docker:
|
||||||
- image: opensuse/leap:15.1
|
- image: opensuse/leap:15.2
|
||||||
steps:
|
steps:
|
||||||
- install_opensuse_dependencies
|
- install_opensuse_dependencies
|
||||||
- checkout
|
- checkout
|
||||||
@@ -407,24 +263,6 @@ jobs:
|
|||||||
- build_source
|
- build_source
|
||||||
|
|
||||||
|
|
||||||
build_opensuse_tumbleweed:
|
|
||||||
docker:
|
|
||||||
- image: opensuse/tumbleweed
|
|
||||||
environment:
|
|
||||||
RPM_BUILD_NCPUS: "2"
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Update packages
|
|
||||||
command: zypper --non-interactive --gpg-auto-import-keys ref
|
|
||||||
- run:
|
|
||||||
name: Upgrade packages
|
|
||||||
command: zypper --non-interactive --gpg-auto-import-keys dup
|
|
||||||
- install_opensuse_dependencies
|
|
||||||
- checkout
|
|
||||||
- cmake
|
|
||||||
- build_source
|
|
||||||
- build_rpm
|
|
||||||
|
|
||||||
build_opensuse_lp151:
|
build_opensuse_lp151:
|
||||||
docker:
|
docker:
|
||||||
- image: opensuse/leap:15.1
|
- image: opensuse/leap:15.1
|
||||||
@@ -450,18 +288,6 @@ jobs:
|
|||||||
- build_rpm
|
- build_rpm
|
||||||
|
|
||||||
|
|
||||||
build_fedora_31:
|
|
||||||
docker:
|
|
||||||
- image: fedora:31
|
|
||||||
environment:
|
|
||||||
RPM_BUILD_NCPUS: "2"
|
|
||||||
steps:
|
|
||||||
- install_fedora_dependencies
|
|
||||||
- checkout
|
|
||||||
- cmake
|
|
||||||
- build_source
|
|
||||||
- build_rpm
|
|
||||||
|
|
||||||
build_fedora_32:
|
build_fedora_32:
|
||||||
docker:
|
docker:
|
||||||
- image: fedora:32
|
- image: fedora:32
|
||||||
@@ -474,42 +300,19 @@ jobs:
|
|||||||
- build_source
|
- build_source
|
||||||
- build_rpm
|
- build_rpm
|
||||||
|
|
||||||
|
build_fedora_33:
|
||||||
build_centos_8:
|
|
||||||
docker:
|
docker:
|
||||||
- image: centos:8
|
- image: fedora:33
|
||||||
environment:
|
environment:
|
||||||
RPM_BUILD_NCPUS: "2"
|
RPM_BUILD_NCPUS: "2"
|
||||||
steps:
|
steps:
|
||||||
- install_centos_dependencies
|
- install_fedora_dependencies
|
||||||
- checkout
|
- checkout
|
||||||
- cmake
|
- cmake
|
||||||
- build_source
|
- build_source
|
||||||
- build_rpm
|
- build_rpm
|
||||||
|
|
||||||
|
|
||||||
build_mageia_7:
|
|
||||||
docker:
|
|
||||||
- image: mageia:7
|
|
||||||
environment:
|
|
||||||
RPM_BUILD_NCPUS: "2"
|
|
||||||
steps:
|
|
||||||
- install_mageia_dependencies
|
|
||||||
- checkout
|
|
||||||
- cmake
|
|
||||||
- build_source
|
|
||||||
- build_rpm
|
|
||||||
|
|
||||||
|
|
||||||
build_debian_stretch:
|
|
||||||
docker:
|
|
||||||
- image: debian:stretch
|
|
||||||
steps:
|
|
||||||
- install_debian_dependencies
|
|
||||||
- checkout
|
|
||||||
- cmake
|
|
||||||
- build_deb
|
|
||||||
|
|
||||||
build_debian_buster:
|
build_debian_buster:
|
||||||
docker:
|
docker:
|
||||||
- image: debian:buster
|
- image: debian:buster
|
||||||
@@ -567,10 +370,6 @@ workflows:
|
|||||||
only: /.*/
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
- build_opensuse_tumbleweed:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build_opensuse_lp151:
|
- build_opensuse_lp151:
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
@@ -581,32 +380,16 @@ workflows:
|
|||||||
only: /.*/
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
- build_fedora_31:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build_fedora_32:
|
- build_fedora_32:
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
- build_fedora_33:
|
||||||
|
|
||||||
- build_mageia_7:
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
- build_centos_8:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
|
|
||||||
|
|
||||||
- build_debian_stretch:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build_debian_buster:
|
- build_debian_buster:
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
105
.clang-format
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
BasedOnStyle: Chromium
|
||||||
|
Language: Cpp
|
||||||
|
Standard: Cpp11
|
||||||
|
AccessModifierOffset: -1
|
||||||
|
AlignAfterOpenBracket: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignEscapedNewlines: true
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: true
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortLambdasOnASingleLine: true
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: No
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BreakBeforeBraces: false
|
||||||
|
BreakBeforeBinaryOperators: false
|
||||||
|
BreakBeforeBraces: Stroustrup
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 0
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 2
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: true
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: true
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 100
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PenaltyBreakAssignment: 0
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 0
|
||||||
|
PenaltyBreakComment: 0
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 0
|
||||||
|
PenaltyBreakTemplateDeclaration: 0
|
||||||
|
PenaltyExcessCharacter: 0
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 0
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterFunction: false
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: true
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
|||||||
github: jonaski
|
github: jonaski
|
||||||
|
patreon: jonaskvinge
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
For general technical questions and help with technical issues please use the forum on https://forum.strawberrymusicplayer.org/
|
For technical issues, questions and feature suggestions/requests please use the forum on https://forum.strawberrymusicplayer.org/
|
||||||
|
|
||||||
|
Check the Changelog to see if the issue is already fixed:
|
||||||
|
https://github.com/strawberrymusicplayer/strawberry/blob/master/Changelog
|
||||||
|
|
||||||
|
If it's fixed, try the latest development build from: https://builds.strawberrymusicplayer.org/
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
517
.github/workflows/ccpp.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
name: Create source tarball
|
name: Create source tarball
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: opensuse/leap:15.1
|
image: opensuse/leap:15.2
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
- name: Update packages
|
- name: Update packages
|
||||||
@@ -38,7 +38,6 @@ jobs:
|
|||||||
libpulse-devel
|
libpulse-devel
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
gstreamer-plugins-base-devel
|
gstreamer-plugins-base-devel
|
||||||
libxine-devel
|
|
||||||
vlc-devel
|
vlc-devel
|
||||||
libQt5Core-devel
|
libQt5Core-devel
|
||||||
libQt5Gui-devel
|
libQt5Gui-devel
|
||||||
@@ -54,9 +53,7 @@ jobs:
|
|||||||
libqt5-linguist-devel
|
libqt5-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
@@ -109,7 +106,6 @@ jobs:
|
|||||||
libpulse-devel
|
libpulse-devel
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
gstreamer-plugins-base-devel
|
gstreamer-plugins-base-devel
|
||||||
libxine-devel
|
|
||||||
vlc-devel
|
vlc-devel
|
||||||
libQt5Core-devel
|
libQt5Core-devel
|
||||||
libQt5Gui-devel
|
libQt5Gui-devel
|
||||||
@@ -125,9 +121,7 @@ jobs:
|
|||||||
libqt5-linguist-devel
|
libqt5-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
@@ -188,7 +182,6 @@ jobs:
|
|||||||
libpulse-devel
|
libpulse-devel
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
gstreamer-plugins-base-devel
|
gstreamer-plugins-base-devel
|
||||||
libxine-devel
|
|
||||||
vlc-devel
|
vlc-devel
|
||||||
libQt5Core-devel
|
libQt5Core-devel
|
||||||
libQt5Gui-devel
|
libQt5Gui-devel
|
||||||
@@ -204,9 +197,7 @@ jobs:
|
|||||||
libqt5-linguist-devel
|
libqt5-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
@@ -232,8 +223,8 @@ jobs:
|
|||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
build_opensuse_tumbleweed:
|
build_opensuse_tumbleweed_qt5:
|
||||||
name: Build openSUSE Tumbleweed
|
name: Build openSUSE Tumbleweed Qt 5
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: opensuse/tumbleweed
|
image: opensuse/tumbleweed
|
||||||
@@ -269,7 +260,6 @@ jobs:
|
|||||||
libpulse-devel
|
libpulse-devel
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
gstreamer-plugins-base-devel
|
gstreamer-plugins-base-devel
|
||||||
libxine-devel
|
|
||||||
vlc-devel
|
vlc-devel
|
||||||
libQt5Core-devel
|
libQt5Core-devel
|
||||||
libQt5Gui-devel
|
libQt5Gui-devel
|
||||||
@@ -285,9 +275,7 @@ jobs:
|
|||||||
libqt5-linguist-devel
|
libqt5-linguist-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
desktop-file-utils
|
desktop-file-utils
|
||||||
update-desktop-files
|
update-desktop-files
|
||||||
@@ -299,7 +287,87 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT5=ON
|
||||||
|
- name: Create source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: ../dist/scripts/maketarball.sh
|
||||||
|
- name: Create RPM build sources directories
|
||||||
|
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||||
|
- name: Copy source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||||
|
- name: Build RPM
|
||||||
|
working-directory: build
|
||||||
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
|
build_opensuse_tumbleweed_qt6:
|
||||||
|
name: Build openSUSE Tumbleweed Qt 6
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: opensuse/tumbleweed
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
- name: Add Qt 6 repository
|
||||||
|
run: zypper -n ar -c -f -n 'repo-qt6' https://download.opensuse.org/repositories/home:/jonaski:/qt6/openSUSE_Tumbleweed/ repo-qt6
|
||||||
|
- name: Update packages
|
||||||
|
run: zypper --non-interactive --gpg-auto-import-keys ref
|
||||||
|
- name: Upgrade packages
|
||||||
|
run: zypper --non-interactive --gpg-auto-import-keys dup
|
||||||
|
- name: Install openSUSE dependencies
|
||||||
|
run: >
|
||||||
|
zypper --non-interactive --gpg-auto-import-keys install
|
||||||
|
lsb-release
|
||||||
|
rpm-build
|
||||||
|
git
|
||||||
|
tar
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
gcc
|
||||||
|
gcc-c++
|
||||||
|
gettext-tools
|
||||||
|
glibc-devel
|
||||||
|
libboost_headers-devel
|
||||||
|
boost-devel
|
||||||
|
glib2-devel
|
||||||
|
glib2-tools
|
||||||
|
dbus-1-devel
|
||||||
|
alsa-devel
|
||||||
|
libnotify-devel
|
||||||
|
libgnutls-devel
|
||||||
|
protobuf-devel
|
||||||
|
sqlite3-devel
|
||||||
|
libpulse-devel
|
||||||
|
gstreamer-devel
|
||||||
|
gstreamer-plugins-base-devel
|
||||||
|
vlc-devel
|
||||||
|
qt6-core-devel
|
||||||
|
qt6-gui-devel
|
||||||
|
qt6-widgets-devel
|
||||||
|
qt6-concurrent-devel
|
||||||
|
qt6-network-devel
|
||||||
|
qt6-sql-devel
|
||||||
|
qt6-dbus-devel
|
||||||
|
qt6-test-devel
|
||||||
|
qt6-x11extras-devel
|
||||||
|
qt6-base-common-devel
|
||||||
|
qt6-sql-sqlite
|
||||||
|
qt6-linguist-devel
|
||||||
|
libcdio-devel
|
||||||
|
libgpod-devel
|
||||||
|
libmtp-devel
|
||||||
|
libchromaprint-devel
|
||||||
|
desktop-file-utils
|
||||||
|
update-desktop-files
|
||||||
|
appstream-glib
|
||||||
|
hicolor-icon-theme
|
||||||
|
- name: Create Build Environment
|
||||||
|
shell: bash
|
||||||
|
run: cmake -E make_directory build
|
||||||
|
- name: Configure CMake
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
|
||||||
- name: Create source tarball
|
- name: Create source tarball
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: ../dist/scripts/maketarball.sh
|
run: ../dist/scripts/maketarball.sh
|
||||||
@@ -364,8 +432,160 @@ jobs:
|
|||||||
taglib-devel
|
taglib-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
libmtp-devel
|
||||||
libusbmuxd-devel
|
libchromaprint-devel
|
||||||
|
fftw-devel
|
||||||
|
desktop-file-utils
|
||||||
|
libappstream-glib
|
||||||
|
hicolor-icon-theme
|
||||||
|
- name: Create Build Environment
|
||||||
|
shell: bash
|
||||||
|
run: cmake -E make_directory build
|
||||||
|
- name: Configure CMake
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||||
|
- name: Create source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: ../dist/scripts/maketarball.sh
|
||||||
|
- name: Create RPM build sources directories
|
||||||
|
working-directory: build
|
||||||
|
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||||
|
- name: Copy source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
|
- name: Build RPM
|
||||||
|
working-directory: build
|
||||||
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
|
build_fedora_33:
|
||||||
|
name: Build Fedora 33
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: fedora:33
|
||||||
|
env:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
|
||||||
|
- name: Update packages
|
||||||
|
run: yum update --assumeyes
|
||||||
|
|
||||||
|
- name: Upgrade packages
|
||||||
|
run: yum upgrade --assumeyes
|
||||||
|
|
||||||
|
- name: Install Fedora dependencies
|
||||||
|
run: >
|
||||||
|
dnf install --assumeyes
|
||||||
|
@development-tools
|
||||||
|
redhat-lsb-core
|
||||||
|
git
|
||||||
|
glibc
|
||||||
|
gcc-c++
|
||||||
|
rpmdevtools
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
pkgconfig
|
||||||
|
glib
|
||||||
|
man
|
||||||
|
tar
|
||||||
|
gettext
|
||||||
|
openssh
|
||||||
|
boost-devel
|
||||||
|
dbus-devel
|
||||||
|
protobuf-devel
|
||||||
|
protobuf-compiler
|
||||||
|
sqlite-devel
|
||||||
|
alsa-lib-devel
|
||||||
|
pulseaudio-libs-devel
|
||||||
|
libnotify-devel
|
||||||
|
gnutls-devel
|
||||||
|
qt5-qtbase-devel
|
||||||
|
qt5-qtx11extras-devel
|
||||||
|
qt5-qttools-devel
|
||||||
|
gstreamer1-devel
|
||||||
|
gstreamer1-plugins-base-devel
|
||||||
|
taglib-devel
|
||||||
|
libcdio-devel
|
||||||
|
libgpod-devel
|
||||||
|
libmtp-devel
|
||||||
|
libchromaprint-devel
|
||||||
|
fftw-devel
|
||||||
|
desktop-file-utils
|
||||||
|
libappstream-glib
|
||||||
|
hicolor-icon-theme
|
||||||
|
- name: Create Build Environment
|
||||||
|
shell: bash
|
||||||
|
run: cmake -E make_directory build
|
||||||
|
- name: Configure CMake
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||||
|
- name: Create source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: ../dist/scripts/maketarball.sh
|
||||||
|
- name: Create RPM build sources directories
|
||||||
|
working-directory: build
|
||||||
|
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||||
|
- name: Copy source tarball
|
||||||
|
working-directory: build
|
||||||
|
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
|
- name: Build RPM
|
||||||
|
working-directory: build
|
||||||
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
|
build_fedora_34:
|
||||||
|
name: Build Fedora 34
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: fedora:34
|
||||||
|
env:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
|
||||||
|
- name: Update packages
|
||||||
|
run: yum update --assumeyes
|
||||||
|
|
||||||
|
- name: Upgrade packages
|
||||||
|
run: yum upgrade --assumeyes
|
||||||
|
|
||||||
|
- name: Install Fedora dependencies
|
||||||
|
run: >
|
||||||
|
dnf install --assumeyes
|
||||||
|
@development-tools
|
||||||
|
redhat-lsb-core
|
||||||
|
git
|
||||||
|
glibc
|
||||||
|
gcc-c++
|
||||||
|
rpmdevtools
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
pkgconfig
|
||||||
|
glib
|
||||||
|
man
|
||||||
|
tar
|
||||||
|
gettext
|
||||||
|
openssh
|
||||||
|
boost-devel
|
||||||
|
dbus-devel
|
||||||
|
protobuf-devel
|
||||||
|
protobuf-compiler
|
||||||
|
sqlite-devel
|
||||||
|
alsa-lib-devel
|
||||||
|
pulseaudio-libs-devel
|
||||||
|
libnotify-devel
|
||||||
|
gnutls-devel
|
||||||
|
qt5-qtbase-devel
|
||||||
|
qt5-qtx11extras-devel
|
||||||
|
qt5-qttools-devel
|
||||||
|
gstreamer1-devel
|
||||||
|
gstreamer1-plugins-base-devel
|
||||||
|
taglib-devel
|
||||||
|
libcdio-devel
|
||||||
|
libgpod-devel
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
fftw-devel
|
fftw-devel
|
||||||
@@ -403,15 +623,21 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
|
|
||||||
|
- name: Install dnf-plugins-core
|
||||||
|
run: dnf install -y dnf-plugins-core
|
||||||
- name: Install epel-release
|
- name: Install epel-release
|
||||||
run: dnf install -y epel-release
|
run: dnf install -y epel-release
|
||||||
|
|
||||||
- name: Install config-manager
|
- name: Install config-manager
|
||||||
run: dnf install -y 'dnf-command(config-manager)'
|
run: dnf install -y 'dnf-command(config-manager)'
|
||||||
- name: Enable PowerTools
|
|
||||||
run: dnf config-manager --set-enabled PowerTools
|
- name: Enable powertools
|
||||||
- name: DNF Clean All
|
run: dnf config-manager --set-enabled powertools
|
||||||
|
|
||||||
|
- name: Clean all
|
||||||
run: dnf clean all
|
run: dnf clean all
|
||||||
- name: DNF Update
|
|
||||||
|
- name: Update
|
||||||
run: dnf update -y
|
run: dnf update -y
|
||||||
|
|
||||||
- name: Install CentOS dependencies
|
- name: Install CentOS dependencies
|
||||||
@@ -437,10 +663,9 @@ jobs:
|
|||||||
gnutls-devel
|
gnutls-devel
|
||||||
sqlite-devel
|
sqlite-devel
|
||||||
protobuf-devel
|
protobuf-devel
|
||||||
protobuf-compiler
|
protobuf-c
|
||||||
alsa-lib-devel
|
alsa-lib-devel
|
||||||
pulseaudio-libs-devel
|
pulseaudio-libs-devel
|
||||||
qt5-devel
|
|
||||||
qt5-qtbase-devel
|
qt5-qtbase-devel
|
||||||
qt5-qtx11extras-devel
|
qt5-qtx11extras-devel
|
||||||
qt5-qttools-devel
|
qt5-qttools-devel
|
||||||
@@ -448,8 +673,6 @@ jobs:
|
|||||||
libchromaprint-devel
|
libchromaprint-devel
|
||||||
libcdio-devel
|
libcdio-devel
|
||||||
libgpod-devel
|
libgpod-devel
|
||||||
libplist-devel
|
|
||||||
libusbmuxd-devel
|
|
||||||
libmtp-devel
|
libmtp-devel
|
||||||
libjpeg-devel
|
libjpeg-devel
|
||||||
cairo-devel
|
cairo-devel
|
||||||
@@ -463,7 +686,6 @@ jobs:
|
|||||||
hicolor-icon-theme
|
hicolor-icon-theme
|
||||||
gstreamer1-devel
|
gstreamer1-devel
|
||||||
gstreamer1-plugins-base-devel
|
gstreamer1-plugins-base-devel
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -494,14 +716,14 @@ jobs:
|
|||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
|
|
||||||
- name: Update packages
|
- name: Update packages
|
||||||
run: urpmi.update -a
|
run: urpmi.update --auto -a
|
||||||
|
|
||||||
- name: Configure auto update
|
- name: Configure auto update
|
||||||
run: urpmi --auto-update
|
run: urpmi --auto --auto-update
|
||||||
|
|
||||||
- name: Install Mageia dependencies
|
- name: Install Mageia dependencies
|
||||||
run: >
|
run: >
|
||||||
urpmi --force
|
urpmi --auto --force
|
||||||
urpmi-debuginfo-install
|
urpmi-debuginfo-install
|
||||||
git
|
git
|
||||||
tar
|
tar
|
||||||
@@ -537,8 +759,6 @@ jobs:
|
|||||||
lib64gstreamer-plugins-base1.0-devel
|
lib64gstreamer-plugins-base1.0-devel
|
||||||
lib64cdio-devel
|
lib64cdio-devel
|
||||||
lib64gpod-devel
|
lib64gpod-devel
|
||||||
lib64plist-devel
|
|
||||||
lib64usbmuxd-devel
|
|
||||||
lib64mtp-devel
|
lib64mtp-devel
|
||||||
lib64raw1394-devel
|
lib64raw1394-devel
|
||||||
lib64chromaprint-devel
|
lib64chromaprint-devel
|
||||||
@@ -570,65 +790,6 @@ jobs:
|
|||||||
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
run: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
|
||||||
|
|
||||||
build_debian_stretch:
|
|
||||||
name: Build Debian Stretch
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: debian:stretch
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1.2.0
|
|
||||||
- name: Install Debian dependencies
|
|
||||||
run: >
|
|
||||||
apt-get update && apt-get install -y
|
|
||||||
build-essential
|
|
||||||
ssh
|
|
||||||
git
|
|
||||||
make
|
|
||||||
cmake
|
|
||||||
gcc
|
|
||||||
pkg-config
|
|
||||||
fakeroot
|
|
||||||
gettext
|
|
||||||
lsb-release
|
|
||||||
libglib2.0-dev
|
|
||||||
dpkg-dev
|
|
||||||
libdbus-1-dev
|
|
||||||
libboost-dev
|
|
||||||
libprotobuf-dev
|
|
||||||
protobuf-compiler
|
|
||||||
libsqlite3-dev
|
|
||||||
libgnutls28-dev
|
|
||||||
libasound2-dev
|
|
||||||
libpulse-dev
|
|
||||||
qtbase5-dev
|
|
||||||
qtbase5-dev-tools
|
|
||||||
qtbase5-private-dev
|
|
||||||
libqt5x11extras5-dev
|
|
||||||
qttools5-dev
|
|
||||||
libgstreamer1.0-dev
|
|
||||||
libgstreamer-plugins-base1.0-dev
|
|
||||||
gstreamer1.0-alsa
|
|
||||||
gstreamer1.0-pulseaudio
|
|
||||||
libchromaprint-dev
|
|
||||||
libfftw3-dev
|
|
||||||
libcdio-dev
|
|
||||||
libmtp-dev
|
|
||||||
libgpod-dev
|
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
|
||||||
shell: bash
|
|
||||||
run: cmake -E make_directory build
|
|
||||||
- name: Configure CMake
|
|
||||||
shell: bash
|
|
||||||
working-directory: build
|
|
||||||
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
|
||||||
- name: make deb
|
|
||||||
shell: bash
|
|
||||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
|
||||||
|
|
||||||
|
|
||||||
build_debian_buster:
|
build_debian_buster:
|
||||||
name: Build Debian Buster
|
name: Build Debian Buster
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -640,6 +801,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -673,9 +835,6 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -699,6 +858,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -732,9 +892,6 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -760,6 +917,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -796,9 +954,6 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -824,6 +979,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -860,9 +1016,6 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -888,6 +1041,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
apt-get update && apt-get install -y
|
apt-get update && apt-get install -y
|
||||||
build-essential
|
build-essential
|
||||||
|
dh-make
|
||||||
ssh
|
ssh
|
||||||
git
|
git
|
||||||
make
|
make
|
||||||
@@ -924,9 +1078,6 @@ jobs:
|
|||||||
libcdio-dev
|
libcdio-dev
|
||||||
libmtp-dev
|
libmtp-dev
|
||||||
libgpod-dev
|
libgpod-dev
|
||||||
libimobiledevice-dev
|
|
||||||
libplist-dev
|
|
||||||
libusbmuxd-dev
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -E make_directory build
|
run: cmake -E make_directory build
|
||||||
@@ -939,13 +1090,11 @@ jobs:
|
|||||||
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
run: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||||
|
|
||||||
|
|
||||||
build-macos:
|
build-macos-catalina:
|
||||||
name: Build macOS
|
name: Build macOS Catalina
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1.2.0
|
- uses: actions/checkout@v1.2.0
|
||||||
- name: Unlink python
|
|
||||||
run: brew unlink python@2
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: >
|
run: >
|
||||||
brew install
|
brew install
|
||||||
@@ -969,8 +1118,6 @@ jobs:
|
|||||||
gst-libav
|
gst-libav
|
||||||
libcdio
|
libcdio
|
||||||
libmtp
|
libmtp
|
||||||
libimobiledevice
|
|
||||||
libplist
|
|
||||||
create-dmg
|
create-dmg
|
||||||
taglib
|
taglib
|
||||||
|
|
||||||
@@ -999,10 +1146,79 @@ jobs:
|
|||||||
working-directory: build
|
working-directory: build
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make install
|
run: make install
|
||||||
#- name: Create DMG
|
- name: Create DMG
|
||||||
# working-directory: build
|
working-directory: build
|
||||||
# shell: bash
|
shell: bash
|
||||||
# run: make dmg
|
run: make dmg
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: upload-macos-catalina
|
||||||
|
path: build/strawberry-*-catalina.dmg
|
||||||
|
|
||||||
|
build-macos-bigsur:
|
||||||
|
name: Build macOS Big Sur
|
||||||
|
runs-on: macos-11.0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
- name: Install packages
|
||||||
|
run: >
|
||||||
|
brew install
|
||||||
|
glib
|
||||||
|
pkgconfig
|
||||||
|
boost
|
||||||
|
libffi
|
||||||
|
protobuf
|
||||||
|
protobuf-c
|
||||||
|
qt
|
||||||
|
gettext
|
||||||
|
gnutls
|
||||||
|
fftw
|
||||||
|
sqlite
|
||||||
|
chromaprint
|
||||||
|
gstreamer
|
||||||
|
gst-plugins-base
|
||||||
|
gst-plugins-good
|
||||||
|
gst-plugins-bad
|
||||||
|
gst-plugins-ugly
|
||||||
|
gst-libav
|
||||||
|
libcdio
|
||||||
|
libmtp
|
||||||
|
create-dmg
|
||||||
|
taglib
|
||||||
|
|
||||||
|
- name: Delete conflicting taglib system headers
|
||||||
|
shell: bash
|
||||||
|
run: rm -rf /usr/local/include/taglib
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
shell: bash
|
||||||
|
run: cmake -E make_directory build
|
||||||
|
- name: Configure CMake
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
|
||||||
|
Qt5_DIR: /usr/local/opt/qt5/lib/cmake
|
||||||
|
Qt5LinguistTools_DIR: /usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
|
||||||
|
GST_SCANNER_PATH: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
|
||||||
|
GST_PLUGIN_PATH: /usr/local/lib/gstreamer-1.0
|
||||||
|
working-directory: build
|
||||||
|
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLE=ON
|
||||||
|
- name: Build
|
||||||
|
working-directory: build
|
||||||
|
shell: bash
|
||||||
|
run: cmake --build . --config $BUILD_TYPE
|
||||||
|
- name: Install
|
||||||
|
working-directory: build
|
||||||
|
shell: bash
|
||||||
|
run: make install
|
||||||
|
- name: Create DMG
|
||||||
|
working-directory: build
|
||||||
|
shell: bash
|
||||||
|
run: make dmg
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: upload-macos-bigsur
|
||||||
|
path: build/strawberry-*-bigsur.dmg
|
||||||
|
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
@@ -1034,22 +1250,16 @@ jobs:
|
|||||||
-DENABLE_WIN32_CONSOLE=OFF
|
-DENABLE_WIN32_CONSOLE=OFF
|
||||||
-DENABLE_DBUS=OFF
|
-DENABLE_DBUS=OFF
|
||||||
-DENABLE_LIBGPOD=OFF
|
-DENABLE_LIBGPOD=OFF
|
||||||
-DENABLE_IMOBILEDEVICE=OFF
|
|
||||||
-DENABLE_LIBMTP=OFF
|
-DENABLE_LIBMTP=OFF
|
||||||
-DENABLE_XINE=OFF
|
|
||||||
-DProtobuf_PROTOC_EXECUTABLE=/usr/src/strawberry-mxe/usr/x86_64-pc-linux-gnu/bin/protoc
|
-DProtobuf_PROTOC_EXECUTABLE=/usr/src/strawberry-mxe/usr/x86_64-pc-linux-gnu/bin/protoc
|
||||||
|
|
||||||
- name: Run Make
|
- name: Run Make
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: make -j2
|
run: make -j2
|
||||||
|
|
||||||
- name: Strip executables
|
|
||||||
working-directory: build
|
|
||||||
run: /usr/src/strawberry-mxe/usr/bin/x86_64-w64-mingw32.shared-strip *.exe
|
|
||||||
|
|
||||||
- name: Create directories
|
- name: Create directories
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins xine-plugins nsisplugins
|
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins nsisplugins
|
||||||
|
|
||||||
- name: Copy GIO modules
|
- name: Copy GIO modules
|
||||||
working-directory: build
|
working-directory: build
|
||||||
@@ -1123,13 +1333,9 @@ jobs:
|
|||||||
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstlibav.dll
|
/usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/gstreamer-1.0/libgstlibav.dll
|
||||||
${GITHUB_WORKSPACE}/build/gstreamer-plugins/
|
${GITHUB_WORKSPACE}/build/gstreamer-plugins/
|
||||||
|
|
||||||
- name: Copy killproc.exe
|
- name: Copy extra binaries
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/killproc.exe .
|
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,gst-launch-1.0.exe} .
|
||||||
|
|
||||||
- name: Copy liborc-0.4-0.dll
|
|
||||||
working-directory: build
|
|
||||||
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/liborc-0.4-0.dll .
|
|
||||||
|
|
||||||
- name: Copy dependencies
|
- name: Copy dependencies
|
||||||
working-directory: build
|
working-directory: build
|
||||||
@@ -1145,6 +1351,10 @@ jobs:
|
|||||||
-F ./gstreamer-plugins
|
-F ./gstreamer-plugins
|
||||||
-R /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared
|
-R /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared
|
||||||
|
|
||||||
|
- name: Strip binaries
|
||||||
|
working-directory: build
|
||||||
|
run: find . -type f \( -iname \*.dll -o -iname \*.exe \) -exec /usr/src/strawberry-mxe/usr/bin/x86_64-w64-mingw32.shared-strip {} \;
|
||||||
|
|
||||||
- name: Copy nsis files
|
- name: Copy nsis files
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: cp ${GITHUB_WORKSPACE}/dist/windows/*.nsi ${GITHUB_WORKSPACE}/dist/windows/*.nsh ${GITHUB_WORKSPACE}/dist/windows/*.ico .
|
run: cp ${GITHUB_WORKSPACE}/dist/windows/*.nsi ${GITHUB_WORKSPACE}/dist/windows/*.nsh ${GITHUB_WORKSPACE}/dist/windows/*.ico .
|
||||||
@@ -1152,3 +1362,50 @@ jobs:
|
|||||||
- name: Build Windows installer
|
- name: Build Windows installer
|
||||||
working-directory: build
|
working-directory: build
|
||||||
run: makensis strawberry.nsi
|
run: makensis strawberry.nsi
|
||||||
|
|
||||||
|
|
||||||
|
upload-macos-catalina:
|
||||||
|
name: Upload macOS Catalina DMG
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
needs:
|
||||||
|
- build-macos-catalina
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: uploads
|
||||||
|
- name: Install SSH keys
|
||||||
|
uses: shimataro/ssh-key-action@v2
|
||||||
|
with:
|
||||||
|
known_hosts: ${{ secrets.KNOWN_HOSTS2 }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
- name: rsync
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
for i in $(find uploads -type f -name 'strawberry-*-catalina.dmg'); do
|
||||||
|
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/catalina/
|
||||||
|
done
|
||||||
|
|
||||||
|
upload-macos-bigsur:
|
||||||
|
name: Upload macOS Big Sur DMG
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
needs:
|
||||||
|
- build-macos-bigsur
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1.2.0
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: uploads
|
||||||
|
- name: Install SSH keys
|
||||||
|
uses: shimataro/ssh-key-action@v2
|
||||||
|
with:
|
||||||
|
known_hosts: ${{ secrets.KNOWN_HOSTS2 }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
- name: rsync
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
for i in $(find uploads -type f -name 'strawberry-*-bigsur.dmg'); do
|
||||||
|
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/bigsur/
|
||||||
|
done
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -104,7 +104,6 @@ Thumbs.db
|
|||||||
|
|
||||||
# Stuff in dist
|
# Stuff in dist
|
||||||
maketarball.sh
|
maketarball.sh
|
||||||
create-dmg.sh
|
|
||||||
changelog
|
changelog
|
||||||
PKGBUILD
|
PKGBUILD
|
||||||
|
|
||||||
|
|||||||
67
.travis.yml
@@ -1,53 +1,42 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
language: C++
|
language: C++
|
||||||
os:
|
os: osx
|
||||||
- osx
|
osx_image: xcode11.3
|
||||||
services:
|
compiler: clang
|
||||||
- docker
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||||
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
|
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
|
||||||
chmod 600 ~/.ssh/id_rsa ;
|
chmod 600 ~/.ssh/id_rsa ;
|
||||||
fi
|
fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
- git fetch --unshallow
|
||||||
docker build -f Dockerfile -t strawberry-build . || travis_terminate 1;
|
- git pull
|
||||||
docker run --name build -itd strawberry-build /bin/bash || travis_terminate 1;
|
- brew update
|
||||||
docker exec build git clone https://github.com/strawberrymusicplayer/strawberry;
|
- travis_wait 400 brew upgrade || echo "Failed"
|
||||||
fi
|
- travis_wait 400 brew upgrade || echo "Failed"
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
- brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib
|
||||||
git fetch --unshallow || travis_terminate 1;
|
- brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
|
||||||
git pull || travis_terminate 1;
|
- brew install libcdio libmtp
|
||||||
brew unlink python@2 || travis_terminate 1;
|
- brew install create-dmg
|
||||||
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib;
|
- brew cask install sparkle
|
||||||
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav;
|
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework
|
||||||
brew install libcdio libmtp libimobiledevice libplist;
|
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM
|
||||||
brew install create-dmg;
|
- export Qt5_DIR=/usr/local/opt/qt5/lib/cmake
|
||||||
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
|
- export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
|
||||||
export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools;
|
- ls /usr/local/lib/gstreamer-1.0
|
||||||
export PATH="/usr/local/opt/gettext/bin:$PATH";
|
|
||||||
export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig/:/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH";
|
|
||||||
ls /usr/local/lib/gstreamer-1.0;
|
|
||||||
fi
|
|
||||||
before_script:
|
before_script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild ; fi
|
- mkdir build
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON ; fi
|
- cd build
|
||||||
|
- cmake .. -DUSE_BUNDLE=ON
|
||||||
script:
|
script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
- make -j8
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
- make install
|
||||||
make -j8 || travis_terminate 1;
|
- make dmg2
|
||||||
make install || travis_terminate 1;
|
|
||||||
make dmg;
|
|
||||||
fi
|
|
||||||
after_success:
|
after_success:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry*.dmg; fi
|
- ls -lh strawberry*.dmg
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||||
if [[ "$TRAVIS_BRANCH" == "master" ]]; then
|
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
|
||||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos;
|
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
|
||||||
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
|
|
||||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos;
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
31
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -10,38 +10,37 @@ endif()
|
|||||||
|
|
||||||
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
||||||
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
||||||
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
|
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||||
|
else()
|
||||||
|
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||||
|
endif()
|
||||||
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
||||||
target_include_directories(singleapplication SYSTEM PRIVATE
|
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
${Qt5Widgets_INCLUDE_DIRS}
|
|
||||||
${Qt5Network_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
target_include_directories(singleapplication PRIVATE
|
target_include_directories(singleapplication PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
target_link_libraries(singleapplication PRIVATE
|
target_link_libraries(singleapplication PRIVATE
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${Qt5Widgets_LIBRARIES}
|
${QtWidgets_LIBRARIES}
|
||||||
${Qt5Network_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
||||||
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
||||||
QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
|
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||||
|
else()
|
||||||
|
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||||
|
endif()
|
||||||
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||||
target_include_directories(singlecoreapplication SYSTEM PRIVATE
|
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
${Qt5Network_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
target_include_directories(singlecoreapplication PRIVATE
|
target_include_directories(singlecoreapplication PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
target_link_libraries(singlecoreapplication PRIVATE
|
target_link_libraries(singlecoreapplication PRIVATE
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${Qt5Network_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||||
|
|||||||
186
3rdparty/singleapplication/singleapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -35,113 +35,131 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QCoreApplication>
|
#include <QApplication>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
||||||
# include <QRandomGenerator>
|
|
||||||
#else
|
|
||||||
# include <QDateTime>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
#include "singleapplication_p.h"
|
#include "singleapplication_p.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor.
|
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||||
* Checks and fires up LocalServer or closes the program if another instance already exists
|
|
||||||
* @param argc
|
* @param argc
|
||||||
* @param argv
|
* @param argv
|
||||||
* @param {bool} allowSecondaryInstances
|
* @param allowSecondary Whether to enable secondary instance support
|
||||||
|
* @param options Optional flags to toggle specific behaviour
|
||||||
|
* @param timeout Maximum time blocking functions are allowed during app load
|
||||||
*/
|
*/
|
||||||
SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||||
: app_t(argc, argv), d_ptr(new SingleApplicationPrivate(this)) {
|
: app_t(argc, argv),
|
||||||
|
d_ptr(new SingleApplicationPrivate(this)) {
|
||||||
|
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
|
|
||||||
// Store the current mode of the program
|
// Store the current mode of the program
|
||||||
d->options = options;
|
d->options_ = options;
|
||||||
|
|
||||||
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||||
d->genBlockServerName();
|
d->genBlockServerName();
|
||||||
|
|
||||||
|
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||||
|
d->randomSleep();
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// By explicitly attaching it and then deleting it we make sure that the
|
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||||
// memory is deleted even after the process has crashed on Unix.
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
d->memory = new QSharedMemory(d->blockServerName);
|
d->memory_->attach();
|
||||||
d->memory->attach();
|
delete d->memory_;
|
||||||
delete d->memory;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Guarantee thread safe behaviour with a shared memory block.
|
// Guarantee thread safe behaviour with a shared memory block.
|
||||||
d->memory = new QSharedMemory(d->blockServerName);
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
|
|
||||||
// Create a shared memory block
|
// Create a shared memory block
|
||||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||||
// Initialize the shared memory block
|
// Initialize the shared memory block
|
||||||
d->memory->lock();
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleApplication: Unable to lock memory block after create.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
d->initializeMemoryBlock();
|
d->initializeMemoryBlock();
|
||||||
d->memory->unlock();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Attempt to attach to the memory segment
|
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||||
if (! d->memory->attach()) {
|
// Attempt to attach to the memory segment
|
||||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
if (!d->memory_->attach()) {
|
||||||
qCritical() << d->memory->errorString();
|
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||||
delete d;
|
abortSafely();
|
||||||
::exit(EXIT_FAILURE);
|
}
|
||||||
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCritical() << "SingleApplication: Unable to create block.";
|
||||||
|
abortSafely();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||||
QElapsedTimer time;
|
QElapsedTimer time;
|
||||||
time.start();
|
time.start();
|
||||||
|
|
||||||
// Make sure the shared memory block is initialised and in consistent state
|
// Make sure the shared memory block is initialised and in consistent state
|
||||||
while (true) {
|
forever {
|
||||||
d->memory->lock();
|
// If the shared memory block's checksum is valid continue
|
||||||
|
|
||||||
if (d->blockChecksum() == inst->checksum) break;
|
if (d->blockChecksum() == inst->checksum) break;
|
||||||
|
|
||||||
|
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||||
if (time.elapsed() > 5000) {
|
if (time.elapsed() > 5000) {
|
||||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||||
d->initializeMemoryBlock();
|
d->initializeMemoryBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
// Otherwise wait for a random period and try again.
|
||||||
|
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||||
// Random sleep here limits the probability of a collision between two racing apps
|
if (!d->memory_->unlock()) {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
|
||||||
QThread::sleep(QRandomGenerator::global()->bounded(8u, 18u));
|
qDebug() << d->memory_->errorString();
|
||||||
#else
|
}
|
||||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
d->randomSleep();
|
||||||
QThread::sleep(8 + static_cast<unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
if (!d->memory_->lock()) {
|
||||||
#endif
|
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->primary == false) {
|
if (inst->primary == false) {
|
||||||
d->startPrimary();
|
d->startPrimary();
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if another instance can be started
|
// Check if another instance can be started
|
||||||
if (allowSecondary) {
|
if (allowSecondary) {
|
||||||
inst->secondary += 1;
|
|
||||||
inst->checksum = d->blockChecksum();
|
|
||||||
d->instanceNumber = inst->secondary;
|
|
||||||
d->startSecondary();
|
d->startSecondary();
|
||||||
if (d->options & Mode::SecondaryNotification) {
|
if (d->options_ & Mode::SecondaryNotification) {
|
||||||
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
|
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
|
||||||
}
|
}
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
|
||||||
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
|
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
|
||||||
|
|
||||||
@@ -151,34 +169,73 @@ SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondar
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
SingleApplication::~SingleApplication() {
|
SingleApplication::~SingleApplication() {
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current application instance is primary.
|
||||||
|
* @return Returns true if the instance is primary, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleApplication::isPrimary() {
|
bool SingleApplication::isPrimary() {
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
return d->server != nullptr;
|
return d->server_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current application instance is secondary.
|
||||||
|
* @return Returns true if the instance is secondary, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleApplication::isSecondary() {
|
bool SingleApplication::isSecondary() {
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
return d->server == nullptr;
|
return d->server_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||||
|
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||||
|
* @return Returns a unique instance id.
|
||||||
|
*/
|
||||||
quint32 SingleApplication::instanceId() {
|
quint32 SingleApplication::instanceId() {
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
return d->instanceNumber;
|
return d->instanceNumber_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||||
|
* Especially useful when SingleApplication is coupled with OS. specific APIs.
|
||||||
|
* @return Returns the primary instance PID.
|
||||||
|
*/
|
||||||
qint64 SingleApplication::primaryPid() {
|
qint64 SingleApplication::primaryPid() {
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
return d->primaryPid();
|
return d->primaryPid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username the primary instance is running as.
|
||||||
|
* @return Returns the username the primary instance is running as.
|
||||||
|
*/
|
||||||
|
QString SingleApplication::primaryUser() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->primaryUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username the current instance is running as.
|
||||||
|
* @return Returns the username the current instance is running as.
|
||||||
|
*/
|
||||||
|
QString SingleApplication::currentUser() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends message to the Primary Instance.
|
||||||
|
* @param message The message to send.
|
||||||
|
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||||
|
* @return true if the message was sent successfuly, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||||
|
|
||||||
Q_D(SingleApplication);
|
Q_D(SingleApplication);
|
||||||
@@ -187,11 +244,26 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
|||||||
if (isPrimary()) return false;
|
if (isPrimary()) return false;
|
||||||
|
|
||||||
// Make sure the socket is connected
|
// Make sure the socket is connected
|
||||||
d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect);
|
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect))
|
||||||
|
return false;
|
||||||
|
|
||||||
d->socket->write(message);
|
d->socket_->write(message);
|
||||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||||
d->socket->flush();
|
d->socket_->flush();
|
||||||
return dataWritten;
|
return dataWritten;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the shared memory block and exits with a failure.
|
||||||
|
* This function halts program execution.
|
||||||
|
*/
|
||||||
|
void SingleApplication::abortSafely() {
|
||||||
|
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
|
||||||
|
qCritical() << "SingleApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||||
|
delete d;
|
||||||
|
::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
41
3rdparty/singleapplication/singleapplication.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -42,16 +42,15 @@
|
|||||||
class SingleApplicationPrivate;
|
class SingleApplicationPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The SingleApplication class handles multipe instances of the same
|
* @brief The SingleApplication class handles multipe instances of the same Application
|
||||||
* Application
|
* @see QApplication
|
||||||
* @see QCoreApplication
|
|
||||||
*/
|
*/
|
||||||
class SingleApplication : public QApplication {
|
class SingleApplication : public QApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
typedef QApplication app_t;
|
typedef QApplication app_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Mode of operation of SingleApplication.
|
* @brief Mode of operation of SingleApplication.
|
||||||
* Whether the block should be user-wide or system-wide and whether the
|
* Whether the block should be user-wide or system-wide and whether the
|
||||||
@@ -63,11 +62,11 @@ public:
|
|||||||
* @enum
|
* @enum
|
||||||
*/
|
*/
|
||||||
enum Mode {
|
enum Mode {
|
||||||
User = 1 << 0,
|
User = 1 << 0,
|
||||||
System = 1 << 1,
|
System = 1 << 1,
|
||||||
SecondaryNotification = 1 << 2,
|
SecondaryNotification = 1 << 2,
|
||||||
ExcludeAppVersion = 1 << 3,
|
ExcludeAppVersion = 1 << 3,
|
||||||
ExcludeAppPath = 1 << 4
|
ExcludeAppPath = 1 << 4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Options, Mode)
|
Q_DECLARE_FLAGS(Options, Mode)
|
||||||
|
|
||||||
@@ -91,7 +90,7 @@ public:
|
|||||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||||
*/
|
*/
|
||||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
explicit SingleApplication(int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000);
|
||||||
~SingleApplication() override;
|
~SingleApplication() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,6 +117,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the username of the user running the primary instance
|
||||||
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString primaryUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the username of the current user
|
||||||
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString currentUser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sends a message to the primary instance. Returns true on success.
|
* @brief Sends a message to the primary instance. Returns true on success.
|
||||||
* @param {int} timeout - Timeout for connecting
|
* @param {int} timeout - Timeout for connecting
|
||||||
@@ -125,18 +136,18 @@ public:
|
|||||||
* @note sendMessage() will return false if invoked from the primary
|
* @note sendMessage() will return false if invoked from the primary
|
||||||
* instance.
|
* instance.
|
||||||
*/
|
*/
|
||||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void instanceStarted();
|
void instanceStarted();
|
||||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SingleApplicationPrivate *d_ptr;
|
SingleApplicationPrivate *d_ptr;
|
||||||
Q_DECLARE_PRIVATE(SingleApplication)
|
Q_DECLARE_PRIVATE(SingleApplication)
|
||||||
|
void abortSafely();
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||||
|
|
||||||
#endif // SINGLEAPPLICATION_H
|
#endif // SINGLEAPPLICATION_H
|
||||||
|
|||||||
355
3rdparty/singleapplication/singleapplication_p.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
# include <pwd.h>
|
# include <pwd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@@ -52,6 +54,12 @@
|
|||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
# include <QRandomGenerator>
|
||||||
|
#else
|
||||||
|
# include <QDateTime>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
#include "singleapplication_p.h"
|
#include "singleapplication_p.h"
|
||||||
@@ -61,33 +69,73 @@
|
|||||||
# include <lmcons.h>
|
# include <lmcons.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *_q_ptr)
|
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *ptr)
|
||||||
: q_ptr(_q_ptr),
|
: q_ptr(ptr),
|
||||||
memory(nullptr),
|
memory_(nullptr),
|
||||||
socket(nullptr),
|
socket_(nullptr),
|
||||||
server(nullptr),
|
server_(nullptr),
|
||||||
instanceNumber(-1)
|
instanceNumber_(-1) {}
|
||||||
{}
|
|
||||||
|
|
||||||
SingleApplicationPrivate::~SingleApplicationPrivate() {
|
SingleApplicationPrivate::~SingleApplicationPrivate() {
|
||||||
|
|
||||||
if (socket != nullptr) {
|
if (socket_ != nullptr) {
|
||||||
socket->close();
|
socket_->close();
|
||||||
delete socket;
|
delete socket_;
|
||||||
|
socket_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory->lock();
|
if (memory_ != nullptr) {
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
memory_->lock();
|
||||||
if (server != nullptr) {
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
server->close();
|
if (server_ != nullptr) {
|
||||||
delete server;
|
server_->close();
|
||||||
inst->primary = false;
|
delete server_;
|
||||||
inst->primaryPid = -1;
|
inst->primary = false;
|
||||||
inst->checksum = blockChecksum();
|
inst->primaryPid = -1;
|
||||||
}
|
inst->primaryUser[0] = '\0';
|
||||||
memory->unlock();
|
inst->checksum = blockChecksum();
|
||||||
|
}
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
delete memory;
|
delete memory_;
|
||||||
|
memory_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SingleApplicationPrivate::getUsername() {
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
QString username;
|
||||||
|
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||||
|
struct passwd *pw = getpwuid(geteuid());
|
||||||
|
if (pw) {
|
||||||
|
username = QString::fromLocal8Bit(pw->pw_name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
username = qEnvironmentVariable("USER");
|
||||||
|
#else
|
||||||
|
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return username;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
wchar_t username[UNLEN + 1];
|
||||||
|
// Specifies size of the buffer on input
|
||||||
|
DWORD usernameLength = UNLEN + 1;
|
||||||
|
if (GetUserNameW(username, &usernameLength)) {
|
||||||
|
return QString::fromWCharArray(username);
|
||||||
|
}
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
return qEnvironmentVariable("USERNAME");
|
||||||
|
#else
|
||||||
|
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +147,11 @@ void SingleApplicationPrivate::genBlockServerName() {
|
|||||||
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
|
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
|
||||||
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
|
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
|
||||||
|
|
||||||
if (!(options & SingleApplication::Mode::ExcludeAppVersion)) {
|
if (!(options_ & SingleApplication::Mode::ExcludeAppVersion)) {
|
||||||
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
|
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (options & SingleApplication::Mode::ExcludeAppPath)) {
|
if (!(options_ & SingleApplication::Mode::ExcludeAppPath)) {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
|
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||||
#else
|
#else
|
||||||
@@ -112,42 +160,22 @@ void SingleApplicationPrivate::genBlockServerName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// User level block requires a user specific data in the hash
|
// User level block requires a user specific data in the hash
|
||||||
if (options & SingleApplication::Mode::User) {
|
if (options_ & SingleApplication::Mode::User) {
|
||||||
#ifdef Q_OS_UNIX
|
appData.addData(getUsername().toUtf8());
|
||||||
QByteArray username;
|
|
||||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
|
||||||
struct passwd *pw = getpwuid(geteuid());
|
|
||||||
if (pw) {
|
|
||||||
username = pw->pw_name;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (username.isEmpty()) username = qgetenv("USER");
|
|
||||||
appData.addData(username);
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
wchar_t username [ UNLEN + 1 ];
|
|
||||||
// Specifies size of the buffer on input
|
|
||||||
DWORD usernameLength = UNLEN + 1;
|
|
||||||
if (GetUserNameW(username, &usernameLength)) {
|
|
||||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
appData.addData(qgetenv("USERNAME"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleApplicationPrivate::initializeMemoryBlock() {
|
void SingleApplicationPrivate::initializeMemoryBlock() {
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
inst->primary = false;
|
inst->primary = false;
|
||||||
inst->secondary = 0;
|
inst->secondary = 0;
|
||||||
inst->primaryPid = -1;
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
inst->checksum = blockChecksum();
|
inst->checksum = blockChecksum();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -156,133 +184,161 @@ void SingleApplicationPrivate::startPrimary() {
|
|||||||
|
|
||||||
Q_Q(SingleApplication);
|
Q_Q(SingleApplication);
|
||||||
|
|
||||||
// Successful creation means that no main process exists
|
|
||||||
// So we start a QLocalServer to listen for connections
|
|
||||||
QLocalServer::removeServer(blockServerName);
|
|
||||||
server = new QLocalServer();
|
|
||||||
|
|
||||||
// Restrict access to the socket according to the
|
|
||||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
|
||||||
if (options & SingleApplication::Mode::User) {
|
|
||||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
server->listen(blockServerName);
|
|
||||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
|
||||||
|
|
||||||
// Reset the number of connections
|
// Reset the number of connections
|
||||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
inst->primary = true;
|
inst->primary = true;
|
||||||
inst->primaryPid = q->applicationPid();
|
inst->primaryPid = q->applicationPid();
|
||||||
|
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||||
inst->checksum = blockChecksum();
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = 0;
|
||||||
|
// Successful creation means that no main process exists
|
||||||
|
// So we start a QLocalServer to listen for connections
|
||||||
|
QLocalServer::removeServer(blockServerName_);
|
||||||
|
server_ = new QLocalServer();
|
||||||
|
|
||||||
instanceNumber = 0;
|
// Restrict access to the socket according to the SingleApplication::Mode::User flag on User level or no restrictions
|
||||||
|
if (options_ & SingleApplication::Mode::User) {
|
||||||
|
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
server_->listen(blockServerName_);
|
||||||
|
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleApplicationPrivate::startSecondary() {}
|
void SingleApplicationPrivate::startSecondary() {
|
||||||
|
|
||||||
void SingleApplicationPrivate::connectToPrimary(const int msecs, const ConnectionType connectionType) {
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->secondary += 1;
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = inst->secondary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
|
||||||
|
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||||
if (socket == nullptr) {
|
if (socket_ == nullptr) {
|
||||||
socket = new QLocalSocket();
|
socket_ = new QLocalSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If already connected - we are done;
|
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||||
if (socket->state() == QLocalSocket::ConnectedState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If not connect
|
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
|
||||||
socket->state() == QLocalSocket::ClosingState) {
|
|
||||||
socket->connectToServer(blockServerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for being connected
|
forever {
|
||||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
randomSleep();
|
||||||
socket->waitForConnected(msecs);
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||||
|
socket_->connectToServer(blockServerName_);
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||||
|
socket_->waitForConnected(timeout - time.elapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connected break out of the loop
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||||
|
|
||||||
|
// If elapsed time since start is longer than the method timeout return
|
||||||
|
if (time.elapsed() >= timeout) return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisation message according to the SingleApplication protocol
|
// Initialisation message according to the SingleApplication protocol
|
||||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
QByteArray initMsg;
|
||||||
// Notify the parent that a new instance had been started;
|
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||||
QByteArray initMsg;
|
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
writeStream << blockServerName_.toLatin1();
|
||||||
|
writeStream << static_cast<quint8>(connectionType);
|
||||||
|
writeStream << instanceNumber_;
|
||||||
|
|
||||||
writeStream << blockServerName.toLatin1();
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
writeStream << static_cast<quint8>(connectionType);
|
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||||
writeStream << instanceNumber;
|
#else
|
||||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||||
writeStream << checksum;
|
#endif
|
||||||
|
|
||||||
// The header indicates the message length that follows
|
writeStream << checksum;
|
||||||
QByteArray header;
|
|
||||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
// The header indicates the message length that follows
|
||||||
|
QByteArray header;
|
||||||
|
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
headerStream << static_cast<quint64>(initMsg.length());
|
||||||
|
|
||||||
headerStream << static_cast <quint64>(initMsg.length());
|
socket_->write(header);
|
||||||
|
socket_->write(initMsg);
|
||||||
|
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
|
||||||
|
socket_->flush();
|
||||||
|
|
||||||
socket->write(header);
|
return result;
|
||||||
socket->write(initMsg);
|
|
||||||
socket->flush();
|
|
||||||
socket->waitForBytesWritten(msecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 SingleApplicationPrivate::blockChecksum() {
|
quint16 SingleApplicationPrivate::blockChecksum() {
|
||||||
|
|
||||||
return qChecksum(static_cast <const char *>(memory->data()), offsetof(InstancesInfo, checksum));
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||||
|
#else
|
||||||
|
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleApplicationPrivate::primaryPid() {
|
qint64 SingleApplicationPrivate::primaryPid() {
|
||||||
|
|
||||||
qint64 pid;
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
memory->lock();
|
qint64 pid = inst->primaryPid;
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
memory_->unlock();
|
||||||
pid = inst->primaryPid;
|
|
||||||
memory->unlock();
|
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SingleApplicationPrivate::primaryUser() {
|
||||||
|
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
QByteArray username = inst->primaryUser;
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
return QString::fromUtf8(username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executed when a connection has been made to the LocalServer
|
* @brief Executed when a connection has been made to the LocalServer
|
||||||
*/
|
*/
|
||||||
void SingleApplicationPrivate::slotConnectionEstablished() {
|
void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||||
|
|
||||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this]() {
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
auto &info = connectionMap[nextConnSocket];
|
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this](){
|
connectionMap_.remove(nextConnSocket);
|
||||||
connectionMap.remove(nextConnSocket);
|
nextConnSocket->deleteLater();
|
||||||
nextConnSocket->deleteLater();
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this]() {
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
auto &info = connectionMap[nextConnSocket];
|
switch (info.stage) {
|
||||||
switch(info.stage) {
|
|
||||||
case StageHeader:
|
case StageHeader:
|
||||||
readInitMessageHeader(nextConnSocket);
|
readInitMessageHeader(nextConnSocket);
|
||||||
break;
|
break;
|
||||||
@@ -294,15 +350,14 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||||
|
|
||||||
if (!connectionMap.contains(sock)) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,13 +366,12 @@ void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QDataStream headerStream(sock);
|
QDataStream headerStream(sock);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
|
|
||||||
// Read the header to know the message length
|
// Read the header to know the message length
|
||||||
quint64 msgLen = 0;
|
quint64 msgLen = 0;
|
||||||
headerStream >> msgLen;
|
headerStream >> msgLen;
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
info.stage = StageBody;
|
info.stage = StageBody;
|
||||||
info.msgLen = msgLen;
|
info.msgLen = msgLen;
|
||||||
|
|
||||||
@@ -331,11 +385,11 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
|
|
||||||
Q_Q(SingleApplication);
|
Q_Q(SingleApplication);
|
||||||
|
|
||||||
if (!connectionMap.contains(sock)) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -343,8 +397,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
// Read the message body
|
// Read the message body
|
||||||
QByteArray msgBytes = sock->read(info.msgLen);
|
QByteArray msgBytes = sock->read(info.msgLen);
|
||||||
QDataStream readStream(msgBytes);
|
QDataStream readStream(msgBytes);
|
||||||
|
readStream.setVersion(QDataStream::Qt_5_8);
|
||||||
readStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
|
|
||||||
// server name
|
// server name
|
||||||
QByteArray latin1Name;
|
QByteArray latin1Name;
|
||||||
@@ -354,7 +407,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
ConnectionType connectionType = InvalidConnection;
|
ConnectionType connectionType = InvalidConnection;
|
||||||
quint8 connTypeVal = InvalidConnection;
|
quint8 connTypeVal = InvalidConnection;
|
||||||
readStream >> connTypeVal;
|
readStream >> connTypeVal;
|
||||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||||
|
|
||||||
// instance id
|
// instance id
|
||||||
quint32 instanceId = 0;
|
quint32 instanceId = 0;
|
||||||
@@ -364,9 +417,13 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
quint16 msgChecksum = 0;
|
quint16 msgChecksum = 0;
|
||||||
readStream >> msgChecksum;
|
readStream >> msgChecksum;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||||
|
#else
|
||||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
sock->close();
|
sock->close();
|
||||||
@@ -376,7 +433,7 @@ void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
info.instanceId = instanceId;
|
info.instanceId = instanceId;
|
||||||
info.stage = StageConnected;
|
info.stage = StageConnected;
|
||||||
|
|
||||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification)) {
|
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleApplication::Mode::SecondaryNotification)) {
|
||||||
Q_EMIT q->instanceStarted();
|
Q_EMIT q->instanceStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +452,19 @@ void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const
|
|||||||
|
|
||||||
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||||
|
|
||||||
if (closedSocket->bytesAvailable() > 0)
|
if (closedSocket->bytesAvailable() > 0) {
|
||||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::randomSleep() {
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||||
|
#else
|
||||||
|
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||||
|
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
26
3rdparty/singleapplication/singleapplication_p.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2016
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
@@ -48,6 +49,7 @@ struct InstancesInfo {
|
|||||||
bool primary;
|
bool primary;
|
||||||
quint32 secondary;
|
quint32 secondary;
|
||||||
qint64 primaryPid;
|
qint64 primaryPid;
|
||||||
|
char primaryUser[128];
|
||||||
quint16 checksum;
|
quint16 checksum;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ struct ConnectionInfo {
|
|||||||
|
|
||||||
class SingleApplicationPrivate : public QObject {
|
class SingleApplicationPrivate : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ConnectionType : quint8 {
|
enum ConnectionType : quint8 {
|
||||||
InvalidConnection = 0,
|
InvalidConnection = 0,
|
||||||
@@ -74,27 +77,30 @@ class SingleApplicationPrivate : public QObject {
|
|||||||
};
|
};
|
||||||
Q_DECLARE_PUBLIC(SingleApplication)
|
Q_DECLARE_PUBLIC(SingleApplication)
|
||||||
|
|
||||||
explicit SingleApplicationPrivate(SingleApplication *_q_ptr);
|
explicit SingleApplicationPrivate(SingleApplication *ptr);
|
||||||
~SingleApplicationPrivate() override;
|
~SingleApplicationPrivate() override;
|
||||||
|
|
||||||
|
QString getUsername();
|
||||||
void genBlockServerName();
|
void genBlockServerName();
|
||||||
void initializeMemoryBlock();
|
void initializeMemoryBlock();
|
||||||
void startPrimary();
|
void startPrimary();
|
||||||
void startSecondary();
|
void startSecondary();
|
||||||
void connectToPrimary(const int msecs, const ConnectionType connectionType);
|
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||||
quint16 blockChecksum();
|
quint16 blockChecksum();
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
QString primaryUser();
|
||||||
void readInitMessageHeader(QLocalSocket *socket);
|
void readInitMessageHeader(QLocalSocket *socket);
|
||||||
void readInitMessageBody(QLocalSocket *socket);
|
void readInitMessageBody(QLocalSocket *socket);
|
||||||
|
void randomSleep();
|
||||||
|
|
||||||
SingleApplication *q_ptr;
|
SingleApplication *q_ptr;
|
||||||
QSharedMemory *memory;
|
QSharedMemory *memory_;
|
||||||
QLocalSocket *socket;
|
QLocalSocket *socket_;
|
||||||
QLocalServer *server;
|
QLocalServer *server_;
|
||||||
quint32 instanceNumber;
|
quint32 instanceNumber_;
|
||||||
QString blockServerName;
|
QString blockServerName_;
|
||||||
SingleApplication::Options options;
|
SingleApplication::Options options_;
|
||||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotConnectionEstablished();
|
void slotConnectionEstablished();
|
||||||
|
|||||||
188
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -42,106 +42,124 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
||||||
# include <QRandomGenerator>
|
|
||||||
#else
|
|
||||||
# include <QDateTime>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
#include "singlecoreapplication_p.h"
|
#include "singlecoreapplication_p.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||||
* if another instance already exists
|
|
||||||
* @param argc
|
* @param argc
|
||||||
* @param argv
|
* @param argv
|
||||||
* @param {bool} allowSecondaryInstances
|
* @param allowSecondary Whether to enable secondary instance support
|
||||||
|
* @param options Optional flags to toggle specific behaviour
|
||||||
|
* @param timeout Maximum time blocking functions are allowed during app load
|
||||||
*/
|
*/
|
||||||
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||||
: app_t(argc, argv), d_ptr(new SingleCoreApplicationPrivate(this)) {
|
: app_t(argc, argv),
|
||||||
|
d_ptr(new SingleCoreApplicationPrivate(this)) {
|
||||||
|
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
|
|
||||||
// Store the current mode of the program
|
// Store the current mode of the program
|
||||||
d->options = options;
|
d->options_ = options;
|
||||||
|
|
||||||
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||||
d->genBlockServerName();
|
d->genBlockServerName();
|
||||||
|
|
||||||
|
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||||
|
d->randomSleep();
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// By explicitly attaching it and then deleting it we make sure that the
|
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||||
// memory is deleted even after the process has crashed on Unix.
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
d->memory = new QSharedMemory(d->blockServerName);
|
d->memory_->attach();
|
||||||
d->memory->attach();
|
delete d->memory_;
|
||||||
delete d->memory;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Guarantee thread safe behaviour with a shared memory block.
|
// Guarantee thread safe behaviour with a shared memory block.
|
||||||
d->memory = new QSharedMemory(d->blockServerName);
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
|
|
||||||
// Create a shared memory block
|
// Create a shared memory block
|
||||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||||
// Initialize the shared memory block
|
// Initialize the shared memory block
|
||||||
d->memory->lock();
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to lock memory block after create.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
d->initializeMemoryBlock();
|
d->initializeMemoryBlock();
|
||||||
d->memory->unlock();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Attempt to attach to the memory segment
|
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||||
if (!d->memory->attach()) {
|
// Attempt to attach to the memory segment
|
||||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
if (!d->memory_->attach()) {
|
||||||
qCritical() << d->memory->errorString();
|
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||||
delete d;
|
abortSafely();
|
||||||
::exit(EXIT_FAILURE);
|
}
|
||||||
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to lock memory block after attach.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to create block.";
|
||||||
|
abortSafely();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||||
QElapsedTimer time;
|
QElapsedTimer time;
|
||||||
time.start();
|
time.start();
|
||||||
|
|
||||||
// Make sure the shared memory block is initialised and in consistent state
|
// Make sure the shared memory block is initialised and in consistent state
|
||||||
while (true) {
|
forever {
|
||||||
d->memory->lock();
|
// If the shared memory block's checksum is valid continue
|
||||||
|
if (d->blockChecksum() == inst->checksum) break;
|
||||||
if(d->blockChecksum() == inst->checksum) break;
|
|
||||||
|
|
||||||
|
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||||
if (time.elapsed() > 5000) {
|
if (time.elapsed() > 5000) {
|
||||||
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||||
d->initializeMemoryBlock();
|
d->initializeMemoryBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
// Otherwise wait for a random period and try again.
|
||||||
|
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||||
// Random sleep here limits the probability of a collision between two racing apps
|
if (!d->memory_->unlock()) {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
qDebug() << "SingleCoreApplication: Unable to unlock memory for random wait.";
|
||||||
QThread::sleep(QRandomGenerator::global()->bounded(8u, 18u));
|
qDebug() << d->memory_->errorString();
|
||||||
#else
|
}
|
||||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
d->randomSleep();
|
||||||
QThread::sleep(8 + static_cast<unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
if (!d->memory_->lock()) {
|
||||||
#endif
|
qCritical() << "SingleCoreApplication: Unable to lock memory after random wait.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->primary == false) {
|
if (inst->primary == false) {
|
||||||
d->startPrimary();
|
d->startPrimary();
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if another instance can be started
|
// Check if another instance can be started
|
||||||
if (allowSecondary) {
|
if (allowSecondary) {
|
||||||
inst->secondary += 1;
|
|
||||||
inst->checksum = d->blockChecksum();
|
|
||||||
d->instanceNumber = inst->secondary;
|
|
||||||
d->startSecondary();
|
d->startSecondary();
|
||||||
if(d->options & Mode::SecondaryNotification) {
|
if (d->options_ & Mode::SecondaryNotification) {
|
||||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
|
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
|
||||||
}
|
}
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory after secondary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory at end of execution.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
|
||||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
|
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
|
||||||
|
|
||||||
@@ -151,47 +169,101 @@ SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allow
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
SingleCoreApplication::~SingleCoreApplication() {
|
SingleCoreApplication::~SingleCoreApplication() {
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current application instance is primary.
|
||||||
|
* @return Returns true if the instance is primary, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleCoreApplication::isPrimary() {
|
bool SingleCoreApplication::isPrimary() {
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
return d->server != nullptr;
|
return d->server_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current application instance is secondary.
|
||||||
|
* @return Returns true if the instance is secondary, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleCoreApplication::isSecondary() {
|
bool SingleCoreApplication::isSecondary() {
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
return d->server == nullptr;
|
return d->server_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||||
|
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||||
|
* @return Returns a unique instance id.
|
||||||
|
*/
|
||||||
quint32 SingleCoreApplication::instanceId() {
|
quint32 SingleCoreApplication::instanceId() {
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
return d->instanceNumber;
|
return d->instanceNumber_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||||
|
* Especially useful when SingleCoreApplication is coupled with OS. specific APIs.
|
||||||
|
* @return Returns the primary instance PID.
|
||||||
|
*/
|
||||||
qint64 SingleCoreApplication::primaryPid() {
|
qint64 SingleCoreApplication::primaryPid() {
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
return d->primaryPid();
|
return d->primaryPid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username the primary instance is running as.
|
||||||
|
* @return Returns the username the primary instance is running as.
|
||||||
|
*/
|
||||||
|
QString SingleCoreApplication::primaryUser() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->primaryUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username the current instance is running as.
|
||||||
|
* @return Returns the username the current instance is running as.
|
||||||
|
*/
|
||||||
|
QString SingleCoreApplication::currentUser() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends message to the Primary Instance.
|
||||||
|
* @param message The message to send.
|
||||||
|
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||||
|
* @return true if the message was sent successfuly, false otherwise.
|
||||||
|
*/
|
||||||
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||||
|
|
||||||
Q_D(SingleCoreApplication);
|
Q_D(SingleCoreApplication);
|
||||||
|
|
||||||
// Nobody to connect to
|
// Nobody to connect to
|
||||||
if(isPrimary()) return false;
|
if (isPrimary()) return false;
|
||||||
|
|
||||||
// Make sure the socket is connected
|
// Make sure the socket is connected
|
||||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect);
|
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect))
|
||||||
|
return false;
|
||||||
|
|
||||||
d->socket->write(message);
|
d->socket_->write(message);
|
||||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||||
d->socket->flush();
|
d->socket_->flush();
|
||||||
return dataWritten;
|
return dataWritten;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the shared memory block and exits with a failure.
|
||||||
|
* This function halts program execution.
|
||||||
|
*/
|
||||||
|
void SingleCoreApplication::abortSafely() {
|
||||||
|
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
|
||||||
|
qCritical() << "SingleCoreApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||||
|
delete d;
|
||||||
|
::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -42,8 +42,7 @@
|
|||||||
class SingleCoreApplicationPrivate;
|
class SingleCoreApplicationPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The SingleCoreApplication class handles multiple instances of the same
|
* @brief The SingleCoreApplication class handles multipe instances of the same Application
|
||||||
* Application
|
|
||||||
* @see QCoreApplication
|
* @see QCoreApplication
|
||||||
*/
|
*/
|
||||||
class SingleCoreApplication : public QCoreApplication {
|
class SingleCoreApplication : public QCoreApplication {
|
||||||
@@ -51,7 +50,7 @@ class SingleCoreApplication : public QCoreApplication {
|
|||||||
|
|
||||||
typedef QCoreApplication app_t;
|
typedef QCoreApplication app_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Mode of operation of SingleCoreApplication.
|
* @brief Mode of operation of SingleCoreApplication.
|
||||||
* Whether the block should be user-wide or system-wide and whether the
|
* Whether the block should be user-wide or system-wide and whether the
|
||||||
@@ -63,11 +62,11 @@ public:
|
|||||||
* @enum
|
* @enum
|
||||||
*/
|
*/
|
||||||
enum Mode {
|
enum Mode {
|
||||||
User = 1 << 0,
|
User = 1 << 0,
|
||||||
System = 1 << 1,
|
System = 1 << 1,
|
||||||
SecondaryNotification = 1 << 2,
|
SecondaryNotification = 1 << 2,
|
||||||
ExcludeAppVersion = 1 << 3,
|
ExcludeAppVersion = 1 << 3,
|
||||||
ExcludeAppPath = 1 << 4
|
ExcludeAppPath = 1 << 4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Options, Mode)
|
Q_DECLARE_FLAGS(Options, Mode)
|
||||||
|
|
||||||
@@ -89,9 +88,8 @@ public:
|
|||||||
* operations. It does not guarantee that the SingleCoreApplication
|
* operations. It does not guarantee that the SingleCoreApplication
|
||||||
* initialisation will be completed in given time, though is a good hint.
|
* initialisation will be completed in given time, though is a good hint.
|
||||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
|
||||||
*/
|
*/
|
||||||
explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
explicit SingleCoreApplication(int &argc, char *argv[], const bool allowSecondary = false, const Options options = Mode::User, const int timeout = 1000);
|
||||||
~SingleCoreApplication() override;
|
~SingleCoreApplication() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,6 +116,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the username of the user running the primary instance
|
||||||
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString primaryUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the username of the current user
|
||||||
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString currentUser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sends a message to the primary instance. Returns true on success.
|
* @brief Sends a message to the primary instance. Returns true on success.
|
||||||
* @param {int} timeout - Timeout for connecting
|
* @param {int} timeout - Timeout for connecting
|
||||||
@@ -125,17 +135,18 @@ public:
|
|||||||
* @note sendMessage() will return false if invoked from the primary
|
* @note sendMessage() will return false if invoked from the primary
|
||||||
* instance.
|
* instance.
|
||||||
*/
|
*/
|
||||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void instanceStarted();
|
void instanceStarted();
|
||||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SingleCoreApplicationPrivate *d_ptr;
|
SingleCoreApplicationPrivate *d_ptr;
|
||||||
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
||||||
|
void abortSafely();
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
||||||
|
|
||||||
#endif // SINGLECOREAPPLICATION_H
|
#endif // SINGLECOREAPPLICATION_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
# include <pwd.h>
|
# include <pwd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@@ -52,6 +54,12 @@
|
|||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
# include <QRandomGenerator>
|
||||||
|
#else
|
||||||
|
# include <QDateTime>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
#include "singlecoreapplication_p.h"
|
#include "singlecoreapplication_p.h"
|
||||||
@@ -61,33 +69,73 @@
|
|||||||
# include <lmcons.h>
|
# include <lmcons.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *_q_ptr)
|
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *ptr)
|
||||||
: q_ptr(_q_ptr),
|
: q_ptr(ptr),
|
||||||
memory(nullptr),
|
memory_(nullptr),
|
||||||
socket(nullptr),
|
socket_(nullptr),
|
||||||
server(nullptr),
|
server_(nullptr),
|
||||||
instanceNumber(-1)
|
instanceNumber_(-1) {}
|
||||||
{}
|
|
||||||
|
|
||||||
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
|
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
|
||||||
|
|
||||||
if (socket != nullptr) {
|
if (socket_ != nullptr) {
|
||||||
socket->close();
|
socket_->close();
|
||||||
delete socket;
|
delete socket_;
|
||||||
|
socket_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory->lock();
|
if (memory_ != nullptr) {
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
memory_->lock();
|
||||||
if (server != nullptr) {
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
server->close();
|
if (server_ != nullptr) {
|
||||||
delete server;
|
server_->close();
|
||||||
inst->primary = false;
|
delete server_;
|
||||||
inst->primaryPid = -1;
|
inst->primary = false;
|
||||||
inst->checksum = blockChecksum();
|
inst->primaryPid = -1;
|
||||||
}
|
inst->primaryUser[0] = '\0';
|
||||||
memory->unlock();
|
inst->checksum = blockChecksum();
|
||||||
|
}
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
delete memory;
|
delete memory_;
|
||||||
|
memory_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SingleCoreApplicationPrivate::getUsername() {
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
QString username;
|
||||||
|
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||||
|
struct passwd *pw = getpwuid(geteuid());
|
||||||
|
if (pw) {
|
||||||
|
username = QString::fromLocal8Bit(pw->pw_name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
username = qEnvironmentVariable("USER");
|
||||||
|
#else
|
||||||
|
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return username;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
wchar_t username[UNLEN + 1];
|
||||||
|
// Specifies size of the buffer on input
|
||||||
|
DWORD usernameLength = UNLEN + 1;
|
||||||
|
if (GetUserNameW(username, &usernameLength)) {
|
||||||
|
return QString::fromWCharArray(username);
|
||||||
|
}
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
return qEnvironmentVariable("USERNAME");
|
||||||
|
#else
|
||||||
|
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +147,11 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
|||||||
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
|
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
|
||||||
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
|
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
|
||||||
|
|
||||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
||||||
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
|
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
|
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||||
#else
|
#else
|
||||||
@@ -112,42 +160,22 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// User level block requires a user specific data in the hash
|
// User level block requires a user specific data in the hash
|
||||||
if (options & SingleCoreApplication::Mode::User) {
|
if (options_ & SingleCoreApplication::Mode::User) {
|
||||||
#ifdef Q_OS_UNIX
|
appData.addData(getUsername().toUtf8());
|
||||||
QByteArray username;
|
|
||||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
|
||||||
struct passwd *pw = getpwuid(geteuid());
|
|
||||||
if (pw) {
|
|
||||||
username = pw->pw_name;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (username.isEmpty()) username = qgetenv("USER");
|
|
||||||
appData.addData(username);
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
wchar_t username [ UNLEN + 1 ];
|
|
||||||
// Specifies size of the buffer on input
|
|
||||||
DWORD usernameLength = UNLEN + 1;
|
|
||||||
if (GetUserNameW(username, &usernameLength)) {
|
|
||||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
appData.addData(qgetenv("USERNAME"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
inst->primary = false;
|
inst->primary = false;
|
||||||
inst->secondary = 0;
|
inst->secondary = 0;
|
||||||
inst->primaryPid = -1;
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
inst->checksum = blockChecksum();
|
inst->checksum = blockChecksum();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -156,133 +184,161 @@ void SingleCoreApplicationPrivate::startPrimary() {
|
|||||||
|
|
||||||
Q_Q(SingleCoreApplication);
|
Q_Q(SingleCoreApplication);
|
||||||
|
|
||||||
// Successful creation means that no main process exists
|
|
||||||
// So we start a QLocalServer to listen for connections
|
|
||||||
QLocalServer::removeServer(blockServerName);
|
|
||||||
server = new QLocalServer();
|
|
||||||
|
|
||||||
// Restrict access to the socket according to the
|
|
||||||
// SingleCoreApplication::Mode::User flag on User level or no restrictions
|
|
||||||
if (options & SingleCoreApplication::Mode::User) {
|
|
||||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
server->listen(blockServerName);
|
|
||||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
|
||||||
|
|
||||||
// Reset the number of connections
|
// Reset the number of connections
|
||||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
inst->primary = true;
|
inst->primary = true;
|
||||||
inst->primaryPid = q->applicationPid();
|
inst->primaryPid = q->applicationPid();
|
||||||
|
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||||
inst->checksum = blockChecksum();
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = 0;
|
||||||
|
// Successful creation means that no main process exists
|
||||||
|
// So we start a QLocalServer to listen for connections
|
||||||
|
QLocalServer::removeServer(blockServerName_);
|
||||||
|
server_ = new QLocalServer();
|
||||||
|
|
||||||
instanceNumber = 0;
|
// Restrict access to the socket according to the SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||||
|
if (options_ & SingleCoreApplication::Mode::User) {
|
||||||
|
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
server_->listen(blockServerName_);
|
||||||
|
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::startSecondary() {}
|
void SingleCoreApplicationPrivate::startSecondary() {
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::connectToPrimary(const int msecs, const ConnectionType connectionType) {
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->secondary += 1;
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = inst->secondary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleCoreApplicationPrivate::connectToPrimary(int timeout, ConnectionType connectionType) {
|
||||||
|
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||||
if (socket == nullptr) {
|
if (socket_ == nullptr) {
|
||||||
socket = new QLocalSocket();
|
socket_ = new QLocalSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If already connected - we are done;
|
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||||
if (socket->state() == QLocalSocket::ConnectedState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If not connect
|
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
|
||||||
socket->state() == QLocalSocket::ClosingState) {
|
|
||||||
socket->connectToServer(blockServerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for being connected
|
forever {
|
||||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
randomSleep();
|
||||||
socket->waitForConnected(msecs);
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||||
|
socket_->connectToServer(blockServerName_);
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||||
|
socket_->waitForConnected(timeout - time.elapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connected break out of the loop
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||||
|
|
||||||
|
// If elapsed time since start is longer than the method timeout return
|
||||||
|
if (time.elapsed() >= timeout) return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisation message according to the SingleCoreApplication protocol
|
// Initialisation message according to the SingleCoreApplication protocol
|
||||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
QByteArray initMsg;
|
||||||
// Notify the parent that a new instance had been started;
|
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||||
QByteArray initMsg;
|
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
writeStream << blockServerName_.toLatin1();
|
||||||
|
writeStream << static_cast<quint8>(connectionType);
|
||||||
|
writeStream << instanceNumber_;
|
||||||
|
|
||||||
writeStream << blockServerName.toLatin1();
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
writeStream << static_cast<quint8>(connectionType);
|
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||||
writeStream << instanceNumber;
|
#else
|
||||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||||
writeStream << checksum;
|
#endif
|
||||||
|
|
||||||
// The header indicates the message length that follows
|
writeStream << checksum;
|
||||||
QByteArray header;
|
|
||||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
// The header indicates the message length that follows
|
||||||
|
QByteArray header;
|
||||||
|
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
headerStream << static_cast<quint64>(initMsg.length());
|
||||||
|
|
||||||
headerStream << static_cast <quint64>(initMsg.length());
|
socket_->write(header);
|
||||||
|
socket_->write(initMsg);
|
||||||
|
bool result = socket_->waitForBytesWritten(timeout - time.elapsed());
|
||||||
|
socket_->flush();
|
||||||
|
|
||||||
socket->write(header);
|
return result;
|
||||||
socket->write(initMsg);
|
|
||||||
socket->flush();
|
|
||||||
socket->waitForBytesWritten(msecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||||
|
|
||||||
return qChecksum(static_cast <const char*> (memory->data()), offsetof(InstancesInfo, checksum));
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||||
|
#else
|
||||||
|
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||||
|
|
||||||
qint64 pid;
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
memory->lock();
|
qint64 pid = inst->primaryPid;
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
memory_->unlock();
|
||||||
pid = inst->primaryPid;
|
|
||||||
memory->unlock();
|
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SingleCoreApplicationPrivate::primaryUser() {
|
||||||
|
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
QByteArray username = inst->primaryUser;
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
return QString::fromUtf8(username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executed when a connection has been made to the LocalServer
|
* @brief Executed when a connection has been made to the LocalServer
|
||||||
*/
|
*/
|
||||||
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||||
|
|
||||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this]() {
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
auto &info = connectionMap[nextConnSocket];
|
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this](){
|
connectionMap_.remove(nextConnSocket);
|
||||||
connectionMap.remove(nextConnSocket);
|
nextConnSocket->deleteLater();
|
||||||
nextConnSocket->deleteLater();
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this]() {
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
auto &info = connectionMap[nextConnSocket];
|
switch (info.stage) {
|
||||||
switch(info.stage) {
|
|
||||||
case StageHeader:
|
case StageHeader:
|
||||||
readInitMessageHeader(nextConnSocket);
|
readInitMessageHeader(nextConnSocket);
|
||||||
break;
|
break;
|
||||||
@@ -294,15 +350,14 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||||
|
|
||||||
if (!connectionMap.contains(sock)) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,13 +366,12 @@ void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QDataStream headerStream(sock);
|
QDataStream headerStream(sock);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
|
|
||||||
// Read the header to know the message length
|
// Read the header to know the message length
|
||||||
quint64 msgLen = 0;
|
quint64 msgLen = 0;
|
||||||
headerStream >> msgLen;
|
headerStream >> msgLen;
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
info.stage = StageBody;
|
info.stage = StageBody;
|
||||||
info.msgLen = msgLen;
|
info.msgLen = msgLen;
|
||||||
|
|
||||||
@@ -331,11 +385,11 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
|
|
||||||
Q_Q(SingleCoreApplication);
|
Q_Q(SingleCoreApplication);
|
||||||
|
|
||||||
if (!connectionMap.contains(sock)) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -343,8 +397,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
// Read the message body
|
// Read the message body
|
||||||
QByteArray msgBytes = sock->read(info.msgLen);
|
QByteArray msgBytes = sock->read(info.msgLen);
|
||||||
QDataStream readStream(msgBytes);
|
QDataStream readStream(msgBytes);
|
||||||
|
readStream.setVersion(QDataStream::Qt_5_8);
|
||||||
readStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
|
|
||||||
// server name
|
// server name
|
||||||
QByteArray latin1Name;
|
QByteArray latin1Name;
|
||||||
@@ -354,7 +407,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
ConnectionType connectionType = InvalidConnection;
|
ConnectionType connectionType = InvalidConnection;
|
||||||
quint8 connTypeVal = InvalidConnection;
|
quint8 connTypeVal = InvalidConnection;
|
||||||
readStream >> connTypeVal;
|
readStream >> connTypeVal;
|
||||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||||
|
|
||||||
// instance id
|
// instance id
|
||||||
quint32 instanceId = 0;
|
quint32 instanceId = 0;
|
||||||
@@ -364,9 +417,13 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
quint16 msgChecksum = 0;
|
quint16 msgChecksum = 0;
|
||||||
readStream >> msgChecksum;
|
readStream >> msgChecksum;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||||
|
#else
|
||||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
sock->close();
|
sock->close();
|
||||||
@@ -376,7 +433,7 @@ void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
|||||||
info.instanceId = instanceId;
|
info.instanceId = instanceId;
|
||||||
info.stage = StageConnected;
|
info.stage = StageConnected;
|
||||||
|
|
||||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleCoreApplication::Mode::SecondaryNotification)) {
|
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleCoreApplication::Mode::SecondaryNotification)) {
|
||||||
Q_EMIT q->instanceStarted();
|
Q_EMIT q->instanceStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +452,19 @@ void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, c
|
|||||||
|
|
||||||
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||||
|
|
||||||
if (closedSocket->bytesAvailable() > 0)
|
if (closedSocket->bytesAvailable() > 0) {
|
||||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::randomSleep() {
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||||
|
#else
|
||||||
|
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||||
|
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2016
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
@@ -48,6 +49,7 @@ struct InstancesInfo {
|
|||||||
bool primary;
|
bool primary;
|
||||||
quint32 secondary;
|
quint32 secondary;
|
||||||
qint64 primaryPid;
|
qint64 primaryPid;
|
||||||
|
char primaryUser[128];
|
||||||
quint16 checksum;
|
quint16 checksum;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ struct ConnectionInfo {
|
|||||||
|
|
||||||
class SingleCoreApplicationPrivate : public QObject {
|
class SingleCoreApplicationPrivate : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ConnectionType : quint8 {
|
enum ConnectionType : quint8 {
|
||||||
InvalidConnection = 0,
|
InvalidConnection = 0,
|
||||||
@@ -74,27 +77,30 @@ class SingleCoreApplicationPrivate : public QObject {
|
|||||||
};
|
};
|
||||||
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
||||||
|
|
||||||
explicit SingleCoreApplicationPrivate(SingleCoreApplication *_q_ptr);
|
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
|
||||||
~SingleCoreApplicationPrivate() override;
|
~SingleCoreApplicationPrivate() override;
|
||||||
|
|
||||||
|
QString getUsername();
|
||||||
void genBlockServerName();
|
void genBlockServerName();
|
||||||
void initializeMemoryBlock();
|
void initializeMemoryBlock();
|
||||||
void startPrimary();
|
void startPrimary();
|
||||||
void startSecondary();
|
void startSecondary();
|
||||||
void connectToPrimary(const int msecs, const ConnectionType connectionType);
|
bool connectToPrimary(const int msecs, const ConnectionType connectionType);
|
||||||
quint16 blockChecksum();
|
quint16 blockChecksum();
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
QString primaryUser();
|
||||||
void readInitMessageHeader(QLocalSocket *socket);
|
void readInitMessageHeader(QLocalSocket *socket);
|
||||||
void readInitMessageBody(QLocalSocket *socket);
|
void readInitMessageBody(QLocalSocket *socket);
|
||||||
|
void randomSleep();
|
||||||
|
|
||||||
SingleCoreApplication *q_ptr;
|
SingleCoreApplication *q_ptr;
|
||||||
QSharedMemory *memory;
|
QSharedMemory *memory_;
|
||||||
QLocalSocket *socket;
|
QLocalSocket *socket_;
|
||||||
QLocalServer *server;
|
QLocalServer *server_;
|
||||||
quint32 instanceNumber;
|
quint32 instanceNumber_;
|
||||||
QString blockServerName;
|
QString blockServerName_;
|
||||||
SingleCoreApplication::Options options;
|
SingleCoreApplication::Options options_;
|
||||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotConnectionEstablished();
|
void slotConnectionEstablished();
|
||||||
|
|||||||
14
3rdparty/taglib/ConfigureChecks.cmake
vendored
@@ -6,32 +6,32 @@ include(CheckCXXSourceCompiles)
|
|||||||
# Check if the size of numeric types are suitable.
|
# Check if the size of numeric types are suitable.
|
||||||
|
|
||||||
check_type_size("short" SIZEOF_SHORT)
|
check_type_size("short" SIZEOF_SHORT)
|
||||||
if(NOT ${SIZEOF_SHORT} EQUAL 2)
|
if(NOT SIZEOF_SHORT EQUAL 2)
|
||||||
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_type_size("int" SIZEOF_INT)
|
check_type_size("int" SIZEOF_INT)
|
||||||
if(NOT ${SIZEOF_INT} EQUAL 4)
|
if(NOT SIZEOF_INT EQUAL 4)
|
||||||
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_type_size("long long" SIZEOF_LONGLONG)
|
check_type_size("long long" SIZEOF_LONGLONG)
|
||||||
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
if(NOT SIZEOF_LONGLONG EQUAL 8)
|
||||||
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||||
if(${SIZEOF_WCHAR_T} LESS 2)
|
if(SIZEOF_WCHAR_T LESS 2)
|
||||||
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_type_size("float" SIZEOF_FLOAT)
|
check_type_size("float" SIZEOF_FLOAT)
|
||||||
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
|
if(NOT SIZEOF_FLOAT EQUAL 4)
|
||||||
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
|
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_type_size("double" SIZEOF_DOUBLE)
|
check_type_size("double" SIZEOF_DOUBLE)
|
||||||
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
if(NOT SIZEOF_DOUBLE EQUAL 8)
|
||||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -212,5 +212,5 @@ endif()
|
|||||||
|
|
||||||
# Detect WinRT mode
|
# Detect WinRT mode
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||||
set(PLATFORM WINRT 1)
|
set(PLATFORM WINRT 1)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
5
3rdparty/taglib/toolkit/tutils.h
vendored
@@ -160,6 +160,9 @@ inline String formatString(const char *format, ...) {
|
|||||||
char buf[BufferSize];
|
char buf[BufferSize];
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||||
|
|
||||||
# if defined(HAVE_VSNPRINTF)
|
# if defined(HAVE_VSNPRINTF)
|
||||||
|
|
||||||
length = vsnprintf(buf, BufferSize, format, args);
|
length = vsnprintf(buf, BufferSize, format, args);
|
||||||
@@ -180,6 +183,8 @@ inline String formatString(const char *format, ...) {
|
|||||||
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
|
|||||||
1
3rdparty/utf8-cpp/CMakeLists.txt
vendored
@@ -1,2 +1 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
|||||||
214
CMakeLists.txt
@@ -11,11 +11,6 @@ include(cmake/Version.cmake)
|
|||||||
include(cmake/Summary.cmake)
|
include(cmake/Summary.cmake)
|
||||||
include(cmake/OptionalSource.cmake)
|
include(cmake/OptionalSource.cmake)
|
||||||
include(cmake/ParseArguments.cmake)
|
include(cmake/ParseArguments.cmake)
|
||||||
include(cmake/Rpm.cmake)
|
|
||||||
include(cmake/Deb.cmake)
|
|
||||||
include(cmake/Dmg.cmake)
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(LINUX ON)
|
set(LINUX ON)
|
||||||
@@ -27,13 +22,22 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
|||||||
set(OPENBSD ON)
|
set(OPENBSD ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
if(LINUX)
|
||||||
|
include(cmake/Rpm.cmake)
|
||||||
|
include(cmake/Deb.cmake)
|
||||||
|
endif()
|
||||||
|
if(APPLE)
|
||||||
|
include(cmake/Dmg.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
list(APPEND COMPILE_OPTIONS
|
list(APPEND COMPILE_OPTIONS
|
||||||
$<$<COMPILE_LANGUAGE:C>:--std=c99>
|
$<$<COMPILE_LANGUAGE:C>:-std=c99>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:--std=c++11>
|
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
|
||||||
-U__STRICT_ANSI__
|
|
||||||
-Wall
|
-Wall
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wpedantic
|
-Wpedantic
|
||||||
@@ -50,16 +54,11 @@ list(APPEND COMPILE_OPTIONS
|
|||||||
-Wunused-parameter
|
-Wunused-parameter
|
||||||
-Wformat=2
|
-Wformat=2
|
||||||
-Wdisabled-optimization
|
-Wdisabled-optimization
|
||||||
-Wno-sign-conversion
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
|
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
|
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
|
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
list(APPEND COMPILE_OPTIONS -Wno-unused-parameter)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(BUILD_WERROR "Build with -Werror" OFF)
|
option(BUILD_WERROR "Build with -Werror" OFF)
|
||||||
if(BUILD_WERROR)
|
if(BUILD_WERROR)
|
||||||
list(APPEND COMPILE_OPTIONS -Werror)
|
list(APPEND COMPILE_OPTIONS -Werror)
|
||||||
@@ -77,6 +76,11 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
|||||||
set(DEBUG ON)
|
set(DEBUG ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||||
|
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
|
||||||
|
endif()
|
||||||
|
|
||||||
find_program(CCACHE_EXECUTABLE NAMES ccache)
|
find_program(CCACHE_EXECUTABLE NAMES ccache)
|
||||||
if (CCACHE_EXECUTABLE)
|
if (CCACHE_EXECUTABLE)
|
||||||
message(STATUS "ccache found: will be used for compilation and linkage")
|
message(STATUS "ccache found: will be used for compilation and linkage")
|
||||||
@@ -91,9 +95,12 @@ find_package(Backtrace QUIET)
|
|||||||
if(Backtrace_FOUND)
|
if(Backtrace_FOUND)
|
||||||
set(HAVE_BACKTRACE ON)
|
set(HAVE_BACKTRACE ON)
|
||||||
endif()
|
endif()
|
||||||
|
find_package(Iconv QUIET)
|
||||||
find_package(GnuTLS REQUIRED)
|
find_package(GnuTLS REQUIRED)
|
||||||
find_package(Protobuf REQUIRED)
|
find_package(Protobuf REQUIRED)
|
||||||
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
|
if (NOT Protobuf_PROTOC_EXECUTABLE)
|
||||||
|
message(FATAL_ERROR "Missing protobuf compiler.")
|
||||||
|
endif()
|
||||||
if(LINUX)
|
if(LINUX)
|
||||||
find_package(ALSA REQUIRED)
|
find_package(ALSA REQUIRED)
|
||||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||||
@@ -118,62 +125,88 @@ pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
|
|||||||
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
|
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
|
||||||
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
|
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
|
||||||
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
|
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
|
||||||
pkg_check_modules(LIBXINE libxine)
|
|
||||||
pkg_check_modules(LIBVLC libvlc)
|
pkg_check_modules(LIBVLC libvlc)
|
||||||
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
|
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
|
||||||
pkg_check_modules(LIBPULSE libpulse)
|
pkg_check_modules(LIBPULSE libpulse)
|
||||||
pkg_check_modules(CHROMAPRINT libchromaprint)
|
pkg_check_modules(CHROMAPRINT libchromaprint)
|
||||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
|
pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
|
||||||
pkg_search_module(LIBUSBMUXD libusbmuxd-2.0 libusbmuxd)
|
|
||||||
pkg_search_module(LIBPLIST libplist-2.0 libplist)
|
|
||||||
find_package(Gettext)
|
find_package(Gettext)
|
||||||
find_package(FFTW3)
|
find_package(FFTW3)
|
||||||
|
|
||||||
# QT
|
if(NOT QT_DEFAULT_MAJOR_VERSION)
|
||||||
set(QT_MIN_VERSION 5.6)
|
set(QT_DEFAULT_MAJOR_VERSION 5)
|
||||||
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
|
||||||
|
|
||||||
if(X11_FOUND)
|
|
||||||
list(APPEND QT_COMPONENTS X11Extras)
|
|
||||||
endif()
|
endif()
|
||||||
if(DBUS_FOUND)
|
set(QT_MAJOR_VERSION ${QT_DEFAULT_MAJOR_VERSION} CACHE STRING "Qt version to use (5 or 6), defaults to ${QT_DEFAULT_MAJOR_VERSION}")
|
||||||
|
|
||||||
|
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
|
||||||
|
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
|
||||||
|
|
||||||
|
if(WITH_QT6)
|
||||||
|
set(BUILD_WITH_QT6 ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_QT5)
|
||||||
|
set(QT_MAJOR_VERSION 5)
|
||||||
|
elseif(BUILD_WITH_QT6)
|
||||||
|
set(QT_MAJOR_VERSION 6)
|
||||||
|
else()
|
||||||
|
if(QT_MAJOR_VERSION EQUAL 5)
|
||||||
|
set(BUILD_WITH_QT5 ON)
|
||||||
|
elseif(QT_MAJOR_VERSION EQUAL 6)
|
||||||
|
set(BUILD_WITH_QT6 ON)
|
||||||
|
else()
|
||||||
|
set(BUILD_WITH_QT5 ON)
|
||||||
|
set(QT_MAJOR_VERSION 5)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
|
||||||
|
unset(OPTIONAL_COMPONENTS)
|
||||||
|
|
||||||
|
if(QT_MAJOR_VERSION EQUAL 5)
|
||||||
|
set(QT_MIN_VERSION 5.8)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(DBUS_FOUND AND NOT WIN32)
|
||||||
list(APPEND QT_COMPONENTS DBus)
|
list(APPEND QT_COMPONENTS DBus)
|
||||||
endif()
|
endif()
|
||||||
if(APPLE)
|
if(X11_FOUND)
|
||||||
list(APPEND QT_COMPONENTS MacExtras)
|
list(APPEND OPTIONAL_COMPONENTS X11Extras)
|
||||||
endif()
|
endif()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND QT_COMPONENTS WinExtras)
|
list(APPEND OPTIONAL_COMPONENTS WinExtras)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${OPTIONAL_COMPONENTS})
|
||||||
|
|
||||||
set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Sql_LIBRARIES})
|
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
|
||||||
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
|
||||||
|
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
|
||||||
if(Qt5DBus_FOUND)
|
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
|
||||||
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
|
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
|
||||||
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
|
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
|
||||||
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
|
set(QT_LIBRARIES Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Concurrent Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Sql)
|
||||||
|
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
|
||||||
|
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||||
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
|
||||||
|
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
|
||||||
endif()
|
endif()
|
||||||
if(Qt5X11Extras_FOUND)
|
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
|
||||||
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
|
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||||
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
|
||||||
|
set(HAVE_X11EXTRAS ON)
|
||||||
endif()
|
endif()
|
||||||
if(Qt5MacExtras_FOUND)
|
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
|
||||||
list(APPEND QT_LIBRARIES ${Qt5MacExtras_LIBRARIES})
|
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||||
list(APPEND QT_INCLUDE_DIRS ${Qt5MacExtras_INCLUDE_DIRS})
|
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
|
||||||
endif()
|
set(HAVE_WINEXTRAS ON)
|
||||||
if(Qt5WinExtras_FOUND)
|
|
||||||
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
|
|
||||||
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
|
||||||
if (Qt5LinguistTools_FOUND)
|
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
|
||||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(X11_FOUND)
|
if(X11_FOUND)
|
||||||
@@ -191,6 +224,15 @@ if(X11_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
endif(X11_FOUND)
|
endif(X11_FOUND)
|
||||||
|
|
||||||
|
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
if(QPA_QPLATFORMNATIVEINTERFACE_H)
|
||||||
|
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
|
||||||
|
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
|
||||||
|
else()
|
||||||
|
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
|
||||||
|
endif()
|
||||||
|
|
||||||
# TAGLIB
|
# TAGLIB
|
||||||
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
|
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
|
||||||
if(USE_SYSTEM_TAGLIB)
|
if(USE_SYSTEM_TAGLIB)
|
||||||
@@ -221,9 +263,20 @@ set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/singleap
|
|||||||
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
|
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
|
||||||
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
|
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
|
||||||
|
|
||||||
if (APPLE)
|
if(APPLE)
|
||||||
find_library(SPARKLE Sparkle)
|
find_library(SPARKLE Sparkle)
|
||||||
endif (APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
|
if(NOT SPARKLE AND (APPLE OR WIN32))
|
||||||
|
if(BUILD_WITH_QT6)
|
||||||
|
pkg_check_modules(QTSPARKLE qtsparkle-qt6)
|
||||||
|
else()
|
||||||
|
pkg_check_modules(QTSPARKLE qtsparkle-qt5)
|
||||||
|
endif()
|
||||||
|
if(QTSPARKLE_FOUND)
|
||||||
|
set(HAVE_QTSPARKLE ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# RC compiler
|
# RC compiler
|
||||||
@@ -258,10 +311,6 @@ optional_component(GSTREAMER ON "Engine: GStreamer backend"
|
|||||||
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
|
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(XINE ON "Engine: Xine backend"
|
|
||||||
DEPENDS "libxine" LIBXINE_FOUND
|
|
||||||
)
|
|
||||||
|
|
||||||
optional_component(VLC ON "Engine: VLC backend"
|
optional_component(VLC ON "Engine: VLC backend"
|
||||||
DEPENDS "libvlc" LIBVLC_FOUND
|
DEPENDS "libvlc" LIBVLC_FOUND
|
||||||
)
|
)
|
||||||
@@ -278,6 +327,10 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
|
|||||||
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
|
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
|
||||||
|
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||||
)
|
)
|
||||||
@@ -293,31 +346,35 @@ optional_component(GIO ON "Devices: GIO device backend"
|
|||||||
|
|
||||||
optional_component(LIBGPOD ON "Devices: iPod classic support"
|
optional_component(LIBGPOD ON "Devices: iPod classic support"
|
||||||
DEPENDS "libgpod" LIBGPOD_FOUND
|
DEPENDS "libgpod" LIBGPOD_FOUND
|
||||||
|
DEPENDS "gdk-pixbuf" GDK_PIXBUF_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(LIBMTP ON "Devices: MTP support"
|
optional_component(LIBMTP ON "Devices: MTP support"
|
||||||
DEPENDS "libmtp" LIBMTP_FOUND
|
DEPENDS "libmtp" LIBMTP_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(IMOBILEDEVICE ON "Devices: iPhone, iPod Touch, iPad and Apple TV support"
|
|
||||||
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
|
|
||||||
DEPENDS "libplist" LIBPLIST_FOUND
|
|
||||||
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
|
|
||||||
DEPENDS "libgpod" HAVE_LIBGPOD
|
|
||||||
)
|
|
||||||
|
|
||||||
optional_component(SPARKLE ON "Sparkle integration"
|
optional_component(SPARKLE ON "Sparkle integration"
|
||||||
DEPENDS "macOS" APPLE
|
DEPENDS "macOS" APPLE
|
||||||
DEPENDS "Sparkle" SPARKLE
|
DEPENDS "Sparkle" SPARKLE
|
||||||
)
|
)
|
||||||
|
|
||||||
optional_component(TRANSLATIONS ON "Translations"
|
if(BUILD_WITH_QT6)
|
||||||
DEPENDS "gettext" GETTEXT_FOUND
|
optional_component(TRANSLATIONS ON "Translations"
|
||||||
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
DEPENDS "gettext" GETTEXT_FOUND
|
||||||
)
|
DEPENDS "Qt6LinguistTools" Qt6LinguistTools_FOUND
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
optional_component(TRANSLATIONS ON "Translations"
|
||||||
|
DEPENDS "gettext" GETTEXT_FOUND
|
||||||
|
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(INSTALL_TRANSLATIONS "Install translations" OFF)
|
||||||
|
|
||||||
optional_component(SUBSONIC ON "Subsonic support")
|
optional_component(SUBSONIC ON "Subsonic support")
|
||||||
optional_component(TIDAL ON "Tidal support")
|
optional_component(TIDAL ON "Tidal support")
|
||||||
|
optional_component(QOBUZ ON "Qobuz support")
|
||||||
|
|
||||||
optional_component(MOODBAR ON "Moodbar"
|
optional_component(MOODBAR ON "Moodbar"
|
||||||
DEPENDS "fftw3" FFTW3_FOUND
|
DEPENDS "fftw3" FFTW3_FOUND
|
||||||
@@ -342,9 +399,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
|
|||||||
# Check that we have sqlite3 with FTS5
|
# Check that we have sqlite3 with FTS5
|
||||||
|
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
set(CMAKE_REQUIRED_FLAGS "--std=c++11")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Sql_LIBRARIES})
|
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
|
|
||||||
check_cxx_source_runs("
|
check_cxx_source_runs("
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
@@ -361,20 +417,6 @@ if(NOT CMAKE_CROSSCOMPILING)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAVE_XINE)
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#define METRONOM_INTERNAL
|
|
||||||
#include <iostream>
|
|
||||||
#include <xine/metronom.h>
|
|
||||||
int main() {
|
|
||||||
metronom_t metronom;
|
|
||||||
std::cout << metronom.pts_per_smpls;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
XINE_ANALYZER)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set up definitions
|
# Set up definitions
|
||||||
|
|
||||||
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
|
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
|
||||||
@@ -415,8 +457,8 @@ add_custom_target(uninstall
|
|||||||
|
|
||||||
# Show a summary of what we have enabled
|
# Show a summary of what we have enabled
|
||||||
summary_show()
|
summary_show()
|
||||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC)
|
if(NOT HAVE_GSTREAMER AND NOT HAVE_VLC)
|
||||||
message(FATAL_ERROR "You need to have either GStreamer, Xine or VLC to compile!")
|
message(FATAL_ERROR "You need to have either GStreamer or VLC to compile!")
|
||||||
elseif(NOT HAVE_GSTREAMER)
|
elseif(NOT HAVE_GSTREAMER)
|
||||||
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
165
Changelog
@@ -2,6 +2,171 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
|
0.8.5:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fix return type of SmartPlaylistQueryWizardPlugin::type().
|
||||||
|
* Fix comparison between QChar and integer to use QChar::unicode().
|
||||||
|
* Fix return type of qHash with Qt 6 to use size_t instead of uint.
|
||||||
|
* Fix tag edit dialog save process sometimes stuck.
|
||||||
|
* Fix repeat and shuffle buttons greyed out when a dynamic playlist is open.
|
||||||
|
* Fix CUE parser handling of values with empty quotes.
|
||||||
|
* Fix broken year and disc collection groupings with CUE songs.
|
||||||
|
* Fix HTML escaping showing up in OSD notifications when using custom text.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Add Swedish translation.
|
||||||
|
* Made Qt X11Extras and WinExtras modules optional.
|
||||||
|
* Save and restore geometry in edit tag dialog.
|
||||||
|
* Add command line option to play a playlist based on name.
|
||||||
|
* Change double-click behaviour in cover manager to open fullsize cover.
|
||||||
|
|
||||||
|
0.8.4:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fix preventing session logout when window is maxmimized.
|
||||||
|
* Fix empty space in organize window when copying songs/playlists to devices.
|
||||||
|
* Fix crash when opening about dialog in a wayland session.
|
||||||
|
* Fix stretched fancy/side tabbar style issue with adwaita style (Fedora/Gnome).
|
||||||
|
* Fix centering star icon on playlist tabbar.
|
||||||
|
* Fix network proxy settings for streaming.
|
||||||
|
* Fix copy URL to clipboard to handle non-ASCII characters.
|
||||||
|
* Fix HiDPI scaling for glow animation and drag over playlist.
|
||||||
|
* Fix smart playlist search by filename.
|
||||||
|
* Fix single letter collection nodes showing before dividers.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Add support for native global shortcuts on KDE.
|
||||||
|
* Add track progress in system tray icon as an option.
|
||||||
|
* Only strip problematic characters in suggested filename when saving a playlist to file.
|
||||||
|
* Change star/unstar playlist to doubleclick instead of singleclick.
|
||||||
|
* Don't edit playlist name on doubleclick in playlists view.
|
||||||
|
* Make context view top label text selectable.
|
||||||
|
* Add setting to change Qt style.
|
||||||
|
* Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags.
|
||||||
|
* Remove remaining uses of QTextCodec.
|
||||||
|
* Remove Core5Compat dependency.
|
||||||
|
|
||||||
|
0.8.3:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed updating playing widget song details in small cover mode.
|
||||||
|
* Fixed file extension when transcoding songs.
|
||||||
|
* Fixed updating album cover to collection in edit tag dialog when pressing save.
|
||||||
|
* Fixed songs with empty artist in collection.
|
||||||
|
* Fixed possible crashes with stream discovery.
|
||||||
|
* Fixed setting engine state to null.
|
||||||
|
* Fixed tagreader crash with empty APE tags.
|
||||||
|
* Fixed a gstreamer memory leak.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* (Windows) Added WASAPI plugin.
|
||||||
|
|
||||||
|
0.8.2:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed broken transition to next song for CUE files with certain audio formats (regression since version 0.6.13).
|
||||||
|
* Fixed all collection divider keys showing on top with some language collate settings (regression in version 0.8.1).
|
||||||
|
* Fixed SQL querying songs by song ID when song ID is a string.
|
||||||
|
* Fixed saving album covers for LMS Subsonic servers.
|
||||||
|
* Fixed reading song creation dates with LMS Subsonic servers.
|
||||||
|
* Fixed saving initial settings.
|
||||||
|
* Removed use of HTML in system tray icon tooltip for all desktop environments instead of just KDE and Cinnamon.
|
||||||
|
* (Windows) Ignore "IDirectSoundBuffer_GetStatus The operation completed successfully" false error when switching device while playing.
|
||||||
|
|
||||||
|
0.8.1:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed engine selection in backend settings with Qt 6.
|
||||||
|
* Fixed pixelated playlist source icon for currently playing song.
|
||||||
|
* Fixed crash when deleting queued songs from playlist.
|
||||||
|
* Fixed situations where songs could disappear or be shown multiple times with certain collection groupings.
|
||||||
|
* Fixed initial sizes of playlist header columns.
|
||||||
|
* Fixed Strawberry preventing logout.
|
||||||
|
* Fixed incorrectly splitting of basename for moodbar and transcoding for filenames with several dots.
|
||||||
|
* Fixed certain cases where "playing now" for scrobbler were sent twice.
|
||||||
|
* Fixed album cover loaded twice for certain songs causing slugglish playing widget.
|
||||||
|
* Fixed playing widget to draw text after album cover is fully shown.
|
||||||
|
* Fixed crash when trying to copy a closed playlist to a device.
|
||||||
|
* Fixed incorrect song source for CUE songs when added through the collection watcher.
|
||||||
|
* Disable use of HTML in system tray tooltip on Cinnamon too.
|
||||||
|
* Remove problematic '&' character from OSD messages.
|
||||||
|
* (macOS) Fixed crash on exit when cover manager is open.
|
||||||
|
* (macOS) Fixed graphical corruption.
|
||||||
|
* (Windows) Fixed GStreamer registry problems.
|
||||||
|
* (Windows) Register Tidal URL Scheme in Windows installer.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Improved playlist autoscrolling.
|
||||||
|
* Only allow playlist right click tag editing for editable songs.
|
||||||
|
* Read song creation time from subsonic API.
|
||||||
|
* Remember manually set compilation status for albums when songs are rescanned.
|
||||||
|
* Added icons for edit tag playlist right click menu actions.
|
||||||
|
* Maximize dialogs if they are already open when clicked again in the menu.
|
||||||
|
* Added support for compilation tag to edit tag dialog.
|
||||||
|
* Show song info and album cover in OSD on stop and pause.
|
||||||
|
* Reshow OSD on song restart.
|
||||||
|
* Always save initial settings.
|
||||||
|
* Removed use of deprecated gstreamer "low-percent" (Minimum buffer fill setting).
|
||||||
|
* Added buffer low and high watermark settings to backend settings.
|
||||||
|
* Make use of newer version of the desktop notifications service when available.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Added setting for enabling scrobbling based on song source.
|
||||||
|
* Added optional delete from disk in collection and playlist.
|
||||||
|
* Added Last.fm import data wizard.
|
||||||
|
* Added smart and dynamic playlists.
|
||||||
|
* Added song ratings.
|
||||||
|
* Added Qobuz streaming support.
|
||||||
|
* Added Subsonic server side scrobbling support.
|
||||||
|
* Load thumbnails from iPods to show under device collection.
|
||||||
|
|
||||||
|
0.7.2:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed installation directory for translations.
|
||||||
|
* Fixed collection sorting for non-ASCII characters.
|
||||||
|
* Fixed closing connected devices on exit.
|
||||||
|
|
||||||
|
0.7.1:
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Fixed incorrectly mapped global shortcuts keys "2" and "3".
|
||||||
|
* Fixed Last.fm scrobbling to correctly start array notation for parameters at 0 and not 1.
|
||||||
|
* Fixed sending trackNumber correctly for Last.fm and Libre.fm scrobbling.
|
||||||
|
* Fixed collection search when using special characters in the search query.
|
||||||
|
* Fixed reading and saving MP4 lyrics tag.
|
||||||
|
* Fixed reading ASF comment tag.
|
||||||
|
* Fixed adding playlist songs outside the collection when there are multiple files with the same URL.
|
||||||
|
* Fixed the rescan songs option to work with local songs outside of the collection.
|
||||||
|
* Fixed problems with editing song metadata in the playlists.
|
||||||
|
* Fixed saving and restoring playlist scrollbar position when switching between playlists.
|
||||||
|
* Fixed minor issue in cue parser with date and genre.
|
||||||
|
* (macOS) Fixed gst-libav plugin issue resulting in MP3 not working.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* Simplified and improved startup behaviour code.
|
||||||
|
* Adapted all source code to be compatible with Qt 6, and increased required Qt version to 5.8.
|
||||||
|
* Added option to compile with Qt 6 (-DWITH_QT6=ON).
|
||||||
|
* Base warning for show in file browser on unique directories to avoid unneeded warning about opening many files.
|
||||||
|
* Use album artist instead of artist for album repeat mode when available.
|
||||||
|
* Added extra safety for overwriting files for filesystem storages when organizing files.
|
||||||
|
* Remove diacritics in FTS search.
|
||||||
|
* Improved playlist context menu.
|
||||||
|
* Added fatal CMake error for missing protobuf compiler.
|
||||||
|
* Added support for parsing radio streams metadata with tilde in title.
|
||||||
|
* Added CMake option to install translation files.
|
||||||
|
* Increased maximum time step for seeking to 60.
|
||||||
|
* (Unix) Added playback actions to desktop file.
|
||||||
|
* (macOS) Hide behaviour settings that are unavailable on macOS.
|
||||||
|
* (macOS) Fixed compile warnings.
|
||||||
|
* (macOS) Added Sparkle integration to notify on new versions.
|
||||||
|
* (Windows) Added QtSparkle support to notify on new versions.
|
||||||
|
|
||||||
|
Removed features:
|
||||||
|
* Removed Xine engine support.
|
||||||
|
* Removed broken imobiledevice (iPhone) support.
|
||||||
|
|
||||||
0.6.13:
|
0.6.13:
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
from jonaski/opensuse:lp151
|
|
||||||
|
|
||||||
run mkdir -p /usr/src/app
|
|
||||||
workdir /usr/src/app
|
|
||||||
copy . /usr/src/app
|
|
||||||
29
README.md
@@ -1,8 +1,9 @@
|
|||||||
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
:strawberry: Strawberry Music Player [](https://github.com/strawberrymusicplayer/strawberry/actions)
|
||||||
[](https://paypal.me/jonaskvinge)
|
[](https://paypal.me/jonaskvinge)
|
||||||
|
[](https://patreon.com/jonaskvinge)
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt 5 framework.
|
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -35,7 +36,8 @@ You can also make a one-time payment through [paypal.me/jonaskvinge](https://pay
|
|||||||
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
|
||||||
* Audio CD playback
|
* Audio CD playback
|
||||||
* Native desktop notifications
|
* Native desktop notifications
|
||||||
* Playlists in multiple formats
|
* Playlist management
|
||||||
|
* Smart and dynamic playlists
|
||||||
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
* Advanced audio output and device configuration for bit-perfect playback on Linux
|
||||||
* Edit tags on music files
|
* Edit tags on music files
|
||||||
* Fetch tags from MusicBrainz
|
* Fetch tags from MusicBrainz
|
||||||
@@ -44,9 +46,9 @@ You can also make a one-time payment through [paypal.me/jonaskvinge](https://pay
|
|||||||
* Support for multiple backends
|
* Support for multiple backends
|
||||||
* Audio analyzer
|
* Audio analyzer
|
||||||
* Audio equalizer
|
* Audio equalizer
|
||||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
* Transfer music to iPod, MTP or mass-storage USB player
|
||||||
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
|
||||||
* Subsonic and Tidal streaming support
|
* Subsonic, Tidal and Qobuz streaming support
|
||||||
|
|
||||||
|
|
||||||
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
|
||||||
@@ -63,14 +65,14 @@ To build Strawberry from source you need the following installed on your system
|
|||||||
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
|
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
|
||||||
* [GLib](https://developer.gnome.org/glib/)
|
* [GLib](https://developer.gnome.org/glib/)
|
||||||
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
|
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
|
||||||
* [Qt 5.6 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
* [Qt 5.8 or higher (or Qt 6) with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
|
||||||
* [Qt 5 components X11Extras and DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
|
* [Qt components X11Extras and D-Bus for Linux/BSD and WinExtras for Windows](https://www.qt.io/)
|
||||||
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
|
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
|
||||||
* [Chromaprint library](https://acoustid.org/chromaprint)
|
* [Chromaprint library](https://acoustid.org/chromaprint)
|
||||||
* [ALSA library (linux)](https://www.alsa-project.org/)
|
* [ALSA library (linux)](https://www.alsa-project.org/)
|
||||||
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
* [D-Bus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||||
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org) or [VLC](https://www.videolan.org)
|
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
||||||
* [GnuTLS](https://www.gnutls.org/)
|
* [GnuTLS](https://www.gnutls.org/)
|
||||||
|
|
||||||
Optional dependencies:
|
Optional dependencies:
|
||||||
@@ -78,10 +80,9 @@ Optional dependencies:
|
|||||||
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
|
||||||
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
|
||||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||||
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
|
|
||||||
* Moodbar: [fftw3](http://www.fftw.org/)
|
* Moodbar: [fftw3](http://www.fftw.org/)
|
||||||
|
|
||||||
Either GStreamer, Xine or VLC engine is required, but only GStreamer is fully implemented so far.
|
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented so far.
|
||||||
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
||||||
|
|
||||||
### :wrench: Compiling from source
|
### :wrench: Compiling from source
|
||||||
@@ -95,8 +96,12 @@ You should also install the gstreamer plugins base and good, and optionally bad
|
|||||||
cd strawberry
|
cd strawberry
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j4
|
make -j$(nproc)
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
|
To compile with Qt 6 use:
|
||||||
|
|
||||||
|
cmake .. -DBUILD_WITH_QT6=ON
|
||||||
|
|
||||||
### :penguin: Packaging status
|
### :penguin: Packaging status
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
|
|||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
|
|
||||||
if (DEB_CODENAME)
|
if (DEB_CODENAME AND DEB_DATE)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
|
|
||||||
add_custom_target(deb
|
add_custom_target(deb
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us
|
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macversion.sh OUTPUT_VARIABLE MACOS_VERSION_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
add_custom_target(dmg
|
add_custom_target(dmg
|
||||||
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
|
COMMAND create-dmg --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(dmg2
|
||||||
|
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||||
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||||
|
COMMAND create-dmg --skip-jenkins --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ macro(optional_source TOGGLE)
|
|||||||
list(APPEND OTHER_SOURCES ${OPTIONAL_SOURCE_HEADERS})
|
list(APPEND OTHER_SOURCES ${OPTIONAL_SOURCE_HEADERS})
|
||||||
|
|
||||||
set(_uic_sources)
|
set(_uic_sources)
|
||||||
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
if(BUILD_WITH_QT6)
|
||||||
|
qt6_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||||
|
else()
|
||||||
|
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
|
||||||
|
endif()
|
||||||
list(APPEND OTHER_SOURCES ${_uic_sources})
|
list(APPEND OTHER_SOURCES ${_uic_sources})
|
||||||
list(APPEND OTHER_UIC_SOURCES ${_uic_sources})
|
list(APPEND OTHER_UIC_SOURCES ${_uic_sources})
|
||||||
endif(${TOGGLE})
|
endif(${TOGGLE})
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# From http://www.cmake.org/Wiki/CMakeMacroParseArguments
|
# From http://www.cmake.org/Wiki/CMakeMacroParseArguments
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.6)
|
|
||||||
|
|
||||||
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
|
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
|
||||||
SET(DEFAULT_ARGS)
|
SET(DEFAULT_ARGS)
|
||||||
FOREACH(arg_name ${arg_names})
|
FOREACH(arg_name ${arg_names})
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
|||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
if (DIST_NAME)
|
if (DIST_NAME)
|
||||||
|
|
||||||
message(STATUS "Distro Name: ${DIST_NAME}")
|
message(STATUS "Distro Name: ${DIST_NAME}")
|
||||||
if (DIST_RELEASE)
|
if (DIST_RELEASE)
|
||||||
message(STATUS "Distro Release: ${DIST_RELEASE}")
|
message(STATUS "Distro Release: ${DIST_RELEASE}")
|
||||||
@@ -24,45 +25,40 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
|
|||||||
if (DIST_VERSION)
|
if (DIST_VERSION)
|
||||||
message(STATUS "Distro Version: ${DIST_VERSION}")
|
message(STATUS "Distro Version: ${DIST_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
|
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
|
||||||
if (${DIST_NAME} STREQUAL "opensuse")
|
|
||||||
if (DIST_RELEASE)
|
if (${DIST_NAME} STREQUAL "opensuse" AND DIST_RELEASE)
|
||||||
if (${DIST_RELEASE} STREQUAL "leap")
|
if (${DIST_RELEASE} STREQUAL "leap")
|
||||||
if (DIST_VERSION)
|
if (DIST_VERSION)
|
||||||
set(RPM_DISTRO "lp${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO "lp${DIST_VERSION}")
|
||||||
else()
|
else()
|
||||||
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO ${DIST_RELEASE})
|
||||||
endif()
|
|
||||||
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
|
|
||||||
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
|
|
||||||
else ()
|
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
|
||||||
endif()
|
endif()
|
||||||
else()
|
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
set(RPM_DISTRO ${DIST_RELEASE})
|
||||||
endif()
|
endif()
|
||||||
elseif (${DIST_NAME} STREQUAL "fedora")
|
elseif (${DIST_NAME} STREQUAL "fedora" AND DIST_VERSION)
|
||||||
if (DIST_VERSION)
|
set(RPM_DISTRO "fc${DIST_VERSION}")
|
||||||
set(RPM_DISTRO "fc${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
|
elseif (${DIST_NAME} STREQUAL "centos" AND DIST_VERSION)
|
||||||
else ()
|
set(RPM_DISTRO "el${DIST_VERSION}")
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
elseif (${DIST_NAME} STREQUAL "mageia" AND DIST_RELEASE)
|
||||||
endif()
|
set(RPM_DISTRO "mga${DIST_RELEASE}")
|
||||||
elseif (${DIST_NAME} STREQUAL "mageia")
|
|
||||||
if (DIST_RELEASE)
|
|
||||||
set(RPM_DISTRO "mga${DIST_RELEASE}" CACHE STRING "Suffix of the rpm file")
|
|
||||||
else ()
|
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT RPM_DISTRO)
|
||||||
|
set(RPM_DISTRO ${DIST_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
|
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec.in ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec @ONLY)
|
|
||||||
add_custom_target(rpm
|
add_custom_target(rpm
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
|
||||||
COMMAND ${RPMBUILD_EXEC} -bs ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
COMMAND ${RPMBUILD_EXEC} -bs ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||||
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.11)
|
|
||||||
|
|
||||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||||
message(FATAL_ERROR "Could not find xgettext executable")
|
message(FATAL_ERROR "Could not find xgettext executable")
|
||||||
@@ -19,6 +17,8 @@ set (XGETTEXT_OPTIONS
|
|||||||
--from-code=utf-8
|
--from-code=utf-8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
|
||||||
|
|
||||||
macro(add_pot outfiles header pot)
|
macro(add_pot outfiles header pot)
|
||||||
# Make relative filenames for all source files
|
# Make relative filenames for all source files
|
||||||
set(add_pot_sources)
|
set(add_pot_sources)
|
||||||
@@ -64,14 +64,21 @@ macro(add_po outfiles po_prefix)
|
|||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND ${outfiles} ${_qm_filepath})
|
list(APPEND ${outfiles} ${_qm_filepath})
|
||||||
|
list(APPEND INSTALL_TRANSLATIONS_FILES ${_qm_filepath})
|
||||||
endforeach (_lang)
|
endforeach (_lang)
|
||||||
|
|
||||||
# Generate a qrc file for the translations
|
# Generate a qrc file for the translations
|
||||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
if(NOT INSTALL_TRANSLATIONS)
|
||||||
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
||||||
foreach(_lang ${ADD_PO_LANGUAGES})
|
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
||||||
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
foreach(_lang ${ADD_PO_LANGUAGES})
|
||||||
endforeach(_lang)
|
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
||||||
file(APPEND ${_qrc} "</qresource></RCC>")
|
endforeach(_lang)
|
||||||
qt5_add_resources(${outfiles} ${_qrc})
|
file(APPEND ${_qrc} "</qresource></RCC>")
|
||||||
|
if(BUILD_WITH_QT6)
|
||||||
|
qt6_add_resources(${outfiles} ${_qrc})
|
||||||
|
else()
|
||||||
|
qt5_add_resources(${outfiles} ${_qrc})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
endmacro(add_po)
|
endmacro(add_po)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||||
set(STRAWBERRY_VERSION_MINOR 6)
|
set(STRAWBERRY_VERSION_MINOR 8)
|
||||||
set(STRAWBERRY_VERSION_PATCH 13)
|
set(STRAWBERRY_VERSION_PATCH 5)
|
||||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||||
|
|
||||||
set(INCLUDE_GIT_REVISION OFF)
|
set(INCLUDE_GIT_REVISION OFF)
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
<file>schema/schema-10.sql</file>
|
<file>schema/schema-10.sql</file>
|
||||||
<file>schema/schema-11.sql</file>
|
<file>schema/schema-11.sql</file>
|
||||||
<file>schema/schema-12.sql</file>
|
<file>schema/schema-12.sql</file>
|
||||||
|
<file>schema/schema-13.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>html/playing-tooltip.html</file>
|
<file>style/smartplaylistsearchterm.css</file>
|
||||||
<file>html/oauthsuccess.html</file>
|
<file>html/oauthsuccess.html</file>
|
||||||
<file>pictures/strawberry.png</file>
|
<file>pictures/strawberry.png</file>
|
||||||
|
<file>pictures/strawberry-grey.png</file>
|
||||||
<file>pictures/strawberry-faded.png</file>
|
<file>pictures/strawberry-faded.png</file>
|
||||||
<file>pictures/strawbs.png</file>
|
<file>pictures/strawbs.png</file>
|
||||||
<file>pictures/nomusic.png</file>
|
<file>pictures/nomusic.png</file>
|
||||||
@@ -40,6 +42,8 @@
|
|||||||
<file>pictures/osd_shadow_edge.png</file>
|
<file>pictures/osd_shadow_edge.png</file>
|
||||||
<file>pictures/nyancat.png</file>
|
<file>pictures/nyancat.png</file>
|
||||||
<file>pictures/rainbowdash.png</file>
|
<file>pictures/rainbowdash.png</file>
|
||||||
|
<file>pictures/star-on.png</file>
|
||||||
|
<file>pictures/star-off.png</file>
|
||||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||||
<file>mood/sample.mood</file>
|
<file>mood/sample.mood</file>
|
||||||
<file>text/ghosts.txt</file>
|
<file>text/ghosts.txt</file>
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
<table cellspacing="5" cellpadding="5">
|
|
||||||
<tr>
|
|
||||||
<td colspan="%columns">
|
|
||||||
<center><h4>%appName</h4></center>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
%image
|
|
||||||
<td>
|
|
||||||
<table cellspacing="1" cellpadding="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p align="right">%titleKey</p>
|
|
||||||
</td>
|
|
||||||
<td>%titleValue</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p align="right">%artistKey</p>
|
|
||||||
</td>
|
|
||||||
<td>%artistValue</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p align="right">%albumKey</p>
|
|
||||||
</td>
|
|
||||||
<td>%albumValue</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" height="20" />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p align="right">%lengthKey</p>
|
|
||||||
</td>
|
|
||||||
<td>%lengthValue</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
<file>icons/128x128/star-grey.png</file>
|
<file>icons/128x128/star-grey.png</file>
|
||||||
<file>icons/128x128/star.png</file>
|
<file>icons/128x128/star.png</file>
|
||||||
<file>icons/128x128/strawberry.png</file>
|
<file>icons/128x128/strawberry.png</file>
|
||||||
|
<file>icons/128x128/strawberry-grey.png</file>
|
||||||
<file>icons/128x128/tools-wizard.png</file>
|
<file>icons/128x128/tools-wizard.png</file>
|
||||||
<file>icons/128x128/view-choose.png</file>
|
<file>icons/128x128/view-choose.png</file>
|
||||||
<file>icons/128x128/view-fullscreen.png</file>
|
<file>icons/128x128/view-fullscreen.png</file>
|
||||||
@@ -81,7 +82,6 @@
|
|||||||
<file>icons/128x128/view-refresh.png</file>
|
<file>icons/128x128/view-refresh.png</file>
|
||||||
<file>icons/128x128/library-music.png</file>
|
<file>icons/128x128/library-music.png</file>
|
||||||
<file>icons/128x128/vlc.png</file>
|
<file>icons/128x128/vlc.png</file>
|
||||||
<file>icons/128x128/xine.png</file>
|
|
||||||
<file>icons/128x128/zoom-in.png</file>
|
<file>icons/128x128/zoom-in.png</file>
|
||||||
<file>icons/128x128/zoom-out.png</file>
|
<file>icons/128x128/zoom-out.png</file>
|
||||||
<file>icons/128x128/scrobble.png</file>
|
<file>icons/128x128/scrobble.png</file>
|
||||||
@@ -90,6 +90,8 @@
|
|||||||
<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/qobuz.png</file>
|
||||||
|
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/64x64/albums.png</file>
|
<file>icons/64x64/albums.png</file>
|
||||||
<file>icons/64x64/alsa.png</file>
|
<file>icons/64x64/alsa.png</file>
|
||||||
<file>icons/64x64/application-exit.png</file>
|
<file>icons/64x64/application-exit.png</file>
|
||||||
@@ -163,6 +165,7 @@
|
|||||||
<file>icons/64x64/star-grey.png</file>
|
<file>icons/64x64/star-grey.png</file>
|
||||||
<file>icons/64x64/star.png</file>
|
<file>icons/64x64/star.png</file>
|
||||||
<file>icons/64x64/strawberry.png</file>
|
<file>icons/64x64/strawberry.png</file>
|
||||||
|
<file>icons/64x64/strawberry-grey.png</file>
|
||||||
<file>icons/64x64/tools-wizard.png</file>
|
<file>icons/64x64/tools-wizard.png</file>
|
||||||
<file>icons/64x64/view-choose.png</file>
|
<file>icons/64x64/view-choose.png</file>
|
||||||
<file>icons/64x64/view-fullscreen.png</file>
|
<file>icons/64x64/view-fullscreen.png</file>
|
||||||
@@ -172,7 +175,6 @@
|
|||||||
<file>icons/64x64/view-refresh.png</file>
|
<file>icons/64x64/view-refresh.png</file>
|
||||||
<file>icons/64x64/library-music.png</file>
|
<file>icons/64x64/library-music.png</file>
|
||||||
<file>icons/64x64/vlc.png</file>
|
<file>icons/64x64/vlc.png</file>
|
||||||
<file>icons/64x64/xine.png</file>
|
|
||||||
<file>icons/64x64/zoom-in.png</file>
|
<file>icons/64x64/zoom-in.png</file>
|
||||||
<file>icons/64x64/zoom-out.png</file>
|
<file>icons/64x64/zoom-out.png</file>
|
||||||
<file>icons/64x64/scrobble.png</file>
|
<file>icons/64x64/scrobble.png</file>
|
||||||
@@ -181,6 +183,8 @@
|
|||||||
<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/qobuz.png</file>
|
||||||
|
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/48x48/albums.png</file>
|
<file>icons/48x48/albums.png</file>
|
||||||
<file>icons/48x48/alsa.png</file>
|
<file>icons/48x48/alsa.png</file>
|
||||||
<file>icons/48x48/application-exit.png</file>
|
<file>icons/48x48/application-exit.png</file>
|
||||||
@@ -258,6 +262,7 @@
|
|||||||
<file>icons/48x48/star-grey.png</file>
|
<file>icons/48x48/star-grey.png</file>
|
||||||
<file>icons/48x48/star.png</file>
|
<file>icons/48x48/star.png</file>
|
||||||
<file>icons/48x48/strawberry.png</file>
|
<file>icons/48x48/strawberry.png</file>
|
||||||
|
<file>icons/48x48/strawberry-grey.png</file>
|
||||||
<file>icons/48x48/tools-wizard.png</file>
|
<file>icons/48x48/tools-wizard.png</file>
|
||||||
<file>icons/48x48/view-choose.png</file>
|
<file>icons/48x48/view-choose.png</file>
|
||||||
<file>icons/48x48/view-fullscreen.png</file>
|
<file>icons/48x48/view-fullscreen.png</file>
|
||||||
@@ -267,7 +272,6 @@
|
|||||||
<file>icons/48x48/view-refresh.png</file>
|
<file>icons/48x48/view-refresh.png</file>
|
||||||
<file>icons/48x48/library-music.png</file>
|
<file>icons/48x48/library-music.png</file>
|
||||||
<file>icons/48x48/vlc.png</file>
|
<file>icons/48x48/vlc.png</file>
|
||||||
<file>icons/48x48/xine.png</file>
|
|
||||||
<file>icons/48x48/zoom-in.png</file>
|
<file>icons/48x48/zoom-in.png</file>
|
||||||
<file>icons/48x48/zoom-out.png</file>
|
<file>icons/48x48/zoom-out.png</file>
|
||||||
<file>icons/48x48/scrobble.png</file>
|
<file>icons/48x48/scrobble.png</file>
|
||||||
@@ -276,6 +280,8 @@
|
|||||||
<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/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/32x32/albums.png</file>
|
<file>icons/32x32/albums.png</file>
|
||||||
<file>icons/32x32/alsa.png</file>
|
<file>icons/32x32/alsa.png</file>
|
||||||
<file>icons/32x32/application-exit.png</file>
|
<file>icons/32x32/application-exit.png</file>
|
||||||
@@ -353,6 +359,7 @@
|
|||||||
<file>icons/32x32/star-grey.png</file>
|
<file>icons/32x32/star-grey.png</file>
|
||||||
<file>icons/32x32/star.png</file>
|
<file>icons/32x32/star.png</file>
|
||||||
<file>icons/32x32/strawberry.png</file>
|
<file>icons/32x32/strawberry.png</file>
|
||||||
|
<file>icons/32x32/strawberry-grey.png</file>
|
||||||
<file>icons/32x32/tools-wizard.png</file>
|
<file>icons/32x32/tools-wizard.png</file>
|
||||||
<file>icons/32x32/view-choose.png</file>
|
<file>icons/32x32/view-choose.png</file>
|
||||||
<file>icons/32x32/view-fullscreen.png</file>
|
<file>icons/32x32/view-fullscreen.png</file>
|
||||||
@@ -362,7 +369,6 @@
|
|||||||
<file>icons/32x32/view-refresh.png</file>
|
<file>icons/32x32/view-refresh.png</file>
|
||||||
<file>icons/32x32/library-music.png</file>
|
<file>icons/32x32/library-music.png</file>
|
||||||
<file>icons/32x32/vlc.png</file>
|
<file>icons/32x32/vlc.png</file>
|
||||||
<file>icons/32x32/xine.png</file>
|
|
||||||
<file>icons/32x32/zoom-in.png</file>
|
<file>icons/32x32/zoom-in.png</file>
|
||||||
<file>icons/32x32/zoom-out.png</file>
|
<file>icons/32x32/zoom-out.png</file>
|
||||||
<file>icons/32x32/scrobble.png</file>
|
<file>icons/32x32/scrobble.png</file>
|
||||||
@@ -371,6 +377,8 @@
|
|||||||
<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/qobuz.png</file>
|
||||||
|
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
|
||||||
<file>icons/22x22/albums.png</file>
|
<file>icons/22x22/albums.png</file>
|
||||||
<file>icons/22x22/alsa.png</file>
|
<file>icons/22x22/alsa.png</file>
|
||||||
<file>icons/22x22/application-exit.png</file>
|
<file>icons/22x22/application-exit.png</file>
|
||||||
@@ -448,6 +456,7 @@
|
|||||||
<file>icons/22x22/star-grey.png</file>
|
<file>icons/22x22/star-grey.png</file>
|
||||||
<file>icons/22x22/star.png</file>
|
<file>icons/22x22/star.png</file>
|
||||||
<file>icons/22x22/strawberry.png</file>
|
<file>icons/22x22/strawberry.png</file>
|
||||||
|
<file>icons/22x22/strawberry-grey.png</file>
|
||||||
<file>icons/22x22/tools-wizard.png</file>
|
<file>icons/22x22/tools-wizard.png</file>
|
||||||
<file>icons/22x22/view-choose.png</file>
|
<file>icons/22x22/view-choose.png</file>
|
||||||
<file>icons/22x22/view-fullscreen.png</file>
|
<file>icons/22x22/view-fullscreen.png</file>
|
||||||
@@ -457,7 +466,6 @@
|
|||||||
<file>icons/22x22/view-refresh.png</file>
|
<file>icons/22x22/view-refresh.png</file>
|
||||||
<file>icons/22x22/library-music.png</file>
|
<file>icons/22x22/library-music.png</file>
|
||||||
<file>icons/22x22/vlc.png</file>
|
<file>icons/22x22/vlc.png</file>
|
||||||
<file>icons/22x22/xine.png</file>
|
|
||||||
<file>icons/22x22/zoom-in.png</file>
|
<file>icons/22x22/zoom-in.png</file>
|
||||||
<file>icons/22x22/zoom-out.png</file>
|
<file>icons/22x22/zoom-out.png</file>
|
||||||
<file>icons/22x22/scrobble.png</file>
|
<file>icons/22x22/scrobble.png</file>
|
||||||
@@ -466,5 +474,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/qobuz.png</file>
|
||||||
|
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
BIN
data/icons/128x128/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
data/icons/128x128/qobuz.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
data/icons/128x128/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
BIN
data/icons/22x22/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 918 B |
BIN
data/icons/22x22/qobuz.png
Normal file
|
After Width: | Height: | Size: 964 B |
BIN
data/icons/22x22/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
data/icons/32x32/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
data/icons/32x32/qobuz.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
data/icons/32x32/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
BIN
data/icons/48x48/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
data/icons/48x48/qobuz.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
data/icons/48x48/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
data/icons/64x64/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
data/icons/64x64/qobuz.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
data/icons/64x64/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
BIN
data/icons/full/multimedia-player-ipod-standard-black.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
data/icons/full/qobuz.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
data/icons/full/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
BIN
data/pictures/star-off.png
Normal file
|
After Width: | Height: | Size: 351 B |
BIN
data/pictures/star-on.png
Normal file
|
After Width: | Height: | Size: 284 B |
BIN
data/pictures/strawberry-grey.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
@@ -62,7 +62,9 @@ CREATE TABLE device_%deviceid_songs (
|
|||||||
effective_albumartist TEXT,
|
effective_albumartist TEXT,
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
cue_path TEXT
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -72,7 +74,7 @@ CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (c
|
|||||||
|
|
||||||
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
||||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
UPDATE devices SET schema_version=1 WHERE ROWID=%deviceid;
|
UPDATE devices SET schema_version=2 WHERE ROWID=%deviceid;
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
231
data/schema/schema-13.sql
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
ALTER TABLE %allsongstables ADD COLUMN rating INTEGER DEFAULT -1;
|
||||||
|
|
||||||
|
ALTER TABLE playlists ADD COLUMN dynamic_playlist_type INTEGER;
|
||||||
|
|
||||||
|
ALTER TABLE playlists ADD COLUMN dynamic_playlist_backend TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE playlists ADD COLUMN dynamic_playlist_data BLOB;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE schema_version SET version=13;
|
||||||
@@ -13,7 +13,7 @@ CREATE VIRTUAL TABLE %allsongstables_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ CREATE VIRTUAL TABLE playlist_items_fts_ USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|||||||
|
|
||||||
DELETE FROM schema_version;
|
DELETE FROM schema_version;
|
||||||
|
|
||||||
INSERT INTO schema_version (version) VALUES (12);
|
INSERT INTO schema_version (version) VALUES (13);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS directories (
|
CREATE TABLE IF NOT EXISTS directories (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS songs (
|
|||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
@@ -70,178 +70,9 @@ CREATE TABLE IF NOT EXISTS songs (
|
|||||||
effective_albumartist TEXT,
|
effective_albumartist TEXT,
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
cue_path TEXT
|
cue_path TEXT,
|
||||||
|
|
||||||
);
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
|
||||||
|
|
||||||
title TEXT,
|
|
||||||
album TEXT,
|
|
||||||
artist TEXT,
|
|
||||||
albumartist TEXT,
|
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
genre TEXT,
|
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
|
||||||
composer TEXT,
|
|
||||||
performer TEXT,
|
|
||||||
grouping TEXT,
|
|
||||||
comment TEXT,
|
|
||||||
lyrics TEXT,
|
|
||||||
|
|
||||||
artist_id TEXT,
|
|
||||||
album_id TEXT,
|
|
||||||
song_id TEXT,
|
|
||||||
|
|
||||||
beginning INTEGER NOT NULL DEFAULT 0,
|
|
||||||
length INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
source INTEGER NOT NULL DEFAULT 0,
|
|
||||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
filetype INTEGER NOT NULL DEFAULT 0,
|
|
||||||
filesize INTEGER NOT NULL DEFAULT -1,
|
|
||||||
mtime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
ctime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
unavailable INTEGER DEFAULT 0,
|
|
||||||
|
|
||||||
playcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
compilation_detected INTEGER DEFAULT 0,
|
|
||||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
art_automatic TEXT,
|
|
||||||
art_manual TEXT,
|
|
||||||
|
|
||||||
effective_albumartist TEXT,
|
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
cue_path TEXT
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
|
||||||
|
|
||||||
title TEXT,
|
|
||||||
album TEXT,
|
|
||||||
artist TEXT,
|
|
||||||
albumartist TEXT,
|
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
genre TEXT,
|
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
|
||||||
composer TEXT,
|
|
||||||
performer TEXT,
|
|
||||||
grouping TEXT,
|
|
||||||
comment TEXT,
|
|
||||||
lyrics TEXT,
|
|
||||||
|
|
||||||
artist_id TEXT,
|
|
||||||
album_id TEXT,
|
|
||||||
song_id TEXT,
|
|
||||||
|
|
||||||
beginning INTEGER NOT NULL DEFAULT 0,
|
|
||||||
length INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
source INTEGER NOT NULL DEFAULT 0,
|
|
||||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
filetype INTEGER NOT NULL DEFAULT 0,
|
|
||||||
filesize INTEGER NOT NULL DEFAULT -1,
|
|
||||||
mtime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
ctime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
unavailable INTEGER DEFAULT 0,
|
|
||||||
|
|
||||||
playcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
compilation_detected INTEGER DEFAULT 0,
|
|
||||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
art_automatic TEXT,
|
|
||||||
art_manual TEXT,
|
|
||||||
|
|
||||||
effective_albumartist TEXT,
|
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
cue_path TEXT
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tidal_songs (
|
|
||||||
|
|
||||||
title TEXT,
|
|
||||||
album TEXT,
|
|
||||||
artist TEXT,
|
|
||||||
albumartist TEXT,
|
|
||||||
track INTEGER NOT NULL DEFAULT -1,
|
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
genre TEXT,
|
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
|
||||||
composer TEXT,
|
|
||||||
performer TEXT,
|
|
||||||
grouping TEXT,
|
|
||||||
comment TEXT,
|
|
||||||
lyrics TEXT,
|
|
||||||
|
|
||||||
artist_id TEXT,
|
|
||||||
album_id TEXT,
|
|
||||||
song_id TEXT,
|
|
||||||
|
|
||||||
beginning INTEGER NOT NULL DEFAULT 0,
|
|
||||||
length INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
bitrate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
samplerate INTEGER NOT NULL DEFAULT -1,
|
|
||||||
bitdepth INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
source INTEGER NOT NULL DEFAULT 0,
|
|
||||||
directory_id INTEGER NOT NULL DEFAULT -1,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
filetype INTEGER NOT NULL DEFAULT 0,
|
|
||||||
filesize INTEGER NOT NULL DEFAULT -1,
|
|
||||||
mtime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
ctime INTEGER NOT NULL DEFAULT -1,
|
|
||||||
unavailable INTEGER DEFAULT 0,
|
|
||||||
|
|
||||||
playcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
|
||||||
lastplayed INTEGER NOT NULL DEFAULT -1,
|
|
||||||
|
|
||||||
compilation_detected INTEGER DEFAULT 0,
|
|
||||||
compilation_on INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_off INTEGER NOT NULL DEFAULT 0,
|
|
||||||
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
art_automatic TEXT,
|
|
||||||
art_manual TEXT,
|
|
||||||
|
|
||||||
effective_albumartist TEXT,
|
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
cue_path TEXT
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -254,7 +85,7 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
|||||||
track INTEGER NOT NULL DEFAULT -1,
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
disc INTEGER NOT NULL DEFAULT -1,
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
year INTEGER NOT NULL DEFAULT -1,
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
originalyear INTEGER NOT NULL DEFAULT 0,
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
genre TEXT,
|
genre TEXT,
|
||||||
compilation INTEGER NOT NULL DEFAULT 0,
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
composer TEXT,
|
composer TEXT,
|
||||||
@@ -298,7 +129,363 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
|
|||||||
effective_albumartist TEXT,
|
effective_albumartist TEXT,
|
||||||
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
cue_path TEXT
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS tidal_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qobuz_songs (
|
||||||
|
|
||||||
|
title TEXT,
|
||||||
|
album TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
albumartist TEXT,
|
||||||
|
track INTEGER NOT NULL DEFAULT -1,
|
||||||
|
disc INTEGER NOT NULL DEFAULT -1,
|
||||||
|
year INTEGER NOT NULL DEFAULT -1,
|
||||||
|
originalyear INTEGER NOT NULL DEFAULT -1,
|
||||||
|
genre TEXT,
|
||||||
|
compilation INTEGER NOT NULL DEFAULT 0,
|
||||||
|
composer TEXT,
|
||||||
|
performer TEXT,
|
||||||
|
grouping TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
lyrics TEXT,
|
||||||
|
|
||||||
|
artist_id TEXT,
|
||||||
|
album_id TEXT,
|
||||||
|
song_id TEXT,
|
||||||
|
|
||||||
|
beginning INTEGER NOT NULL DEFAULT 0,
|
||||||
|
length INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
bitrate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
samplerate INTEGER NOT NULL DEFAULT -1,
|
||||||
|
bitdepth INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
source INTEGER NOT NULL DEFAULT 0,
|
||||||
|
directory_id INTEGER NOT NULL DEFAULT -1,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filetype INTEGER NOT NULL DEFAULT 0,
|
||||||
|
filesize INTEGER NOT NULL DEFAULT -1,
|
||||||
|
mtime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
ctime INTEGER NOT NULL DEFAULT -1,
|
||||||
|
unavailable INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
playcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
lastplayed INTEGER NOT NULL DEFAULT -1,
|
||||||
|
|
||||||
|
compilation_detected INTEGER DEFAULT 0,
|
||||||
|
compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||||
|
compilation_effective INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
art_automatic TEXT,
|
||||||
|
art_manual TEXT,
|
||||||
|
|
||||||
|
effective_albumartist TEXT,
|
||||||
|
effective_originalyear INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -309,7 +496,11 @@ CREATE TABLE IF NOT EXISTS playlists (
|
|||||||
ui_order INTEGER NOT NULL DEFAULT 0,
|
ui_order INTEGER NOT NULL DEFAULT 0,
|
||||||
special_type TEXT,
|
special_type TEXT,
|
||||||
ui_path TEXT,
|
ui_path TEXT,
|
||||||
is_favorite INTEGER NOT NULL DEFAULT 0
|
is_favorite INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
dynamic_playlist_type INTEGER,
|
||||||
|
dynamic_playlist_backend TEXT,
|
||||||
|
dynamic_playlist_data BLOB
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -371,7 +562,9 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
|||||||
effective_albumartist TEXT,
|
effective_albumartist TEXT,
|
||||||
effective_originalyear INTEGER,
|
effective_originalyear INTEGER,
|
||||||
|
|
||||||
cue_path TEXT
|
cue_path TEXT,
|
||||||
|
|
||||||
|
rating INTEGER DEFAULT -1
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -410,52 +603,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
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 0"
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
|
||||||
|
|
||||||
ftstitle,
|
|
||||||
ftsalbum,
|
|
||||||
ftsartist,
|
|
||||||
ftsalbumartist,
|
|
||||||
ftscomposer,
|
|
||||||
ftsperformer,
|
|
||||||
ftsgrouping,
|
|
||||||
ftsgenre,
|
|
||||||
ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
|
||||||
|
|
||||||
ftstitle,
|
|
||||||
ftsalbum,
|
|
||||||
ftsartist,
|
|
||||||
ftsalbumartist,
|
|
||||||
ftscomposer,
|
|
||||||
ftsperformer,
|
|
||||||
ftsgrouping,
|
|
||||||
ftsgenre,
|
|
||||||
ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -470,11 +618,11 @@ CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_songs_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts5(
|
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
|
||||||
|
|
||||||
ftstitle,
|
ftstitle,
|
||||||
ftsalbum,
|
ftsalbum,
|
||||||
@@ -485,7 +633,82 @@ CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts5(
|
||||||
|
|
||||||
|
ftstitle,
|
||||||
|
ftsalbum,
|
||||||
|
ftsartist,
|
||||||
|
ftsalbumartist,
|
||||||
|
ftscomposer,
|
||||||
|
ftsperformer,
|
||||||
|
ftsgrouping,
|
||||||
|
ftsgenre,
|
||||||
|
ftscomment,
|
||||||
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -500,6 +723,6 @@ CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts5(
|
|||||||
ftsgrouping,
|
ftsgrouping,
|
||||||
ftsgenre,
|
ftsgenre,
|
||||||
ftscomment,
|
ftscomment,
|
||||||
tokenize = "unicode61 remove_diacritics 0"
|
tokenize = "unicode61 remove_diacritics 1"
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
42
data/style/smartplaylistsearchterm.css
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#frame {
|
||||||
|
border: 1px solid palette(mid);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#remove {
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
border: 0px solid transparent;
|
||||||
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||||
|
stop:0 %light,
|
||||||
|
stop:0.4 %light,
|
||||||
|
stop:0.6 %dark,
|
||||||
|
stop:1 %dark);
|
||||||
|
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#remove:hover {
|
||||||
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||||
|
stop:0 %light2,
|
||||||
|
stop:0.4 %light2,
|
||||||
|
stop:0.6 %base,
|
||||||
|
stop:1 %base);
|
||||||
|
border: 0px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#remove:pressed {
|
||||||
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||||
|
stop:0 %base,
|
||||||
|
stop:0.4 %base,
|
||||||
|
stop:0.6 %light2,
|
||||||
|
stop:1 %light2);
|
||||||
|
border: 0px solid transparent;
|
||||||
|
}
|
||||||
3
debian/control
vendored
@@ -24,10 +24,7 @@ Build-Depends: debhelper (>= 11),
|
|||||||
libgstreamer-plugins-base1.0-dev,
|
libgstreamer-plugins-base1.0-dev,
|
||||||
libcdio-dev,
|
libcdio-dev,
|
||||||
libgpod-dev,
|
libgpod-dev,
|
||||||
libimobiledevice-dev,
|
|
||||||
libmtp-dev,
|
libmtp-dev,
|
||||||
libplist-dev,
|
|
||||||
libusbmuxd-dev,
|
|
||||||
libchromaprint-dev,
|
libchromaprint-dev,
|
||||||
libfftw3-dev
|
libfftw3-dev
|
||||||
Standards-Version: 4.2.1
|
Standards-Version: 4.2.1
|
||||||
|
|||||||
38
debian/copyright
vendored
@@ -12,7 +12,6 @@ Files: src/core/timeconstants.h
|
|||||||
ext/libstrawberry-common/core/logging.h
|
ext/libstrawberry-common/core/logging.h
|
||||||
ext/libstrawberry-common/core/messagehandler.cpp
|
ext/libstrawberry-common/core/messagehandler.cpp
|
||||||
ext/libstrawberry-common/core/messagehandler.h
|
ext/libstrawberry-common/core/messagehandler.h
|
||||||
ext/libstrawberry-common/core/override.h
|
|
||||||
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
|
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
|
|
||||||
@@ -122,8 +121,14 @@ Files: src/core/main.cpp
|
|||||||
src/context/contextalbumsview.h
|
src/context/contextalbumsview.h
|
||||||
src/widgets/playingwidget.cpp
|
src/widgets/playingwidget.cpp
|
||||||
src/widgets/playingwidget.h
|
src/widgets/playingwidget.h
|
||||||
src/widgets/osdpretty.cpp
|
src/osd/osdbase.cpp
|
||||||
src/widgets/osdpretty.h
|
src/osd/osdbase.h
|
||||||
|
src/osd/osdpretty.cpp
|
||||||
|
src/osd/osdpretty.h
|
||||||
|
src/osd/osddbus.cpp
|
||||||
|
src/osd/osddbus.h
|
||||||
|
src/osd/osdmac.cpp
|
||||||
|
src/osd/osdmac.h
|
||||||
src/dialogs/about.cpp
|
src/dialogs/about.cpp
|
||||||
src/dialogs/about.h
|
src/dialogs/about.h
|
||||||
src/playlist/playlist.cpp
|
src/playlist/playlist.cpp
|
||||||
@@ -171,12 +176,12 @@ Files: src/core/main.cpp
|
|||||||
src/settings/shortcutssettingspage.h
|
src/settings/shortcutssettingspage.h
|
||||||
src/settings/appearancesettingspage.cpp
|
src/settings/appearancesettingspage.cpp
|
||||||
src/settings/appearancesettingspage.h
|
src/settings/appearancesettingspage.h
|
||||||
src/organise/organise.cpp
|
src/organize/organize.cpp
|
||||||
src/organise/organise.h
|
src/organize/organize.h
|
||||||
src/organise/organisedialog.cpp
|
src/organize/organizedialog.cpp
|
||||||
src/organise/organisedialog.h
|
src/organize/organizedialog.h
|
||||||
src/organise/organiseerrordialog.cpp
|
src/organize/organizeerrordialog.cpp
|
||||||
src/organise/organiseerrordialog.h
|
src/organize/organizeerrordialog.h
|
||||||
src/transcoder/transcoder.cpp
|
src/transcoder/transcoder.cpp
|
||||||
src/transcoder/transcoder.h
|
src/transcoder/transcoder.h
|
||||||
src/musicbrainz/musicbrainzclient.cpp
|
src/musicbrainz/musicbrainzclient.cpp
|
||||||
@@ -213,21 +218,6 @@ Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
|||||||
2003-2005, Mark Kretschmann <markey@web.de>
|
2003-2005, Mark Kretschmann <markey@web.de>
|
||||||
License: GPL-2+
|
License: GPL-2+
|
||||||
|
|
||||||
Files: src/engine/xineengine.cpp
|
|
||||||
src/engine/xineengine.h
|
|
||||||
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
2005, Ian Monroe <ian@monroe.nu>
|
|
||||||
2005, Christophe Thommeret <hftom@free.fr>
|
|
||||||
2005, 2006, Mark Kretschmann <markey@web.de>
|
|
||||||
2004, 2005, Max Howell <max.howell@methylblue.com>
|
|
||||||
2003, 2004, J. Kofler <kaffeine@gmx.net>
|
|
||||||
License: GPL-2+
|
|
||||||
|
|
||||||
Files: src/engine/xinescope.c
|
|
||||||
src/engine/xinescope.h
|
|
||||||
Copyright: 2004, Max Howell <max.howell@methylblue.com>
|
|
||||||
License: GPL-2+
|
|
||||||
|
|
||||||
Files: src/widgets/fancytabwidget.cpp
|
Files: src/widgets/fancytabwidget.cpp
|
||||||
src/widgets/fancytabwidget.h
|
src/widgets/fancytabwidget.h
|
||||||
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>
|
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>
|
||||||
|
|||||||
1
debian/rules
vendored
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
override_dh_auto_clean:
|
override_dh_auto_clean:
|
||||||
rm -f dist/macos/Info.plist
|
rm -f dist/macos/Info.plist
|
||||||
rm -f dist/macos/create-dmg.sh
|
|
||||||
rm -f dist/unix/PKGBUILD
|
rm -f dist/unix/PKGBUILD
|
||||||
rm -f dist/unix/strawberry.spec
|
rm -f dist/unix/strawberry.spec
|
||||||
rm -f dist/scripts/maketarball.sh
|
rm -f dist/scripts/maketarball.sh
|
||||||
|
|||||||
7
dist/CMakeLists.txt
vendored
@@ -1,9 +1,14 @@
|
|||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
|
||||||
|
if(RPM_DISTRO AND RPM_DATE)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec @ONLY)
|
||||||
|
endif(RPM_DISTRO AND RPM_DATE)
|
||||||
|
if(DEB_CODENAME AND DEB_DATE)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
|
||||||
|
endif(DEB_CODENAME AND DEB_DATE)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
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)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh)
|
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|||||||
4
dist/macos/Info.plist.in
vendored
@@ -34,6 +34,10 @@
|
|||||||
<string>public.app-category.music</string>
|
<string>public.app-category.music</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.13.4</string>
|
<string>10.13.4</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|||||||
24
dist/macos/create-dmg.sh.in
vendored
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
version="@STRAWBERRY_VERSION_PACKAGE@"
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "Usage: $0 <bundle.app> (append)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
name=$(basename "$1" | perl -pe 's/(.*).app/\1/')
|
|
||||||
bundle_dir="$1"
|
|
||||||
temp_dir="dmg/$name"
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
output_file="$name-$version.dmg"
|
|
||||||
else
|
|
||||||
output_file="$name-$2-$version.dmg"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf "$temp_dir"
|
|
||||||
rm -f "$output_file"
|
|
||||||
|
|
||||||
mkdir -p "$temp_dir"
|
|
||||||
|
|
||||||
/usr/local/bin/create-dmg --volname "$name" --background "@CMAKE_SOURCE_DIR@/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon $bundle_dir 150 218 --window-size 600 450 $output_file $bundle_dir
|
|
||||||
183
dist/macos/macdeploy.py
vendored
@@ -21,17 +21,17 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import commands
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
LOGGER = logging.getLogger('macdeploy')
|
LOGGER = logging.getLogger('macdeploy')
|
||||||
|
|
||||||
LIBRARY_SEARCH_PATH = ['/usr/local/lib']
|
LIBRARY_SEARCH_PATH = ['/usr/local/lib', '/usr/local/opt/icu4c/lib']
|
||||||
|
|
||||||
FRAMEWORK_SEARCH_PATH = [
|
FRAMEWORK_SEARCH_PATH = [
|
||||||
'/Library/Frameworks',
|
'/Library/Frameworks',
|
||||||
os.path.join(os.environ['HOME'], 'Library/Frameworks')
|
os.path.join(os.environ['HOME'], 'Library/Frameworks'),
|
||||||
|
'/Library/Frameworks/Sparkle.framework/Versions'
|
||||||
]
|
]
|
||||||
|
|
||||||
QT_PLUGINS = [
|
QT_PLUGINS = [
|
||||||
@@ -66,56 +66,56 @@ GSTREAMER_SEARCH_PATH = [
|
|||||||
|
|
||||||
GSTREAMER_PLUGINS = [
|
GSTREAMER_PLUGINS = [
|
||||||
|
|
||||||
'libgstapetag.so',
|
'libgstapetag.dylib',
|
||||||
'libgstapp.so',
|
'libgstapp.dylib',
|
||||||
'libgstaudioconvert.so',
|
'libgstaudioconvert.dylib',
|
||||||
'libgstaudiofx.so',
|
'libgstaudiofx.dylib',
|
||||||
'libgstaudiomixer.so',
|
'libgstaudiomixer.dylib',
|
||||||
'libgstaudioparsers.so',
|
'libgstaudioparsers.dylib',
|
||||||
'libgstaudiorate.so',
|
'libgstaudiorate.dylib',
|
||||||
'libgstaudioresample.so',
|
'libgstaudioresample.dylib',
|
||||||
'libgstaudiotestsrc.so',
|
'libgstaudiotestsrc.dylib',
|
||||||
'libgstaudiovisualizers.so',
|
'libgstaudiovisualizers.dylib',
|
||||||
'libgstauparse.so',
|
'libgstauparse.dylib',
|
||||||
'libgstautoconvert.so',
|
'libgstautoconvert.dylib',
|
||||||
'libgstautodetect.so',
|
'libgstautodetect.dylib',
|
||||||
'libgstcoreelements.so',
|
'libgstcoreelements.dylib',
|
||||||
'libgstequalizer.so',
|
'libgstequalizer.dylib',
|
||||||
'libgstgio.so',
|
'libgstgio.dylib',
|
||||||
'libgsticydemux.so',
|
'libgsticydemux.dylib',
|
||||||
'libgstid3demux.so',
|
'libgstid3demux.dylib',
|
||||||
'libgstlevel.so',
|
'libgstlevel.dylib',
|
||||||
'libgstosxaudio.so',
|
'libgstosxaudio.dylib',
|
||||||
'libgstplayback.so',
|
'libgstplayback.dylib',
|
||||||
'libgstrawparse.so',
|
'libgstrawparse.dylib',
|
||||||
'libgstrealmedia.so',
|
'libgstreplaygain.dylib',
|
||||||
'libgstreplaygain.so',
|
'libgstsoup.dylib',
|
||||||
'libgstsoup.so',
|
'libgstspectrum.dylib',
|
||||||
'libgstspectrum.so',
|
'libgsttypefindfunctions.dylib',
|
||||||
'libgsttypefindfunctions.so',
|
'libgstvolume.dylib',
|
||||||
'libgstvolume.so',
|
'libgstxingmux.dylib',
|
||||||
'libgstxingmux.so',
|
'libgsttcp.dylib',
|
||||||
'libgsttcp.so',
|
'libgstudp.dylib',
|
||||||
'libgstudp.so',
|
'libgstpbtypes.dylib',
|
||||||
'libgstpbtypes.so',
|
'libgstrtp.dylib',
|
||||||
'libgstrtp.so',
|
'libgstrtsp.dylib',
|
||||||
'libgstrtsp.so',
|
|
||||||
|
|
||||||
'libgstflac.so',
|
'libgstflac.dylib',
|
||||||
'libgstwavparse.so',
|
'libgstwavparse.dylib',
|
||||||
'libgstfaac.so',
|
'libgstfaac.dylib',
|
||||||
'libgstfaad.so',
|
'libgstfaad.dylib',
|
||||||
'libgstogg.so',
|
'libgstogg.dylib',
|
||||||
'libgstopus.so',
|
'libgstopus.dylib',
|
||||||
'libgstopusparse.so',
|
'libgstopusparse.dylib',
|
||||||
'libgstasf.so',
|
'libgstasf.dylib',
|
||||||
'libgstspeex.so',
|
'libgstspeex.dylib',
|
||||||
'libgsttaglib.so',
|
'libgsttaglib.dylib',
|
||||||
'libgstvorbis.so',
|
'libgstvorbis.dylib',
|
||||||
'libgstisomp4.so',
|
'libgstisomp4.dylib',
|
||||||
'libgstlibav.so',
|
'libgstlibav.dylib',
|
||||||
'libgstaiff.so',
|
'libgstaiff.dylib',
|
||||||
'libgstlame.so',
|
'libgstlame.dylib',
|
||||||
|
'libgstmusepack.dylib',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -154,14 +154,8 @@ class InstallNameToolError(Error):
|
|||||||
class CouldNotFindGstreamerPluginError(Error):
|
class CouldNotFindGstreamerPluginError(Error):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CouldNotFindXinePluginError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CouldNotFindVLCPluginError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print 'Usage: %s <bundle.app>' % sys.argv[0]
|
print('Usage: %s <bundle.app>' % sys.argv[0])
|
||||||
|
|
||||||
bundle_dir = sys.argv[1]
|
bundle_dir = sys.argv[1]
|
||||||
|
|
||||||
@@ -181,27 +175,40 @@ fixed_frameworks = set()
|
|||||||
|
|
||||||
|
|
||||||
def GetBrokenLibraries(binary):
|
def GetBrokenLibraries(binary):
|
||||||
#print "Checking libs for binary: %s" % binary
|
#print("Checking libs for binary: %s" % binary)
|
||||||
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0]
|
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
|
||||||
broken_libs = {'frameworks': [], 'libs': []}
|
broken_libs = {'frameworks': [], 'libs': []}
|
||||||
for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
|
for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
|
||||||
#print "Checking line: %s" % line
|
#print("Checking line: %s" % line)
|
||||||
if not line: # skip empty lines
|
if not line: # skip empty lines
|
||||||
continue
|
continue
|
||||||
if os.path.basename(binary) == os.path.basename(line):
|
if os.path.basename(binary) == os.path.basename(line):
|
||||||
#print "mnope %s-%s" % (os.path.basename(binary), os.path.basename(line))
|
#print("mnope %s-%s" % (os.path.basename(binary), os.path.basename(line)))
|
||||||
continue
|
continue
|
||||||
if re.match(r'^\s*/System/', line):
|
if re.match(r'^\s*/System/', line):
|
||||||
|
#print("system framework: %s" % line)
|
||||||
continue # System framework
|
continue # System framework
|
||||||
elif re.match(r'^\s*/usr/lib/', line):
|
elif re.match(r'^\s*/usr/lib/', line):
|
||||||
#print "unix style system lib"
|
#print("unix style system lib: %s" % line)
|
||||||
continue # unix style system library
|
continue # unix style system library
|
||||||
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
|
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@rpath', line) or re.match(r'^\s*@loader_path', line):
|
||||||
# Potentially already fixed library
|
# Potentially already fixed library
|
||||||
relative_path = os.path.join(*line.split('/')[3:])
|
if line.count('/') == 1:
|
||||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
relative_path = os.path.join(*line.split('/')[1:])
|
||||||
broken_libs['frameworks'].append(relative_path)
|
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||||
|
broken_libs['libs'].append(relative_path)
|
||||||
|
elif line.count('/') == 2:
|
||||||
|
relative_path = os.path.join(*line.split('/')[2:])
|
||||||
|
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||||
|
broken_libs['libs'].append(relative_path)
|
||||||
|
elif line.count('/') >= 3:
|
||||||
|
relative_path = os.path.join(*line.split('/')[3:])
|
||||||
|
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||||
|
broken_libs['frameworks'].append(relative_path)
|
||||||
|
else:
|
||||||
|
print("GetBrokenLibraries Error: %s" % line)
|
||||||
elif re.search(r'\w+\.framework', line):
|
elif re.search(r'\w+\.framework', line):
|
||||||
|
#print("framework: %s" % line)
|
||||||
broken_libs['frameworks'].append(line)
|
broken_libs['frameworks'].append(line)
|
||||||
else:
|
else:
|
||||||
broken_libs['libs'].append(line)
|
broken_libs['libs'].append(line)
|
||||||
@@ -261,14 +268,21 @@ def FixFramework(path):
|
|||||||
|
|
||||||
|
|
||||||
def FixLibrary(path):
|
def FixLibrary(path):
|
||||||
if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
|
|
||||||
|
if path in fixed_libraries:
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
fixed_libraries.add(path)
|
# Always bundle libraries provided by homebrew (/usr/local).
|
||||||
|
if not re.match(r'^\s*/usr/local', path) and FindSystemLibrary(os.path.basename(path)) is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
fixed_libraries.add(path)
|
||||||
|
|
||||||
abs_path = FindLibrary(path)
|
abs_path = FindLibrary(path)
|
||||||
if abs_path == "":
|
if abs_path == "":
|
||||||
print "Could not resolve %s, not fixing!" % path
|
print("Could not resolve %s, not fixing!" % path)
|
||||||
return
|
return
|
||||||
|
|
||||||
broken_libs = GetBrokenLibraries(abs_path)
|
broken_libs = GetBrokenLibraries(abs_path)
|
||||||
FixAllLibraries(broken_libs)
|
FixAllLibraries(broken_libs)
|
||||||
|
|
||||||
@@ -416,7 +430,7 @@ def FindSystemLibrary(library_name):
|
|||||||
|
|
||||||
def FixLibraryInstallPath(library_path, library):
|
def FixLibraryInstallPath(library_path, library):
|
||||||
system_library = FindSystemLibrary(os.path.basename(library_path))
|
system_library = FindSystemLibrary(os.path.basename(library_path))
|
||||||
if system_library is None:
|
if system_library is None or re.match(r'^\s*/usr/local', library_path):
|
||||||
new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path)
|
new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path)
|
||||||
FixInstallPath(library_path, library, new_path)
|
FixInstallPath(library_path, library, new_path)
|
||||||
else:
|
else:
|
||||||
@@ -425,21 +439,14 @@ def FixLibraryInstallPath(library_path, library):
|
|||||||
|
|
||||||
def FixFrameworkInstallPath(library_path, library):
|
def FixFrameworkInstallPath(library_path, library):
|
||||||
parts = library_path.split(os.sep)
|
parts = library_path.split(os.sep)
|
||||||
|
full_path = ""
|
||||||
for i, part in enumerate(parts):
|
for i, part in enumerate(parts):
|
||||||
if re.match(r'\w+\.framework', part):
|
if re.match(r'\w+\.framework', part):
|
||||||
full_path = os.path.join(*parts[i:])
|
full_path = os.path.join(*parts[i:])
|
||||||
break
|
break
|
||||||
new_path = '@executable_path/../Frameworks/%s' % full_path
|
if full_path:
|
||||||
FixInstallPath(library_path, library, new_path)
|
new_path = '@executable_path/../Frameworks/%s' % full_path
|
||||||
|
FixInstallPath(library_path, library, new_path)
|
||||||
|
|
||||||
def FindXinePlugin(name):
|
|
||||||
for path in XINEPLUGIN_SEARCH_PATH:
|
|
||||||
if os.path.exists(path):
|
|
||||||
for dir, dirs, files in os.walk(path):
|
|
||||||
if name in files:
|
|
||||||
return os.path.join(dir, name)
|
|
||||||
raise CouldNotFindXinePluginError(name)
|
|
||||||
|
|
||||||
|
|
||||||
def FindQtPlugin(name):
|
def FindQtPlugin(name):
|
||||||
@@ -476,7 +483,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
FixPlugin('strawberry-tagreader', '.')
|
FixPlugin('strawberry-tagreader', '.')
|
||||||
except:
|
except:
|
||||||
print 'Failed to find blob: %s' % traceback.format_exc()
|
print('Failed to find blob: %s' % traceback.format_exc())
|
||||||
|
|
||||||
for plugin in GSTREAMER_PLUGINS:
|
for plugin in GSTREAMER_PLUGINS:
|
||||||
FixPlugin(FindGstreamerPlugin(plugin), 'gstreamer')
|
FixPlugin(FindGstreamerPlugin(plugin), 'gstreamer')
|
||||||
@@ -489,11 +496,11 @@ def main():
|
|||||||
#FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
#FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
||||||
|
|
||||||
if len(sys.argv) <= 2:
|
if len(sys.argv) <= 2:
|
||||||
print 'Would run %d commands:' % len(commands)
|
print('Would run %d commands:' % len(commands))
|
||||||
for command in commands:
|
for command in commands:
|
||||||
print ' '.join(command)
|
print(' '.join(command))
|
||||||
|
|
||||||
#print 'OK?'
|
#print('OK?')
|
||||||
#raw_input()
|
#raw_input()
|
||||||
|
|
||||||
for command in commands:
|
for command in commands:
|
||||||
|
|||||||
22
dist/macos/macversion.sh
vendored
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
macos_version=$(sw_vers -productVersion)
|
||||||
|
macos_version_major=$(echo $macos_version | awk -F '[.]' '{print $1}')
|
||||||
|
macos_version_minor=$(echo $macos_version | awk -F '[.]' '{print $2}')
|
||||||
|
|
||||||
|
if [ "${macos_version_major}" = "10" ]; then
|
||||||
|
macos_codenames=(
|
||||||
|
["13"]="highsierra"
|
||||||
|
["14"]="mojave"
|
||||||
|
["15"]="catalina"
|
||||||
|
)
|
||||||
|
if [[ -n "${macos_codenames[$macos_version_minor]}" ]]; then
|
||||||
|
echo "${macos_codenames[$macos_version_minor]}"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
|
elif [ "${macos_version_major}" = "11" ]; then
|
||||||
|
echo "bigsur"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
1
dist/scripts/maketarball.sh.in
vendored
@@ -38,7 +38,6 @@ tar -cJf $name-$version.tar.xz \
|
|||||||
--exclude="$root/debian/changelog" \
|
--exclude="$root/debian/changelog" \
|
||||||
--exclude="$root/dist/scripts/maketarball.sh" \
|
--exclude="$root/dist/scripts/maketarball.sh" \
|
||||||
--exclude="$root/dist/unix/PKGBUILD" \
|
--exclude="$root/dist/unix/PKGBUILD" \
|
||||||
--exclude="$root/dist/macos/create-dmg.sh" \
|
|
||||||
--exclude="$root/dist/macos/Info.plist" \
|
--exclude="$root/dist/macos/Info.plist" \
|
||||||
--exclude="$root/dist/windows/windres.rc" \
|
--exclude="$root/dist/windows/windres.rc" \
|
||||||
--exclude="$root/src/translations/translations.pot" \
|
--exclude="$root/src/translations/translations.pot" \
|
||||||
|
|||||||
112
dist/scripts/verify-icons.sh
vendored
@@ -16,85 +16,61 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
# along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
sizes="128x128 64x64 48x48 32x32 22x22"
|
sizes="full 128 64 48 32 22"
|
||||||
|
|
||||||
#
|
for s in $sizes
|
||||||
|
|
||||||
#for i in full/*
|
|
||||||
#do
|
|
||||||
# source=$i
|
|
||||||
# file=`basename $i`
|
|
||||||
|
|
||||||
# id=`identify "$i"` || exit 1
|
|
||||||
# if [ "$id" = "" ] ; then
|
|
||||||
# echo "ERROR: Cannot determine format and geometry for image: \"$i\"."
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
# g=`echo $id | awk '{print $3}'` || exit 1
|
|
||||||
# if [ "$g" = "" ] ; then
|
|
||||||
# echo "ERROR: Cannot determine geometry for image: \"$i\"."
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# Geometry can be 563x144+0+0 or 75x98
|
|
||||||
# we need to get rid of the plus (+) and the x characters:
|
|
||||||
# w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
|
||||||
# if [ "$w" = "" ] ; then
|
|
||||||
# echo "ERROR: Cannot determine width for image: \"$x\"."
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
# h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
|
||||||
# if [ "$h" = "" ] ; then
|
|
||||||
# echo "ERROR: Cannot determine height for image: \"$x\"."
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# for x in $sizes
|
|
||||||
# do
|
|
||||||
|
|
||||||
# dest="$x/$file"
|
|
||||||
# if [ -f $dest ]; then
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# x_w=$(echo $x | cut -d 'x' -f1)
|
|
||||||
# x_h=$(echo $x | cut -d 'x' -f2)
|
|
||||||
|
|
||||||
# if [ "$w" -lt "$x_w" ] || [ "$h" -lt "$x_h" ]; then
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
|
|
||||||
#echo "convert -verbose -resize $x $source $dest"
|
|
||||||
#convert -verbose -resize $x $source $dest
|
|
||||||
|
|
||||||
# done
|
|
||||||
#done
|
|
||||||
|
|
||||||
|
|
||||||
for i in $sizes
|
|
||||||
do
|
do
|
||||||
for x in $i/*
|
if [ "$s" = "full" ]; then
|
||||||
|
dir=$s
|
||||||
|
else
|
||||||
|
dir=${s}x${s}
|
||||||
|
fi
|
||||||
|
if ! [ -d "$dir" ]; then
|
||||||
|
echo "Missing $dir directory."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
for f in ${dir}/*
|
||||||
do
|
do
|
||||||
file=`basename $x`
|
file=`basename $f`
|
||||||
|
|
||||||
for y in $sizes
|
for y in $sizes
|
||||||
do
|
do
|
||||||
if [ "$y" = "$i" ]; then
|
|
||||||
|
if [ "$s" = "$y" ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if ! [ -f "$y/$file" ]; then
|
|
||||||
echo "Warning: $y/$file does not exist, but $x exists."
|
if [ "$y" = "full" ]; then
|
||||||
|
dir2=$y
|
||||||
|
else
|
||||||
|
dir2=${y}x${y}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dir2" = "full" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ "$s" = "full" ] && [ $y -gt $s ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -f "${dir2}/$file" ]; then
|
||||||
|
echo "Warning: ${dir2}/$file does not exist, but ${dir}/${file} exists."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
id=`identify "$x"` || exit 1
|
if [ "$dir" = "full" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
id=`identify "$f"` || exit 1
|
||||||
if [ "$id" = "" ] ; then
|
if [ "$id" = "" ] ; then
|
||||||
echo "ERROR: Cannot determine format and geometry for image: \"$x\"."
|
echo "ERROR: Cannot determine format and geometry for image: \"$f\"."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
g=`echo $id | awk '{print $3}'` || exit 1
|
g=`echo $id | awk '{print $3}'` || exit 1
|
||||||
if [ "$g" = "" ] ; then
|
if [ "$g" = "" ] ; then
|
||||||
echo "ERROR: Cannot determine geometry for image: \"$x\"."
|
echo "ERROR: Cannot determine geometry for image: \"$f\"."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -102,17 +78,17 @@ do
|
|||||||
# we need to get rid of the plus (+) and the x characters:
|
# we need to get rid of the plus (+) and the x characters:
|
||||||
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||||
if [ "$w" = "" ] ; then
|
if [ "$w" = "" ] ; then
|
||||||
echo "ERROR: Cannot determine width for image: \"$x\"."
|
echo "ERROR: Cannot determine width for image: \"$f\"."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||||
if [ "$h" = "" ] ; then
|
if [ "$h" = "" ] ; then
|
||||||
echo "ERROR: Cannot determine height for image: \"$x\"."
|
echo "ERROR: Cannot determine height for image: \"$f\"."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! [ "${h}x${w}" = "$i" ]; then
|
if ! [ "${h}x${w}" = "$dir" ]; then
|
||||||
echo "Warning: $x is not $i, but ${h}x${w}!"
|
echo "Warning: $f is not $dir, but ${h}x${w}!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|||||||
4
dist/unix/PKGBUILD.in
vendored
@@ -23,15 +23,11 @@ depends=(
|
|||||||
gstreamer
|
gstreamer
|
||||||
gst-plugins-base
|
gst-plugins-base
|
||||||
gst-plugins-good
|
gst-plugins-good
|
||||||
xine-lib
|
|
||||||
vlc
|
vlc
|
||||||
chromaprint
|
chromaprint
|
||||||
libgpod
|
libgpod
|
||||||
libcdio
|
libcdio
|
||||||
libmtp
|
libmtp
|
||||||
libusbmuxd
|
|
||||||
libplist
|
|
||||||
libimobiledevice
|
|
||||||
fftw
|
fftw
|
||||||
)
|
)
|
||||||
optdepends=(
|
optdepends=(
|
||||||
|
|||||||
@@ -12,3 +12,28 @@ Categories=AudioVideo;Player;Qt;Audio;
|
|||||||
StartupNotify=false
|
StartupNotify=false
|
||||||
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
|
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
|
||||||
StartupWMClass=strawberry
|
StartupWMClass=strawberry
|
||||||
|
Actions=Play;Pause;Stop;StopAfterCurrent;Previous;Next;
|
||||||
|
|
||||||
|
[Desktop Action Play]
|
||||||
|
Name=Play
|
||||||
|
Exec=strawberry --play
|
||||||
|
|
||||||
|
[Desktop Action Pause]
|
||||||
|
Name=Pause
|
||||||
|
Exec=strawberry --pause
|
||||||
|
|
||||||
|
[Desktop Action Stop]
|
||||||
|
Name=Stop
|
||||||
|
Exec=strawberry --stop
|
||||||
|
|
||||||
|
[Desktop Action StopAfterCurrent]
|
||||||
|
Name=Stop after this track
|
||||||
|
Exec=strawberry --stop-after-current
|
||||||
|
|
||||||
|
[Desktop Action Previous]
|
||||||
|
Name=Previous
|
||||||
|
Exec=strawberry --previous
|
||||||
|
|
||||||
|
[Desktop Action Next]
|
||||||
|
Name=Next
|
||||||
|
Exec=strawberry --next
|
||||||
|
|||||||
124
dist/unix/strawberry.spec.in
vendored
@@ -1,12 +1,15 @@
|
|||||||
Name: strawberry
|
Name: strawberry
|
||||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||||
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
|
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
|
||||||
|
%else
|
||||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||||
Summary: A music player and music collection organiser
|
%endif
|
||||||
Group: Applications/Multimedia
|
Summary: A music player and music collection organizer
|
||||||
|
Group: Productivity/Multimedia/Sound/Players
|
||||||
License: GPL-3.0+
|
License: GPL-3.0+
|
||||||
URL: https://www.strawberrymusicplayer.org/
|
URL: https://www.strawberrymusicplayer.org/
|
||||||
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
%if 0%{?suse_version} && 0%{?suse_version} > 1325
|
||||||
BuildRequires: libboost_headers-devel
|
BuildRequires: libboost_headers-devel
|
||||||
@@ -26,14 +29,11 @@ BuildRequires: update-desktop-files
|
|||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
BuildRequires: appstream-glib
|
BuildRequires: appstream-glib
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
BuildRequires: libappstream-glib
|
BuildRequires: libappstream-glib
|
||||||
%else
|
%else
|
||||||
BuildRequires: appstream-util
|
BuildRequires: appstream-util
|
||||||
%endif
|
%endif
|
||||||
%endif
|
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
|
||||||
BuildRequires: cmake(Qt5LinguistTools)
|
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig
|
BuildRequires: pkgconfig
|
||||||
BuildRequires: pkgconfig(glib-2.0)
|
BuildRequires: pkgconfig(glib-2.0)
|
||||||
@@ -49,15 +49,29 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
|
|||||||
BuildRequires: pkgconfig(taglib)
|
BuildRequires: pkgconfig(taglib)
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig(fftw3)
|
BuildRequires: pkgconfig(fftw3)
|
||||||
BuildRequires: pkgconfig(Qt5Core)
|
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
|
||||||
BuildRequires: pkgconfig(Qt5Gui)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
|
||||||
BuildRequires: pkgconfig(Qt5Widgets)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
|
||||||
BuildRequires: pkgconfig(Qt5Concurrent)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
|
||||||
BuildRequires: pkgconfig(Qt5Network)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||||
BuildRequires: pkgconfig(Qt5Sql)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
|
||||||
BuildRequires: pkgconfig(Qt5X11Extras)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
|
||||||
BuildRequires: pkgconfig(Qt5DBus)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
|
||||||
BuildRequires: pkgconfig(Qt5Test)
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@DBus)
|
||||||
|
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Test)
|
||||||
|
%else
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Core)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Gui)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Widgets)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Concurrent)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Network)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Sql)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@DBus)
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Test)
|
||||||
|
%endif
|
||||||
|
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
|
||||||
|
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@LinguistTools)
|
||||||
|
%endif
|
||||||
BuildRequires: pkgconfig(gstreamer-1.0)
|
BuildRequires: pkgconfig(gstreamer-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||||
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
||||||
@@ -70,22 +84,17 @@ BuildRequires: pkgconfig(libpulse)
|
|||||||
BuildRequires: pkgconfig(libcdio)
|
BuildRequires: pkgconfig(libcdio)
|
||||||
BuildRequires: pkgconfig(libgpod-1.0)
|
BuildRequires: pkgconfig(libgpod-1.0)
|
||||||
BuildRequires: pkgconfig(libmtp)
|
BuildRequires: pkgconfig(libmtp)
|
||||||
BuildRequires: pkgconfig(libnotify)
|
|
||||||
BuildRequires: pkgconfig(libudf)
|
|
||||||
%if 0%{?suse_version} || 0%{?fedora_version}
|
%if 0%{?suse_version} || 0%{?fedora_version}
|
||||||
BuildRequires: pkgconfig(libxine)
|
|
||||||
BuildRequires: pkgconfig(libvlc)
|
BuildRequires: pkgconfig(libvlc)
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
Requires: libQt5Sql5-sqlite
|
%if "@QT_MAJOR_VERSION@" == "6"
|
||||||
%endif
|
Requires: qt6-sql-sqlite
|
||||||
|
%endif
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
%if "@QT_MAJOR_VERSION@" == "5"
|
||||||
Requires(post): update-desktop-files
|
Requires: libQt5Sql5-sqlite
|
||||||
Requires(post): gtk3-tools
|
%endif
|
||||||
Requires(postun): update-desktop-files
|
|
||||||
Requires(postun): gtk3-tools
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@@ -107,7 +116,7 @@ Features:
|
|||||||
- Support for multiple backends
|
- Support for multiple backends
|
||||||
- Audio analyzer
|
- Audio analyzer
|
||||||
- Audio equalizer
|
- Audio equalizer
|
||||||
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
- Transfer music to iPod, MTP or mass-storage USB player
|
||||||
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||||
- Streaming support for Subsonic
|
- Streaming support for Subsonic
|
||||||
|
|
||||||
@@ -115,62 +124,37 @@ Features:
|
|||||||
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%if 0%{?suse_version} || 0%{?mageia}
|
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
|
||||||
%{cmake} ..
|
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
|
||||||
%else
|
|
||||||
mkdir -p %{_target_platform}
|
|
||||||
pushd %{_target_platform}
|
|
||||||
%{cmake} ..
|
|
||||||
popd
|
|
||||||
%endif
|
%endif
|
||||||
|
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_MAJOR_VERSION=@QT_MAJOR_VERSION@
|
||||||
%if 0%{?suse_version} || 0%{?mageia}
|
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7)
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
%make_build
|
||||||
make %{?_smp_mflags}
|
|
||||||
%else
|
|
||||||
%make_build
|
|
||||||
%endif
|
|
||||||
%else
|
%else
|
||||||
%make_build -C %{_target_platform}
|
%cmake_build
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%install
|
%install
|
||||||
%if 0%{?suse_version}
|
%if 0%{?centos}
|
||||||
%cmake_install
|
%make_install
|
||||||
%else
|
%else
|
||||||
%if 0%{?mageia}
|
%if 0%{?mageia}
|
||||||
%make_install -C build
|
%make_install -C build
|
||||||
%else
|
%else
|
||||||
%make_install -C %{_target_platform}
|
%cmake_install
|
||||||
%endif
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
|
||||||
rm -f %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
%suse_update_desktop_file org.strawberrymusicplayer.strawberry Qt AudioVideo Audio Player
|
%suse_update_desktop_file org.strawberrymusicplayer.strawberry Qt AudioVideo Audio Player
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?suse_version} && 0%{?suse_version} < 1500
|
|
||||||
%post
|
|
||||||
%desktop_database_post
|
|
||||||
%icon_theme_cache_post
|
|
||||||
|
|
||||||
%postun
|
|
||||||
%desktop_database_postun
|
|
||||||
%icon_theme_cache_postun
|
|
||||||
%endif
|
|
||||||
|
|
||||||
%check
|
%check
|
||||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
|
||||||
%if 0%{?fedora_version}
|
%if 0%{?fedora_version}
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
|
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||||
%else
|
%else
|
||||||
%if ! 0%{?suse_version} || ( 0%{?suse_version} && 0%{?suse_version} >= 1500 )
|
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
|
|
||||||
%endif
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%files
|
%files
|
||||||
@@ -184,9 +168,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicpl
|
|||||||
%if 0%{?fedora_version}
|
%if 0%{?fedora_version}
|
||||||
%{_metainfodir}/*.appdata.xml
|
%{_metainfodir}/*.appdata.xml
|
||||||
%else
|
%else
|
||||||
%if ! 0%{?suse_version} || ( 0%{?suse_version} && 0%{?suse_version} >= 1500 )
|
%{_datadir}/metainfo/*.appdata.xml
|
||||||
%{_datadir}/metainfo/*.appdata.xml
|
|
||||||
%endif
|
|
||||||
%endif
|
%endif
|
||||||
%{_mandir}/man1/%{name}.1.*
|
%{_mandir}/man1/%{name}.1.*
|
||||||
%{_mandir}/man1/%{name}-tagreader.1.*
|
%{_mandir}/man1/%{name}-tagreader.1.*
|
||||||
|
|||||||
168
dist/windows/strawberry.nsi.in
vendored
@@ -18,6 +18,10 @@
|
|||||||
!define debug
|
!define debug
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
|
!if "@BUILD_WITH_QT6@" == "ON"
|
||||||
|
!define with_qt6
|
||||||
|
!endif
|
||||||
|
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
!define PRODUCT_NAME "Strawberry Music Player Debug"
|
!define PRODUCT_NAME "Strawberry Music Player Debug"
|
||||||
!define PRODUCT_NAME_SHORT "Strawberry"
|
!define PRODUCT_NAME_SHORT "Strawberry"
|
||||||
@@ -91,17 +95,33 @@ SetCompressor /SOLID lzma
|
|||||||
Name "${PRODUCT_NAME}"
|
Name "${PRODUCT_NAME}"
|
||||||
!ifdef arch_x86
|
!ifdef arch_x86
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
|
!ifdef with_qt6
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
|
||||||
|
!else
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x86.exe"
|
||||||
|
!endif
|
||||||
!else
|
!else
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
|
!ifdef with_qt6
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
|
||||||
|
!else
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x86.exe"
|
||||||
|
!endif
|
||||||
!endif
|
!endif
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef arch_x64
|
!ifdef arch_x64
|
||||||
!ifdef debug
|
!ifdef debug
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
|
!ifdef with_qt6
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
|
||||||
|
!else
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x64.exe"
|
||||||
|
!endif
|
||||||
!else
|
!else
|
||||||
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
|
!ifdef with_qt6
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
|
||||||
|
!else
|
||||||
|
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x64.exe"
|
||||||
|
!endif
|
||||||
!endif
|
!endif
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
@@ -154,7 +174,28 @@ Section "Strawberry" Strawberry
|
|||||||
File "strawberry.exe"
|
File "strawberry.exe"
|
||||||
File "strawberry-tagreader.exe"
|
File "strawberry-tagreader.exe"
|
||||||
File "strawberry.ico"
|
File "strawberry.ico"
|
||||||
|
File "sqlite3.exe"
|
||||||
|
File "gst-launch-1.0.exe"
|
||||||
|
|
||||||
|
!ifdef arch_x86
|
||||||
|
File "libgcc_s_sjlj-1.dll"
|
||||||
|
File "libcrypto-1_1.dll"
|
||||||
|
File "libssl-1_1.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef arch_x64
|
||||||
|
File "libgcc_s_seh-1.dll"
|
||||||
|
File "libcrypto-1_1-x64.dll"
|
||||||
|
File "libssl-1_1-x64.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
File "avcodec-58.dll"
|
||||||
|
File "avfilter-7.dll"
|
||||||
|
File "avformat-58.dll"
|
||||||
|
File "avresample-4.dll"
|
||||||
|
File "avutil-56.dll"
|
||||||
|
File "libbrotlicommon.dll"
|
||||||
|
File "libbrotlidec.dll"
|
||||||
File "libbz2.dll"
|
File "libbz2.dll"
|
||||||
File "libcdio-19.dll"
|
File "libcdio-19.dll"
|
||||||
File "libchromaprint.dll"
|
File "libchromaprint.dll"
|
||||||
@@ -194,21 +235,41 @@ Section "Strawberry" Strawberry
|
|||||||
File "libnettle-8.dll"
|
File "libnettle-8.dll"
|
||||||
File "libogg-0.dll"
|
File "libogg-0.dll"
|
||||||
File "libopus-0.dll"
|
File "libopus-0.dll"
|
||||||
|
File "liborc-0.4-0.dll"
|
||||||
File "libpcre-1.dll"
|
File "libpcre-1.dll"
|
||||||
File "libpcre2-16-0.dll"
|
File "libpcre2-16-0.dll"
|
||||||
File "libpng16-16.dll"
|
File "libpng16-16.dll"
|
||||||
File "libprotobuf-23.dll"
|
File "libprotobuf-25.dll"
|
||||||
|
File "libpsl-5.dll"
|
||||||
File "libsoup-2.4-1.dll"
|
File "libsoup-2.4-1.dll"
|
||||||
File "libspeex-1.dll"
|
File "libspeex-1.dll"
|
||||||
File "libsqlite3-0.dll"
|
File "libsqlite3-0.dll"
|
||||||
|
File "libssp-0.dll"
|
||||||
File "libstdc++-6.dll"
|
File "libstdc++-6.dll"
|
||||||
File "libtag.dll"
|
File "libtag.dll"
|
||||||
|
File "libtasn1-6.dll"
|
||||||
File "libunistring-2.dll"
|
File "libunistring-2.dll"
|
||||||
File "libvorbis-0.dll"
|
File "libvorbis-0.dll"
|
||||||
File "libvorbisenc-2.dll"
|
File "libvorbisenc-2.dll"
|
||||||
File "libwavpack-1.dll"
|
File "libwavpack-1.dll"
|
||||||
File "libwinpthread-1.dll"
|
File "libwinpthread-1.dll"
|
||||||
File "libxml2-2.dll"
|
File "libxml2-2.dll"
|
||||||
|
File "libzstd.dll"
|
||||||
|
File "postproc-55.dll"
|
||||||
|
File "swresample-3.dll"
|
||||||
|
File "swscale-5.dll"
|
||||||
|
File "zlib1.dll"
|
||||||
|
|
||||||
|
!ifdef with_qt6
|
||||||
|
File "Qt6Concurrent.dll"
|
||||||
|
File "Qt6Core.dll"
|
||||||
|
File "Qt6Gui.dll"
|
||||||
|
File "Qt6Network.dll"
|
||||||
|
File "Qt6Sql.dll"
|
||||||
|
File "Qt6Widgets.dll"
|
||||||
|
;File "Qt6WinExtras.dll"
|
||||||
|
File "libqtsparkle-qt6.dll"
|
||||||
|
!else
|
||||||
File "Qt5Concurrent.dll"
|
File "Qt5Concurrent.dll"
|
||||||
File "Qt5Core.dll"
|
File "Qt5Core.dll"
|
||||||
File "Qt5Gui.dll"
|
File "Qt5Gui.dll"
|
||||||
@@ -216,24 +277,17 @@ Section "Strawberry" Strawberry
|
|||||||
File "Qt5Sql.dll"
|
File "Qt5Sql.dll"
|
||||||
File "Qt5Widgets.dll"
|
File "Qt5Widgets.dll"
|
||||||
File "Qt5WinExtras.dll"
|
File "Qt5WinExtras.dll"
|
||||||
File "zlib1.dll"
|
File "libqtsparkle-qt5.dll"
|
||||||
File "libzstd.dll"
|
|
||||||
File "libtasn1-6.dll"
|
|
||||||
File "libbrotlicommon.dll"
|
|
||||||
File "libbrotlidec.dll"
|
|
||||||
File "libpsl-5.dll"
|
|
||||||
File "liborc-0.4-0.dll"
|
|
||||||
|
|
||||||
!ifdef arch_x86
|
|
||||||
File "libgcc_s_sjlj-1.dll"
|
|
||||||
File "libcrypto-1_1.dll"
|
|
||||||
File "libssl-1_1.dll"
|
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef arch_x64
|
!ifdef debug
|
||||||
File "libgcc_s_seh-1.dll"
|
File "gdb.exe"
|
||||||
File "libcrypto-1_1-x64.dll"
|
File "libdl.dll"
|
||||||
File "libssl-1_1-x64.dll"
|
File "libexpat-1.dll"
|
||||||
|
File "libmman.dll"
|
||||||
|
File "libmpfr-6.dll"
|
||||||
|
File "libreadline8.dll"
|
||||||
|
File "libtermcap.dll"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
File "killproc.exe"
|
File "killproc.exe"
|
||||||
@@ -318,7 +372,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
|||||||
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
|
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
|
||||||
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
|
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
|
||||||
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
|
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
|
||||||
;File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
||||||
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
|
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
|
||||||
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
|
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
|
||||||
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
|
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
|
||||||
@@ -359,6 +413,12 @@ Section "Start menu items" startmenu
|
|||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Tidal URL Scheme"
|
||||||
|
WriteRegStr HKCR "tidal" "URL Protocol" ""
|
||||||
|
WriteRegStr HKCR "tidal" "" "URL:tidal"
|
||||||
|
WriteRegStr HKCR 'tidal\shell\open\command' '' '"${PRODUCT_INSTALL_DIR}\strawberry.exe" "%1"'
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
Section "Uninstaller"
|
Section "Uninstaller"
|
||||||
; Create uninstaller
|
; Create uninstaller
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
@@ -383,7 +443,28 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\strawberry.ico"
|
Delete "$INSTDIR\strawberry.ico"
|
||||||
Delete "$INSTDIR\strawberry.exe"
|
Delete "$INSTDIR\strawberry.exe"
|
||||||
Delete "$INSTDIR\strawberry-tagreader.exe"
|
Delete "$INSTDIR\strawberry-tagreader.exe"
|
||||||
|
Delete "$INSTDIR\sqlite3.exe"
|
||||||
|
Delete "$INSTDIR\gst-launch-1.0.exe"
|
||||||
|
|
||||||
|
!ifdef arch_x86
|
||||||
|
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||||
|
Delete "$INSTDIR\libcrypto-1_1.dll"
|
||||||
|
Delete "$INSTDIR\libssl-1_1.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef arch_x64
|
||||||
|
Delete "$INSTDIR\libgcc_s_seh-1.dll"
|
||||||
|
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
|
||||||
|
Delete "$INSTDIR\libssl-1_1-x64.dll"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
Delete "$INSTDIR\avcodec-58.dll"
|
||||||
|
Delete "$INSTDIR\avfilter-7.dll"
|
||||||
|
Delete "$INSTDIR\avformat-58.dll"
|
||||||
|
Delete "$INSTDIR\avresample-4.dll"
|
||||||
|
Delete "$INSTDIR\avutil-56.dll"
|
||||||
|
Delete "$INSTDIR\libbrotlicommon.dll"
|
||||||
|
Delete "$INSTDIR\libbrotlidec.dll"
|
||||||
Delete "$INSTDIR\libbz2.dll"
|
Delete "$INSTDIR\libbz2.dll"
|
||||||
Delete "$INSTDIR\libcdio-19.dll"
|
Delete "$INSTDIR\libcdio-19.dll"
|
||||||
Delete "$INSTDIR\libchromaprint.dll"
|
Delete "$INSTDIR\libchromaprint.dll"
|
||||||
@@ -423,21 +504,29 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\libnettle-8.dll"
|
Delete "$INSTDIR\libnettle-8.dll"
|
||||||
Delete "$INSTDIR\libogg-0.dll"
|
Delete "$INSTDIR\libogg-0.dll"
|
||||||
Delete "$INSTDIR\libopus-0.dll"
|
Delete "$INSTDIR\libopus-0.dll"
|
||||||
|
Delete "$INSTDIR\liborc-0.4-0.dll"
|
||||||
Delete "$INSTDIR\libpcre-1.dll"
|
Delete "$INSTDIR\libpcre-1.dll"
|
||||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||||
Delete "$INSTDIR\libpng16-16.dll"
|
Delete "$INSTDIR\libpng16-16.dll"
|
||||||
Delete "$INSTDIR\libprotobuf-23.dll"
|
Delete "$INSTDIR\libprotobuf-25.dll"
|
||||||
|
Delete "$INSTDIR\libpsl-5.dll"
|
||||||
|
Delete "$INSTDIR\libqtsparkle-qt5.dll"
|
||||||
|
Delete "$INSTDIR\libqtsparkle-qt6.dll"
|
||||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||||
Delete "$INSTDIR\libspeex-1.dll"
|
Delete "$INSTDIR\libspeex-1.dll"
|
||||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||||
|
Delete "$INSTDIR\libssp-0.dll"
|
||||||
Delete "$INSTDIR\libstdc++-6.dll"
|
Delete "$INSTDIR\libstdc++-6.dll"
|
||||||
Delete "$INSTDIR\libtag.dll"
|
Delete "$INSTDIR\libtag.dll"
|
||||||
|
Delete "$INSTDIR\libtasn1-6.dll"
|
||||||
Delete "$INSTDIR\libunistring-2.dll"
|
Delete "$INSTDIR\libunistring-2.dll"
|
||||||
Delete "$INSTDIR\libvorbis-0.dll"
|
Delete "$INSTDIR\libvorbis-0.dll"
|
||||||
Delete "$INSTDIR\libvorbisenc-2.dll"
|
Delete "$INSTDIR\libvorbisenc-2.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\libxml2-2.dll"
|
||||||
|
Delete "$INSTDIR\libzstd.dll"
|
||||||
|
Delete "$INSTDIR\postproc-55.dll"
|
||||||
Delete "$INSTDIR\Qt5Concurrent.dll"
|
Delete "$INSTDIR\Qt5Concurrent.dll"
|
||||||
Delete "$INSTDIR\Qt5Core.dll"
|
Delete "$INSTDIR\Qt5Core.dll"
|
||||||
Delete "$INSTDIR\Qt5Gui.dll"
|
Delete "$INSTDIR\Qt5Gui.dll"
|
||||||
@@ -445,24 +534,25 @@ Section "Uninstall"
|
|||||||
Delete "$INSTDIR\Qt5Sql.dll"
|
Delete "$INSTDIR\Qt5Sql.dll"
|
||||||
Delete "$INSTDIR\Qt5Widgets.dll"
|
Delete "$INSTDIR\Qt5Widgets.dll"
|
||||||
Delete "$INSTDIR\Qt5WinExtras.dll"
|
Delete "$INSTDIR\Qt5WinExtras.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Concurrent.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Core.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Gui.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Network.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Sql.dll"
|
||||||
|
Delete "$INSTDIR\Qt6Widgets.dll"
|
||||||
|
Delete "$INSTDIR\Qt6WinExtras.dll"
|
||||||
|
Delete "$INSTDIR\swresample-3.dll"
|
||||||
|
Delete "$INSTDIR\swscale-5.dll"
|
||||||
Delete "$INSTDIR\zlib1.dll"
|
Delete "$INSTDIR\zlib1.dll"
|
||||||
Delete "$INSTDIR\libzstd.dll"
|
|
||||||
Delete "$INSTDIR\libtasn1-6.dll"
|
|
||||||
Delete "$INSTDIR\libbrotlicommon.dll"
|
|
||||||
Delete "$INSTDIR\libbrotlidec.dll"
|
|
||||||
Delete "$INSTDIR\libpsl-5.dll"
|
|
||||||
Delete "$INSTDIR\liborc-0.4-0.dll"
|
|
||||||
|
|
||||||
!ifdef arch_x86
|
!ifdef debug
|
||||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
Delete "$INSTDIR\gdb.exe"
|
||||||
Delete "$INSTDIR\libcrypto-1_1.dll"
|
Delete "$INSTDIR\libdl.dll"
|
||||||
Delete "$INSTDIR\libssl-1_1.dll"
|
Delete "$INSTDIR\libexpat-1.dll"
|
||||||
!endif
|
Delete "$INSTDIR\libmman.dll"
|
||||||
|
Delete "$INSTDIR\libmpfr-6.dll"
|
||||||
!ifdef arch_x64
|
Delete "$INSTDIR\libreadline8.dll"
|
||||||
Delete "$INSTDIR\libgcc_s_seh-1.dll"
|
Delete "$INSTDIR\libtermcap.dll"
|
||||||
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
|
|
||||||
Delete "$INSTDIR\libssl-1_1-x64.dll"
|
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ link_directories(
|
|||||||
${GSTREAMER_BASE_LIBRARY_DIRS}
|
${GSTREAMER_BASE_LIBRARY_DIRS}
|
||||||
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
${GSTREAMER_AUDIO_LIBRARY_DIRS}
|
||||||
${FFTW3_LIBRARY_DIRS}
|
${FFTW3_LIBRARY_DIRS}
|
||||||
${Qt5Core_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(gstmoodbar STATIC ${SOURCES})
|
add_library(gstmoodbar STATIC ${SOURCES})
|
||||||
@@ -21,7 +20,6 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
|
|||||||
${GSTREAMER_BASE_INCLUDE_DIRS}
|
${GSTREAMER_BASE_INCLUDE_DIRS}
|
||||||
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
${GSTREAMER_AUDIO_INCLUDE_DIRS}
|
||||||
${FFTW3_INCLUDE_DIR}
|
${FFTW3_INCLUDE_DIR}
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
@@ -33,5 +31,5 @@ target_link_libraries(gstmoodbar PRIVATE
|
|||||||
${GSTREAMER_BASE_LIBRARIES}
|
${GSTREAMER_BASE_LIBRARIES}
|
||||||
${GSTREAMER_AUDIO_LIBRARIES}
|
${GSTREAMER_AUDIO_LIBRARIES}
|
||||||
${FFTW3_FFTW_LIBRARY}
|
${FFTW3_FFTW_LIBRARY}
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ static void gst_fastspectrum_init (GstFastSpectrum * spectrum) {
|
|||||||
spectrum->interval = DEFAULT_INTERVAL;
|
spectrum->interval = DEFAULT_INTERVAL;
|
||||||
spectrum->bands = DEFAULT_BANDS;
|
spectrum->bands = DEFAULT_BANDS;
|
||||||
|
|
||||||
spectrum->channel_data_initialised = false;
|
spectrum->channel_data_initialized = false;
|
||||||
|
|
||||||
g_mutex_init (&spectrum->lock);
|
g_mutex_init (&spectrum->lock);
|
||||||
|
|
||||||
@@ -137,14 +137,14 @@ static void gst_fastspectrum_alloc_channel_data (GstFastSpectrum * spectrum) {
|
|||||||
QMutexLocker l(klass->fftw_lock);
|
QMutexLocker l(klass->fftw_lock);
|
||||||
spectrum->plan = fftw_plan_dft_r2c_1d(nfft, spectrum->fft_input, spectrum->fft_output, FFTW_ESTIMATE);
|
spectrum->plan = fftw_plan_dft_r2c_1d(nfft, spectrum->fft_input, spectrum->fft_output, FFTW_ESTIMATE);
|
||||||
}
|
}
|
||||||
spectrum->channel_data_initialised = true;
|
spectrum->channel_data_initialized = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gst_fastspectrum_free_channel_data (GstFastSpectrum * spectrum) {
|
static void gst_fastspectrum_free_channel_data (GstFastSpectrum * spectrum) {
|
||||||
|
|
||||||
GstFastSpectrumClass* klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
|
GstFastSpectrumClass* klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
|
||||||
if (spectrum->channel_data_initialised) {
|
if (spectrum->channel_data_initialized) {
|
||||||
{
|
{
|
||||||
QMutexLocker l(klass->fftw_lock);
|
QMutexLocker l(klass->fftw_lock);
|
||||||
fftw_destroy_plan(spectrum->plan);
|
fftw_destroy_plan(spectrum->plan);
|
||||||
@@ -154,7 +154,7 @@ static void gst_fastspectrum_free_channel_data (GstFastSpectrum * spectrum) {
|
|||||||
delete[] spectrum->input_ring_buffer;
|
delete[] spectrum->input_ring_buffer;
|
||||||
delete[] spectrum->spect_magnitude;
|
delete[] spectrum->spect_magnitude;
|
||||||
|
|
||||||
spectrum->channel_data_initialised = false;
|
spectrum->channel_data_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ static void gst_fastspectrum_reset_state (GstFastSpectrum * spectrum) {
|
|||||||
|
|
||||||
static void gst_fastspectrum_finalize (GObject * object) {
|
static void gst_fastspectrum_finalize (GObject * object) {
|
||||||
|
|
||||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (object);
|
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(object);
|
||||||
|
|
||||||
gst_fastspectrum_reset_state (spectrum);
|
gst_fastspectrum_reset_state (spectrum);
|
||||||
g_mutex_clear (&spectrum->lock);
|
g_mutex_clear (&spectrum->lock);
|
||||||
@@ -190,7 +190,7 @@ static void gst_fastspectrum_finalize (GObject * object) {
|
|||||||
|
|
||||||
static void gst_fastspectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) {
|
static void gst_fastspectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) {
|
||||||
|
|
||||||
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
|
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_INTERVAL:{
|
case PROP_INTERVAL:{
|
||||||
@@ -222,7 +222,7 @@ static void gst_fastspectrum_set_property (GObject * object, guint prop_id, cons
|
|||||||
|
|
||||||
static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) {
|
static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) {
|
||||||
|
|
||||||
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
|
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_INTERVAL:
|
case PROP_INTERVAL:
|
||||||
@@ -240,7 +240,7 @@ static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GVal
|
|||||||
|
|
||||||
static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
|
static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
|
||||||
|
|
||||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||||
|
|
||||||
gst_fastspectrum_reset_state (spectrum);
|
gst_fastspectrum_reset_state (spectrum);
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
|
|||||||
|
|
||||||
static gboolean gst_fastspectrum_stop (GstBaseTransform * trans) {
|
static gboolean gst_fastspectrum_stop (GstBaseTransform * trans) {
|
||||||
|
|
||||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||||
|
|
||||||
gst_fastspectrum_reset_state (spectrum);
|
gst_fastspectrum_reset_state (spectrum);
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint
|
|||||||
|
|
||||||
static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInfo * info) {
|
static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInfo * info) {
|
||||||
|
|
||||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (base);
|
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(base);
|
||||||
GstFastSpectrumInputData input_data = nullptr;
|
GstFastSpectrumInputData input_data = nullptr;
|
||||||
|
|
||||||
g_mutex_lock (&spectrum->lock);
|
g_mutex_lock (&spectrum->lock);
|
||||||
@@ -392,7 +392,7 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
|
|||||||
|
|
||||||
static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, GstBuffer *buffer) {
|
static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, GstBuffer *buffer) {
|
||||||
|
|
||||||
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
|
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
|
||||||
guint rate = GST_AUDIO_FILTER_RATE (spectrum);
|
guint rate = GST_AUDIO_FILTER_RATE (spectrum);
|
||||||
guint bps = GST_AUDIO_FILTER_BPS (spectrum);
|
guint bps = GST_AUDIO_FILTER_BPS (spectrum);
|
||||||
guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
|
guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
|
||||||
@@ -422,7 +422,7 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
|
|||||||
/* If we don't have a FFT context yet (or it was reset due to parameter
|
/* If we don't have a FFT context yet (or it was reset due to parameter
|
||||||
* changes) get one and allocate memory for everything
|
* changes) get one and allocate memory for everything
|
||||||
*/
|
*/
|
||||||
if (!spectrum->channel_data_initialised) {
|
if (!spectrum->channel_data_initialized) {
|
||||||
GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
|
GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
|
||||||
|
|
||||||
gst_fastspectrum_alloc_channel_data (spectrum);
|
gst_fastspectrum_alloc_channel_data (spectrum);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ struct GstFastSpectrum {
|
|||||||
GstClockTime message_ts; /* starttime for next message */
|
GstClockTime message_ts; /* starttime for next message */
|
||||||
|
|
||||||
/* <private> */
|
/* <private> */
|
||||||
bool channel_data_initialised;
|
bool channel_data_initialized;
|
||||||
double* input_ring_buffer;
|
double* input_ring_buffer;
|
||||||
double* fft_input;
|
double* fft_input;
|
||||||
fftw_complex* fft_output;
|
fftw_complex* fft_output;
|
||||||
|
|||||||
@@ -20,20 +20,20 @@ if(APPLE)
|
|||||||
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
|
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
qt5_wrap_cpp(MOC ${HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
|
qt6_wrap_cpp(MOC ${HEADERS})
|
||||||
|
else()
|
||||||
|
qt5_wrap_cpp(MOC ${HEADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
link_directories(
|
link_directories(
|
||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${Qt5Core_LIBRARY_DIRS}
|
|
||||||
${Qt5Network_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
|
||||||
|
|
||||||
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
target_include_directories(libstrawberry-common SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
${Qt5Network_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(libstrawberry-common PRIVATE
|
target_include_directories(libstrawberry-common PRIVATE
|
||||||
@@ -46,8 +46,8 @@ target_include_directories(libstrawberry-common PRIVATE
|
|||||||
target_link_libraries(libstrawberry-common PRIVATE
|
target_link_libraries(libstrawberry-common PRIVATE
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${Qt5Network_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(Backtrace_FOUND)
|
if(Backtrace_FOUND)
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
/* This file is part of Strawberry.
|
|
||||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
|
||||||
|
|
||||||
Strawberry is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Strawberry is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CONCURRENTRUN_H
|
|
||||||
#define CONCURRENTRUN_H
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThreadPool>
|
|
||||||
|
|
||||||
/*
|
|
||||||
The aim of ThreadFunctor classes and ConcurrentRun::Run() functions is to
|
|
||||||
complete QtConcurrentRun, which lack support for using a particular
|
|
||||||
QThreadPool, as it always uses QThreadPool::globalInstance().
|
|
||||||
|
|
||||||
This is problematic when we do not want to share the same thread pool over
|
|
||||||
all the application, but want to keep the convenient QtConcurrent::run()
|
|
||||||
functor syntax.
|
|
||||||
With ConcurrentRun::Run(), time critical changes can be performed in their
|
|
||||||
own pool, which is not empty by other actions (as it happens when using
|
|
||||||
QtConcurrentRun::run()).
|
|
||||||
|
|
||||||
ThreadFunctor classes are used to store a functor and its arguments, and
|
|
||||||
Run() functions are used for convenience: to directly create a new
|
|
||||||
ThreadFunctor object and start it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Base abstract classes ThreadFunctorBase and ThreadFunctor (for void and
|
|
||||||
non-void result):
|
|
||||||
*/
|
|
||||||
template<typename ReturnType>
|
|
||||||
class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable {
|
|
||||||
public:
|
|
||||||
ThreadFunctorBase() {}
|
|
||||||
|
|
||||||
QFuture<ReturnType> Start(QThreadPool* thread_pool) {
|
|
||||||
this->setRunnable(this);
|
|
||||||
this->reportStarted();
|
|
||||||
Q_ASSERT(thread_pool);
|
|
||||||
QFuture<ReturnType> future = this->future();
|
|
||||||
thread_pool->start(this, 0 /* priority: currently we do not support changing the priority. Might be added later if needed */);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() override = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ReturnType, typename... Args>
|
|
||||||
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
|
||||||
public:
|
|
||||||
explicit ThreadFunctor(std::function<ReturnType (Args...)> function, Args... args)
|
|
||||||
: function_(std::bind(function, args...)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() override {
|
|
||||||
this->reportResult(function_());
|
|
||||||
this->reportFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::function<ReturnType()> function_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Partial specialisation for void return type.
|
|
||||||
template <typename... Args>
|
|
||||||
class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
|
|
||||||
public:
|
|
||||||
explicit ThreadFunctor(std::function<void (Args...)> function, Args... args)
|
|
||||||
: function_(std::bind(function, args...)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() override {
|
|
||||||
function_();
|
|
||||||
this->reportFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::function<void()> function_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Run functions
|
|
||||||
*/
|
|
||||||
namespace ConcurrentRun {
|
|
||||||
|
|
||||||
// Empty argument form.
|
|
||||||
template <typename ReturnType>
|
|
||||||
QFuture<ReturnType> Run(QThreadPool* threadpool, std::function<ReturnType ()> function) {
|
|
||||||
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function object with arguments form.
|
|
||||||
template <typename ReturnType, typename... Args>
|
|
||||||
QFuture<ReturnType> Run(QThreadPool* threadpool, std::function<ReturnType (Args...)> function, const Args&... args) {
|
|
||||||
return (new ThreadFunctor<ReturnType, Args...>(function, args...))->Start(threadpool);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support passing C function pointers instead of function objects.
|
|
||||||
template <typename ReturnType, typename... Args>
|
|
||||||
QFuture<ReturnType> Run(QThreadPool* threadpool, ReturnType (*function) (Args...), const Args&... args) {
|
|
||||||
return Run(threadpool, std::function<ReturnType (Args...)>(function), args...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CONCURRENTRUN_H
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// Helper for lazy initialisation of objects.
|
// Helper for lazy initialization of objects.
|
||||||
// Usage:
|
// Usage:
|
||||||
// Lazy<Foo> my_lazy_object([]() { return new Foo; });
|
// Lazy<Foo> my_lazy_object([]() { return new Foo; });
|
||||||
|
|
||||||
@@ -34,33 +34,32 @@ class Lazy {
|
|||||||
Lazy() : init_([]() { return new T; }) {}
|
Lazy() : init_([]() { return new T; }) {}
|
||||||
|
|
||||||
T* get() const {
|
T* get() const {
|
||||||
CheckInitialised();
|
CheckInitialized();
|
||||||
return ptr_.get();
|
return ptr_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
typename std::add_lvalue_reference<T>::type operator*() const {
|
typename std::add_lvalue_reference<T>::type operator*() const {
|
||||||
CheckInitialised();
|
CheckInitialized();
|
||||||
return *ptr_;
|
return *ptr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* operator->() const { return get(); }
|
T* operator->() const { return get(); }
|
||||||
|
|
||||||
// Returns true if the object is not yet initialised.
|
// Returns true if the object is not yet initialized.
|
||||||
explicit operator bool() const { return ptr_; }
|
explicit operator bool() const { return ptr_; }
|
||||||
|
|
||||||
// Deletes the underlying object and will re-run the initialisation function
|
// Deletes the underlying object and will re-run the initialization function if the object is requested again.
|
||||||
// if the object is requested again.
|
void reset() { ptr_.reset(); }
|
||||||
void reset() { ptr_.reset(nullptr); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CheckInitialised() const {
|
void CheckInitialized() const {
|
||||||
if (!ptr_) {
|
if (!ptr_) {
|
||||||
ptr_.reset(init_());
|
ptr_.reset(init_(), [](T*obj) { obj->deleteLater(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::function<T*()> init_;
|
const std::function<T*()> init_;
|
||||||
mutable std::unique_ptr<T> ptr_;
|
mutable std::shared_ptr<T> ptr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LAZY_H
|
#endif // LAZY_H
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QRegExp>
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@@ -306,11 +307,12 @@ QString LinuxDemangle(const QString &symbol);
|
|||||||
|
|
||||||
QString LinuxDemangle(const QString &symbol) {
|
QString LinuxDemangle(const QString &symbol) {
|
||||||
|
|
||||||
QRegExp regex("\\(([^+]+)");
|
QRegularExpression regex("\\(([^+]+)");
|
||||||
if (!symbol.contains(regex)) {
|
QRegularExpressionMatch match = regex.match(symbol);
|
||||||
|
if (!match.hasMatch()) {
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
}
|
||||||
QString mangled_function = regex.cap(1);
|
QString mangled_function = match.captured(1);
|
||||||
return CXXDemangle(mangled_function);
|
return CXXDemangle(mangled_function);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,15 +102,15 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
|
|||||||
|
|
||||||
// Sorry.
|
// Sorry.
|
||||||
if (flush_abstract_socket_) {
|
if (flush_abstract_socket_) {
|
||||||
((static_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
|
((qobject_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
|
||||||
}
|
}
|
||||||
else if (flush_local_socket_) {
|
else if (flush_local_socket_) {
|
||||||
((static_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _MessageHandlerBase::DeviceClosed() {
|
void _MessageHandlerBase::DeviceClosed() {
|
||||||
is_device_closed_ = true;
|
is_device_closed_ = true;
|
||||||
AbortAll();
|
AbortAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class QIODevice;
|
|||||||
class _MessageHandlerBase : public QObject {
|
class _MessageHandlerBase : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
||||||
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
||||||
|
|
||||||
@@ -52,16 +52,16 @@ public:
|
|||||||
// After this is true, messages cannot be sent to the handler any more.
|
// After this is true, messages cannot be sent to the handler any more.
|
||||||
bool is_device_closed() const { return is_device_closed_; }
|
bool is_device_closed() const { return is_device_closed_; }
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void WriteMessage(const QByteArray &data);
|
void WriteMessage(const QByteArray &data);
|
||||||
void DeviceReadyRead();
|
void DeviceReadyRead();
|
||||||
virtual void DeviceClosed();
|
virtual void DeviceClosed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
||||||
virtual void AbortAll() = 0;
|
virtual void AbortAll() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
||||||
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ protected:
|
|||||||
// You should subclass this and implement the MessageArrived(MessageType) method.
|
// You should subclass this and implement the MessageArrived(MessageType) method.
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
class AbstractMessageHandler : public _MessageHandlerBase {
|
class AbstractMessageHandler : public _MessageHandlerBase {
|
||||||
public:
|
public:
|
||||||
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
||||||
~AbstractMessageHandler() override { AbortAll(); }
|
~AbstractMessageHandler() override { AbortAll(); }
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
// Sets the "id" field of reply to the same as the request, and sends the reply on the socket. Used on the worker side.
|
// Sets the "id" field of reply to the same as the request, and sends the reply on the socket. Used on the worker side.
|
||||||
void SendReply(const MessageType &request, MessageType *reply);
|
void SendReply(const MessageType &request, MessageType *reply);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Called when a message is received from the socket.
|
// Called when a message is received from the socket.
|
||||||
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
|
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ protected:
|
|||||||
bool RawMessageArrived(const QByteArray &data) override;
|
bool RawMessageArrived(const QByteArray &data) override;
|
||||||
void AbortAll() override;
|
void AbortAll() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<int, ReplyType*> pending_replies_;
|
QMap<qint64, ReplyType*> pending_replies_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
@@ -146,6 +146,7 @@ void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageTy
|
|||||||
|
|
||||||
template<typename MT>
|
template<typename MT>
|
||||||
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
||||||
|
|
||||||
MessageType message;
|
MessageType message;
|
||||||
if (!message.ParseFromArray(data.constData(), data.size())) {
|
if (!message.ParseFromArray(data.constData(), data.size())) {
|
||||||
return false;
|
return false;
|
||||||
@@ -161,14 +162,17 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MT>
|
template<typename MT>
|
||||||
void AbstractMessageHandler<MT>::AbortAll() {
|
void AbstractMessageHandler<MT>::AbortAll() {
|
||||||
|
|
||||||
for (ReplyType *reply : pending_replies_) {
|
for (ReplyType *reply : pending_replies_) {
|
||||||
reply->Abort();
|
reply->Abort();
|
||||||
}
|
}
|
||||||
pending_replies_.clear();
|
pending_replies_.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MESSAGEHANDLER_H
|
#endif // MESSAGEHANDLER_H
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
#include <QSemaphore>
|
#include <QSemaphore>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ class _MessageReplyBase : public QObject {
|
|||||||
public:
|
public:
|
||||||
explicit _MessageReplyBase(QObject *parent = nullptr);
|
explicit _MessageReplyBase(QObject *parent = nullptr);
|
||||||
|
|
||||||
virtual int id() const = 0;
|
virtual qint64 id() const = 0;
|
||||||
bool is_finished() const { return finished_; }
|
bool is_finished() const { return finished_; }
|
||||||
bool is_successful() const { return success_; }
|
bool is_successful() const { return success_; }
|
||||||
|
|
||||||
@@ -57,29 +58,27 @@ class _MessageReplyBase : public QObject {
|
|||||||
template <typename MessageType>
|
template <typename MessageType>
|
||||||
class MessageReply : public _MessageReplyBase {
|
class MessageReply : public _MessageReplyBase {
|
||||||
public:
|
public:
|
||||||
explicit MessageReply(const MessageType& request_message, QObject *parent = nullptr);
|
explicit MessageReply(const MessageType &request_message, QObject *parent = nullptr);
|
||||||
|
|
||||||
int id() const override { return request_message_.id(); }
|
qint64 id() const override { return request_message_.id(); }
|
||||||
const MessageType& request_message() const { return request_message_; }
|
const MessageType &request_message() const { return request_message_; }
|
||||||
const MessageType& message() const { return reply_message_; }
|
const MessageType &message() const { return reply_message_; }
|
||||||
|
|
||||||
void SetReply(const MessageType& message);
|
void SetReply(const MessageType &message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageType request_message_;
|
MessageType request_message_;
|
||||||
MessageType reply_message_;
|
MessageType reply_message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename MessageType>
|
template<typename MessageType>
|
||||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
|
MessageReply<MessageType>::MessageReply(const MessageType &request_message, QObject *parent) : _MessageReplyBase(parent) {
|
||||||
: _MessageReplyBase(parent)
|
|
||||||
{
|
|
||||||
request_message_.MergeFrom(request_message);
|
request_message_.MergeFrom(request_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MessageType>
|
template<typename MessageType>
|
||||||
void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
void MessageReply<MessageType>::SetReply(const MessageType &message) {
|
||||||
|
|
||||||
Q_ASSERT(!finished_);
|
Q_ASSERT(!finished_);
|
||||||
|
|
||||||
@@ -90,6 +89,9 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
|||||||
qLog(Debug) << "Releasing ID" << id() << "(finished)";
|
qLog(Debug) << "Releasing ID" << id() << "(finished)";
|
||||||
semaphore_.release();
|
semaphore_.release();
|
||||||
|
|
||||||
|
// The signal is not always emitted without this.
|
||||||
|
QThread::usleep(10);
|
||||||
|
|
||||||
emit Finished(success_);
|
emit Finished(success_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
/* This file is part of Strawberry.
|
|
||||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OVERRIDE_H
|
|
||||||
#define OVERRIDE_H
|
|
||||||
|
|
||||||
// Defines the OVERRIDE macro as C++11's override control keyword if
|
|
||||||
// it is available.
|
|
||||||
|
|
||||||
#ifndef __has_extension
|
|
||||||
#define __has_extension(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_extension(cxx_override_control) // Clang feature checking macro.
|
|
||||||
# define OVERRIDE override
|
|
||||||
#else
|
|
||||||
# define OVERRIDE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // OVERRIDE_H
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInteger>
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
# include <QRandomGenerator>
|
# include <QRandomGenerator>
|
||||||
#endif
|
#endif
|
||||||
@@ -48,11 +48,11 @@ class _WorkerPoolBase : public QObject {
|
|||||||
public:
|
public:
|
||||||
explicit _WorkerPoolBase(QObject *parent = nullptr);
|
explicit _WorkerPoolBase(QObject *parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
|
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
|
||||||
void WorkerFailedToStart();
|
void WorkerFailedToStart();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void DoStart() {}
|
virtual void DoStart() {}
|
||||||
virtual void NewConnection() {}
|
virtual void NewConnection() {}
|
||||||
virtual void ProcessError(QProcess::ProcessError) {}
|
virtual void ProcessError(QProcess::ProcessError) {}
|
||||||
@@ -78,7 +78,7 @@ class WorkerPool : public _WorkerPoolBase {
|
|||||||
void SetExecutableName(const QString &executable_name);
|
void SetExecutableName(const QString &executable_name);
|
||||||
|
|
||||||
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
|
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
|
||||||
void SetWorkerCount(int count);
|
void SetWorkerCount(const int count);
|
||||||
|
|
||||||
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
|
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
|
||||||
// Defaults to QApplication::applicationName().
|
// Defaults to QApplication::applicationName().
|
||||||
@@ -93,14 +93,14 @@ class WorkerPool : public _WorkerPoolBase {
|
|||||||
// Can be called from any thread.
|
// Can be called from any thread.
|
||||||
ReplyType *SendMessageWithReply(MessageType *message);
|
ReplyType *SendMessageWithReply(MessageType *message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
||||||
void DoStart() override;
|
void DoStart() override;
|
||||||
void NewConnection() override;
|
void NewConnection() override;
|
||||||
void ProcessError(QProcess::ProcessError error) override;
|
void ProcessError(QProcess::ProcessError error) override;
|
||||||
void SendQueuedMessages() override;
|
void SendQueuedMessages() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Worker {
|
struct Worker {
|
||||||
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
|
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ private:
|
|||||||
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
|
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
|
||||||
HandlerType *NextHandler() const;
|
HandlerType *NextHandler() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString local_server_name_;
|
QString local_server_name_;
|
||||||
QString executable_name_;
|
QString executable_name_;
|
||||||
QString executable_path_;
|
QString executable_path_;
|
||||||
@@ -147,7 +147,7 @@ private:
|
|||||||
mutable int next_worker_;
|
mutable int next_worker_;
|
||||||
QList<Worker> workers_;
|
QList<Worker> workers_;
|
||||||
|
|
||||||
QAtomicInt next_id_;
|
QAtomicInteger<qint64> next_id_;
|
||||||
|
|
||||||
QMutex message_queue_mutex_;
|
QMutex message_queue_mutex_;
|
||||||
QQueue<ReplyType*> message_queue_;
|
QQueue<ReplyType*> message_queue_;
|
||||||
@@ -170,9 +170,10 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
|||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
WorkerPool<HandlerType>::~WorkerPool() {
|
WorkerPool<HandlerType>::~WorkerPool() {
|
||||||
|
|
||||||
for (const Worker &worker : workers_) {
|
for (const Worker &worker : workers_) {
|
||||||
if (worker.local_socket_ && worker.process_) {
|
if (worker.local_socket_ && worker.process_) {
|
||||||
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
|
||||||
|
|
||||||
// The worker is connected. Close his socket and wait for him to exit.
|
// The worker is connected. Close his socket and wait for him to exit.
|
||||||
qLog(Debug) << "Closing worker socket";
|
qLog(Debug) << "Closing worker socket";
|
||||||
@@ -193,10 +194,11 @@ WorkerPool<HandlerType>::~WorkerPool() {
|
|||||||
for (ReplyType *reply : message_queue_) {
|
for (ReplyType *reply : message_queue_) {
|
||||||
reply->Abort();
|
reply->Abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SetWorkerCount(int count) {
|
void WorkerPool<HandlerType>::SetWorkerCount(const int count) {
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
worker_count_ = count;
|
worker_count_ = count;
|
||||||
}
|
}
|
||||||
@@ -220,6 +222,7 @@ void WorkerPool<HandlerType>::Start() {
|
|||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::DoStart() {
|
void WorkerPool<HandlerType>::DoStart() {
|
||||||
|
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
Q_ASSERT(!executable_name_.isEmpty());
|
Q_ASSERT(!executable_name_.isEmpty());
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
@@ -248,10 +251,12 @@ void WorkerPool<HandlerType>::DoStart() {
|
|||||||
|
|
||||||
workers_ << worker;
|
workers_ << worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
||||||
|
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
DeleteQObjectPointerLater(&worker->local_server_);
|
DeleteQObjectPointerLater(&worker->local_server_);
|
||||||
@@ -263,7 +268,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
|||||||
worker->process_ = new QProcess(this);
|
worker->process_ = new QProcess(this);
|
||||||
|
|
||||||
connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||||
connect(worker->process_, SIGNAL(error(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
|
connect(worker->process_, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
|
||||||
|
|
||||||
// Create a server, find an unused name and start listening
|
// Create a server, find an unused name and start listening
|
||||||
forever {
|
forever {
|
||||||
@@ -284,6 +289,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
|
|||||||
// Start the process
|
// Start the process
|
||||||
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
|
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
@@ -338,20 +344,24 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
|||||||
StartOneWorker(worker);
|
StartOneWorker(worker);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
typename WorkerPool<HandlerType>::ReplyType*
|
typename WorkerPool<HandlerType>::ReplyType*
|
||||||
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
||||||
const int id = next_id_.fetchAndAddOrdered(1);
|
|
||||||
|
const qint64 id = next_id_.fetchAndAddOrdered(1);
|
||||||
message->set_id(id);
|
message->set_id(id);
|
||||||
|
|
||||||
return new ReplyType(*message);
|
return new ReplyType(*message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
typename WorkerPool<HandlerType>::ReplyType*
|
typename WorkerPool<HandlerType>::ReplyType*
|
||||||
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||||
|
|
||||||
ReplyType *reply = NewReply(message);
|
ReplyType *reply = NewReply(message);
|
||||||
|
|
||||||
// Add the pending reply to the queue
|
// Add the pending reply to the queue
|
||||||
@@ -364,10 +374,12 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
|||||||
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SendQueuedMessages() {
|
void WorkerPool<HandlerType>::SendQueuedMessages() {
|
||||||
|
|
||||||
QMutexLocker l(&message_queue_mutex_);
|
QMutexLocker l(&message_queue_mutex_);
|
||||||
|
|
||||||
while (!message_queue_.isEmpty()) {
|
while (!message_queue_.isEmpty()) {
|
||||||
@@ -384,10 +396,12 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
|
|||||||
|
|
||||||
handler->SendRequest(reply);
|
handler->SendRequest(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
||||||
|
|
||||||
for (int i = 0; i < workers_.count(); ++i) {
|
for (int i = 0; i < workers_.count(); ++i) {
|
||||||
const int worker_index = (next_worker_ + i) % workers_.count();
|
const int worker_index = (next_worker_ + i) % workers_.count();
|
||||||
|
|
||||||
@@ -398,6 +412,7 @@ HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WORKERPOOL_H
|
#endif // WORKERPOOL_H
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
set(MESSAGES tagreadermessages.proto)
|
set(MESSAGES tagreadermessages.proto)
|
||||||
set(SOURCES fmpsparser.cpp tagreader.cpp)
|
set(SOURCES tagreader.cpp)
|
||||||
|
|
||||||
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ link_directories(
|
|||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${PROTOBUF_LIBRARY_DIRS}
|
${PROTOBUF_LIBRARY_DIRS}
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
${Qt5Core_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||||
@@ -17,8 +16,6 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
|||||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
${Qt5Network_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||||
@@ -34,6 +31,7 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
|||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${PROTOBUF_LIBRARY}
|
${PROTOBUF_LIBRARY}
|
||||||
${TAGLIB_LIBRARIES}
|
${TAGLIB_LIBRARIES}
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
|
${QtNetwork_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
/* This file is part of Strawberry.
|
|
||||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
|
|
||||||
Strawberry is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Strawberry is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QString>
|
|
||||||
#include <QChar>
|
|
||||||
|
|
||||||
#include "fmpsparser.h"
|
|
||||||
|
|
||||||
using std::placeholders::_1;
|
|
||||||
using std::placeholders::_2;
|
|
||||||
|
|
||||||
FMPSParser::FMPSParser() :
|
|
||||||
// The float regex ends with (?:$|(?=::|;;)) to ensure it matches all the way
|
|
||||||
// up to the end of the value. Without it, it would match a string that
|
|
||||||
// starts with a number, like "123abc".
|
|
||||||
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
|
|
||||||
|
|
||||||
// Matches any character except unescaped slashes, colons and semicolons.
|
|
||||||
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
|
|
||||||
|
|
||||||
// Used for replacing escaped characters.
|
|
||||||
escape_re_("\\\\([\\\\:;])") {}
|
|
||||||
|
|
||||||
// Parses a list of things (of type T) that are separated by two consecutive
|
|
||||||
// Separator characters. Each individual thing is parsed by the F function.
|
|
||||||
// For example, to parse this data:
|
|
||||||
// foo::bar::baz
|
|
||||||
// Use:
|
|
||||||
// QVariantList ret;
|
|
||||||
// ParseContainer<':'>(data, ParseValue, &ret);
|
|
||||||
// ret will then contain "foo", "bar", and "baz".
|
|
||||||
// Returns the number of characters that were consumed from data.
|
|
||||||
//
|
|
||||||
// You can parse lists of lists by using different separator characters:
|
|
||||||
// ParseContainer<';'>(data, ParseContainer<':'>, &ret);
|
|
||||||
template <char Separator, typename F, typename T>
|
|
||||||
static int ParseContainer(const QStringRef& data, F f, QList<T>* ret) {
|
|
||||||
ret->clear();
|
|
||||||
|
|
||||||
T value;
|
|
||||||
int pos = 0;
|
|
||||||
while (pos < data.length()) {
|
|
||||||
const int len = data.length() - pos;
|
|
||||||
int matched_len = f(QStringRef(data.string(), data.position() + pos, len), &value);
|
|
||||||
if (matched_len == -1 || matched_len > len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret->append(value);
|
|
||||||
pos += matched_len;
|
|
||||||
|
|
||||||
// Expect two separators in a row
|
|
||||||
if (pos + 2 <= data.length() && data.at(pos) == Separator && data.at(pos+1) == Separator) {
|
|
||||||
pos += 2;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FMPSParser::Parse(const QString &data) {
|
|
||||||
|
|
||||||
result_ = Result();
|
|
||||||
|
|
||||||
// Only return success if we matched the whole string
|
|
||||||
return ParseListList(data, &result_) == data.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FMPSParser::ParseValueRef(const QStringRef& data, QVariant* ret) const {
|
|
||||||
// Try to match a float
|
|
||||||
int pos = float_re_.indexIn(*data.string(), data.position());
|
|
||||||
if (pos == data.position()) {
|
|
||||||
*ret = float_re_.cap(1).toDouble();
|
|
||||||
return float_re_.matchedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise try to match a string
|
|
||||||
pos = string_re_.indexIn(*data.string(), data.position());
|
|
||||||
if (pos == data.position()) {
|
|
||||||
// Replace escape sequences with their actual characters
|
|
||||||
QString value = string_re_.cap(1);
|
|
||||||
value.replace(escape_re_, "\\1");
|
|
||||||
*ret = value;
|
|
||||||
return string_re_.matchedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses an inner list - a list of values
|
|
||||||
int FMPSParser::ParseListRef(const QStringRef &data, QVariantList *ret) const {
|
|
||||||
return ParseContainer<':'>(data, std::bind(&FMPSParser::ParseValueRef, this, _1, _2), ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses an outer list - a list of lists
|
|
||||||
int FMPSParser::ParseListListRef(const QStringRef &data, Result *ret) const {
|
|
||||||
return ParseContainer<';'>(data, std::bind(&FMPSParser::ParseListRef, this, _1, _2), ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience functions that take QStrings instead of QStringRefs. Use the QStringRef versions if possible, they're faster.
|
|
||||||
int FMPSParser::ParseValue(const QString &data, QVariant *ret) const {
|
|
||||||
return ParseValueRef(QStringRef(&data), ret);
|
|
||||||
}
|
|
||||||
int FMPSParser::ParseList(const QString &data, QVariantList *ret) const {
|
|
||||||
return ParseListRef(QStringRef(&data), ret);
|
|
||||||
}
|
|
||||||
int FMPSParser::ParseListList(const QString &data, Result *ret) const {
|
|
||||||
return ParseListListRef(QStringRef(&data), ret);
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/* This file is part of Strawberry.
|
|
||||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
|
|
||||||
Strawberry is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Strawberry is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMPSPARSER_H
|
|
||||||
#define FMPSPARSER_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QMetaType>
|
|
||||||
#include <QString>
|
|
||||||
#include <QRegExp>
|
|
||||||
|
|
||||||
class QVariant;
|
|
||||||
|
|
||||||
class FMPSParser {
|
|
||||||
public:
|
|
||||||
FMPSParser();
|
|
||||||
|
|
||||||
// A FMPS result is a list of lists of values (where a value is a string or
|
|
||||||
// a float).
|
|
||||||
typedef QList<QVariantList> Result;
|
|
||||||
|
|
||||||
// Parses a FMPS value and returns true on success.
|
|
||||||
bool Parse(const QString &data);
|
|
||||||
|
|
||||||
// Gets the result of the last successful Parse.
|
|
||||||
Result result() const { return result_; }
|
|
||||||
|
|
||||||
// Returns true if result() is empty.
|
|
||||||
bool is_empty() const { return result().isEmpty() || result()[0].isEmpty(); }
|
|
||||||
|
|
||||||
// Internal functions, public for unit tests
|
|
||||||
int ParseValue(const QString &data, QVariant *ret) const;
|
|
||||||
int ParseValueRef(const QStringRef &data, QVariant *ret) const;
|
|
||||||
|
|
||||||
int ParseList(const QString &data, QVariantList *ret) const;
|
|
||||||
int ParseListRef(const QStringRef &data, QVariantList *ret) const;
|
|
||||||
|
|
||||||
int ParseListList(const QString &data, Result *ret) const;
|
|
||||||
int ParseListListRef(const QStringRef &data, Result *ret) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QRegExp float_re_;
|
|
||||||
QRegExp string_re_;
|
|
||||||
QRegExp escape_re_;
|
|
||||||
Result result_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // FMPSPARSER_H
|
|
||||||
@@ -84,14 +84,11 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/messagehandler.h"
|
#include "core/messagehandler.h"
|
||||||
|
|
||||||
#include "fmpsparser.h"
|
|
||||||
#include "core/timeconstants.h"
|
#include "core/timeconstants.h"
|
||||||
|
|
||||||
class FileRefFactory {
|
class FileRefFactory {
|
||||||
@@ -140,6 +137,15 @@ TagReader::~TagReader() {
|
|||||||
delete factory_;
|
delete factory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TagReader::IsMediaFile(const QString &filename) const {
|
||||||
|
|
||||||
|
qLog(Debug) << "Checking for valid file" << filename;
|
||||||
|
|
||||||
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||||
|
return !fileref->isNull() && fileref->tag();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||||
|
|
||||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
||||||
@@ -177,11 +183,11 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
song->set_basefilename(DataCommaSizeFromQString(info.fileName()));
|
song->set_basefilename(DataCommaSizeFromQString(info.fileName()));
|
||||||
song->set_url(url.constData(), url.size());
|
song->set_url(url.constData(), url.size());
|
||||||
song->set_filesize(info.size());
|
song->set_filesize(info.size());
|
||||||
song->set_mtime(info.lastModified().toTime_t());
|
song->set_mtime(info.lastModified().toSecsSinceEpoch());
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||||
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toTime_t() : info.lastModified().toTime_t());
|
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toSecsSinceEpoch() : info.lastModified().toSecsSinceEpoch());
|
||||||
#else
|
#else
|
||||||
song->set_ctime(info.created().toTime_t());
|
song->set_ctime(info.created().toSecsSinceEpoch());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||||
@@ -200,10 +206,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
|
|
||||||
TagLib::Tag *tag = fileref->tag();
|
TagLib::Tag *tag = fileref->tag();
|
||||||
if (tag) {
|
if (tag) {
|
||||||
Decode(tag->title(), nullptr, song->mutable_title());
|
Decode(tag->title(), song->mutable_title());
|
||||||
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
|
Decode(tag->artist(), song->mutable_artist()); // TPE1
|
||||||
Decode(tag->album(), nullptr, song->mutable_album());
|
Decode(tag->album(), song->mutable_album());
|
||||||
Decode(tag->genre(), nullptr, song->mutable_genre());
|
Decode(tag->genre(), song->mutable_genre());
|
||||||
song->set_year(tag->year());
|
song->set_year(tag->year());
|
||||||
song->set_track(tag->track());
|
song->set_track(tag->track());
|
||||||
song->set_valid(true);
|
song->set_valid(true);
|
||||||
@@ -216,7 +222,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
||||||
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
|
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
|
||||||
if (TagLib::Ogg::XiphComment *tag_ogg = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
if (TagLib::Ogg::XiphComment *tag_ogg = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||||
ParseOggTag(tag_ogg->fieldListMap(), nullptr, &disc, &compilation, song);
|
ParseOggTag(tag_ogg->fieldListMap(), &disc, &compilation, song);
|
||||||
if (!tag_ogg->pictureList().isEmpty()) {
|
if (!tag_ogg->pictureList().isEmpty()) {
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
song->set_art_automatic(kEmbeddedCover);
|
||||||
}
|
}
|
||||||
@@ -227,28 +233,28 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
song->set_bitdepth(file_flac->audioProperties()->bitsPerSample());
|
song->set_bitdepth(file_flac->audioProperties()->bitsPerSample());
|
||||||
|
|
||||||
if (file_flac->xiphComment()) {
|
if (file_flac->xiphComment()) {
|
||||||
ParseOggTag(file_flac->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
ParseOggTag(file_flac->xiphComment()->fieldListMap(), &disc, &compilation, song);
|
||||||
if (!file_flac->pictureList().isEmpty()) {
|
if (!file_flac->pictureList().isEmpty()) {
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
song->set_art_automatic(kEmbeddedCover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
||||||
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
|
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
|
||||||
if (file_wavpack->tag()) {
|
if (file_wavpack->APETag()) {
|
||||||
ParseAPETag(file_wavpack->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
ParseAPETag(file_wavpack->APETag()->itemListMap(), &disc, &compilation, song);
|
||||||
}
|
}
|
||||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||||
if (file_ape->tag()) {
|
if (file_ape->APETag()) {
|
||||||
ParseAPETag(file_ape->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
ParseAPETag(file_ape->APETag()->itemListMap(), &disc, &compilation, song);
|
||||||
}
|
}
|
||||||
song->set_bitdepth(file_ape->audioProperties()->bitsPerSample());
|
song->set_bitdepth(file_ape->audioProperties()->bitsPerSample());
|
||||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
@@ -257,22 +263,22 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
|
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
|
||||||
|
|
||||||
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
||||||
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
|
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), song->mutable_composer());
|
||||||
|
|
||||||
// content group
|
// content group
|
||||||
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), nullptr, song->mutable_grouping());
|
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), song->mutable_grouping());
|
||||||
|
|
||||||
// ID3v2: lead performer/soloist
|
// ID3v2: lead performer/soloist
|
||||||
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), nullptr, song->mutable_performer());
|
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), song->mutable_performer());
|
||||||
|
|
||||||
// original artist/performer
|
// original artist/performer
|
||||||
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), nullptr, song->mutable_performer());
|
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), song->mutable_performer());
|
||||||
|
|
||||||
// Skip TPE1 (which is the artist) here because we already fetched it
|
// Skip TPE1 (which is the artist) here because we already fetched it
|
||||||
|
|
||||||
|
|
||||||
// non-standard: Apple, Microsoft
|
// non-standard: Apple, Microsoft
|
||||||
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), nullptr, song->mutable_albumartist());
|
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), song->mutable_albumartist());
|
||||||
|
|
||||||
if (!map["TCMP"].isEmpty()) compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
if (!map["TCMP"].isEmpty()) compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
||||||
|
|
||||||
@@ -282,33 +288,24 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!map["USLT"].isEmpty()) {
|
if (!map["USLT"].isEmpty()) {
|
||||||
Decode(map["USLT"].front()->toString(), nullptr, song->mutable_lyrics());
|
Decode(map["USLT"].front()->toString(), song->mutable_lyrics());
|
||||||
}
|
}
|
||||||
else if (!map["SYLT"].isEmpty()) {
|
else if (!map["SYLT"].isEmpty()) {
|
||||||
Decode(map["SYLT"].front()->toString(), nullptr, song->mutable_lyrics());
|
Decode(map["SYLT"].front()->toString(), song->mutable_lyrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||||
|
|
||||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||||
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
for (uint i = 0 ; i < map["COMM"].size() ; ++i) {
|
||||||
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||||
|
|
||||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
||||||
Decode(frame->text(), nullptr, song->mutable_comment());
|
Decode(frame->text(), song->mutable_comment());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse FMPS frames
|
|
||||||
for (uint i = 0; i < map["TXXX"].size(); ++i) {
|
|
||||||
const TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
|
|
||||||
|
|
||||||
if (frame && frame->description().startsWith("FMPS_")) {
|
|
||||||
ParseFMPSFrame(TStringToQString(frame->description()), TStringToQString(frame->fieldList()[1]), song);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +320,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
if (mp4_tag->item("aART").isValid()) {
|
if (mp4_tag->item("aART").isValid()) {
|
||||||
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
|
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
|
||||||
if (!album_artists.isEmpty()) {
|
if (!album_artists.isEmpty()) {
|
||||||
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
|
Decode(album_artists.front(), song->mutable_albumartist());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,17 +334,20 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mp4_tag->item("\251wrt").isValid()) {
|
if (mp4_tag->item("\251wrt").isValid()) {
|
||||||
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), nullptr, song->mutable_composer());
|
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), song->mutable_composer());
|
||||||
}
|
}
|
||||||
if (mp4_tag->item("\251grp").isValid()) {
|
if (mp4_tag->item("\251grp").isValid()) {
|
||||||
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), nullptr, song->mutable_grouping());
|
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), song->mutable_grouping());
|
||||||
|
}
|
||||||
|
if (mp4_tag->item("\251lyr").isValid()) {
|
||||||
|
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), song->mutable_lyrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
|
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
|
||||||
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
|
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
|
Decode(mp4_tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,6 +355,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
|
|
||||||
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
|
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
|
||||||
|
|
||||||
|
if (file_asf->tag()) {
|
||||||
|
Decode(file_asf->tag()->comment(), song->mutable_comment());
|
||||||
|
}
|
||||||
|
|
||||||
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
|
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
|
||||||
|
|
||||||
if (attributes_map.contains(kASF_OriginalDate_ID)) {
|
if (attributes_map.contains(kASF_OriginalDate_ID)) {
|
||||||
@@ -371,15 +375,15 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MPC::File* file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
else if (TagLib::MPC::File *file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||||
if (file_mpc->tag()) {
|
if (file_mpc->APETag()) {
|
||||||
ParseAPETag(file_mpc->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
ParseAPETag(file_mpc->APETag()->itemListMap(), &disc, &compilation, song);
|
||||||
}
|
}
|
||||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
if (tag) Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (tag) {
|
else if (tag) {
|
||||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
Decode(tag->comment(), song->mutable_comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!disc.isEmpty()) {
|
if (!disc.isEmpty()) {
|
||||||
@@ -395,7 +399,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
|
|
||||||
if (compilation.isEmpty()) {
|
if (compilation.isEmpty()) {
|
||||||
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
||||||
if (QStringFromStdString(song->artist()).toLower() == "various artists") {
|
if (QStringFromStdString(song->artist()).toLower() == "various artists" || QStringFromStdString(song->albumartist()).toLower() == "various artists") {
|
||||||
song->set_compilation(true);
|
song->set_compilation(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,42 +422,27 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::Decode(const TagLib::String &tag, const QTextCodec *codec, std::string *output) {
|
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
|
||||||
|
|
||||||
QString tmp;
|
|
||||||
|
|
||||||
if (codec && tag.isLatin1()) { // Never override UTF-8.
|
|
||||||
const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString();
|
|
||||||
tmp = codec->toUnicode(fixed.c_str()).trimmed();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tmp = TStringToQString(tag).trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
QString tmp = TStringToQString(tag).trimmed();
|
||||||
output->assign(DataCommaSizeFromQString(tmp));
|
output->assign(DataCommaSizeFromQString(tmp));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::Decode(const QString &tag, const QTextCodec *codec, std::string *output) {
|
void TagReader::Decode(const QString &tag, std::string *output) {
|
||||||
|
|
||||||
if (!codec) {
|
output->assign(DataCommaSizeFromQString(tag));
|
||||||
output->assign(DataCommaSizeFromQString(tag));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const QString decoded(codec->toUnicode(tag.toUtf8()));
|
|
||||||
output->assign(DataCommaSizeFromQString(decoded));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
||||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
||||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), song->mutable_grouping());
|
||||||
|
|
||||||
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
|
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), song->mutable_albumartist());
|
||||||
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
|
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), song->mutable_albumartist());
|
||||||
|
|
||||||
if (!map["ORIGINALDATE"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
if (!map["ORIGINALDATE"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
||||||
else if (!map["ORIGINALYEAR"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
else if (!map["ORIGINALYEAR"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
||||||
@@ -465,20 +454,18 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCod
|
|||||||
|
|
||||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
||||||
|
|
||||||
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
|
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), song->mutable_lyrics());
|
||||||
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
|
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), song->mutable_lyrics());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
Q_UNUSED(codec);
|
|
||||||
|
|
||||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||||
if (it != map.end()) {
|
if (it != map.end()) {
|
||||||
TagLib::StringList album_artists = it->second.values();
|
TagLib::StringList album_artists = it->second.values();
|
||||||
if (!album_artists.isEmpty()) {
|
if (!album_artists.isEmpty()) {
|
||||||
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
|
Decode(album_artists.front(), song->mutable_albumartist());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,19 +479,19 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("PERFORMER")) {
|
if (map.contains("PERFORMER")) {
|
||||||
Decode(map["PERFORMER"].values().toString(", "), nullptr, song->mutable_performer());
|
Decode(map["PERFORMER"].values().toString(", "), song->mutable_performer());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("COMPOSER")) {
|
if (map.contains("COMPOSER")) {
|
||||||
Decode(map["COMPOSER"].values().toString(", "), nullptr, song->mutable_composer());
|
Decode(map["COMPOSER"].values().toString(", "), song->mutable_composer());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("GROUPING")) {
|
if (map.contains("GROUPING")) {
|
||||||
Decode(map["GROUPING"].values().toString(" "), nullptr, song->mutable_grouping());
|
Decode(map["GROUPING"].values().toString(" "), song->mutable_grouping());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("LYRICS")) {
|
if (map.contains("LYRICS")) {
|
||||||
Decode(map["LYRICS"].toString(), nullptr, song->mutable_lyrics());
|
Decode(map["LYRICS"].toString(), song->mutable_lyrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.contains("FMPS_PLAYCOUNT")) {
|
if (map.contains("FMPS_PLAYCOUNT")) {
|
||||||
@@ -516,40 +503,13 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCode
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const {
|
|
||||||
|
|
||||||
qLog(Debug) << "Parsing FMPSFrame" << name << ", " << value;
|
|
||||||
FMPSParser parser;
|
|
||||||
|
|
||||||
if (!parser.Parse(value) || parser.is_empty()) return;
|
|
||||||
|
|
||||||
QVariant var;
|
|
||||||
|
|
||||||
if (name == "FMPS_PlayCount") {
|
|
||||||
var = parser.result()[0][0];
|
|
||||||
if (var.type() == QVariant::Double) {
|
|
||||||
song->set_playcount(var.toDouble());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (name == "FMPS_PlayCount_User") {
|
|
||||||
// Take a user playcount only if there's no playcount already set
|
|
||||||
if (song->playcount() == 0 && parser.result()[0].count() >= 2) {
|
|
||||||
var = parser.result()[0][1];
|
|
||||||
if (var.type() == QVariant::Double) {
|
|
||||||
song->set_playcount(var.toDouble());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const {
|
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
||||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
|
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
|
vorbis_comments->addField("COMPILATION", QStringToTaglibString(song.compilation() ? "1" : QString()), true);
|
||||||
|
|
||||||
// Try to be coherent, the two forms are used but the first one is preferred
|
// Try to be coherent, the two forms are used but the first one is preferred
|
||||||
|
|
||||||
@@ -563,18 +523,21 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
|||||||
|
|
||||||
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
if (filename.isNull() || filename.isEmpty()) return false;
|
if (filename.isEmpty()) return false;
|
||||||
|
|
||||||
qLog(Debug) << "Saving tags to" << filename;
|
qLog(Debug) << "Saving tags to" << filename;
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||||
if (!fileref || fileref->isNull()) return false;
|
if (!fileref || fileref->isNull()) return false;
|
||||||
|
|
||||||
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
|
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
|
||||||
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
|
fileref->tag()->setArtist(song.artist().empty() ? TagLib::String() : StdStringToTaglibString(song.artist()));
|
||||||
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
|
fileref->tag()->setAlbum(song.album().empty() ? TagLib::String() : StdStringToTaglibString(song.album()));
|
||||||
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
|
fileref->tag()->setGenre(song.genre().empty() ? TagLib::String() : StdStringToTaglibString(song.genre()));
|
||||||
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
|
fileref->tag()->setComment(song.comment().empty() ? TagLib::String() : StdStringToTaglibString(song.comment()));
|
||||||
fileref->tag()->setYear(song.year());
|
fileref->tag()->setYear(song.year() <= 0 ? 0 : song.year());
|
||||||
fileref->tag()->setTrack(song.track());
|
fileref->tag()->setTrack(song.track() <= 0 ? 0 : song.track());
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
||||||
@@ -602,14 +565,14 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||||
if (!tag) return false;
|
if (!tag) return false;
|
||||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
||||||
SetTextFrame("TCOM", song.composer(), tag);
|
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
||||||
SetTextFrame("TIT1", song.grouping(), tag);
|
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
||||||
SetTextFrame("TOPE", song.performer(), tag);
|
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
||||||
// Skip TPE1 (which is the artist) here because we already set it
|
// Skip TPE1 (which is the artist) here because we already set it
|
||||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
||||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
||||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
@@ -617,6 +580,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0));
|
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0));
|
||||||
tag->setItem("\251wrt", TagLib::StringList(song.composer().c_str()));
|
tag->setItem("\251wrt", TagLib::StringList(song.composer().c_str()));
|
||||||
tag->setItem("\251grp", TagLib::StringList(song.grouping().c_str()));
|
tag->setItem("\251grp", TagLib::StringList(song.grouping().c_str()));
|
||||||
|
tag->setItem("\251lyr", TagLib::StringList(song.lyrics().c_str()));
|
||||||
tag->setItem("aART", TagLib::StringList(song.albumartist().c_str()));
|
tag->setItem("aART", TagLib::StringList(song.albumartist().c_str()));
|
||||||
tag->setItem("cpil", TagLib::StringList(song.compilation() ? "1" : "0"));
|
tag->setItem("cpil", TagLib::StringList(song.compilation() ? "1" : "0"));
|
||||||
}
|
}
|
||||||
@@ -627,53 +591,26 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
SetVorbisComments(tag, song);
|
SetVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = fileref->save();
|
result = fileref->save();
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
if (ret) {
|
if (result) {
|
||||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
||||||
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_LINUX
|
#endif // Q_OS_LINUX
|
||||||
|
|
||||||
return ret;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||||
tag->setItem("disc", TagLib::APE::Item("disc", TagLib::String::number(song.disc() <= 0 - 1 ? 0 : song.disc())));
|
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
||||||
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
||||||
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
||||||
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
||||||
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
|
tag->addValue("compilation", QStringToTaglibString(song.compilation() ? QString::number(1) : QString()), true);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReader::SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
|
||||||
|
|
||||||
const QByteArray descr_utf8(description.toUtf8());
|
|
||||||
const QByteArray value_utf8(value.toUtf8());
|
|
||||||
qLog(Debug) << "Setting FMPSFrame:" << description << ", " << value;
|
|
||||||
SetUserTextFrame(std::string(descr_utf8.constData(), descr_utf8.length()), std::string(value_utf8.constData(), value_utf8.length()), tag);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReader::SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
|
||||||
|
|
||||||
const TagLib::String t_description = StdStringToTaglibString(description);
|
|
||||||
// Remove the frame if it already exists
|
|
||||||
TagLib::ID3v2::UserTextIdentificationFrame *frame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, t_description);
|
|
||||||
if (frame) {
|
|
||||||
tag->removeFrame(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and add a new frame
|
|
||||||
frame = new TagLib::ID3v2::UserTextIdentificationFrame(TagLib::String::UTF8);
|
|
||||||
|
|
||||||
frame->setDescription(t_description);
|
|
||||||
frame->setText(StdStringToTaglibString(value));
|
|
||||||
tag->addFrame(frame);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,6 +618,7 @@ void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2
|
|||||||
|
|
||||||
const QByteArray utf8(value.toUtf8());
|
const QByteArray utf8(value.toUtf8());
|
||||||
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
@@ -694,6 +632,8 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
||||||
@@ -701,9 +641,9 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update and add the frames
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// add frame takes ownership and clears the memory
|
||||||
@@ -712,15 +652,6 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
|
||||||
|
|
||||||
qLog(Debug) << "Checking for valid file" << filename;
|
|
||||||
|
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
|
||||||
return !fileref->isNull() && fileref->tag();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return QByteArray();
|
if (filename.isEmpty()) return QByteArray();
|
||||||
@@ -740,8 +671,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||||||
if (flac_file && flac_file->xiphComment()) {
|
if (flac_file && flac_file->xiphComment()) {
|
||||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||||
if (!pics.isEmpty()) {
|
if (!pics.isEmpty()) {
|
||||||
// Use the first picture in the file - this could be made cleverer and
|
// Use the first picture in the file - this could be made cleverer and pick the front cover if it's present.
|
||||||
// pick the front cover if it's present.
|
|
||||||
|
|
||||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||||
TagLib::FLAC::Picture *picture = *it;
|
TagLib::FLAC::Picture *picture = *it;
|
||||||
@@ -846,7 +776,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
|
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
|
|
||||||
TagLib::ByteVector id_vector("USLT");
|
TagLib::ByteVector id_vector("USLT");
|
||||||
QVector<TagLib::ByteVector> frames_buffer;
|
QVector<TagLib::ByteVector> frames_buffer;
|
||||||
@@ -857,6 +787,8 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
|||||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
||||||
@@ -865,9 +797,9 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update and add the frames
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// add frame takes ownership and clears the memory
|
||||||
|
|||||||
@@ -35,9 +35,6 @@
|
|||||||
|
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
class QTextCodec;
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef USE_SYSTEM_TAGLIB
|
#ifndef USE_SYSTEM_TAGLIB
|
||||||
using namespace Strawberry_TagLib;
|
using namespace Strawberry_TagLib;
|
||||||
#endif
|
#endif
|
||||||
@@ -53,28 +50,24 @@ class TagReader {
|
|||||||
explicit TagReader();
|
explicit TagReader();
|
||||||
~TagReader();
|
~TagReader();
|
||||||
|
|
||||||
|
bool IsMediaFile(const QString &filename) const;
|
||||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
||||||
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const;
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||||
|
|
||||||
static void Decode(const TagLib::String& tag, const QTextCodec *codec, std::string *output);
|
static void Decode(const TagLib::String &tag, std::string *output);
|
||||||
static void Decode(const QString &tag, const QTextCodec *codec, std::string *output);
|
static void Decode(const QString &tag, std::string *output);
|
||||||
|
|
||||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||||
void ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const;
|
|
||||||
|
|
||||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||||
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
||||||
|
|
||||||
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
|
||||||
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;
|
|
||||||
|
|
||||||
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ message SongMetadata {
|
|||||||
optional string basefilename = 22;
|
optional string basefilename = 22;
|
||||||
optional FileType filetype = 23;
|
optional FileType filetype = 23;
|
||||||
optional int32 filesize = 24;
|
optional int32 filesize = 24;
|
||||||
optional int32 mtime = 25;
|
optional int64 mtime = 25;
|
||||||
optional int32 ctime = 26;
|
optional int64 ctime = 26;
|
||||||
|
|
||||||
optional int32 playcount = 27;
|
optional int32 playcount = 27;
|
||||||
optional int32 skipcount = 28;
|
optional int32 skipcount = 28;
|
||||||
@@ -101,7 +101,7 @@ message LoadEmbeddedArtResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Message {
|
message Message {
|
||||||
optional int32 id = 1;
|
optional int64 id = 1;
|
||||||
|
|
||||||
optional ReadFileRequest read_file_request = 2;
|
optional ReadFileRequest read_file_request = 2;
|
||||||
optional ReadFileResponse read_file_response = 3;
|
optional ReadFileResponse read_file_response = 3;
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
|
|||||||
|
|
||||||
set(SOURCES main.cpp tagreaderworker.cpp)
|
set(SOURCES main.cpp tagreaderworker.cpp)
|
||||||
|
|
||||||
qt5_wrap_cpp(MOC ${HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
qt5_add_resources(QRC data/data.qrc)
|
qt6_wrap_cpp(MOC ${HEADERS})
|
||||||
|
qt6_add_resources(QRC data/data.qrc)
|
||||||
|
else()
|
||||||
|
qt5_wrap_cpp(MOC ${HEADERS})
|
||||||
|
qt5_add_resources(QRC data/data.qrc)
|
||||||
|
endif()
|
||||||
|
|
||||||
link_directories(
|
link_directories(
|
||||||
${GLIB_LIBRARY_DIRS}
|
${GLIB_LIBRARY_DIRS}
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
${Qt5Core_LIBRARY_DIRS}
|
|
||||||
${Qt5Network_LIBRARY_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||||
@@ -19,8 +22,6 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
|||||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
|
||||||
${Qt5Network_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(strawberry-tagreader PRIVATE
|
target_include_directories(strawberry-tagreader PRIVATE
|
||||||
@@ -34,8 +35,8 @@ target_include_directories(strawberry-tagreader PRIVATE
|
|||||||
target_link_libraries(strawberry-tagreader PRIVATE
|
target_link_libraries(strawberry-tagreader PRIVATE
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${TAGLIB_LIBRARIES}
|
${TAGLIB_LIBRARIES}
|
||||||
${Qt5Core_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${Qt5Network_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
libstrawberry-tagreader
|
libstrawberry-tagreader
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,11 +27,9 @@
|
|||||||
#include "tagreaderworker.h"
|
#include "tagreaderworker.h"
|
||||||
|
|
||||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
||||||
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent)
|
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
void TagReaderWorker::MessageArrived(const pb::tagreader::Message &message) {
|
||||||
|
|
||||||
pb::tagreader::Message reply;
|
pb::tagreader::Message reply;
|
||||||
|
|
||||||
@@ -51,6 +49,7 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SendReply(message, &reply);
|
SendReply(message, &reply);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||