Compare commits
2373 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
94c196108d | ||
![]() |
263d1ba002 | ||
![]() |
2dba06dc34 | ||
![]() |
811860c3b4 | ||
![]() |
8439119e24 | ||
![]() |
b5b40d8235 | ||
![]() |
b904f8af03 | ||
![]() |
ebfbb74f9e | ||
![]() |
7b4225aa1f | ||
![]() |
71a5311b06 | ||
![]() |
a62a35e1db | ||
![]() |
ca2439f595 | ||
![]() |
f9a0db716a | ||
![]() |
cfe024ea13 | ||
![]() |
993d85125e | ||
![]() |
64c39af556 | ||
![]() |
04eb911a51 | ||
![]() |
351b39e0c5 | ||
![]() |
3b6d4e6673 | ||
![]() |
e8f328d8ad | ||
![]() |
5f5b5f63af | ||
![]() |
ad6e303047 | ||
![]() |
b0e9538855 | ||
![]() |
694debd4cc | ||
![]() |
0f56ddb805 | ||
![]() |
dde77ec6bd | ||
![]() |
5d73eda115 | ||
![]() |
1985786ed2 | ||
![]() |
8e0d39ae94 | ||
![]() |
1761fb14af | ||
![]() |
ef2fc4e6f6 | ||
![]() |
b979245d6c | ||
![]() |
17b0ac75ca | ||
![]() |
bde64a13e2 | ||
![]() |
96875921b7 | ||
![]() |
551c941b5a | ||
![]() |
624c77ab43 | ||
![]() |
ba13b4b5d6 | ||
![]() |
4b2d9e544c | ||
![]() |
97c43954e8 | ||
![]() |
9fa3984a2f | ||
![]() |
5355335f19 | ||
![]() |
64fa76c568 | ||
![]() |
19a44076cf | ||
![]() |
809a18913a | ||
![]() |
5eab2d96f4 | ||
![]() |
716784f632 | ||
![]() |
eb630ca655 | ||
![]() |
18628bf89e | ||
![]() |
2052b461af | ||
![]() |
5019bdcd52 | ||
![]() |
8be0bcbdb9 | ||
![]() |
af72a22ed8 | ||
![]() |
6ed9668fea | ||
![]() |
175d2c6d29 | ||
![]() |
ab487b9a99 | ||
![]() |
ac59ec34f9 | ||
![]() |
82da57b7ce | ||
![]() |
aa6dac9bd2 | ||
![]() |
a26bf261a9 | ||
![]() |
c692286c67 | ||
![]() |
3775766605 | ||
![]() |
38e24208f6 | ||
![]() |
fbaedf2262 | ||
![]() |
8f3341cefb | ||
![]() |
4ec4bab3a9 | ||
![]() |
6d567bcd35 | ||
![]() |
363d9f0180 | ||
![]() |
db0682a469 | ||
![]() |
7a6823dcdf | ||
![]() |
bce144a232 | ||
![]() |
0cef84cac6 | ||
![]() |
56c0733b42 | ||
![]() |
0b0acb3981 | ||
![]() |
1375dcc4ec | ||
![]() |
6aeb0e335b | ||
![]() |
c1e2537851 | ||
![]() |
8c690fb737 | ||
![]() |
dad1c21b59 | ||
![]() |
dd10b2bd61 | ||
![]() |
48c7c540df | ||
![]() |
281270cd2a | ||
![]() |
02502514f6 | ||
![]() |
1bc02123f9 | ||
![]() |
3488a47c41 | ||
![]() |
fd82d67678 | ||
![]() |
e66c12105b | ||
![]() |
dbe12a6b90 | ||
![]() |
d3a680cc87 | ||
![]() |
62fc4d5cf4 | ||
![]() |
14465be847 | ||
![]() |
0e49de867d | ||
![]() |
f2e4529707 | ||
![]() |
3547fc7e61 | ||
![]() |
466a05bc52 | ||
![]() |
6de4064cca | ||
![]() |
bcf0fdd3a8 | ||
![]() |
a8f05a7efc | ||
![]() |
c64a3b5dbb | ||
![]() |
16c38c438f | ||
![]() |
48cc4a6ced | ||
![]() |
a169a05e41 | ||
![]() |
a6cb3139db | ||
![]() |
239a83324e | ||
![]() |
8efa5c7641 | ||
![]() |
28e7be248f | ||
![]() |
c3f9b38c97 | ||
![]() |
dbb18a401b | ||
![]() |
e1e41708af | ||
![]() |
638dfc3981 | ||
![]() |
7c09e44ad4 | ||
![]() |
365b798f33 | ||
![]() |
6f51d910ee | ||
![]() |
1215818572 | ||
![]() |
514ed33a02 | ||
![]() |
bfed47b82d | ||
![]() |
8c51440057 | ||
![]() |
018858ec97 | ||
![]() |
3c1988b68f | ||
![]() |
5452428d69 | ||
![]() |
d6bf6e161a | ||
![]() |
a71b76bb3c | ||
![]() |
c1429500b2 | ||
![]() |
0f02bbc2fe | ||
![]() |
b885f358a5 | ||
![]() |
650a30d794 | ||
![]() |
1dc71f383a | ||
![]() |
6dfebf7df9 | ||
![]() |
4bcdcca7f5 | ||
![]() |
c08a8581ee | ||
![]() |
25b0194036 | ||
![]() |
77fe727e69 | ||
![]() |
73f9824ddf | ||
![]() |
1fe0c673bc | ||
![]() |
8a045207a7 | ||
![]() |
fe7c5a4208 | ||
![]() |
8024f7e84d | ||
![]() |
14f0134097 | ||
![]() |
1da27be84d | ||
![]() |
08135f2cb7 | ||
![]() |
5907656bbb | ||
![]() |
2ac2bd26f8 | ||
![]() |
a2be91aea5 | ||
![]() |
579428172e | ||
![]() |
3e484637f9 | ||
![]() |
3e93c392d7 | ||
![]() |
0a97e68aa9 | ||
![]() |
69783a44c8 | ||
![]() |
d72263d28d | ||
![]() |
24a205a1aa | ||
![]() |
3a948515ce | ||
![]() |
9ade93983c | ||
![]() |
6931ce9558 | ||
![]() |
d6fb07a3e4 | ||
![]() |
01d3c2705e | ||
![]() |
29346dc9c5 | ||
![]() |
d19b3df3b0 | ||
![]() |
798e68ef62 | ||
![]() |
79397db5b4 | ||
![]() |
9256190a9b | ||
![]() |
3a0dbb0a67 | ||
![]() |
3d6c9d1b88 | ||
![]() |
5823e79fe7 | ||
![]() |
5f656dffda | ||
![]() |
34d4d9157a | ||
![]() |
22c329cdb4 | ||
![]() |
980ef82216 | ||
![]() |
84a06a72df | ||
![]() |
4833d0891d | ||
![]() |
cd53ca22c6 | ||
![]() |
4d9af9a81b | ||
![]() |
d61341c0e3 | ||
![]() |
eff50b263a | ||
![]() |
2bebc79363 | ||
![]() |
e777fb4edb | ||
![]() |
3fb25d4062 | ||
![]() |
e227596c20 | ||
![]() |
ec76583c33 | ||
![]() |
927f1e03a3 | ||
![]() |
f2c679cfec | ||
![]() |
6a75c48dba | ||
![]() |
48bdd09f64 | ||
![]() |
cf108c389f | ||
![]() |
90d97053a8 | ||
![]() |
e1fe9ebcd6 | ||
![]() |
93016ac6ab | ||
![]() |
fc20a1f10a | ||
![]() |
a4257e51d5 | ||
![]() |
2f2b3f1cdc | ||
![]() |
2ff6a9ad2b | ||
![]() |
17d4873b60 | ||
![]() |
8b41c4f384 | ||
![]() |
17f7098e27 | ||
![]() |
9ff790b7bb | ||
![]() |
ebc1fe2821 | ||
![]() |
bc2988144e | ||
![]() |
b1a9958c66 | ||
![]() |
e6a81bb95c | ||
![]() |
9521c1ad58 | ||
![]() |
6d65cc48d7 | ||
![]() |
681956a963 | ||
![]() |
052f64d648 | ||
![]() |
afe621c25c | ||
![]() |
637cf8a039 | ||
![]() |
2011a6e2ee | ||
![]() |
d54830de12 | ||
![]() |
a7e7312cca | ||
![]() |
6b83fc6b57 | ||
![]() |
74f9e07151 | ||
![]() |
82a61ab3be | ||
![]() |
54c1794cee | ||
![]() |
c962a6be76 | ||
![]() |
922c4bf3f0 | ||
![]() |
932756efce | ||
![]() |
7838265482 | ||
![]() |
b14b0e5634 | ||
![]() |
4d2d0e7bb8 | ||
![]() |
44378b7dbe | ||
![]() |
da642b2890 | ||
![]() |
6f77af20d0 | ||
![]() |
010f65a1d6 | ||
![]() |
c46f97454a | ||
![]() |
844dbd2ec5 | ||
![]() |
db7caa2dac | ||
![]() |
2974737746 | ||
![]() |
b1d7567226 | ||
![]() |
5103eb3039 | ||
![]() |
0cccdcf9b2 | ||
![]() |
22b840c2f1 | ||
![]() |
ed1a995bff | ||
![]() |
0f39dc1edb | ||
![]() |
dc9103befe | ||
![]() |
67760f5283 | ||
![]() |
99405a4c93 | ||
![]() |
b833c5d2c7 | ||
![]() |
bca5d79f88 | ||
![]() |
6e1c8edf09 | ||
![]() |
32b7b2e2fa | ||
![]() |
cfb7f8ab84 | ||
![]() |
8d80280ab9 | ||
![]() |
c95e3dc065 | ||
![]() |
00a520a4c3 | ||
![]() |
6eba621045 | ||
![]() |
a9ad8fa505 | ||
![]() |
85427826aa | ||
![]() |
25e0a90402 | ||
![]() |
938728820b | ||
![]() |
80531ef8d8 | ||
![]() |
a91fba6a3d | ||
![]() |
f8be403c34 | ||
![]() |
28a5cdf319 | ||
![]() |
6b1d264b35 | ||
![]() |
a6c10e9a1c | ||
![]() |
19a46064e9 | ||
![]() |
b57eeaa720 | ||
![]() |
ad059d5804 | ||
![]() |
6e1940e930 | ||
![]() |
103194e32d | ||
![]() |
481c330c17 | ||
![]() |
7ef489e057 | ||
![]() |
d9e5d5ff5b | ||
![]() |
ca02fb7782 | ||
![]() |
d4d06da2f8 | ||
![]() |
efde78db77 | ||
![]() |
f1b8bcd6b2 | ||
![]() |
c2bc3704e1 | ||
![]() |
def120aca4 | ||
![]() |
6d2b09ac2b | ||
![]() |
78b43a9930 | ||
![]() |
da5ff779c6 | ||
![]() |
e7da5b104d | ||
![]() |
4be76f3c8f | ||
![]() |
c58c53293c | ||
![]() |
8695a2806a | ||
![]() |
a59f1b21a6 | ||
![]() |
9e2d09dabc | ||
![]() |
2719f62feb | ||
![]() |
234cedd6c6 | ||
![]() |
5b946e9d95 | ||
![]() |
b46ca50dcc | ||
![]() |
a0d76c3be9 | ||
![]() |
995aafe9cc | ||
![]() |
6e33566cee | ||
![]() |
3b3c1d466d | ||
![]() |
056ab199ab | ||
![]() |
eea0e084af | ||
![]() |
fa82f558be | ||
![]() |
6b555b7017 | ||
![]() |
dafba203e7 | ||
![]() |
a5d382348e | ||
![]() |
74396448df | ||
![]() |
168d6257b4 | ||
![]() |
1afa33c3c7 | ||
![]() |
3a7c9c7c84 | ||
![]() |
6d08e761c8 | ||
![]() |
fee282f49c | ||
![]() |
07d2bc6898 | ||
![]() |
9551166f27 | ||
![]() |
2a8c420cff | ||
![]() |
ec1e04a65d | ||
![]() |
97a2122f41 | ||
![]() |
3825175bfc | ||
![]() |
68f4be323c | ||
![]() |
4949cd98f3 | ||
![]() |
a14ce4c7cb | ||
![]() |
85a5b7dec4 | ||
![]() |
153d464ce8 | ||
![]() |
83391e2bd9 | ||
![]() |
ec0d3ac95d | ||
![]() |
e99f6b5b38 | ||
![]() |
74b2fc7fdc | ||
![]() |
216f62ea14 | ||
![]() |
b7d0001390 | ||
![]() |
687788e4d3 | ||
![]() |
5348f8c9c8 | ||
![]() |
5a4ebf8291 | ||
![]() |
7ae3664c91 | ||
![]() |
1caed3e390 | ||
![]() |
7adb907a55 | ||
![]() |
af2896547a | ||
![]() |
d9583faf06 | ||
![]() |
2788cf9330 | ||
![]() |
92bfdffa42 | ||
![]() |
38b41fc3fd | ||
![]() |
394f69bee1 | ||
![]() |
ba5531f9dd | ||
![]() |
60d19b2380 | ||
![]() |
004d6a3b66 | ||
![]() |
3e79e62c17 | ||
![]() |
27b69330f4 | ||
![]() |
6b50b67339 | ||
![]() |
51ca775a1c | ||
![]() |
1092882f38 | ||
![]() |
8a7986c3bf | ||
![]() |
c7bd8c663d | ||
![]() |
f6c65cba58 | ||
![]() |
f849b07766 | ||
![]() |
2da3cff1e8 | ||
![]() |
0c965d0573 | ||
![]() |
77c14692c9 | ||
![]() |
226eb26300 | ||
![]() |
2d606fa989 | ||
![]() |
7a0342c8bb | ||
![]() |
42c9d765cf | ||
![]() |
a8a80ee689 | ||
![]() |
f9bdb4b0b8 | ||
![]() |
9332527872 | ||
![]() |
84f772357e | ||
![]() |
f2b9785a67 | ||
![]() |
eeaec99c59 | ||
![]() |
b0002e3b73 | ||
![]() |
27c589da97 | ||
![]() |
6484af472b | ||
![]() |
92a218b7a9 | ||
![]() |
d69a1f98af | ||
![]() |
23a6f62ea3 | ||
![]() |
e0d3ca71b3 | ||
![]() |
4f40b9f7cf | ||
![]() |
bb009daf66 | ||
![]() |
dc432f3ffa | ||
![]() |
37710195ca | ||
![]() |
7b9295ff99 | ||
![]() |
5f61d440eb | ||
![]() |
6bc73a9ebe | ||
![]() |
1195eb266e | ||
![]() |
3562a3e51e | ||
![]() |
bbfa6fe632 | ||
![]() |
bf97d13d0b | ||
![]() |
b5673b6333 | ||
![]() |
ee802867df | ||
![]() |
ecaa51e322 | ||
![]() |
0779333064 | ||
![]() |
6f1a4a73b7 | ||
![]() |
945ed2610a | ||
![]() |
d7fcaf33b9 | ||
![]() |
6a65b4c305 | ||
![]() |
a163beee69 | ||
![]() |
31268ad7cd | ||
![]() |
a0d43dd87f | ||
![]() |
1db533c8cf | ||
![]() |
78ee663660 | ||
![]() |
c32a809d38 | ||
![]() |
1406144210 | ||
![]() |
bb6ab67175 | ||
![]() |
ed3d8222d6 | ||
![]() |
41c0bbab13 | ||
![]() |
eeb96eb367 | ||
![]() |
ce93e58944 | ||
![]() |
263b0ffdbb | ||
![]() |
22bea5c97e | ||
![]() |
75802ebcc6 | ||
![]() |
27cc7b352d | ||
![]() |
d64729065e | ||
![]() |
ab318200db | ||
![]() |
947856ca8e | ||
![]() |
cd9ff9d9b0 | ||
![]() |
4cd0f661d6 | ||
![]() |
bf270a5663 | ||
![]() |
6e893f40e3 | ||
![]() |
7690905503 | ||
![]() |
6f822a6f19 | ||
![]() |
ca0179b2a9 | ||
![]() |
6682cf749f | ||
![]() |
492607ecbe | ||
![]() |
e0c75da266 | ||
![]() |
34bb53a29f | ||
![]() |
cb4fdac469 | ||
![]() |
ac46a84391 | ||
![]() |
dffd5831f8 | ||
![]() |
8358b34efa | ||
![]() |
4484d7a5c2 | ||
![]() |
b80a135cf3 | ||
![]() |
4ad525d939 | ||
![]() |
4cb5e69811 | ||
![]() |
b0596291a8 | ||
![]() |
8f0a1a5d82 | ||
![]() |
c0775d328c | ||
![]() |
4ca2c33181 | ||
![]() |
362f391b76 | ||
![]() |
980e32f69c | ||
![]() |
dd639e18b8 | ||
![]() |
c883f178b8 | ||
![]() |
65d257675f | ||
![]() |
56fa7368e8 | ||
![]() |
e9df4116fd | ||
![]() |
5492304254 | ||
![]() |
416d4e4433 | ||
![]() |
eae2863286 | ||
![]() |
39bc196f64 | ||
![]() |
157dfa320f | ||
![]() |
9b4f2ac79b | ||
![]() |
c843bce9f5 | ||
![]() |
e3106a019d | ||
![]() |
3e0ceb12d5 | ||
![]() |
050adf6640 | ||
![]() |
60bbc9f626 | ||
![]() |
065926d6a4 | ||
![]() |
85bab67083 | ||
![]() |
4001379663 | ||
![]() |
382273abc5 | ||
![]() |
85af4d6916 | ||
![]() |
6825e1144e | ||
![]() |
45f8449c72 | ||
![]() |
71bf1a8a3d | ||
![]() |
bc47a16943 | ||
![]() |
566787f041 | ||
![]() |
79b2366387 | ||
![]() |
5acea014b0 | ||
![]() |
5130acf3ea | ||
![]() |
a22d1c88d7 | ||
![]() |
85849c9396 | ||
![]() |
d3c257d97d | ||
![]() |
c13fe63f10 | ||
![]() |
07842abcb0 | ||
![]() |
07e524509f | ||
![]() |
2c05752071 | ||
![]() |
7c8427b0f7 | ||
![]() |
b72801abf3 | ||
![]() |
23d5a2b862 | ||
![]() |
7715311117 | ||
![]() |
7552f70c8d | ||
![]() |
0acc398c52 | ||
![]() |
4c1cfca95b | ||
![]() |
e113ce9621 | ||
![]() |
821d08999a | ||
![]() |
e8213220e2 | ||
![]() |
83f9d2a963 | ||
![]() |
bf97ebf89f | ||
![]() |
5b22d27cbb | ||
![]() |
e907ff43ae | ||
![]() |
b18fc3a8d0 | ||
![]() |
a8e23c4140 | ||
![]() |
fc3861b421 | ||
![]() |
e81bb5d8f1 | ||
![]() |
32f4f15831 | ||
![]() |
e29c06b718 | ||
![]() |
d9d511f33e | ||
![]() |
c61a3b8d13 | ||
![]() |
e10b867fe6 | ||
![]() |
43e230f543 | ||
![]() |
e8380cf2aa | ||
![]() |
b2ae5298a7 | ||
![]() |
17dd21ac7f | ||
![]() |
1a5e0ef7c9 | ||
![]() |
979a7a1dcc | ||
![]() |
291be84704 | ||
![]() |
962cf32ba7 | ||
![]() |
ae23682372 | ||
![]() |
540919f256 | ||
![]() |
398281cd76 | ||
![]() |
88446ccde9 | ||
![]() |
6238cc0734 | ||
![]() |
fd4823c507 | ||
![]() |
68bcfd8bf0 | ||
![]() |
1d332746af | ||
![]() |
f3e133c617 | ||
![]() |
1678a6eb59 | ||
![]() |
b4dc2c07d5 | ||
![]() |
d7838950d8 | ||
![]() |
2e93a83dd5 | ||
![]() |
67c7116f05 | ||
![]() |
9aa432c078 | ||
![]() |
db8b419b8c | ||
![]() |
990f631cbc | ||
![]() |
db46d84458 | ||
![]() |
9e6c4f8d80 | ||
![]() |
41b47f95c5 | ||
![]() |
15939fd87c | ||
![]() |
f63c343f68 | ||
![]() |
1a516e7744 | ||
![]() |
5c9d97775f | ||
![]() |
64aadcd13f | ||
![]() |
1f6a7d6462 | ||
![]() |
e44b953d9a | ||
![]() |
6c85020630 | ||
![]() |
9d910320f3 | ||
![]() |
c53074efc9 | ||
![]() |
3b51c53eca | ||
![]() |
0aa0ffb67b | ||
![]() |
33f70931dd | ||
![]() |
8830ea319f | ||
![]() |
38498d3ee2 | ||
![]() |
35d1d0bc6e | ||
![]() |
fefdb7d96d | ||
![]() |
1d39a35b05 | ||
![]() |
902f18fcca | ||
![]() |
8145f34248 | ||
![]() |
ddb524b6b2 | ||
![]() |
cbcdc73f9a | ||
![]() |
4f6c54ecb3 | ||
![]() |
2bdf1b2284 | ||
![]() |
3f0805e7f6 | ||
![]() |
4c93165a67 | ||
![]() |
5f63ffd86c | ||
![]() |
9df2469e51 | ||
![]() |
2e73e605f7 | ||
![]() |
2bcd8516ea | ||
![]() |
5c3301f9a3 | ||
![]() |
f1487c30bf | ||
![]() |
08c70b0702 | ||
![]() |
df1bf28caa | ||
![]() |
8b67ae0460 | ||
![]() |
dbdf782e59 | ||
![]() |
f102cbb613 | ||
![]() |
5522967286 | ||
![]() |
a2f42e6424 | ||
![]() |
bdfe6c2c45 | ||
![]() |
5e1a2e2a93 | ||
![]() |
7376f31c97 | ||
![]() |
155fc8fa5a | ||
![]() |
7daf80a0c0 | ||
![]() |
eb87c28225 | ||
![]() |
c876d6a51c | ||
![]() |
47f54b5650 | ||
![]() |
fbfa1723e7 | ||
![]() |
a74140842c | ||
![]() |
f5a85a816c | ||
![]() |
2a15fafbd7 | ||
![]() |
2fc4802886 | ||
![]() |
bb3f487ee5 | ||
![]() |
7d97d0ae87 | ||
![]() |
f6dc9bcad6 | ||
![]() |
697531a948 | ||
![]() |
3c745b4bc6 | ||
![]() |
3a08a6ad72 | ||
![]() |
448b397cb8 | ||
![]() |
64a1386eb6 | ||
![]() |
77c2efe171 | ||
![]() |
587c0f6232 | ||
![]() |
64e8abf203 | ||
![]() |
6c40d2a656 | ||
![]() |
cf674e9273 | ||
![]() |
9bda0379af | ||
![]() |
b04c6fbd72 | ||
![]() |
b74a91427d | ||
![]() |
c67372f8af | ||
![]() |
00789de7d4 | ||
![]() |
9964a5ffe8 | ||
![]() |
5ece9685c2 | ||
![]() |
e7c5a42821 | ||
![]() |
36e6079c57 | ||
![]() |
e5f23678ca | ||
![]() |
c3cfb5fe16 | ||
![]() |
749ad7cd83 | ||
![]() |
0b59f4eaee | ||
![]() |
402663de74 | ||
![]() |
eaa66c7ee3 | ||
![]() |
996714d6ff | ||
![]() |
fe48e5596f | ||
![]() |
d7744d2b8e | ||
![]() |
33ee35ab92 | ||
![]() |
5b291ff768 | ||
![]() |
39d6816a6d | ||
![]() |
6517b2d2ac | ||
![]() |
bfdf13dca3 | ||
![]() |
86823af685 | ||
![]() |
daefc61aa4 | ||
![]() |
6fed6e50e4 | ||
![]() |
bc9e074822 | ||
![]() |
8047102542 | ||
![]() |
fe5b81e180 | ||
![]() |
f032925c2d | ||
![]() |
8125a5dddb | ||
![]() |
154170e475 | ||
![]() |
fb83936feb | ||
![]() |
db8bf52f7d | ||
![]() |
756f0b8027 | ||
![]() |
b1fba8d3d7 | ||
![]() |
4d88bddfe2 | ||
![]() |
e606044271 | ||
![]() |
bcbb3371ff | ||
![]() |
de632882d1 | ||
![]() |
745e492d15 | ||
![]() |
c5dc615efe | ||
![]() |
a08d4b3d66 | ||
![]() |
beeb02025e | ||
![]() |
cdf7062597 | ||
![]() |
346084da1e | ||
![]() |
bbceb5eb91 | ||
![]() |
90d85319c2 | ||
![]() |
3d03683e7d | ||
![]() |
d8a74802d1 | ||
![]() |
191919d1b1 | ||
![]() |
df38e7565b | ||
![]() |
cb49a03fd7 | ||
![]() |
faee5bbb78 | ||
![]() |
7befab7e83 | ||
![]() |
4244e61214 | ||
![]() |
46eab05045 | ||
![]() |
5ca137c73c | ||
![]() |
760238fe16 | ||
![]() |
a99b4abae8 | ||
![]() |
472881cb95 | ||
![]() |
c4efc37ad8 | ||
![]() |
691b6a236e | ||
![]() |
c72697ea27 | ||
![]() |
5c7243d3ad | ||
![]() |
44cfdff39a | ||
![]() |
5eedda691a | ||
![]() |
a30d5e1b6a | ||
![]() |
8ef09a0a71 | ||
![]() |
e8044663b3 | ||
![]() |
8444c33514 | ||
![]() |
e709d9d15c | ||
![]() |
2b7328b434 | ||
![]() |
e0e5ed62ee | ||
![]() |
3d7147390f | ||
![]() |
ca705e1e37 | ||
![]() |
3c5ef504f8 | ||
![]() |
25b5ca6435 | ||
![]() |
fd217daad4 | ||
![]() |
d9f9b3df10 | ||
![]() |
a43ee97746 | ||
![]() |
43c32372e7 | ||
![]() |
5716cde1fb | ||
![]() |
b7a99b4a4b | ||
![]() |
c6a7f6dabc | ||
![]() |
24741c5d06 | ||
![]() |
6b3a282db4 | ||
![]() |
7583cfe9b7 | ||
![]() |
aafc9ce75b | ||
![]() |
fea326530b | ||
![]() |
8925cc17d8 | ||
![]() |
14412c867f | ||
![]() |
c5cc256bf2 | ||
![]() |
563c7318f9 | ||
![]() |
e92129f449 | ||
![]() |
96a273bf3b | ||
![]() |
66c27d2c13 | ||
![]() |
374cc51f77 | ||
![]() |
c031f9aa5d | ||
![]() |
068006ebd7 | ||
![]() |
d2362b7c31 | ||
![]() |
9a4059ba39 | ||
![]() |
759f4231d2 | ||
![]() |
0cefb61a2e | ||
![]() |
b7ab1a9d79 | ||
![]() |
d181ecce7b | ||
![]() |
d2d53cc9d6 | ||
![]() |
93d87854e9 | ||
![]() |
e5eac71d72 | ||
![]() |
f20b927858 | ||
![]() |
e4dad42ca1 | ||
![]() |
99afe8e6d1 | ||
![]() |
1008d5f67c | ||
![]() |
169810e8f4 | ||
![]() |
8e07ea7ad8 | ||
![]() |
9f5c6d29b2 | ||
![]() |
f6823cc679 | ||
![]() |
69c0f0fe99 | ||
![]() |
28a00472ff | ||
![]() |
8d540737b9 | ||
![]() |
1112d779be | ||
![]() |
ecced0ce13 | ||
![]() |
d751df0a73 | ||
![]() |
2c084781b0 | ||
![]() |
6e1a21a42a | ||
![]() |
80e8338014 | ||
![]() |
bfaa7afcb0 | ||
![]() |
ae7d550a01 | ||
![]() |
7fdbaa6156 | ||
![]() |
aa7dc62f72 | ||
![]() |
6a4992118a | ||
![]() |
f03cc1012d | ||
![]() |
736a696f98 | ||
![]() |
caec384ed0 | ||
![]() |
8fdc6dec44 | ||
![]() |
5e93e882c9 | ||
![]() |
30d97fe8a0 | ||
![]() |
5cb0080052 | ||
![]() |
8e4ca23727 | ||
![]() |
bdc861f058 | ||
![]() |
8925040262 | ||
![]() |
c065950ced | ||
![]() |
257a77fa35 | ||
![]() |
4e5d6e560b | ||
![]() |
d276d8eda2 | ||
![]() |
ebcb5e9368 | ||
![]() |
69f09648a4 | ||
![]() |
9adda30c38 | ||
![]() |
c5f80dc543 | ||
![]() |
d2d4a0251e | ||
![]() |
f7b6431b6f | ||
![]() |
03b9bd3a9e | ||
![]() |
6cc58ccb9b | ||
![]() |
210c270624 | ||
![]() |
be94b4373a | ||
![]() |
eeec0ee804 | ||
![]() |
a24ef280cc | ||
![]() |
d1d6a3871e | ||
![]() |
61aed60f6d | ||
![]() |
2cc323c9fe | ||
![]() |
f24ab120ee | ||
![]() |
68349bc55c | ||
![]() |
60f957ed64 | ||
![]() |
864d26cd1b | ||
![]() |
ba576ffa37 | ||
![]() |
209364adf2 | ||
![]() |
dae8da7066 | ||
![]() |
cdf8ac001c | ||
![]() |
62d0ceabcc | ||
![]() |
935e622915 | ||
![]() |
1efbbfcd6f | ||
![]() |
e0edf0b206 | ||
![]() |
4e9fa36176 | ||
![]() |
8f178401e4 | ||
![]() |
8c1d78873d | ||
![]() |
9815d10137 | ||
![]() |
97f7270aa8 | ||
![]() |
1787aa5e00 | ||
![]() |
e6a77e1297 | ||
![]() |
e251fd0053 | ||
![]() |
24afdee35c | ||
![]() |
7aea285361 | ||
![]() |
47a7707df1 | ||
![]() |
6fdae1139f | ||
![]() |
a485c4856c | ||
![]() |
3c955639a7 | ||
![]() |
bca9678683 | ||
![]() |
814b2a218d | ||
![]() |
6423670eae | ||
![]() |
90a2109fd1 | ||
![]() |
464b90210c | ||
![]() |
fa45a8adfa | ||
![]() |
1532983fb5 | ||
![]() |
ae5b2643da | ||
![]() |
02556ffce9 | ||
![]() |
18ca734819 | ||
![]() |
8a28f7b0a1 | ||
![]() |
cc72ceb368 | ||
![]() |
c021efced1 | ||
![]() |
0b3acc3eec | ||
![]() |
6c240f667c | ||
![]() |
3040ddb5ec | ||
![]() |
fdb28eb0c4 | ||
![]() |
7ded244a61 | ||
![]() |
8ed533acf3 | ||
![]() |
a27580d0cc | ||
![]() |
905db05cf9 | ||
![]() |
4242aee21e | ||
![]() |
e71bd2a08b | ||
![]() |
e53a4d0a9e | ||
![]() |
159389164a | ||
![]() |
0a92fbc18e | ||
![]() |
138c29320b | ||
![]() |
8f00dbea45 | ||
![]() |
f3fd2eb618 | ||
![]() |
fc92db83cf | ||
![]() |
3b0f8d5516 | ||
![]() |
a5273d6992 | ||
![]() |
6979be008c | ||
![]() |
71792ffd43 | ||
![]() |
3c145c0f49 | ||
![]() |
b18074f899 | ||
![]() |
3d8067a041 | ||
![]() |
f6fe001fa9 | ||
![]() |
55b8f2c533 | ||
![]() |
32a5bf043b | ||
![]() |
8437b141a4 | ||
![]() |
1f0881eec0 | ||
![]() |
8d2079482f | ||
![]() |
c331c75fde | ||
![]() |
6080c3b4ba | ||
![]() |
3c240e2119 | ||
![]() |
57fb153c5d | ||
![]() |
212401d687 | ||
![]() |
dd831d3922 | ||
![]() |
9f8dc31b50 | ||
![]() |
db93bb996c | ||
![]() |
2c02a04566 | ||
![]() |
f13f66487a | ||
![]() |
0a4c5edc3b | ||
![]() |
015cbff93d | ||
![]() |
79e9aff338 | ||
![]() |
3a51fe31df | ||
![]() |
cc3e71d8c7 | ||
![]() |
dd37b4656e | ||
![]() |
e2d2bb8755 | ||
![]() |
a98d627c0b | ||
![]() |
0080eee857 | ||
![]() |
2429cc8778 | ||
![]() |
3a83a6b527 | ||
![]() |
bcf4645263 | ||
![]() |
6c8eb3c7ed | ||
![]() |
870151214d | ||
![]() |
ae4fd576bf | ||
![]() |
747436b17e | ||
![]() |
7a58b8c3e8 | ||
![]() |
56b4b010d6 | ||
![]() |
91c75a133f | ||
![]() |
e620677d7c | ||
![]() |
09d8e44d56 | ||
![]() |
9dc530ab51 | ||
![]() |
2d0798cd4d | ||
![]() |
a269fc988b | ||
![]() |
915c48f748 | ||
![]() |
f04a245769 | ||
![]() |
a8687fb7df | ||
![]() |
3b88bac07c | ||
![]() |
358f231391 | ||
![]() |
f0923231d0 | ||
![]() |
dadf054fbb | ||
![]() |
6593b5998a | ||
![]() |
386235e2d2 | ||
![]() |
ddfd92e547 | ||
![]() |
d5fd309484 | ||
![]() |
6197b29aa0 | ||
![]() |
02294a8236 | ||
![]() |
66bcf04cbd | ||
![]() |
12b97bbe38 | ||
![]() |
5ccfcffcc1 | ||
![]() |
afe2aaa5f6 | ||
![]() |
9b11caa0e6 | ||
![]() |
a689b881d3 | ||
![]() |
e94c436264 | ||
![]() |
bad829509e | ||
![]() |
9c66b0414a | ||
![]() |
4d453a8313 | ||
![]() |
61d7b436a2 | ||
![]() |
cdddaf21b0 | ||
![]() |
b267ba5f0a | ||
![]() |
8270043053 | ||
![]() |
c00ce42bca | ||
![]() |
3852ddbbce | ||
![]() |
672bc3ab67 | ||
![]() |
62229f14da | ||
![]() |
a4c925c8d7 | ||
![]() |
60610e90b1 | ||
![]() |
90184e0ce7 | ||
![]() |
7d7bd51bc0 | ||
![]() |
71e551df42 | ||
![]() |
9c3e1d450a | ||
![]() |
3540cf26b1 | ||
![]() |
60f2116202 | ||
![]() |
4ff2532330 | ||
![]() |
9c15760c4d | ||
![]() |
e1c43ec65f | ||
![]() |
4dd10894ba | ||
![]() |
608d7ec1e7 | ||
![]() |
8474599ed6 | ||
![]() |
ab39f64fc0 | ||
![]() |
185fbca282 | ||
![]() |
6e3b2fd844 | ||
![]() |
dab39dc778 | ||
![]() |
8cd5e79fbd | ||
![]() |
1de3ac6c78 | ||
![]() |
abe06a5fa6 | ||
![]() |
85c27840a3 | ||
![]() |
81c16273c5 | ||
![]() |
801ae86b5d | ||
![]() |
5619fd0bba | ||
![]() |
200258c7c3 | ||
![]() |
5418bb49fb | ||
![]() |
3449c14ff5 | ||
![]() |
cfa4524cb3 | ||
![]() |
4fd0c84f46 | ||
![]() |
e41a52d909 | ||
![]() |
01e00632cc | ||
![]() |
9bad5ee3c5 | ||
![]() |
e87454ae88 | ||
![]() |
f319f88df4 | ||
![]() |
637840264a | ||
![]() |
3888bafc1f | ||
![]() |
adad4c7298 | ||
![]() |
d54acbcffd | ||
![]() |
36a89e8fe7 | ||
![]() |
8e6a21a9c2 | ||
![]() |
86613af37e | ||
![]() |
ba3ff10ccd | ||
![]() |
1ec283d213 | ||
![]() |
2261bcb5d3 | ||
![]() |
0e17629445 | ||
![]() |
0da6344726 | ||
![]() |
c560ec8ea6 | ||
![]() |
cade4e71c4 | ||
![]() |
403612c666 | ||
![]() |
7fe49cf24d | ||
![]() |
d2115e908a | ||
![]() |
56c234b410 | ||
![]() |
61b5ab2663 | ||
![]() |
84f71cec2c | ||
![]() |
a5b136c420 | ||
![]() |
c7144ed5c7 | ||
![]() |
00b9f69c90 | ||
![]() |
6d91b5c7b2 | ||
![]() |
fd71514068 | ||
![]() |
256cfc545d | ||
![]() |
77c6c3fabf | ||
![]() |
e25a3d17e7 | ||
![]() |
7f10e7a610 | ||
![]() |
88d56c01e7 | ||
![]() |
97425d56e7 | ||
![]() |
0afb156a5b | ||
![]() |
a192e7b29b | ||
![]() |
591f51f3d3 | ||
![]() |
5e4b7e2fb7 | ||
![]() |
177371a003 | ||
![]() |
a78841d6a9 | ||
![]() |
3ec9fcfc44 | ||
![]() |
b5d1a09010 | ||
![]() |
85b072b3d3 | ||
![]() |
8a1f1fbe06 | ||
![]() |
45b60b3d38 | ||
![]() |
cefc773992 | ||
![]() |
a885bdba4c | ||
![]() |
b6b15afb5a | ||
![]() |
1d560c8f0f | ||
![]() |
189f6eaa6f | ||
![]() |
87f78b9c39 | ||
![]() |
aa722bd8ac | ||
![]() |
58c7ec07a4 | ||
![]() |
3796247d6d | ||
![]() |
332f480ec3 | ||
![]() |
9a164668f2 | ||
![]() |
6876d160cf | ||
![]() |
a63d0ee8fc | ||
![]() |
d4135935e4 | ||
![]() |
569773cc75 | ||
![]() |
a2f5a63bbc | ||
![]() |
2db8bcc353 | ||
![]() |
c846ee0d1b | ||
![]() |
69a51e12c9 | ||
![]() |
4b57b7f5a5 | ||
![]() |
5cd400f578 | ||
![]() |
edc4989d9c | ||
![]() |
2b3d6461e3 | ||
![]() |
ab9f5d2067 | ||
![]() |
a718086ffb | ||
![]() |
8d40e68dec | ||
![]() |
de0affe115 | ||
![]() |
26e718c7c3 | ||
![]() |
82743dfd02 | ||
![]() |
f00f8b002a | ||
![]() |
33694642bd | ||
![]() |
c71242d743 | ||
![]() |
2229e86673 | ||
![]() |
f24c274f5c | ||
![]() |
3824bf66ca | ||
![]() |
d942f874ae | ||
![]() |
01632d37ef | ||
![]() |
c45f113856 | ||
![]() |
e0a8fd398c | ||
![]() |
3e97058151 | ||
![]() |
51b1dd8672 | ||
![]() |
98a7d8da6c | ||
![]() |
acb29f792f | ||
![]() |
cd364023ae | ||
![]() |
8d34a1cfc6 | ||
![]() |
73a1f078a6 | ||
![]() |
b7ce452308 | ||
![]() |
5faf76051d | ||
![]() |
5fe70a3417 | ||
![]() |
7a68b1e71f | ||
![]() |
d5468dfe89 | ||
![]() |
976372ff63 | ||
![]() |
c977d646c7 | ||
![]() |
ac50bb5d2b | ||
![]() |
85e33f7d60 | ||
![]() |
7646866a32 | ||
![]() |
d072b3cb17 | ||
![]() |
646fef108a | ||
![]() |
d1cc73775f | ||
![]() |
29d05cdb8e | ||
![]() |
322d6f2a40 | ||
![]() |
7729713924 | ||
![]() |
351a4a80d2 | ||
![]() |
87f7b0f0bb | ||
![]() |
6d3190fe5f | ||
![]() |
5d787806fe | ||
![]() |
9abb686eeb | ||
![]() |
dea0cc165d | ||
![]() |
07e0a31d02 | ||
![]() |
f24bcc7f42 | ||
![]() |
89800324cb | ||
![]() |
050e30418c | ||
![]() |
36a678276b | ||
![]() |
d4a6d647a0 | ||
![]() |
5397d18ed9 | ||
![]() |
2d3b51665e | ||
![]() |
7b03f55cb4 | ||
![]() |
b84444b680 | ||
![]() |
1e421cbcb2 | ||
![]() |
a4eed3e330 | ||
![]() |
b9db8ddee6 | ||
![]() |
9cf1385765 | ||
![]() |
a3963de668 | ||
![]() |
7d2c4ec775 | ||
![]() |
1de5bd64d8 | ||
![]() |
1923cf3844 | ||
![]() |
196d5fde65 | ||
![]() |
140d8547c7 | ||
![]() |
42eb69f46f | ||
![]() |
6f579ddc95 | ||
![]() |
8e4cb3217e | ||
![]() |
7bcccbedad | ||
![]() |
7c62887df7 | ||
![]() |
3fc859c42d | ||
![]() |
f1ad21d2bf | ||
![]() |
535a099a27 | ||
![]() |
50003f6ad2 | ||
![]() |
0914644d2b | ||
![]() |
7e41c4de58 | ||
![]() |
452c41b71f | ||
![]() |
4b0444e760 | ||
![]() |
ecad6d936a | ||
![]() |
568deefd68 | ||
![]() |
40d0420648 | ||
![]() |
afb29942b0 | ||
![]() |
15fa780c99 | ||
![]() |
9db3809c7b | ||
![]() |
dfed9546aa | ||
![]() |
469cd9582f | ||
![]() |
bc6eca2115 | ||
![]() |
72ec641f0d | ||
![]() |
4f22f4d357 | ||
![]() |
4c52001a35 | ||
![]() |
faa04966af | ||
![]() |
302eff0a59 | ||
![]() |
bcc4e97c60 | ||
![]() |
4968dd4faa | ||
![]() |
0896f44455 | ||
![]() |
620872390b | ||
![]() |
f7c326dbeb | ||
![]() |
50de3a7886 | ||
![]() |
36cad54ccd | ||
![]() |
5ad6e7fec5 | ||
![]() |
0bb943ba3e | ||
![]() |
b64fdae938 | ||
![]() |
80a0cf694f | ||
![]() |
0b2444450f | ||
![]() |
faf149d08e | ||
![]() |
e01bbad7bb | ||
![]() |
7e3eaa5921 | ||
![]() |
6fe4068c8e | ||
![]() |
8472135859 | ||
![]() |
0c9e25b3c4 | ||
![]() |
943a67c805 | ||
![]() |
881d91f86b | ||
![]() |
54d57fdcc2 | ||
![]() |
f6f30d6d64 | ||
![]() |
1e07d15428 | ||
![]() |
cc7f66822e | ||
![]() |
9cbfa66886 | ||
![]() |
4df98466df | ||
![]() |
ff2e584bde | ||
![]() |
49309b419f | ||
![]() |
879bafb837 | ||
![]() |
6fcea2d484 | ||
![]() |
5d597a3646 | ||
![]() |
56eaf000a4 | ||
![]() |
77271ebc1f | ||
![]() |
fd2b2cf0bc | ||
![]() |
438a6d7595 | ||
![]() |
00ed836aa9 | ||
![]() |
5afec8256a | ||
![]() |
f249a755e2 | ||
![]() |
4029a79dc2 | ||
![]() |
c16233fa74 | ||
![]() |
ac126ede22 | ||
![]() |
7732db0aee | ||
![]() |
37f984ba74 | ||
![]() |
cd612c4eef | ||
![]() |
914ad261ed | ||
![]() |
7551867249 | ||
![]() |
bdd3167495 | ||
![]() |
526c778162 | ||
![]() |
e01bddbd86 | ||
![]() |
2817bf9e95 | ||
![]() |
a37d22de8a | ||
![]() |
452e1c1a6f | ||
![]() |
8db86e2820 | ||
![]() |
c84bae739a | ||
![]() |
925b5954c3 | ||
![]() |
dca79938d5 | ||
![]() |
4013fa15b9 | ||
![]() |
ac1b844c15 | ||
![]() |
b8614048d4 | ||
![]() |
aed0d13591 | ||
![]() |
9d02103ebe | ||
![]() |
61784c2144 | ||
![]() |
7059215795 | ||
![]() |
2190cc7927 | ||
![]() |
75dc9506c2 | ||
![]() |
4f11fa0d41 | ||
![]() |
235b6980b8 | ||
![]() |
ee46150329 | ||
![]() |
79c585bf03 | ||
![]() |
becd81f771 | ||
![]() |
2073a2c1b0 | ||
![]() |
3f3104348e | ||
![]() |
7e80c62c7c | ||
![]() |
4038d8527f | ||
![]() |
3565f0c8ce | ||
![]() |
9647b2cb01 | ||
![]() |
2d5bf53240 | ||
![]() |
a65f7b1006 | ||
![]() |
bc5b647053 | ||
![]() |
1708ae3e3c | ||
![]() |
6bfbc5d320 | ||
![]() |
e7483bc5bc | ||
![]() |
b911ec1a29 | ||
![]() |
ca2633bf26 | ||
![]() |
e0784cd48b | ||
![]() |
566ac171f5 | ||
![]() |
8aaf39efd6 | ||
![]() |
4d95402e4e | ||
![]() |
4d102c4770 | ||
![]() |
91bc41ea20 | ||
![]() |
e565dcf18c | ||
![]() |
5a87fc7c26 | ||
![]() |
64309abc14 | ||
![]() |
ce7ec2b3f5 | ||
![]() |
b11c5f8d30 | ||
![]() |
fada4aa529 | ||
![]() |
aa0e121ade | ||
![]() |
b4700039fd | ||
![]() |
ab41c16eb5 | ||
![]() |
04101f37b8 | ||
![]() |
8c31370534 | ||
![]() |
2306b0d78c | ||
![]() |
cb1a9045e6 | ||
![]() |
e92af06664 | ||
![]() |
af20a1c994 | ||
![]() |
44d7a1d8d2 | ||
![]() |
4937d77cb6 | ||
![]() |
53f8053188 | ||
![]() |
e654c6e005 | ||
![]() |
4b0e288f00 | ||
![]() |
71ace2fbac | ||
![]() |
fb450d2f41 | ||
![]() |
84784badce | ||
![]() |
5990e46de2 | ||
![]() |
7dea5db5df | ||
![]() |
756560eac3 | ||
![]() |
dca0519336 | ||
![]() |
b9a7f30443 | ||
![]() |
32a17a997a | ||
![]() |
803a48e96d | ||
![]() |
bf41d1ad2b | ||
![]() |
d27e534a85 | ||
![]() |
6d54928d7c | ||
![]() |
0dffe05bf7 | ||
![]() |
9ef1f10319 | ||
![]() |
23fcfdbd2a | ||
![]() |
3401d26d4c | ||
![]() |
256753ea46 | ||
![]() |
76cd5f8595 | ||
![]() |
5684025847 | ||
![]() |
744bd1eadc | ||
![]() |
2bc127bb43 | ||
![]() |
7770298a65 | ||
![]() |
fa50cdb39e | ||
![]() |
816ef12088 | ||
![]() |
5ff786e59c | ||
![]() |
80fe88e8f6 | ||
![]() |
a1afe9afc6 | ||
![]() |
fe598e7d30 | ||
![]() |
4475b8ca04 | ||
![]() |
a714bdb0ce | ||
![]() |
087874620f | ||
![]() |
f1116c9258 | ||
![]() |
d01fb6730a | ||
![]() |
7bfe6a3304 | ||
![]() |
57b8e7f651 | ||
![]() |
9a577f8060 | ||
![]() |
d75a0d714e | ||
![]() |
9be3a1554e | ||
![]() |
7764719513 | ||
![]() |
dcbb9fe07c | ||
![]() |
e3b347820a | ||
![]() |
83acbe1002 | ||
![]() |
a84bf5a92e | ||
![]() |
732bdc800d | ||
![]() |
a8661b5931 | ||
![]() |
a72878c5b9 | ||
![]() |
bd4df1ae5d | ||
![]() |
a93b7172aa | ||
![]() |
908b6a1939 | ||
![]() |
561ccf600f | ||
![]() |
aee861c009 | ||
![]() |
2cc1dd28cd | ||
![]() |
f8d7bc1c34 | ||
![]() |
a684b4fff1 | ||
![]() |
c82cef3aa6 | ||
![]() |
683d5848f4 | ||
![]() |
5680a3a4b7 | ||
![]() |
15ce8eb487 | ||
![]() |
b7744be208 | ||
![]() |
63c5d66016 | ||
![]() |
d09bd9178f | ||
![]() |
7d8b1860c3 | ||
![]() |
b06825829b | ||
![]() |
ba4cd47fd8 | ||
![]() |
bbe403f141 | ||
![]() |
5df2707d98 | ||
![]() |
4859ea468f | ||
![]() |
2a8830db70 | ||
![]() |
fed9b6fd74 | ||
![]() |
b02890eb8a | ||
![]() |
da882a6eb6 | ||
![]() |
aeb89aa9d6 | ||
![]() |
f885807ecc | ||
![]() |
b826fd71f0 | ||
![]() |
ae35df1126 | ||
![]() |
80e55f6bfc | ||
![]() |
e7411c0c4b | ||
![]() |
e9af692973 | ||
![]() |
0cf90ee8b6 | ||
![]() |
dc3c0c8866 | ||
![]() |
1c46bb1ba6 | ||
![]() |
2e8f42c6ad | ||
![]() |
b449627265 | ||
![]() |
2b301ffd2c | ||
![]() |
ef0765ca10 | ||
![]() |
9766ac6db3 | ||
![]() |
940206d106 | ||
![]() |
65bbb975d2 | ||
![]() |
32799ff682 | ||
![]() |
ce093be12c | ||
![]() |
2c276770f0 | ||
![]() |
75a592f629 | ||
![]() |
c129ca9f63 | ||
![]() |
1e03457746 | ||
![]() |
13ce07d181 | ||
![]() |
43ac264f54 | ||
![]() |
cbaa98c1a1 | ||
![]() |
ed327c597a | ||
![]() |
496f43e25d | ||
![]() |
d659c7df19 | ||
![]() |
f8403a1d29 | ||
![]() |
ebb952c4ad | ||
![]() |
bea3b954a5 | ||
![]() |
129d8e89b9 | ||
![]() |
65778a3774 | ||
![]() |
d9841668ff | ||
![]() |
3f4437266b | ||
![]() |
799097c385 | ||
![]() |
4ecd4761c2 | ||
![]() |
85d27cbcb9 | ||
![]() |
e1867a99e9 | ||
![]() |
9b95e65bd9 | ||
![]() |
12a86c4975 | ||
![]() |
0b9435858b | ||
![]() |
f0386459ee | ||
![]() |
e98d4670b8 | ||
![]() |
56cc42b752 | ||
![]() |
ead208987d | ||
![]() |
364acc8949 | ||
![]() |
a8f4d2b6fc | ||
![]() |
0eb113e7c6 | ||
![]() |
b2c4a5db14 | ||
![]() |
cadfccfd0c | ||
![]() |
c89c7f71a2 | ||
![]() |
96a9670c69 | ||
![]() |
dcc5ce6792 | ||
![]() |
23d08820a2 | ||
![]() |
b9b906ab20 | ||
![]() |
2f3e94f8d0 | ||
![]() |
f616bfe354 | ||
![]() |
f2c3d86612 | ||
![]() |
d7dbf47a3f | ||
![]() |
3db584a3ea | ||
![]() |
409002b1c3 | ||
![]() |
29b542fd36 | ||
![]() |
c9590db188 | ||
![]() |
0643b5abad | ||
![]() |
964804a4c2 | ||
![]() |
92495d2b0b | ||
![]() |
9270829b5b | ||
![]() |
b6243a9945 | ||
![]() |
496f88653d | ||
![]() |
5ef645df97 | ||
![]() |
bf49c9e4e2 | ||
![]() |
0da9c91af2 | ||
![]() |
d63e2c2641 | ||
![]() |
5fdb804a50 | ||
![]() |
91c1274ac6 | ||
![]() |
9caf90f74f | ||
![]() |
71448e645c | ||
![]() |
0509472636 | ||
![]() |
0b956cf968 | ||
![]() |
2c3eb5b8ad | ||
![]() |
58363cf4dd | ||
![]() |
2574615fa3 | ||
![]() |
15fbd2b4ab | ||
![]() |
ee36a48dbb | ||
![]() |
07f212c98c | ||
![]() |
a1e2602c3d | ||
![]() |
b03e4ae692 | ||
![]() |
57808d1a1b | ||
![]() |
7775691965 | ||
![]() |
a727150c8d | ||
![]() |
949916cba1 | ||
![]() |
497d090814 | ||
![]() |
6a13847287 | ||
![]() |
cbe7d052e8 | ||
![]() |
f4d0bd8205 | ||
![]() |
1bfede120a | ||
![]() |
e96856032f | ||
![]() |
05a29e8458 | ||
![]() |
7f9a8b8748 | ||
![]() |
af3f637d3f | ||
![]() |
97a9adcbec | ||
![]() |
177d3b0178 | ||
![]() |
12beb22c1d | ||
![]() |
29fd3172ee | ||
![]() |
a873137702 | ||
![]() |
e08298a66f | ||
![]() |
fd1826cb91 | ||
![]() |
d5681b678c | ||
![]() |
0fd6235a66 | ||
![]() |
4d11745156 | ||
![]() |
2038620bc4 | ||
![]() |
5dc7cb87bb | ||
![]() |
f885e068c8 | ||
![]() |
0d16772dea | ||
![]() |
2376527d1f | ||
![]() |
7f043367ed | ||
![]() |
45403b44de | ||
![]() |
32f865f146 | ||
![]() |
9f92b59376 | ||
![]() |
2bb5030f70 | ||
![]() |
366de8773c | ||
![]() |
193e637dd9 | ||
![]() |
928bee933d | ||
![]() |
4d1720c886 | ||
![]() |
8f8ed87327 | ||
![]() |
28a441c977 | ||
![]() |
8cf50b08f2 | ||
![]() |
d3cc54d4eb | ||
![]() |
71ef0faa2c | ||
![]() |
328a6de86e | ||
![]() |
f750c8012a | ||
![]() |
b0a04b3da8 | ||
![]() |
9617bd6c85 | ||
![]() |
4c7154bd23 | ||
![]() |
4f5c3b349d | ||
![]() |
4fabfdabde | ||
![]() |
2e9b5e4e78 | ||
![]() |
115dd2b5ce | ||
![]() |
b18003ddfd | ||
![]() |
6ec335dcd5 | ||
![]() |
d5d6746ddf | ||
![]() |
00d7759cee | ||
![]() |
2ecc4e3eed | ||
![]() |
7d98145ea8 | ||
![]() |
e7c5a59e39 | ||
![]() |
71c45d8ebe | ||
![]() |
c9081a206a | ||
![]() |
818b7e0641 | ||
![]() |
e70f40fac1 | ||
![]() |
f2cdbeace6 | ||
![]() |
e6600b8562 | ||
![]() |
bc89ca92b4 | ||
![]() |
b968e1b6de | ||
![]() |
6c9f9c136b | ||
![]() |
9bff5f9e36 | ||
![]() |
2bf26a2ff8 | ||
![]() |
e33b50d9c5 | ||
![]() |
21fa44c0d5 | ||
![]() |
44444e1b89 | ||
![]() |
ca450663d0 | ||
![]() |
04e2d08417 | ||
![]() |
af4ffa91fd | ||
![]() |
f3ed2c0a82 | ||
![]() |
2c35ea92bd | ||
![]() |
26e0e1d25a | ||
![]() |
6412efb6e4 | ||
![]() |
995783bb2f | ||
![]() |
1a08bdf16f | ||
![]() |
48b122f2ed | ||
![]() |
06dac4783f | ||
![]() |
fdaadc19cb | ||
![]() |
2e53e9248a | ||
![]() |
b7abd5691c | ||
![]() |
7a0957d713 | ||
![]() |
2934fc2507 | ||
![]() |
0c8ff56a15 | ||
![]() |
07be44a50a | ||
![]() |
7a473729af | ||
![]() |
402f429b17 | ||
![]() |
4c46ca6b59 | ||
![]() |
76a0bf68c7 | ||
![]() |
9f02beaba9 | ||
![]() |
a478af6759 | ||
![]() |
4c2434788f | ||
![]() |
ca9daf5e19 | ||
![]() |
e98ce710b8 | ||
![]() |
79d1004544 | ||
![]() |
bb7f7bd3e5 | ||
![]() |
ad2b858933 | ||
![]() |
d7aa4fa7d3 | ||
![]() |
57c5603122 | ||
![]() |
1550113506 | ||
![]() |
a82d61a5e4 | ||
![]() |
0c4a7c8004 | ||
![]() |
674ee9d19a | ||
![]() |
3344953db8 | ||
![]() |
f909615b14 | ||
![]() |
92c89f0c86 | ||
![]() |
34246eb7fd | ||
![]() |
5894514ccb | ||
![]() |
dcb07e6ed4 | ||
![]() |
ccffff9870 | ||
![]() |
e34672c9d8 | ||
![]() |
a8f314190f | ||
![]() |
545af857ba | ||
![]() |
01f86e1c25 | ||
![]() |
7a89b1656c | ||
![]() |
07fcf091a2 | ||
![]() |
74a883dbf8 | ||
![]() |
5c550e8b33 | ||
![]() |
433e18b247 | ||
![]() |
2b837277c1 | ||
![]() |
d515a8e99a | ||
![]() |
2c2efaa91f | ||
![]() |
9ae9b2c18f | ||
![]() |
8e0d810968 | ||
![]() |
0f1e13d9ff | ||
![]() |
21b81dfb1d | ||
![]() |
f3d16f6d1b | ||
![]() |
6b51429203 | ||
![]() |
e2da13b0d3 | ||
![]() |
54daa85ac2 | ||
![]() |
575ba51931 | ||
![]() |
96a1c69c29 | ||
![]() |
3895d35a52 | ||
![]() |
b717ab0383 | ||
![]() |
4f61cd0b93 | ||
![]() |
4464cdcc67 | ||
![]() |
989790e7f1 | ||
![]() |
831bc711ca | ||
![]() |
d640961420 | ||
![]() |
828c614d57 | ||
![]() |
4964ad7800 | ||
![]() |
a7976cd0f2 | ||
![]() |
bed8a0e040 | ||
![]() |
b8a64771c0 | ||
![]() |
f357f743a3 | ||
![]() |
91e565d92e | ||
![]() |
a189a9e478 | ||
![]() |
9654a33218 | ||
![]() |
9bcd02d178 | ||
![]() |
2d61e526de | ||
![]() |
7723c481db | ||
![]() |
cf9ee33928 | ||
![]() |
4a47bbd816 | ||
![]() |
7654038d65 | ||
![]() |
e4612ecb66 | ||
![]() |
9c6850210d | ||
![]() |
40a2880857 | ||
![]() |
ade712d711 | ||
![]() |
175e13099c | ||
![]() |
0ed10542cc | ||
![]() |
ab830f9afd | ||
![]() |
349a2ea7eb | ||
![]() |
192ad91010 | ||
![]() |
d4d2bc072e | ||
![]() |
bcccc8f66c | ||
![]() |
848c63e2d5 | ||
![]() |
f6d0310f9c | ||
![]() |
3ef043392c | ||
![]() |
864d6f312d | ||
![]() |
f44c67de09 | ||
![]() |
91fb91d89c | ||
![]() |
8b399b7133 | ||
![]() |
9d24f68f51 | ||
![]() |
44652fdb13 | ||
![]() |
2892a6f5e2 | ||
![]() |
2fc40e5575 | ||
![]() |
7363fe90bb | ||
![]() |
d146bef740 | ||
![]() |
1f4c4be1f1 | ||
![]() |
90067d16c0 | ||
![]() |
cde6c46d2f | ||
![]() |
d305f187d5 | ||
![]() |
4f6a713b32 | ||
![]() |
8f981845dc | ||
![]() |
c764b70b3a | ||
![]() |
52bb03e136 | ||
![]() |
a90685d6cf | ||
![]() |
ae19bda1f2 | ||
![]() |
f2d8fd769d | ||
![]() |
9661062ae2 | ||
![]() |
2a07354cad | ||
![]() |
fc18fd571c | ||
![]() |
51abed9732 | ||
![]() |
d00afc912c | ||
![]() |
9d0fe725eb | ||
![]() |
8a432c9b7f | ||
![]() |
187204f03c | ||
![]() |
5e5fadb5f2 | ||
![]() |
952c793235 | ||
![]() |
3e3d8c7f9d | ||
![]() |
9b99a9897a | ||
![]() |
4f56fdc397 | ||
![]() |
c87d6825ec | ||
![]() |
00830a20e3 | ||
![]() |
d39d2874b4 | ||
![]() |
a0a74951b8 | ||
![]() |
779a6855ff | ||
![]() |
f7ed7446ae | ||
![]() |
9d44a6d2ae | ||
![]() |
10da9ee7ba | ||
![]() |
f9eff31205 | ||
![]() |
1d74a029a2 | ||
![]() |
6b8ca514bb | ||
![]() |
f51e555154 | ||
![]() |
61a3c69a06 | ||
![]() |
089615a01e | ||
![]() |
52bee8f81f | ||
![]() |
adc25e648f | ||
![]() |
31da8eac9b | ||
![]() |
e00464435b | ||
![]() |
b81138bda1 | ||
![]() |
fe2f8c088a | ||
![]() |
6de088140b | ||
![]() |
86d0534638 | ||
![]() |
af99f9fc90 | ||
![]() |
a784c8b1ae | ||
![]() |
991bbea875 | ||
![]() |
1033dbca2b | ||
![]() |
a2d2210713 | ||
![]() |
b955334882 | ||
![]() |
90ea3bf985 | ||
![]() |
83b0871248 | ||
![]() |
d8aec4b2dc | ||
![]() |
39b302dcad | ||
![]() |
426891ab31 | ||
![]() |
b94de51ac4 | ||
![]() |
db024c27d5 | ||
![]() |
326c6ae615 | ||
![]() |
5fa7610264 | ||
![]() |
1c757f8c1c | ||
![]() |
06fbbe2d7b | ||
![]() |
21d91cb1d1 | ||
![]() |
9d3d4fc734 | ||
![]() |
d6660bad03 | ||
![]() |
9d74b1a212 | ||
![]() |
54c7dc029e | ||
![]() |
d8bcdca7ff | ||
![]() |
d663f81420 | ||
![]() |
9cdebc90a0 | ||
![]() |
0a267056d3 | ||
![]() |
4650a903b4 | ||
![]() |
94c9fafe16 | ||
![]() |
8480b834b3 | ||
![]() |
07080574a2 | ||
![]() |
6c22c34300 | ||
![]() |
f54710b100 | ||
![]() |
196db1a8c8 | ||
![]() |
d66ef7eac1 | ||
![]() |
0a32634d8f | ||
![]() |
b12fc3c60d | ||
![]() |
6d013b092f | ||
![]() |
ccb182865c | ||
![]() |
412b04be58 | ||
![]() |
510e6841a0 | ||
![]() |
2089c99348 | ||
![]() |
77b5b4158c | ||
![]() |
08552f3938 | ||
![]() |
2700265769 | ||
![]() |
557098644b | ||
![]() |
f6125f0c35 | ||
![]() |
44aaf51345 | ||
![]() |
4e2a551f30 | ||
![]() |
f780ac418a | ||
![]() |
61a72a5d13 | ||
![]() |
0c0a354753 | ||
![]() |
3c5f860fb8 | ||
![]() |
3da1fa88d0 | ||
![]() |
fac15aaffb | ||
![]() |
5b01373356 | ||
![]() |
a92aa0bedc | ||
![]() |
d66f5a8590 | ||
![]() |
30ca6b8881 | ||
![]() |
c926021599 | ||
![]() |
543776d9c9 | ||
![]() |
cf631fca50 | ||
![]() |
f0ac63d5af | ||
![]() |
c1eb0583c4 | ||
![]() |
549faa8a9c | ||
![]() |
8f6c750064 | ||
![]() |
9fc1668de3 | ||
![]() |
e9190f4249 | ||
![]() |
127b464c59 | ||
![]() |
048990cd2f | ||
![]() |
01fd6e5e82 | ||
![]() |
8bf3f9b874 | ||
![]() |
f07f8f7d88 | ||
![]() |
39b40ac1fd | ||
![]() |
ea639269d8 | ||
![]() |
0abaa3ecc5 | ||
![]() |
c4d3efe71d | ||
![]() |
85e82e3d4d | ||
![]() |
beed004b10 | ||
![]() |
730e67d766 | ||
![]() |
34c6337887 | ||
![]() |
2093e53641 | ||
![]() |
2f243f2295 | ||
![]() |
e69fd0300a | ||
![]() |
f43cafbf7d | ||
![]() |
53faf77d20 | ||
![]() |
bf574dcb0a | ||
![]() |
f44011519c | ||
![]() |
72b8f33272 | ||
![]() |
a17f420d6b | ||
![]() |
f97a9ce765 | ||
![]() |
bf26adf555 | ||
![]() |
0cc94fe30c | ||
![]() |
d5d5705213 | ||
![]() |
96d74e77eb | ||
![]() |
ca8451cdbc | ||
![]() |
28e07e900f | ||
![]() |
c75dc4a647 | ||
![]() |
32380d1db0 | ||
![]() |
c9f1354e4d | ||
![]() |
e3f9e96eef | ||
![]() |
8f9b3cbf0e | ||
![]() |
458a1beed9 | ||
![]() |
47bb1cd8b5 | ||
![]() |
ccc96e25d3 | ||
![]() |
33f5e03e80 | ||
![]() |
2c3eeb7194 | ||
![]() |
79839db3a3 | ||
![]() |
d478bdda8e | ||
![]() |
fd7caab872 | ||
![]() |
e87f0ca771 | ||
![]() |
a139279575 | ||
![]() |
9fcd33cc8d | ||
![]() |
96ff6b9b8b | ||
![]() |
fd5e74dbd0 | ||
![]() |
b64571f4a5 | ||
![]() |
1eae9339f2 | ||
![]() |
22a9e866bc | ||
![]() |
97e6ea57c4 | ||
![]() |
527642a90b | ||
![]() |
aebb1baad8 | ||
![]() |
bd6b7aa88e | ||
![]() |
fcf6415963 | ||
![]() |
be79b44dc8 | ||
![]() |
17f207ffd1 | ||
![]() |
476647bfa0 | ||
![]() |
9f246fc0dc | ||
![]() |
7de6e4dbac | ||
![]() |
15dbb8082e | ||
![]() |
1a7e3bb358 | ||
![]() |
74380d2ae4 | ||
![]() |
d43ce8413a | ||
![]() |
923c1b6220 | ||
![]() |
09884e608b | ||
![]() |
3055c1266d | ||
![]() |
931c3a1de0 | ||
![]() |
a7b30fcb9e | ||
![]() |
e153407b51 | ||
![]() |
5675431eaf | ||
![]() |
8a136b79e5 | ||
![]() |
dffa25c55e | ||
![]() |
72a0aeb265 | ||
![]() |
e556cd20f7 | ||
![]() |
80ec6f976c | ||
![]() |
589639f80f | ||
![]() |
548aa00111 | ||
![]() |
76eb550011 | ||
![]() |
c1719a5200 | ||
![]() |
b07bbb928a | ||
![]() |
3b5a128097 | ||
![]() |
fad60f977e | ||
![]() |
71f9332bd3 | ||
![]() |
3eae3a2826 | ||
![]() |
3c1f7c77f0 | ||
![]() |
3e40b1d9d2 | ||
![]() |
adffbba2a5 | ||
![]() |
e239009295 | ||
![]() |
3fae2150f5 | ||
![]() |
f9ca2f52c1 | ||
![]() |
4b81cf0c2c | ||
![]() |
e7acbf112c | ||
![]() |
120e570da7 | ||
![]() |
0019231a37 | ||
![]() |
9ae1256ae7 | ||
![]() |
e1ac377812 | ||
![]() |
7866d1a005 | ||
![]() |
3e3ee581a8 | ||
![]() |
0e8ca44968 | ||
![]() |
12e75a523a | ||
![]() |
508ba22789 | ||
![]() |
fa13648f2c | ||
![]() |
2f83ed90d0 | ||
![]() |
5d74b5cee1 | ||
![]() |
e8a0ce643a | ||
![]() |
9ed4fac341 | ||
![]() |
81b2b4a85c | ||
![]() |
304d45b551 | ||
![]() |
0f488dcecf | ||
![]() |
17039aec70 | ||
![]() |
fb6cb07912 | ||
![]() |
e9e0e02db3 | ||
![]() |
03507037e8 | ||
![]() |
7739b3960c | ||
![]() |
11ec7117ab | ||
![]() |
c3ccbfd407 | ||
![]() |
de3cd96c76 | ||
![]() |
d8cf7d1ef0 | ||
![]() |
57de2470f1 | ||
![]() |
8fef4af7b2 | ||
![]() |
cfb678d618 | ||
![]() |
4eb101f046 | ||
![]() |
af7970337b | ||
![]() |
96a37da03d | ||
![]() |
ece35552fe | ||
![]() |
7d599c1afc | ||
![]() |
7c565bce1d | ||
![]() |
d17ff18ec0 | ||
![]() |
efc6b1b77a | ||
![]() |
fdbec694c6 | ||
![]() |
b86d8d0cd8 | ||
![]() |
0b4e7b3317 | ||
![]() |
472e4bfd41 | ||
![]() |
d3d70a7eed | ||
![]() |
39046bed85 | ||
![]() |
71a5c8b819 | ||
![]() |
620a39afb4 | ||
![]() |
14cee01ba1 | ||
![]() |
c782fdb698 | ||
![]() |
49ba76167e | ||
![]() |
93ab957800 | ||
![]() |
155c915733 | ||
![]() |
971450f0d4 | ||
![]() |
40a48cfba0 | ||
![]() |
9d1906da8a | ||
![]() |
3d2b180cf8 | ||
![]() |
f987947730 | ||
![]() |
e0d5d88104 | ||
![]() |
585a745484 | ||
![]() |
43fe513de8 | ||
![]() |
c1b853ca7c | ||
![]() |
4b78038b41 | ||
![]() |
d651d1abfd | ||
![]() |
5a8b734cfd | ||
![]() |
31b59a0db6 | ||
![]() |
92f7421715 | ||
![]() |
6f1d5105ee | ||
![]() |
9a78371b5c | ||
![]() |
3fc4da382e | ||
![]() |
6ee7d88af0 | ||
![]() |
bbdf2dcf1e | ||
![]() |
02bb47dd08 | ||
![]() |
0c48b8d084 | ||
![]() |
8462559b2f | ||
![]() |
319c9699fb | ||
![]() |
06a0a4a838 | ||
![]() |
8942be858b | ||
![]() |
66a8fac25e | ||
![]() |
1b902e00b4 | ||
![]() |
45a091c00c | ||
![]() |
923e66738c | ||
![]() |
ff3e2c0514 | ||
![]() |
6922a2f55e | ||
![]() |
219546cb81 | ||
![]() |
555a4d738c | ||
![]() |
813567bf5c | ||
![]() |
16a07bc201 | ||
![]() |
1153715608 | ||
![]() |
b5c7c16fb4 | ||
![]() |
302c0515b7 | ||
![]() |
19e4672a54 | ||
![]() |
c2dd6808e1 | ||
![]() |
2cf6b77627 | ||
![]() |
a5c09f4ddb | ||
![]() |
1acb9bcedb | ||
![]() |
0626e3d21e | ||
![]() |
869d215058 | ||
![]() |
0cf922b2da | ||
![]() |
5e266cd8e4 | ||
![]() |
ca5a400dbe | ||
![]() |
63fe4d1d17 | ||
![]() |
a199f58db5 | ||
![]() |
5277297336 | ||
![]() |
604d08b2c6 | ||
![]() |
ca06d9d3bf | ||
![]() |
ed2db04f43 | ||
![]() |
de0afa0e08 | ||
![]() |
f0d3227d7b | ||
![]() |
fb07a7cecc | ||
![]() |
c6b08a4d48 | ||
![]() |
040e87ad8d | ||
![]() |
d5521ead56 | ||
![]() |
a48604d2e3 | ||
![]() |
98e6a861ca | ||
![]() |
2c6dd04d19 | ||
![]() |
82ca3aa281 | ||
![]() |
b45f5c7bf6 | ||
![]() |
f54877d128 | ||
![]() |
af3ea97a42 | ||
![]() |
8beac03dc4 | ||
![]() |
4a49a5587d | ||
![]() |
d0cfa44c8f | ||
![]() |
5bae6946c6 | ||
![]() |
a8fc805594 | ||
![]() |
a265738528 | ||
![]() |
5641c4baa6 | ||
![]() |
96f889276f | ||
![]() |
214ddee2f5 | ||
![]() |
973c87b351 | ||
![]() |
72fc117393 | ||
![]() |
230ca2e968 | ||
![]() |
9095167039 | ||
![]() |
ad4ca0c449 | ||
![]() |
0a0cc66e8f | ||
![]() |
1b5c1f75a4 | ||
![]() |
040573c636 | ||
![]() |
bc5d4f9494 | ||
![]() |
f8468451c9 | ||
![]() |
65df6ca14e | ||
![]() |
36dec47bf7 | ||
![]() |
478cedcadf | ||
![]() |
dedc4b4b10 | ||
![]() |
cf348f9fae | ||
![]() |
23d56cb6a1 | ||
![]() |
4473816384 | ||
![]() |
2c8d004f78 | ||
![]() |
684bd9153e | ||
![]() |
ec456fc57c | ||
![]() |
7c92eb4360 | ||
![]() |
2c6ebe28e9 | ||
![]() |
401f06f367 | ||
![]() |
2b4e9cc635 | ||
![]() |
afdaaba045 | ||
![]() |
1cfc0cb874 | ||
![]() |
3882c97545 | ||
![]() |
bf9f690c70 | ||
![]() |
392b783c9e | ||
![]() |
6d86902a02 | ||
![]() |
376f4a2b16 | ||
![]() |
b42f19f514 | ||
![]() |
92022658f9 | ||
![]() |
b51bae5500 | ||
![]() |
5bc8cd0ecb | ||
![]() |
d38a079ba1 | ||
![]() |
c75a0f7c75 | ||
![]() |
e740f8d969 | ||
![]() |
9da7509944 | ||
![]() |
36aa204575 | ||
![]() |
2c0a968735 | ||
![]() |
84c406d5f5 | ||
![]() |
0e48747607 | ||
![]() |
f764925edc | ||
![]() |
692c8025a2 | ||
![]() |
a6dc1ab0a9 | ||
![]() |
77c9081f78 | ||
![]() |
c88d5616f7 | ||
![]() |
34d483a34a | ||
![]() |
5a3828ed4a | ||
![]() |
3fe7f27345 | ||
![]() |
0dccadff89 | ||
![]() |
5a915eb0e6 | ||
![]() |
7b48ae4f85 | ||
![]() |
92dc4a0ca7 | ||
![]() |
c7c303eec3 | ||
![]() |
1b62adc894 | ||
![]() |
0641ce79fe | ||
![]() |
b985835d8b | ||
![]() |
e413dcf8c6 | ||
![]() |
ea61e6dde1 | ||
![]() |
f7f858cb07 | ||
![]() |
4d1546cb38 | ||
![]() |
a4bc972aad | ||
![]() |
1415bac1d6 | ||
![]() |
7a98a784b2 | ||
![]() |
162845cc6d | ||
![]() |
a8ee7269bc | ||
![]() |
7c1843ee2e | ||
![]() |
bc8bb41aef | ||
![]() |
a8b94a4507 | ||
![]() |
f1b6deb768 | ||
![]() |
72ebd5ebdd | ||
![]() |
61b2ae0f7c | ||
![]() |
0d2ec5ead2 | ||
![]() |
5b74ed6b3b | ||
![]() |
cabcbb059d | ||
![]() |
5e21b2db3c | ||
![]() |
3a0d6d96c1 | ||
![]() |
f39d2d33c0 | ||
![]() |
ccc58f2a32 | ||
![]() |
ead3dc6a92 | ||
![]() |
7d814cc899 | ||
![]() |
f5b4606c09 | ||
![]() |
d6dbf64efb | ||
![]() |
8d18b4c24b | ||
![]() |
d28307e082 | ||
![]() |
aa5c5bf14f | ||
![]() |
2e80477218 | ||
![]() |
8b9df85daa | ||
![]() |
38d0f02e83 | ||
![]() |
edafe4cad6 | ||
![]() |
3cbadf42a5 | ||
![]() |
1d49f1108f | ||
![]() |
791245dec2 | ||
![]() |
fe8621906d | ||
![]() |
b4fcbdb235 | ||
![]() |
f4b5a28596 | ||
![]() |
6cbd77fc57 | ||
![]() |
1bc78e9f2c | ||
![]() |
cb6282e0a7 | ||
![]() |
8e5e97bfed | ||
![]() |
17dd334b82 | ||
![]() |
ab5eb4f9ce | ||
![]() |
a30af2ba42 | ||
![]() |
9f1c23e217 | ||
![]() |
28fc1d555f | ||
![]() |
ac74f284aa | ||
![]() |
77af999b46 | ||
![]() |
4926763f00 | ||
![]() |
a19eee78c6 | ||
![]() |
6be3c99876 | ||
![]() |
8006911a1f | ||
![]() |
61e5828790 | ||
![]() |
6addc9d6e0 | ||
![]() |
e78d825059 | ||
![]() |
00b04468dc | ||
![]() |
8a07724b23 | ||
![]() |
5256929b17 | ||
![]() |
093bf5d859 | ||
![]() |
4f6144dc71 | ||
![]() |
2d1493ed7a | ||
![]() |
43677d5740 | ||
![]() |
693815bb32 | ||
![]() |
58d7804d66 | ||
![]() |
ea5e6d8f33 | ||
![]() |
f6941f9a44 | ||
![]() |
d2eb4df8fc | ||
![]() |
df33a898d7 | ||
![]() |
325c7b8e8b | ||
![]() |
380656d8c9 | ||
![]() |
9111bc2c21 | ||
![]() |
c1272c72b0 | ||
![]() |
7d1db5c19f | ||
![]() |
2142d070a3 | ||
![]() |
9711cee26d | ||
![]() |
39baa4e364 | ||
![]() |
f339a53e3c | ||
![]() |
d9117a272b | ||
![]() |
b8a8bdeaec | ||
![]() |
8f20edac9d | ||
![]() |
8499a662ea | ||
![]() |
3f05b7d8b4 | ||
![]() |
1d563700a4 | ||
![]() |
def6b936c8 | ||
![]() |
3610f55479 | ||
![]() |
6db84852ae | ||
![]() |
41dc36ba92 | ||
![]() |
fe32db17d7 | ||
![]() |
772aa4f165 | ||
![]() |
38298e0cd8 | ||
![]() |
1213d979f8 | ||
![]() |
a9cb12b745 | ||
![]() |
380f73c112 | ||
![]() |
9f79d034b3 | ||
![]() |
37b54179d8 | ||
![]() |
4a745a399f | ||
![]() |
c340485dd5 | ||
![]() |
f8570dd79f | ||
![]() |
4a49f3cce8 | ||
![]() |
a1ae455c69 | ||
![]() |
7a1b56fe96 | ||
![]() |
511826763a | ||
![]() |
ef10354d06 | ||
![]() |
158458db5f | ||
![]() |
e183ab5cf8 | ||
![]() |
fef839e2a9 | ||
![]() |
9776e43bbe | ||
![]() |
5201147ab1 | ||
![]() |
fb7daa0d05 | ||
![]() |
508e522188 | ||
![]() |
2e9f3d8b9f | ||
![]() |
976731ab6c | ||
![]() |
0d8942e64a | ||
![]() |
37a0f04712 | ||
![]() |
cde9348009 | ||
![]() |
095e6e6ad4 | ||
![]() |
9d0bf5e95c | ||
![]() |
b1b630a4cc | ||
![]() |
c60d374fc8 | ||
![]() |
de4fd4c059 | ||
![]() |
95d8b30864 | ||
![]() |
eb94f409d5 | ||
![]() |
93d91936b5 | ||
![]() |
2220383d83 | ||
![]() |
3231706628 | ||
![]() |
ca4e53859d | ||
![]() |
8b327f1d9b | ||
![]() |
aef0507abb | ||
![]() |
61120d2059 | ||
![]() |
cc1822810f | ||
![]() |
a21c6884f2 | ||
![]() |
2700eed08d | ||
![]() |
ec2badbedd | ||
![]() |
054a7557fa | ||
![]() |
977a4570d9 | ||
![]() |
6c2077eb7c | ||
![]() |
6bab3bcfea | ||
![]() |
1d436b3c86 | ||
![]() |
a854595886 | ||
![]() |
8fc3c5c612 | ||
![]() |
4f408bd952 | ||
![]() |
f86b14bfc5 | ||
![]() |
ec5be91ff6 | ||
![]() |
a7a9490a0c | ||
![]() |
c0d6008781 | ||
![]() |
9f62824e98 | ||
![]() |
b824ba3299 | ||
![]() |
59c4f9a089 | ||
![]() |
7de8fd04a4 | ||
![]() |
8158bd218c | ||
![]() |
aa1d867b72 | ||
![]() |
34c8242133 | ||
![]() |
c673528cff | ||
![]() |
321f01b95c | ||
![]() |
e88667e01c | ||
![]() |
fb96907b52 | ||
![]() |
09ece26200 | ||
![]() |
0c6d22fe47 | ||
![]() |
c563eb81a3 | ||
![]() |
e864a0dd05 | ||
![]() |
42a05bc904 | ||
![]() |
4722175049 | ||
![]() |
e22bdee808 | ||
![]() |
7f87de783f | ||
![]() |
c66389a453 | ||
![]() |
b63c1a2144 | ||
![]() |
3a901098e9 | ||
![]() |
808dd7cc54 | ||
![]() |
62a129c18f | ||
![]() |
c18cd941aa | ||
![]() |
6d12c22653 | ||
![]() |
b76d78e6ae | ||
![]() |
0a6e484b1a | ||
![]() |
a66097129d | ||
![]() |
0bb71f1f20 | ||
![]() |
1aa7cdd602 | ||
![]() |
a4b8a0d801 | ||
![]() |
3bf521d5ca | ||
![]() |
0acb55cde5 | ||
![]() |
6b89fd6100 | ||
![]() |
52ce39dc3e | ||
![]() |
7a3e15d8e5 | ||
![]() |
cf66a60c60 | ||
![]() |
9b26d451e4 | ||
![]() |
137ffba1b4 | ||
![]() |
5c5dc1b7c0 | ||
![]() |
9e9418294a | ||
![]() |
b850eb74b7 | ||
![]() |
67d73a2aee | ||
![]() |
fde9a470dd | ||
![]() |
8d1f30e55b | ||
![]() |
ddd2b60489 | ||
![]() |
8777737861 | ||
![]() |
cb71f6dd04 | ||
![]() |
1881b0e975 | ||
![]() |
eed4e40ec6 | ||
![]() |
6de57b36c7 | ||
![]() |
98b29f6d1c | ||
![]() |
59fdfd25cb | ||
![]() |
0d98677212 | ||
![]() |
1a0865da7a | ||
![]() |
a6ecf6c992 | ||
![]() |
cb100f2e5c | ||
![]() |
bfb7b0117f | ||
![]() |
f6a705c769 | ||
![]() |
0c01840a7e | ||
![]() |
b0b75c54de | ||
![]() |
3fc201d985 | ||
![]() |
5aa453ada3 | ||
![]() |
0009d53b3f | ||
![]() |
05f7a6d1ff | ||
![]() |
0256bbbbaf | ||
![]() |
bce608cdbc | ||
![]() |
38a0844cdf | ||
![]() |
9acc6617d2 | ||
![]() |
4f72f49216 | ||
![]() |
af9840daf7 | ||
![]() |
a67a9c9980 | ||
![]() |
732b2acf35 | ||
![]() |
16906cdcbe | ||
![]() |
96e70659f0 | ||
![]() |
f2cacaf6b6 | ||
![]() |
24cde31328 | ||
![]() |
f6c0688684 | ||
![]() |
c176d94598 | ||
![]() |
f300ea62dc | ||
![]() |
c5df879cf9 | ||
![]() |
0762e5c289 | ||
![]() |
5d18559c1c | ||
![]() |
62c9751ac8 | ||
![]() |
7db9c7f24e | ||
![]() |
c834eb4590 | ||
![]() |
945ea51bd4 | ||
![]() |
38f0c16904 | ||
![]() |
4fbf6b6c95 | ||
![]() |
1f8ff48168 | ||
![]() |
20b6e0d684 | ||
![]() |
713c1f2ba9 | ||
![]() |
a149bc4c5d | ||
![]() |
b3a458338a | ||
![]() |
44422b2b2f | ||
![]() |
f10afd38b5 | ||
![]() |
4c50a5e0b3 | ||
![]() |
f255a485b7 | ||
![]() |
1930d5774d | ||
![]() |
7220a76be0 | ||
![]() |
83f7610dd1 | ||
![]() |
30e0644722 | ||
![]() |
3ada464020 | ||
![]() |
d5983dd362 | ||
![]() |
98258acc37 | ||
![]() |
8002bc752f | ||
![]() |
834ad7a58f | ||
![]() |
e8f2f98048 | ||
![]() |
c672b60d07 | ||
![]() |
ea269c9c92 | ||
![]() |
1fe3a77640 | ||
![]() |
bbaeea1ab7 | ||
![]() |
0a3aee9d82 | ||
![]() |
2434020971 | ||
![]() |
41e0eb7378 | ||
![]() |
6adf964c81 | ||
![]() |
b59f37bc0a | ||
![]() |
cf2d171ccc | ||
![]() |
cc28a7b67f | ||
![]() |
8b5c33cecd | ||
![]() |
6c28adbcd2 | ||
![]() |
2125e3ed57 | ||
![]() |
3da7ecfadf | ||
![]() |
5bb02bbd39 | ||
![]() |
f11aa09f7c | ||
![]() |
02eb4752d3 | ||
![]() |
d9c3215584 | ||
![]() |
110e6d026b | ||
![]() |
c0f57b8a8b | ||
![]() |
57633fbcb3 | ||
![]() |
864c87e6c0 | ||
![]() |
1a516cf3c0 | ||
![]() |
5c25499c5e | ||
![]() |
da4bb4c298 | ||
![]() |
5b8ff61799 | ||
![]() |
56bded07b1 | ||
![]() |
db144a43ad | ||
![]() |
5965f62b56 | ||
![]() |
05aa9f72a9 | ||
![]() |
281461f0f0 | ||
![]() |
f70eb63879 | ||
![]() |
99c23cf139 | ||
![]() |
9aa75e738c | ||
![]() |
e9c45a9140 | ||
![]() |
a065c6e6b9 | ||
![]() |
feb5ff9bd2 | ||
![]() |
92ec3f0881 | ||
![]() |
98c47d9d36 | ||
![]() |
6c67408944 | ||
![]() |
261a816b21 | ||
![]() |
7a23c123c8 | ||
![]() |
e85b24bee0 | ||
![]() |
9e73ea77b4 | ||
![]() |
b0739eca87 | ||
![]() |
848f6aa5ab | ||
![]() |
c9ba4f3f9c | ||
![]() |
c0e9246a66 | ||
![]() |
096c23f27d | ||
![]() |
40bde1eac9 | ||
![]() |
4b55ed17a9 | ||
![]() |
4f757a5add | ||
![]() |
674c137e5f | ||
![]() |
ff1ff1e54a | ||
![]() |
42b22187c8 | ||
![]() |
cfe22502ab | ||
![]() |
d77b0c7dcd | ||
![]() |
5cf889b676 | ||
![]() |
ffc36d5255 | ||
![]() |
0126276e2f | ||
![]() |
58d6ddab9e | ||
![]() |
05db6934eb | ||
![]() |
02c68c5cdb | ||
![]() |
b02fee7309 | ||
![]() |
424f75c9e1 | ||
![]() |
f6e1176f97 | ||
![]() |
e4700c0a27 | ||
![]() |
cf23fd8774 | ||
![]() |
dee8872395 | ||
![]() |
4ba9357a9c | ||
![]() |
48ec09ab1e | ||
![]() |
754f4048a8 | ||
![]() |
037bb07d08 | ||
![]() |
87635c5268 | ||
![]() |
528b4338f4 | ||
![]() |
c780b8bba9 | ||
![]() |
ca34f3250b | ||
![]() |
6a68e1c3f3 | ||
![]() |
85f77ec81d | ||
![]() |
37debed0b8 | ||
![]() |
008383f24a | ||
![]() |
4f7d52dbf2 | ||
![]() |
c7848da8f2 | ||
![]() |
10a6c5c57d | ||
![]() |
2cc2bab309 | ||
![]() |
701fd1d939 | ||
![]() |
d1bdea8edb | ||
![]() |
0cea67ee70 | ||
![]() |
3a0480a482 | ||
![]() |
1fa99da3c2 | ||
![]() |
22d669da18 | ||
![]() |
772681f23d | ||
![]() |
1862a98a44 | ||
![]() |
4634b94c83 | ||
![]() |
6e04a327b4 | ||
![]() |
7ec887eea2 | ||
![]() |
1477b64d4f | ||
![]() |
a2c108f5ef | ||
![]() |
f546e76490 | ||
![]() |
2568bc3957 | ||
![]() |
7104ac963b | ||
![]() |
2cb36590b2 | ||
![]() |
af7b928d7c | ||
![]() |
c0d8a9b07a | ||
![]() |
5b0d23d553 | ||
![]() |
ab30695bd1 | ||
![]() |
53a4de35c4 | ||
![]() |
22e6d95c4b | ||
![]() |
1c7bd7d5c4 | ||
![]() |
3c4ed9cbe3 | ||
![]() |
2677b90244 | ||
![]() |
1b20fa441d | ||
![]() |
18c042d4cf | ||
![]() |
98f92d828a | ||
![]() |
76268773b5 | ||
![]() |
87542e3080 | ||
![]() |
66f5b0fed7 | ||
![]() |
5cb603983e | ||
![]() |
9c5790ab1d | ||
![]() |
4b7078297d | ||
![]() |
841694ccf2 | ||
![]() |
12f4a8255a | ||
![]() |
bda77ffc5b | ||
![]() |
ed9ece5ea3 | ||
![]() |
ce49d99c2f | ||
![]() |
2e450bbf95 | ||
![]() |
303b3071e4 | ||
![]() |
eb6d5f34fc | ||
![]() |
f80126959d | ||
![]() |
4fb4f6d1b7 | ||
![]() |
43df4a7500 | ||
![]() |
4cdcaa8630 | ||
![]() |
04f632296f | ||
![]() |
7c8dbcfaac | ||
![]() |
436ba3c96c | ||
![]() |
5d12f52873 | ||
![]() |
a8bf8ede01 | ||
![]() |
8682183bc3 | ||
![]() |
94c31d0da9 | ||
![]() |
464a4cbeec | ||
![]() |
9f0cbf418a | ||
![]() |
b477f86c92 | ||
![]() |
020371f145 | ||
![]() |
ccafe3f3cf | ||
![]() |
3830748de5 | ||
![]() |
1a43f5145d | ||
![]() |
7f143a83c1 | ||
![]() |
6ccc254179 | ||
![]() |
7db2450447 | ||
![]() |
6c2a6a65e0 | ||
![]() |
4247a757b3 | ||
![]() |
57e34823d8 | ||
![]() |
3c93decdf0 | ||
![]() |
89e7a5018d | ||
![]() |
7235b46e5e | ||
![]() |
0852226a48 | ||
![]() |
e20d215abf | ||
![]() |
e4b9b67e24 | ||
![]() |
685b78828d | ||
![]() |
060908d5c4 | ||
![]() |
0b0f4c61f1 | ||
![]() |
228bf7eb09 | ||
![]() |
5eaf2b8fc3 | ||
![]() |
e097fef79e | ||
![]() |
9a813cd3b1 | ||
![]() |
1c60c8e014 | ||
![]() |
eddda95900 | ||
![]() |
72184dccfc | ||
![]() |
fee75dc766 | ||
![]() |
ba5c856f15 | ||
![]() |
12308a0f55 | ||
![]() |
a958abde2f | ||
![]() |
583208db7e | ||
![]() |
7b5ba15170 | ||
![]() |
d5e0d49f86 | ||
![]() |
73b22d82aa | ||
![]() |
db51cc4e02 | ||
![]() |
be8a52a914 | ||
![]() |
ad597a8ff0 | ||
![]() |
b1fe105904 | ||
![]() |
451b142e3a | ||
![]() |
2833625266 | ||
![]() |
0464028872 | ||
![]() |
98985c03b0 | ||
![]() |
793fd8c479 | ||
![]() |
6c602811df | ||
![]() |
6d48a5684a | ||
![]() |
bd115a4008 | ||
![]() |
08272cdee2 | ||
![]() |
b14a5141a6 | ||
![]() |
aa0e4500c6 | ||
![]() |
4e6b8edf72 | ||
![]() |
ac0852b4e3 | ||
![]() |
6fe43ed969 | ||
![]() |
b34bc06624 | ||
![]() |
08e41e60e5 | ||
![]() |
10ec478a9c | ||
![]() |
86f1074905 | ||
![]() |
8e66b855a3 | ||
![]() |
e3bc85d7bf | ||
![]() |
6f242836e6 | ||
![]() |
f2c926f3b6 | ||
![]() |
aba18924ee | ||
![]() |
aa6bef54dd | ||
![]() |
528f5b9cb9 | ||
![]() |
96ae0ec93a | ||
![]() |
5a5229b499 | ||
![]() |
bba22c9c8c | ||
![]() |
694c437a2c | ||
![]() |
587172efa3 | ||
![]() |
2a926063b2 | ||
![]() |
d6f239e54f | ||
![]() |
b8989fafeb |
17
.clang-format
Normal file
17
.clang-format
Normal file
@@ -0,0 +1,17 @@
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -8
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
SplitEmptyFunction: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
ColumnLimit: 90
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
ContinuationIndentWidth: 8
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 8
|
||||
Standard: c++17
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: MaxK
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report
|
||||
---
|
||||
|
||||
<!-- See https://www.musicpd.org/help/ -->
|
||||
## Bug report
|
||||
### Describe the bug
|
||||
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
|
||||
## Version
|
||||
<!-- Paste the output of "mpd --version" here -->
|
||||
|
||||
|
||||
## Log
|
||||
<!-- Paste relevant portions of the log file here (--verbose) -->
|
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Create a feature request
|
||||
---
|
||||
|
||||
## Feature request
|
9
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about MPD
|
||||
---
|
||||
|
||||
<!-- Before you ask a question on GitHub, please read MPD's
|
||||
documentation. A copy is available at
|
||||
https://www.musicpd.org/doc/html/ -->
|
||||
## Question
|
83
.gitignore
vendored
83
.gitignore
vendored
@@ -1,88 +1,11 @@
|
||||
*.Plo
|
||||
*.Po
|
||||
*.a
|
||||
*.d
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.exe
|
||||
|
||||
*~
|
||||
|
||||
.#*
|
||||
.stgit*
|
||||
.deps
|
||||
.dirstamp
|
||||
|
||||
tags
|
||||
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
/config.h
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.mk
|
||||
/config.status
|
||||
/config_detected.h
|
||||
/config_detected.mk
|
||||
/configure
|
||||
/configure.lineno
|
||||
/depmode
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
/mkinstalldirs
|
||||
/output/
|
||||
/src/mpd
|
||||
/systemd/system/mpd.service
|
||||
/systemd/user/mpd.service
|
||||
/stamp-h1
|
||||
|
||||
/src/dsd2pcm/dsd2pcm
|
||||
/src/win32/mpd_win32_rc.rc
|
||||
|
||||
/doc/doxygen.conf
|
||||
/doc/protocol.html
|
||||
/doc/protocol
|
||||
/doc/user
|
||||
/doc/developer
|
||||
/doc/sticker
|
||||
/doc/api
|
||||
|
||||
/test/software_volume
|
||||
/test/run_convert
|
||||
/test/run_decoder
|
||||
/test/read_tags
|
||||
/test/run_filter
|
||||
/test/run_encoder
|
||||
/test/run_output
|
||||
/test/read_conf
|
||||
/test/run_input
|
||||
/test/read_mixer
|
||||
/test/dump_playlist
|
||||
/test/run_normalize
|
||||
/test/tmp
|
||||
/test/run_inotify
|
||||
/test/test_queue_priority
|
||||
/test/test_protocol
|
||||
/test/run_ntp_server
|
||||
/test/run_resolver
|
||||
/test/run_tcp_connect
|
||||
/test/test_pcm
|
||||
/test/dump_rva2
|
||||
/test/dump_text_file
|
||||
/test/test_util
|
||||
/test/test_byte_reverse
|
||||
/test/test_mixramp
|
||||
/test/test_vorbis_encoder
|
||||
/test/DumpDatabase
|
||||
|
||||
/lib/
|
||||
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
/*.tar.xz
|
||||
/mpd-*/
|
||||
|
||||
__pycache__/
|
||||
|
||||
/.clangd/
|
||||
/compile_commands.json
|
||||
|
97
.travis.yml
97
.travis.yml
@@ -1,32 +1,40 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
# Ubuntu Focal (20.04) with GCC 9.3
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:saiarcot895/chromium-dev' # for ninja-build
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-6
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- boost1.67
|
||||
- python3.6
|
||||
- python3-urllib3
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson
|
||||
env:
|
||||
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
|
||||
- MATRIX_EVAL="export CC=gcc-6 CXX=g++-6 LDFLAGS=-fuse-ld=gold PATH=$HOME/.local/bin:$PATH"
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on big-endian
|
||||
- os: linux
|
||||
arch: s390x
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on ARM64
|
||||
- os: linux
|
||||
arch: arm64
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Trusty (16.04) with GCC 8
|
||||
- os: linux
|
||||
dist: trusty
|
||||
addons:
|
||||
@@ -34,7 +42,7 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:saiarcot895/chromium-dev' # for ninja-build
|
||||
- sourceline: 'ppa:ricotz/toolchain'
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-8
|
||||
@@ -45,31 +53,54 @@ matrix:
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user
|
||||
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
|
||||
env:
|
||||
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
|
||||
- MATRIX_EVAL="export CC=gcc-8 CXX=g++-8 LDFLAGS=-fuse-ld=gold PATH=$HOME/.local/bin:$PATH"
|
||||
- MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.3beta
|
||||
osx_image: xcode11.6
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- meson
|
||||
- googletest
|
||||
- icu4c
|
||||
- ffmpeg
|
||||
- libnfs
|
||||
- yajl
|
||||
- libupnp
|
||||
- libid3tag
|
||||
- chromaprint
|
||||
- libsamplerate
|
||||
- libsoxr
|
||||
# libzzip appears to be broken on Homebrew: "ld: library not found for -lzzip"
|
||||
#- libzzip
|
||||
- flac
|
||||
- opus
|
||||
- libvorbis
|
||||
- faad2
|
||||
- wavpack
|
||||
- libmpdclient
|
||||
env:
|
||||
- MATRIX_EVAL=""
|
||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
||||
|
||||
cache:
|
||||
- apt
|
||||
- ccache
|
||||
apt: true
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/Library/Caches/Homebrew
|
||||
|
||||
before_cache:
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew cleanup
|
||||
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
# C++14
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew update
|
||||
|
||||
install:
|
||||
# C++14
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew install ccache meson
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew install --HEAD https://gist.githubusercontent.com/Kronuz/96ac10fbd8472eb1e7566d740c4034f8/raw/gtest.rb
|
||||
|
||||
before_script:
|
||||
- ccache -s
|
||||
|
2
AUTHORS
2
AUTHORS
@@ -1,5 +1,5 @@
|
||||
Music Player Daemon - http://www.musicpd.org
|
||||
Copyright 2003-2018 The Music Player Daemon Project
|
||||
Copyright 2003-2021 The Music Player Daemon Project
|
||||
|
||||
The following people have contributed code to MPD:
|
||||
|
||||
|
517
NEWS
517
NEWS
@@ -1,3 +1,520 @@
|
||||
ver 0.22.11 (2021/08/24)
|
||||
* protocol
|
||||
- fix "albumart" crash
|
||||
* filter
|
||||
- ffmpeg: pass "channel_layout" instead of "channels" to buffersrc
|
||||
- ffmpeg: fix "av_buffersink_get_frame() failed: Resource temporarily unavailable"
|
||||
- ffmpeg: support double-precision samples (by converting to single precision)
|
||||
* Android
|
||||
- build with NDK r23
|
||||
- playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists"
|
||||
|
||||
ver 0.22.10 (2021/08/06)
|
||||
* protocol
|
||||
- support "albumart" for virtual tracks in CUE sheets
|
||||
* database
|
||||
- simple: fix crash bug
|
||||
- simple: fix absolute paths in CUE "as_directory" entries
|
||||
- simple: prune CUE entries from database for non-existent songs
|
||||
* input
|
||||
- curl: fix crash bug after stream with Icy metadata was closed by peer
|
||||
- tidal: remove defunct unmaintained plugin
|
||||
* tags
|
||||
- fix crash caused by bug in TagBuilder and a few potential reference leaks
|
||||
* output
|
||||
- httpd: fix missing tag after seeking into a new song
|
||||
- oss: fix channel order of multi-channel files
|
||||
* mixer
|
||||
- alsa: fix yet more rounding errors
|
||||
|
||||
ver 0.22.9 (2021/06/23)
|
||||
* database
|
||||
- simple: load all .mpdignore files of all parent directories
|
||||
* tags
|
||||
- fix "readcomments" and "readpicture" on remote files with ID3 tags
|
||||
* decoder
|
||||
- ffmpeg: support the tags "sort_album", "album-sort", "artist-sort"
|
||||
- ffmpeg: fix build failure with FFmpeg 3.4
|
||||
* Android
|
||||
- fix auto-start on boot in Android 8 or later
|
||||
* Windows
|
||||
- fix build failure with SQLite
|
||||
|
||||
ver 0.22.8 (2021/05/22)
|
||||
* fix crash bug in "albumart" command (0.22.7 regression)
|
||||
|
||||
ver 0.22.7 (2021/05/19)
|
||||
* protocol
|
||||
- don't use glibc extension to parse time stamps
|
||||
- optimize the "albumart" command
|
||||
* input
|
||||
- curl: send user/password in the first request, save one roundtrip
|
||||
* decoder
|
||||
- ffmpeg: fix build problem with FFmpeg 3.4
|
||||
- gme: support RSN files
|
||||
* storage
|
||||
- curl: don't use glibc extension
|
||||
* database
|
||||
- simple: fix database corruption bug
|
||||
* output
|
||||
- fix crash when pausing with multiple partitions
|
||||
- jack: enable on Windows
|
||||
- httpd: send header "Access-Control-Allow-Origin: *"
|
||||
- wasapi: add algorithm for finding usable audio format
|
||||
- wasapi: use default device only if none was configured
|
||||
- wasapi: add DoP support
|
||||
|
||||
ver 0.22.6 (2021/02/16)
|
||||
* fix missing tags on songs in queue
|
||||
|
||||
ver 0.22.5 (2021/02/15)
|
||||
* protocol
|
||||
- error for malformed ranges instead of ignoring silently
|
||||
- better error message for open-ended range with "move"
|
||||
* database
|
||||
- simple: fix missing CUE sheet metadata in "addid" command
|
||||
* tags
|
||||
- id: translate TPE3 to Conductor, not Performer
|
||||
* archive
|
||||
- iso9660: another fix for unaligned reads
|
||||
* output
|
||||
- httpd: error handling on Windows improved
|
||||
- pulse: fix deadlock with "always_on"
|
||||
* Windows:
|
||||
- enable https:// support (via Schannel)
|
||||
* Android
|
||||
- work around "Permission denied" on mpd.conf
|
||||
|
||||
ver 0.22.4 (2021/01/21)
|
||||
* protocol
|
||||
- add command "binarylimit" to allow larger chunk sizes
|
||||
- fix "readpicture" on 32 bit machines
|
||||
- show duration and tags of songs in virtual playlist (CUE) folders
|
||||
* storage
|
||||
- curl: fix several WebDAV protocol bugs
|
||||
* decoder
|
||||
- dsdiff: apply padding to odd-sized chunks
|
||||
* filter
|
||||
- ffmpeg: detect the output sample format
|
||||
* output
|
||||
- moveoutput: fix always_on and tag lost on move
|
||||
* Android
|
||||
- enable https:// support (via OpenSSL)
|
||||
|
||||
ver 0.22.3 (2020/11/06)
|
||||
* playlist
|
||||
- add option "as_directory", making CUE file expansion optional
|
||||
* storage
|
||||
- curl: fix crash bug
|
||||
* filter
|
||||
- fix garbage after "Audio format not supported by filter" message
|
||||
- ffmpeg: support planar output
|
||||
- ffmpeg: support sample formats other than 16 bit
|
||||
|
||||
ver 0.22.2 (2020/10/28)
|
||||
* database
|
||||
- simple: purge songs and virtual directories for unavailable plugins
|
||||
on update
|
||||
* input
|
||||
- qobuz/tidal: fix protocol errors due to newlines in error messages
|
||||
- smbclient: disable by default due to libsmbclient crash bug
|
||||
* playlist
|
||||
- soundcloud: fix protocol errors due to newlines in error messages
|
||||
* state_file: save on shutdown
|
||||
|
||||
ver 0.22.1 (2020/10/17)
|
||||
* decoder
|
||||
- opus: apply the OpusHead output gain even if there is no EBU R128 tag
|
||||
- opus: fix track/album ReplayGain fallback
|
||||
* output
|
||||
- alsa: don't deadlock when the ALSA driver is buggy
|
||||
- jack, pulse: reduce the delay when stopping or pausing playback
|
||||
* playlist
|
||||
- cue: fix two crash bugs
|
||||
* state_file: fix the state_file_interval setting
|
||||
|
||||
ver 0.22 (2020/09/23)
|
||||
* protocol
|
||||
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
|
||||
"window" parameters
|
||||
- add command "readpicture" to download embedded pictures
|
||||
- command "moveoutput" moves an output between partitions
|
||||
- command "delpartition" deletes a partition
|
||||
- show partition name in "status" response
|
||||
* tags
|
||||
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
|
||||
* input
|
||||
- curl: support "charset" parameter in URI fragment
|
||||
- ffmpeg: allow partial reads
|
||||
- io_uring: new plugin for local files on Linux (using liburing)
|
||||
- smbclient: close unused SMB/CIFS connections
|
||||
* database
|
||||
- upnp: drop support for libupnp versions older than 1.8
|
||||
* playlist
|
||||
- cue: integrate contents in database
|
||||
* decoder
|
||||
- ffmpeg: support RTSP
|
||||
- mad: remove option "gapless", always do gapless
|
||||
- sidplay: add option "default_genre"
|
||||
- sidplay: map SID name field to "Album" tag
|
||||
- sidplay: add support for new song length format with libsidplayfp 2.0
|
||||
- vorbis, opus: improve seeking accuracy
|
||||
* playlist
|
||||
- flac: support reading CUE sheets from remote FLAC files
|
||||
* filter
|
||||
- ffmpeg: new plugin based on FFmpeg's libavfilter library
|
||||
- hdcd: new plugin based on FFmpeg's "af_hdcd" for HDCD playback
|
||||
- volume: convert S16 to S24 to preserve quality and reduce dithering noise
|
||||
- dsd: add integer-only DSD to PCM converter
|
||||
* output
|
||||
- jack: add option "auto_destination_ports"
|
||||
- jack: report error details
|
||||
- pulse: add option "media_role"
|
||||
- solaris: support S8 and S32
|
||||
* lower the real-time priority from 50 to 40
|
||||
* switch to C++17
|
||||
- GCC 8 or clang 5 (or newer) recommended
|
||||
|
||||
ver 0.21.26 (2020/09/21)
|
||||
* database
|
||||
- inotify: obey ".mpdignore" files
|
||||
* output
|
||||
- osx: fix crash bug
|
||||
- sles: support floating point samples
|
||||
* archive
|
||||
- bzip2: fix crash on corrupt bzip2 file
|
||||
- bzip2: flush output at end of input file
|
||||
- iso9660: fix unaligned reads
|
||||
- iso9660: support seeking
|
||||
- zzip: fix crash on corrupt ZIP file
|
||||
* decoder
|
||||
- ffmpeg: remove "rtsp://" from the list of supported protocols
|
||||
- ffmpeg: add "hls+http://" to the list of supported protocols
|
||||
- opus: support the gain value from the Opus header
|
||||
- sndfile: fix lost samples at end of file
|
||||
* fix "single" mode bug after resuming playback
|
||||
* the default log_level is "default", not "info"
|
||||
|
||||
ver 0.21.25 (2020/07/06)
|
||||
* protocol:
|
||||
- fix crash when using "rangeid" while playing
|
||||
* database
|
||||
- simple: automatically scan new mounts
|
||||
- upnp: fix compatibility with Plex DLNA
|
||||
* storage
|
||||
- fix disappearing mounts after mounting twice
|
||||
- udisks: fix reading ".mpdignore"
|
||||
* input
|
||||
- file: detect premature end of file
|
||||
- smbclient: don't send credentials to MPD clients
|
||||
* decoder
|
||||
- opus: apply pre-skip and end trimming
|
||||
- opus: fix memory leak
|
||||
- opus: fix crash bug
|
||||
- vorbis: fix crash bug
|
||||
* output
|
||||
- osx: improve sample rate selection
|
||||
- osx: fix noise while stopping
|
||||
* neighbor
|
||||
- upnp: fix crash during shutdown
|
||||
* Windows/Android:
|
||||
- fix Boost detection after breaking change in Meson 0.54
|
||||
|
||||
ver 0.21.24 (2020/06/10)
|
||||
* protocol
|
||||
- "tagtypes" requires no permissions
|
||||
* database
|
||||
- simple: fix crash when mounting twice
|
||||
* decoder
|
||||
- modplug: fix Windows build failure
|
||||
- wildmidi: attempt to detect WildMidi using pkg-config
|
||||
- wildmidi: fix Windows build failure
|
||||
* player
|
||||
- don't restart current song if seeking beyond end
|
||||
* Android
|
||||
- enable the decoder plugins GME, ModPlug and WildMidi
|
||||
- fix build failure with Android NDK r21
|
||||
* Windows
|
||||
- fix stream playback
|
||||
- enable the decoder plugins GME, ModPlug and WildMidi
|
||||
- work around Meson bug breaking the Windows build with GCC 10
|
||||
* fix unit test failure
|
||||
|
||||
ver 0.21.23 (2020/04/23)
|
||||
* protocol
|
||||
- add tag fallback for AlbumSort
|
||||
* storage
|
||||
- curl: fix corrupt "href" values in the presence of XML entities
|
||||
- curl: unescape "href" values
|
||||
* input
|
||||
- nfs: fix crash bug
|
||||
- nfs: fix freeze bug on reconnect
|
||||
* decoder
|
||||
- gme: adapt to API change in the upcoming version 0.7.0
|
||||
* output
|
||||
- alsa: implement channel mapping for 5.0 and 7.0
|
||||
* player
|
||||
- drain outputs at end of song in "single" mode
|
||||
* Windows
|
||||
- fix case insensitive search
|
||||
|
||||
ver 0.21.22 (2020/04/02)
|
||||
* database
|
||||
- simple: optimize startup
|
||||
* input
|
||||
- curl: fix streaming errors on Android
|
||||
* playlist
|
||||
- rss: support MIME type application/xml
|
||||
* mixer
|
||||
- android: new mixer plugin for "sles" output
|
||||
* Android
|
||||
- TV support
|
||||
* Windows
|
||||
- fix time zone offset check
|
||||
* fix build failures with uClibc-ng
|
||||
|
||||
ver 0.21.21 (2020/03/19)
|
||||
* configuration
|
||||
- fix bug in "metadata_to_use" setting
|
||||
* playlist
|
||||
- asx, xspf: fix corrupt tags in the presence of XML entities
|
||||
* archive
|
||||
- iso9660: skip empty file names to work around libcdio bug
|
||||
* decoder
|
||||
- gme: ignore empty tags
|
||||
* output
|
||||
- solaris: port to NetBSD
|
||||
* raise default "max_connections" value to 100
|
||||
|
||||
ver 0.21.20 (2020/02/16)
|
||||
* decoder
|
||||
- audiofile, ffmpeg, sndfile: handle MIME type "audio/wav"
|
||||
- ffmpeg: fix playback of AIFF and TTA
|
||||
- vorbis, opus: fix seeking in small files
|
||||
* fix backwards seeking on ARM (and other non-x86 CPUs)
|
||||
|
||||
ver 0.21.19 (2020/01/17)
|
||||
* configuration
|
||||
- allow overriding top-level settings in includes
|
||||
* output
|
||||
- pulse: obey Pulse's maximum sample rate (fixes DSD128 playback)
|
||||
* fix build failure with clang 10
|
||||
* fix build failure with Android NDK r20
|
||||
|
||||
ver 0.21.18 (2019/12/24)
|
||||
* protocol
|
||||
- work around Mac OS X bug in the ISO 8601 parser
|
||||
* output
|
||||
- alsa: fix hang bug with ALSA "null" outputs
|
||||
* storage
|
||||
- curl: fix crash bug
|
||||
* drop support for CURL versions older than 7.32.0
|
||||
* reduce unnecessary CPU wakeups
|
||||
|
||||
ver 0.21.17 (2019/12/16)
|
||||
* protocol
|
||||
- relax the ISO 8601 parser: allow omitting field separators, the
|
||||
time of day and the "Z" suffix
|
||||
* archive
|
||||
- zzip: improve error reporting
|
||||
* outputs
|
||||
- jack: mark ports as terminal
|
||||
- shout: declare metadata as UTF-8
|
||||
* fix build failure with -Ddatabase=false
|
||||
|
||||
ver 0.21.16 (2019/10/16)
|
||||
* queue
|
||||
- fix relative destination offset when moving a range
|
||||
* storage
|
||||
- curl: request the "resourcetype" property to fix database update
|
||||
- curl: URL-encode more paths
|
||||
- curl: follow redirects for collections without trailing slash
|
||||
* update
|
||||
- fix crash when music_directory is not a directory
|
||||
* fix build with iconv() instead of ICU
|
||||
|
||||
ver 0.21.15 (2019/09/25)
|
||||
* decoder
|
||||
- dsdiff, dsf: fix displayed bit rate
|
||||
- mpcdec: fix bogus ReplayGain values
|
||||
* output
|
||||
- solaris: fix build with glibc 2.30
|
||||
|
||||
ver 0.21.14 (2019/08/21)
|
||||
* decoder
|
||||
- sidplay: show track durations in database
|
||||
- sidplay: convert tag values from Windows-1252 charset
|
||||
- sidplay: strip text from "Date" tag
|
||||
* player
|
||||
- fix crash after song change
|
||||
- fix seek position after restarting the decoder
|
||||
* protocol
|
||||
- include command name in error responses
|
||||
|
||||
ver 0.21.13 (2019/08/06)
|
||||
* input
|
||||
- cdio_paranoia: require libcdio-paranoia 10.2+0.93+1
|
||||
* decoder
|
||||
- mad: fix crackling sound (0.21.12 regression)
|
||||
* output
|
||||
- jack: improved Windows compatibility
|
||||
|
||||
ver 0.21.12 (2019/08/03)
|
||||
* decoder
|
||||
- mad: update bit rate after seeking
|
||||
- mad: fix several bugs preventing the plugin from decoding the last frame
|
||||
- opus: ignore case in replay gain tag names
|
||||
- opus, vorbis: decode the "end of stream" packet
|
||||
* output
|
||||
- jack: fix mono-to-stereo conversion
|
||||
* player
|
||||
- don't restart unseekable song after failed seek attempt
|
||||
* Windows
|
||||
- support backslash in relative URIs loaded from playlists
|
||||
|
||||
ver 0.21.11 (2019/07/03)
|
||||
* input
|
||||
- tidal: deprecated because Tidal has changed the protocol
|
||||
* decoder
|
||||
- wildmidi: log error if library initialization fails
|
||||
* output
|
||||
- alsa: fix busy loop while draining
|
||||
- alsa: fix missing drain call
|
||||
- alsa: improve xrun-avoiding silence generator
|
||||
- alsa: log when generating silence due to slow decoder
|
||||
- alsa, osx: fix distortions with DSD_U32 and DoP on 32 bit CPUs
|
||||
* protocol
|
||||
- fix "list" with multiple "group" levels
|
||||
|
||||
ver 0.21.10 (2019/06/05)
|
||||
* decoder
|
||||
- opus: fix duplicate tags
|
||||
* output
|
||||
- httpd: reject some well-known URIs
|
||||
* fix crash bug (0.21.9 regression)
|
||||
|
||||
ver 0.21.9 (2019/05/20)
|
||||
* input
|
||||
- buffer: fix deadlock bug
|
||||
* Android
|
||||
- fix crash on ARMv7
|
||||
- request storage permission on Android 6+
|
||||
* fix spurious "single" mode bug
|
||||
|
||||
ver 0.21.8 (2019/04/23)
|
||||
* input
|
||||
- smbclient: download to buffer instead of throttling transfer
|
||||
* output
|
||||
- httpd: add missing mutex lock
|
||||
- httpd: fix use-after-free bug
|
||||
* playlist
|
||||
- soundcloud: fix "Unsupported URI scheme" (0.21.6 regression)
|
||||
* fix Bonjour bug
|
||||
* fix build failure with GCC 9
|
||||
* fix build failure with -Ddatabase=false
|
||||
* systemd: add user socket unit
|
||||
* doc: "list file" is deprecated
|
||||
|
||||
ver 0.21.7 (2019/04/03)
|
||||
* input
|
||||
- qobuz/tidal: scan tags when loading a playlist
|
||||
* require Meson 0.49.0 for native libgcrypt-config support
|
||||
* fix build failure with -Dlocal_socket=false
|
||||
* Haiku
|
||||
- fix build
|
||||
- add version info
|
||||
|
||||
ver 0.21.6 (2019/03/17)
|
||||
* protocol
|
||||
- allow loading playlists specified as absolute filesystem paths
|
||||
- fix negated filter expressions with multiple tag values
|
||||
- fix "list" with filter expression
|
||||
- omit empty playlist names in "listplaylists"
|
||||
* input
|
||||
- cdio_paranoia: fix build failure due to missing #include
|
||||
* decoder
|
||||
- opus: fix replay gain when there are no other tags
|
||||
- opus: fix seeking to beginning of song
|
||||
- vorbis: fix Tremor conflict resulting in crash
|
||||
* output
|
||||
- pulse: work around error with unusual channel count
|
||||
- osx: fix build failure
|
||||
* playlist
|
||||
- flac: fix use-after-free bug
|
||||
* support abstract sockets on Linux
|
||||
* Windows
|
||||
- remove the unused libwinpthread-1.dll dependency
|
||||
* Android
|
||||
- enable SLES power saving mode
|
||||
|
||||
ver 0.21.5 (2019/02/22)
|
||||
* protocol
|
||||
- fix deadlock in "albumart" command
|
||||
- fix "tagtypes disable" command
|
||||
* database
|
||||
- simple: fix assertion failure
|
||||
- fix assertion failures with mount points
|
||||
* storage
|
||||
- udisks: fix "AlreadyMounted" error
|
||||
- udisks: use relative path from mount URI
|
||||
- fix memory leak
|
||||
* input
|
||||
- buffer: fix crash bug when playing remote WAV file
|
||||
* tags
|
||||
- ape: map "Album Artist"
|
||||
* output
|
||||
- shout: add support for TLS
|
||||
* mixer
|
||||
- pulse: add "scale_volume" setting
|
||||
|
||||
ver 0.21.4 (2019/01/04)
|
||||
* database
|
||||
- inotify: fix crash bug "terminate called after throwing ..."
|
||||
- upnp: implement "list ... group"
|
||||
* output
|
||||
- httpd: declare protocol "HTTP/1.1" instead of "ICY"
|
||||
* remove libwrap support
|
||||
* Windows
|
||||
- fix "Failed to accept connection: unknown error"
|
||||
* fix Haiku build
|
||||
|
||||
ver 0.21.3 (2018/11/16)
|
||||
* output
|
||||
- alsa: fix crash bug
|
||||
- alsa: fix stuttering at start of playback
|
||||
- alsa: fix discarded samples at end of song
|
||||
- alsa: clear error after reopening device
|
||||
* log: default to journal if MPD was started as systemd service
|
||||
|
||||
ver 0.21.2 (2018/11/12)
|
||||
* protocol
|
||||
- operator "=~" matches a regular expression
|
||||
- operator "contains" matches substrings
|
||||
* decoder
|
||||
- ffmpeg: require FFmpeg 3.1 or later
|
||||
- ffmpeg: fix broken sound with certain codecs
|
||||
* output
|
||||
- alsa: fix high CPU usage with dmix
|
||||
- httpd: fix three crash bugs
|
||||
* mixer
|
||||
- alsa: fix more rounding errors
|
||||
* fix zlib support
|
||||
|
||||
ver 0.21.1 (2018/11/04)
|
||||
* protocol
|
||||
- allow escaping quotes in filter expressions
|
||||
- operator "==" never searches substrings in filter expressions
|
||||
* decoder
|
||||
- ffmpeg: fix build failure with non-standard FFmpeg installation path
|
||||
- flac: fix linker failure when building without FLAC support
|
||||
* encoder
|
||||
- vorbis: fix linker failure when building without Vorbis decoder
|
||||
* fix build failure on Linux-PowerPC
|
||||
* fix build failure on FreeBSD
|
||||
* eliminate DLL dependencies on Windows
|
||||
* add warning about buggy Boost version 1.67
|
||||
* require Meson 0.47.2 because a Meson 0.47.1 bug breaks our build
|
||||
|
||||
ver 0.21 (2018/10/31)
|
||||
* configuration
|
||||
- add "include" directive, allows including config files
|
||||
|
@@ -14,7 +14,7 @@ For basic installation instructions
|
||||
|
||||
- [Manual](http://www.musicpd.org/doc/user/)
|
||||
- [Forum](http://forum.musicpd.org/)
|
||||
- [IRC](irc://chat.freenode.net/#mpd)
|
||||
- [IRC](ircs://irc.libera.chat:6697/#mpd)
|
||||
- [Bug tracker](https://github.com/MusicPlayerDaemon/MPD/issues/)
|
||||
|
||||
# Developers
|
||||
|
1
android/.gitignore
vendored
1
android/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
@@ -2,18 +2,26 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="23"
|
||||
android:versionName="0.21">
|
||||
android:versionCode="59"
|
||||
android:versionName="0.22.11">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:banner="@drawable/icon"
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name">
|
||||
@@ -22,6 +30,14 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".Receiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
@@ -24,26 +24,29 @@ android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'toolchain_arch': 'arm-linux-androideabi',
|
||||
'llvm_triple': 'armv7-none-linux-androideabi',
|
||||
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
|
||||
'llvm_triple': 'armv7-linux-androideabi',
|
||||
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'android_api_level': '21',
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'toolchain_arch': 'aarch64-linux-android',
|
||||
'llvm_triple': 'aarch64-none-linux-android',
|
||||
'cflags': '',
|
||||
'llvm_triple': 'aarch64-linux-android',
|
||||
'cflags': '-fpic',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'toolchain_arch': 'x86',
|
||||
'llvm_triple': 'i686-none-linux-android',
|
||||
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
'llvm_triple': 'i686-linux-android',
|
||||
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
|
||||
'x86_64': {
|
||||
'arch': 'x86_64-linux-android',
|
||||
'ndk_arch': 'x86_64',
|
||||
'llvm_triple': 'x86_64-linux-android',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -76,53 +79,37 @@ class AndroidNdkToolchain:
|
||||
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '21'
|
||||
ndk_platform = 'android-' + android_api_level
|
||||
|
||||
# select the NDK compiler
|
||||
gcc_version = '4.9'
|
||||
|
||||
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
|
||||
sysroot = os.path.join(ndk_path, 'sysroot')
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
self.arch = arch
|
||||
self.install_prefix = install_prefix
|
||||
self.sysroot = sysroot
|
||||
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = abi_info['llvm_triple']
|
||||
llvm_triple = abi_info['llvm_triple'] + android_api_level
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' -fPIC'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
self.cc = os.path.join(llvm_bin, 'clang')
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
|
||||
common_flags += ' -target ' + llvm_triple
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
self.ar = os.path.join(toolchain_bin, arch + '-ar')
|
||||
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
|
||||
self.nm = os.path.join(toolchain_bin, arch + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
self.ar = os.path.join(llvm_bin, 'llvm-ar')
|
||||
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
|
||||
self.nm = os.path.join(llvm_bin, 'llvm-nm')
|
||||
self.strip = os.path.join(llvm_bin, 'llvm-strip')
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = '--sysroot=' + sysroot + \
|
||||
' -isystem ' + os.path.join(install_prefix, 'include') + \
|
||||
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
|
||||
' -D__ANDROID_API__=' + android_api_level
|
||||
self.ldflags = '--sysroot=' + sysroot + \
|
||||
' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -L' + os.path.join(target_root, 'usr', 'lib') + \
|
||||
' -B' + os.path.join(target_root, 'usr', 'lib') + \
|
||||
self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
|
||||
self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -Wl,--exclude-libs=ALL' + \
|
||||
' ' + common_flags
|
||||
self.ldflags = common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
@@ -130,13 +117,16 @@ class AndroidNdkToolchain:
|
||||
self.is_aarch64 = ndk_arch == 'arm64'
|
||||
self.is_windows = False
|
||||
|
||||
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
|
||||
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
|
||||
|
||||
libstdcxx_flags = ''
|
||||
libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
|
||||
libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path
|
||||
libstdcxx_libs = '-lc++_static -lc++abi'
|
||||
libstdcxx_cxxflags = ''
|
||||
libstdcxx_ldflags = ''
|
||||
libstdcxx_libs = '-static-libstdc++'
|
||||
|
||||
if self.is_armv7:
|
||||
# On 32 bit ARM, clang generates no ".eh_frame" section;
|
||||
# instead, the LLVM unwinder library is used for unwinding
|
||||
# the stack after a C++ exception was thrown
|
||||
libstdcxx_libs += ' -lunwind'
|
||||
|
||||
if use_cxx:
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
@@ -166,7 +156,11 @@ thirdparty_libs = [
|
||||
opus,
|
||||
flac,
|
||||
libid3tag,
|
||||
libmodplug,
|
||||
wildmidi,
|
||||
gme,
|
||||
ffmpeg,
|
||||
openssl,
|
||||
curl,
|
||||
libexpat,
|
||||
libnfs,
|
||||
|
@@ -5,8 +5,8 @@ android_ndk = get_option('android_ndk')
|
||||
android_sdk = get_option('android_sdk')
|
||||
android_abi = get_option('android_abi')
|
||||
|
||||
android_sdk_build_tools_version = '27.0.0'
|
||||
android_sdk_platform = 'android-21'
|
||||
android_sdk_build_tools_version = '29.0.3'
|
||||
android_sdk_platform = 'android-29'
|
||||
|
||||
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
|
||||
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,6 +21,7 @@ package org.musicpd;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
@@ -35,6 +36,9 @@ import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Main extends Service implements Runnable {
|
||||
private static final String TAG = "Main";
|
||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
||||
@@ -156,11 +160,36 @@ public class Main extends Service implements Runnable {
|
||||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
|
||||
}
|
||||
|
||||
private Notification.Builder createNotificationBuilderWithChannel() {
|
||||
final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null)
|
||||
return null;
|
||||
|
||||
final String id = "org.musicpd";
|
||||
final String name = "MPD service";
|
||||
final int importance = 3; /* NotificationManager.IMPORTANCE_DEFAULT */
|
||||
|
||||
try {
|
||||
Class<?> ncClass = Class.forName("android.app.NotificationChannel");
|
||||
Constructor<?> ncCtor = ncClass.getConstructor(String.class, CharSequence.class, int.class);
|
||||
Object nc = ncCtor.newInstance(id, name, importance);
|
||||
|
||||
Method nmCreateNotificationChannelMethod =
|
||||
NotificationManager.class.getMethod("createNotificationChannel", ncClass);
|
||||
nmCreateNotificationChannelMethod.invoke(notificationManager, nc);
|
||||
|
||||
Constructor nbCtor = Notification.Builder.class.getConstructor(Context.class, String.class);
|
||||
return (Notification.Builder) nbCtor.newInstance(this, id);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "error creating the NotificationChannel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void start() {
|
||||
if (mThread != null)
|
||||
return;
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
final Intent mainIntent = new Intent(this, Settings.class);
|
||||
mainIntent.setAction("android.intent.action.MAIN");
|
||||
@@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
|
||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
Notification notification = new Notification.Builder(this)
|
||||
.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
Notification.Builder nBuilder;
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */)
|
||||
{
|
||||
nBuilder = createNotificationBuilderWithChannel();
|
||||
if (nBuilder == null)
|
||||
return;
|
||||
}
|
||||
else
|
||||
nBuilder = new Notification.Builder(this);
|
||||
|
||||
Notification notification = nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setContentIntent(contentIntent)
|
||||
.build();
|
||||
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
startForeground(R.string.notification_title_mpd_running, notification);
|
||||
startService(new Intent(this, Main.class));
|
||||
}
|
||||
@@ -373,6 +414,15 @@ public class Main extends Service implements Runnable {
|
||||
* start Main service without any callback
|
||||
*/
|
||||
public static void start(Context context, boolean wakelock) {
|
||||
context.startService(new Intent(context, Main.class).putExtra("wakelock", wakelock));
|
||||
Intent intent = new Intent(context, Main.class)
|
||||
.putExtra("wakelock", wakelock);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
/* in Android 8+, we need to use this method
|
||||
or else we'll get "IllegalStateException:
|
||||
app is in background" */
|
||||
context.startForegroundService(intent);
|
||||
else
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,10 +21,12 @@ package org.musicpd;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
@@ -103,12 +105,13 @@ public class Settings extends Activity {
|
||||
else
|
||||
mRunButton.setChecked(false);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("");
|
||||
break;
|
||||
case MSG_STARTED:
|
||||
Log.d(TAG, "onStarted");
|
||||
mRunButton.setChecked(true);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX
|
||||
mTextStatus.setText("MPD service started");
|
||||
break;
|
||||
case MSG_LOG:
|
||||
if (mLogListArray.size() > MAX_LOGS)
|
||||
@@ -178,6 +181,14 @@ public class Settings extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
/* TODO: this sure is the wrong place to request
|
||||
permissions - it will cause MPD to quit
|
||||
immediately; we should request permissions when we
|
||||
need them, but implementing that is complicated, so
|
||||
for now, we do it here to give users a quick
|
||||
solution for the problem */
|
||||
requestAllPermissions();
|
||||
|
||||
setContentView(R.layout.settings);
|
||||
mRunButton = (ToggleButton) findViewById(R.id.run);
|
||||
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
|
||||
@@ -203,6 +214,31 @@ public class Settings extends Activity {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private void checkRequestPermission(String permission) {
|
||||
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.requestPermissions(new String[]{permission}, 0);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "requestPermissions(" + permission + ") failed",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private void requestAllPermissions() {
|
||||
if (android.os.Build.VERSION.SDK_INT < 23)
|
||||
/* we don't need to request permissions on
|
||||
this old Android version */
|
||||
return;
|
||||
|
||||
/* starting with Android 6.0, we need to explicitly
|
||||
request all permissions before using them;
|
||||
mentioning them in the manifest is not enough */
|
||||
|
||||
checkRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
private void connectClient() {
|
||||
mClient = new Main.Client(this, new Main.Client.Callback() {
|
||||
|
||||
|
11
autogen.sh
11
autogen.sh
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf config.cache build
|
||||
mkdir build
|
||||
|
||||
aclocal -I m4 $ACLOCAL_FLAGS
|
||||
autoheader
|
||||
automake --add-missing $AUTOMAKE_FLAGS
|
||||
autoconf
|
@@ -1,5 +1,9 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# This is a wrapper for pkg-config which helps with cross-compiling;
|
||||
# it sets up environment variables to pkg-config searches for
|
||||
# libraries in the sysroot where a copy of this script is located.
|
||||
|
||||
BIN=`dirname $0`
|
||||
ROOT=`dirname "$BIN"`
|
||||
|
||||
|
2
doc/.gitignore
vendored
2
doc/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/html/
|
||||
/doctrees/
|
10
doc/conf.py
10
doc/conf.py
@@ -30,7 +30,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Music Player Daemon'
|
||||
copyright = '2003-2018 The Music Player Daemon Project'
|
||||
copyright = '2003-2020 The Music Player Daemon Project'
|
||||
author = 'Max Kellermann'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
@@ -38,9 +38,9 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21'
|
||||
version = '0.22.11'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.21~git'
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -212,3 +212,7 @@ html_static_path = ['_static']
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#
|
||||
# html_search_scorer = 'scorer.js'
|
||||
man_pages = [
|
||||
('mpd.1', 'mpd', 'MPD documentation', [author], 1),
|
||||
('mpd.conf.5', 'mpd.conf', 'mpd.conf documentation', [author], 5)
|
||||
]
|
||||
|
@@ -2,23 +2,21 @@ Developer's Manual
|
||||
##################
|
||||
|
||||
Introduction
|
||||
============
|
||||
************
|
||||
|
||||
This is a guide for those who wish to hack on the MPD source code. MPD is an open project, and we are always happy about contributions. So far, more than 150 people have contributed patches. This document is work in progress. Most of it may be incomplete yet. Please help!
|
||||
|
||||
Code Style
|
||||
==========
|
||||
**********
|
||||
|
||||
* indent with tabs (width 8)
|
||||
* don't write CPP when you can write C++: use inline functions and constexpr instead of macros
|
||||
* comment your code, document your APIs
|
||||
* the code should be C++14 compliant, and must compile with :program:`GCC` 6.0 and :program:`clang` 3.4
|
||||
* report error conditions with C++ exceptions, preferable derived from :envvar:`std::runtime_error`
|
||||
* the code should be C++17 compliant, and must compile with :program:`GCC` 8 and :program:`clang` 5
|
||||
* all code must be exception-safe
|
||||
* classes and functions names use CamelCase; variables are lower-case with words separated by underscore
|
||||
|
||||
Some example code:
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -32,8 +30,58 @@ Some example code:
|
||||
return xyz;
|
||||
}
|
||||
|
||||
|
||||
Error handling
|
||||
==============
|
||||
|
||||
If an error occurs, throw a C++ exception, preferably derived from
|
||||
:code:`std::runtime_error`. The function's API documentation should
|
||||
mention that. If a function cannot throw exceptions, add
|
||||
:code:`noexcept` to its prototype.
|
||||
|
||||
Some parts of MPD use callbacks to report completion; the handler
|
||||
classes usually have an "error" callback which receives a
|
||||
:code:`std::exception_ptr`
|
||||
(e.g. :code:`BufferedSocket::OnSocketError()`). Wrapping errors in
|
||||
:code:`std::exception_ptr` allows propagating details about the error
|
||||
across thread boundaries to the entity which is interested in handling
|
||||
it (e.g. giving the MPD client details about an I/O error caught by
|
||||
the decoder thread).
|
||||
|
||||
Out-of-memory errors (i.e. :code:`std::bad_alloc`) do not need to be
|
||||
handled. Some operating systems such as Linux do not report
|
||||
out-of-memory to userspace, and instead kill a process to recover.
|
||||
Even if we know we are out of memory, there is little we can do except
|
||||
for aborting the process quickly. Any other attempts to give back
|
||||
memory may cause page faults on the way which make the situation
|
||||
worse.
|
||||
|
||||
Error conditions which are caused by a bug do not need to be handled
|
||||
at runtime; instead, use :code:`assert()` to detect them in debug
|
||||
builds.
|
||||
|
||||
|
||||
git Branches
|
||||
************
|
||||
|
||||
There are two active branches in the git repository:
|
||||
|
||||
- the "unstable" branch called ``master`` where new features are
|
||||
merged. This will become the next major release eventually.
|
||||
- the "stable" branch (currently called ``v0.22.x``) where only bug
|
||||
fixes are merged.
|
||||
|
||||
Once :program:`MPD` 0.23 is released, a new branch called ``v0.23.x``
|
||||
will be created for 0.23 bug-fix releases; after that, ``v0.22.x``
|
||||
will eventually cease to be maintained.
|
||||
|
||||
After bug fixes have been added to the "stable" branch, it will be
|
||||
merged into ``master``. This ensures that all known bugs are fixed in
|
||||
all active branches.
|
||||
|
||||
|
||||
Hacking The Source
|
||||
==================
|
||||
******************
|
||||
|
||||
MPD sources are managed in a git repository on
|
||||
`Github <https://github.com/MusicPlayerDaemon/>`_.
|
||||
@@ -59,7 +107,7 @@ possible, to be sure that you don't break any disabled code.
|
||||
Don't mix several changes in one single patch. Create a separate patch for every change. Tools like :program:`stgit` help you with that. This way, we can review your patches more easily, and we can pick the patches we like most first.
|
||||
|
||||
Basic stgit usage
|
||||
-----------------
|
||||
=================
|
||||
|
||||
stgit allows you to create a set of patches and refine all of them: you can go back to any patch at any time, and re-edit it (both the code and the commit message). You can reorder patches and insert new patches at any position. It encourages creating separate patches for tiny changes.
|
||||
|
||||
@@ -94,35 +142,7 @@ When the whole patch series is finished, convert stgit patches to git commits:
|
||||
stg commit
|
||||
|
||||
Submitting Patches
|
||||
==================
|
||||
******************
|
||||
|
||||
Send your patches to the mailing list:
|
||||
Email: `mpd-devel <mpd-devel@musicpd.org>`_
|
||||
|
||||
:program:`git pull` requests are preferred.
|
||||
|
||||
Development Tools
|
||||
=================
|
||||
|
||||
Clang Static Analyzer
|
||||
---------------------
|
||||
|
||||
The `static analyzer <http://clang-analyzer.llvm.org/>`_ is a tool that helps find bugs. To run it on the MPD code base, install LLVM and clang. configure MPD to use clang:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
./configure --enable-debug CXX=clang++ CC=clang ...
|
||||
|
||||
It is recommended to use :code:`--enable-debug`, because the analyzer
|
||||
takes advantage of :dfn:`assert()` calls, which are only enabled in
|
||||
the debug build.
|
||||
|
||||
Now run the analyzer:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
scan-build --use-c++=clang++ --use-cc=clang make
|
||||
|
||||
The options :code:`--use-c++` and :code:`--use-cc` are necessary
|
||||
because it invokes :command:`cc` for actually compiling the sources by
|
||||
default. That breaks, because MPD requires a C99 compiler.
|
||||
Submit pull requests on GitHub:
|
||||
https://github.com/MusicPlayerDaemon/MPD/pulls
|
||||
|
@@ -1,165 +0,0 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE itemizedlist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artist</varname>: the artist name. Its meaning is not
|
||||
well-defined; see <varname>composer</varname> and
|
||||
<varname>performer</varname> for more specific tags.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artistsort</varname>: same as
|
||||
<varname>artist</varname>, but for sorting. This usually omits
|
||||
prefixes such as "The".
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>album</varname>: the album name.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumsort</varname>: same as <varname>album</varname>,
|
||||
but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartist</varname>: on multi-artist albums, this is
|
||||
the artist name which shall be used for the whole album. The
|
||||
exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartistsort</varname>: same as
|
||||
<varname>albumartist</varname>, but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>title</varname>: the song title.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>track</varname>: the decimal track number within the
|
||||
album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>name</varname>: a name for this song. This is not the
|
||||
song title. The exact meaning of this tag is not well-defined.
|
||||
It is often used by badly configured internet radio stations
|
||||
with broken tags to squeeze both the artist name and the song
|
||||
title in one tag.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>genre</varname>: the music genre.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>date</varname>: the song's release date. This is
|
||||
usually a 4-digit year.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>composer</varname>: the artist who composed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>performer</varname>: the artist who performed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>comment</varname>: a human-readable comment about this
|
||||
song. The exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>disc</varname>: the decimal disc number in a multi-disc
|
||||
album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_artistid</varname>: the artist id in the
|
||||
<ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumid</varname>: the album id in the
|
||||
<ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumartistid</varname>: the album artist
|
||||
id in the <ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_trackid</varname>: the track id in the
|
||||
<ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_releasetrackid</varname>: the release track
|
||||
id in the <ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_workid</varname>: the work id in the
|
||||
<ulink
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
@@ -1,7 +1,14 @@
|
||||
install_man(['mpd.1', 'mpd.conf.5'])
|
||||
if not get_option('html_manual') and not get_option('manpages')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
sphinx = find_program('sphinx-build')
|
||||
sphinx_output = custom_target(
|
||||
sphinx = find_program('sphinx-build', required: get_option('documentation'))
|
||||
if not sphinx.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
if get_option('html_manual')
|
||||
sphinx_output = custom_target(
|
||||
'HTML documentation',
|
||||
output: 'html',
|
||||
input: [
|
||||
@@ -14,20 +21,17 @@ sphinx_output = custom_target(
|
||||
build_by_default: true,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
|
||||
)
|
||||
|
||||
custom_target(
|
||||
'upload',
|
||||
input: sphinx_output,
|
||||
output: 'upload',
|
||||
build_always_stale: true,
|
||||
command: [
|
||||
'rsync', '-vpruz', '--delete', meson.current_build_dir() + '/',
|
||||
'www.musicpd.org:/var/www/mpd/doc/',
|
||||
'--chmod=Dug+rwx,Do+rx,Fug+rw,Fo+r',
|
||||
'--include=html', '--include=html/**',
|
||||
'--exclude=*',
|
||||
],
|
||||
)
|
||||
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('manpages')
|
||||
custom_target(
|
||||
'Manpage documentation',
|
||||
output: ['mpd.1', 'mpd.conf.5'],
|
||||
input: ['mpd.1.rst', 'conf.py'],
|
||||
command: [sphinx, '-q', '-b', 'man', '-d', '@OUTDIR@/man_doctrees', meson.current_source_dir(), '@OUTDIR@'],
|
||||
build_by_default: true,
|
||||
install: true,
|
||||
install_dir: [join_paths(get_option('mandir'), 'man1'), join_paths(get_option('mandir'), 'man5')],
|
||||
)
|
||||
endif
|
||||
|
55
doc/mpd.1
55
doc/mpd.1
@@ -1,55 +0,0 @@
|
||||
.TH "Music Player Daemon" 1
|
||||
.SH NAME
|
||||
MPD \- A daemon for playing music
|
||||
.SH SYNOPSIS
|
||||
.B mpd
|
||||
.RI [ options ]
|
||||
.RI [ CONF_FILE ]
|
||||
.SH DESCRIPTION
|
||||
MPD is a daemon for playing music. Music is played through the configured
|
||||
audio output(s) (which are generally local, but can be remote). The daemon
|
||||
stores info about all available music, and this info can be easily searched and
|
||||
retrieved. Player control, info retrieval, and playlist management can all be
|
||||
managed remotely.
|
||||
|
||||
MPD searches for a config file in \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP then
|
||||
\fB~/.mpdconf\fP then \fB/etc/mpd.conf\fP or uses CONF_FILE.
|
||||
|
||||
Read more about MPD at <\fBhttp://www.musicpd.org/\fP>.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI \-\-help
|
||||
Output a brief help message.
|
||||
.TP
|
||||
.BI \-\-kill
|
||||
Kill the currently running mpd session. The pid_file parameter must be
|
||||
specified in the config file for this to work.
|
||||
.TP
|
||||
.BI \-\-no\-daemon
|
||||
Don't detach from console.
|
||||
.TP
|
||||
.BI \-\-stderr
|
||||
Print messages stderr.
|
||||
.TP
|
||||
.BI \-\-verbose
|
||||
Verbose logging.
|
||||
.TP
|
||||
.BI \-\-version
|
||||
Print version information.
|
||||
.SH FILES
|
||||
.TP
|
||||
.BI ~/.mpdconf
|
||||
User configuration file.
|
||||
.TP
|
||||
.BI /etc/mpd.conf
|
||||
Global configuration file.
|
||||
.SH SEE ALSO
|
||||
mpd.conf(5), mpc(1)
|
||||
.SH BUGS
|
||||
If you find a bug, please report it at
|
||||
.br
|
||||
<\fBhttps://github.com/MusicPlayerDaemon/MPD/issues/\fP>.
|
||||
.SH AUTHORS
|
||||
Max Kellermann <max.kellermann@gmail.com>
|
||||
|
||||
Special thanks to all the people that provided feedback and patches.
|
69
doc/mpd.1.rst
Normal file
69
doc/mpd.1.rst
Normal file
@@ -0,0 +1,69 @@
|
||||
===
|
||||
mpd
|
||||
===
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
``mpd`` [options] [CONF_FILE]
|
||||
|
||||
DESCRIPTION
|
||||
------------
|
||||
|
||||
MPD is a daemon for playing music. Music is played through the configured audio output(s) (which are generally local, but can be remote). The daemon stores info about all available music, and this info can be easily searched and retrieved. Player control, info retrieval, and playlist management can all be managed remotely.
|
||||
|
||||
MPD searches for a config file in ``$XDG_CONFIG_HOME/mpd/mpd.conf``
|
||||
then ``~/.mpdconf`` then ``~/.mpd/mpd.conf`` then ``/etc/mpd.conf`` or uses ``CONF_FILE``.
|
||||
|
||||
Read more about MPD at http://www.musicpd.org/
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. program:: mpd
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Output a brief help message.
|
||||
|
||||
.. option:: --kill
|
||||
|
||||
Kill the currently running mpd session. The pid_file parameter must be specified in the config file for this to work.
|
||||
|
||||
.. option:: --no-config
|
||||
|
||||
Don't read from the configuration file.
|
||||
|
||||
.. option:: --no-daemon
|
||||
|
||||
Don't detach from console.
|
||||
|
||||
.. option:: --stderr
|
||||
|
||||
Print messages to stderr.
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
Verbose logging.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Print version information.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
|
||||
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
|
||||
|
||||
:file:`/etc/mpd.conf`
|
||||
Global configuration file.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`mpd.conf(5)`, :manpage:`mpc(1)`
|
||||
|
||||
BUGS
|
||||
----
|
||||
If you find a bug, please report it at https://github.com/MusicPlayerDaemon/MPD/issues/
|
226
doc/mpd.conf.5
226
doc/mpd.conf.5
@@ -1,226 +0,0 @@
|
||||
.TH mpd.conf 5
|
||||
.SH NAME
|
||||
mpd.conf \- Music Player Daemon configuration file
|
||||
.SH DESCRIPTION
|
||||
\fBmpd.conf\fP is the configuration file for mpd(1). If not specified on the
|
||||
command line, MPD first searches for it at \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP
|
||||
then at \fB~/.mpdconf\fP then at \fB~/.mpd/mpd.conf\fP and then in
|
||||
\fB/etc/mpd.conf\fP.
|
||||
|
||||
Lines beginning with a "#" character are comments. All other non-empty lines
|
||||
specify parameters and their values. These lines contain the parameter name
|
||||
and parameter value (surrounded by double quotes) separated by whitespace
|
||||
(either tabs or spaces). For example:
|
||||
|
||||
parameter "value"
|
||||
|
||||
The exception to this rule is the audio_output parameter, which is of the form:
|
||||
|
||||
audio_output {
|
||||
.br
|
||||
parameter1 "value"
|
||||
parameter2 "value"
|
||||
.br
|
||||
}
|
||||
|
||||
Parameters that take a file or directory as an argument should use absolute
|
||||
paths.
|
||||
|
||||
See \fBdocs/mpdconf.example\fP in the source tarball for an example
|
||||
configuration file.
|
||||
|
||||
This manual is not complete, it lists only the most important options.
|
||||
Please read the MPD user manual for a complete configuration guide:
|
||||
<\fBhttp://www.musicpd.org/doc/user/\fP>
|
||||
.SH REQUIRED PARAMETERS
|
||||
.TP
|
||||
.B db_file <file>
|
||||
This specifies where the db file will be stored.
|
||||
.TP
|
||||
.B log_file <file>
|
||||
This specifies where the log file should be located.
|
||||
The special value "syslog" makes MPD use the local syslog daemon.
|
||||
.SH OPTIONAL PARAMETERS
|
||||
.TP
|
||||
.B sticker_file <file>
|
||||
The location of the sticker database. This is a database which
|
||||
manages dynamic information attached to songs.
|
||||
.TP
|
||||
.B pid_file <file>
|
||||
This specifies the file to save mpd's process ID in.
|
||||
.TP
|
||||
.B music_directory <directory>
|
||||
This specifies the directory where music is located.
|
||||
If you do not configure this, you can only play streams.
|
||||
.TP
|
||||
.B playlist_directory <directory>
|
||||
This specifies the directory where saved playlists are stored.
|
||||
If you do not configure this, you cannot save playlists.
|
||||
.TP
|
||||
.B state_file <file>
|
||||
This specifies if a state file is used and where it is located. The state of
|
||||
mpd will be saved to this file when mpd is terminated by a TERM signal or by
|
||||
the "kill" command. When mpd is restarted, it will read the state file and
|
||||
restore the state of mpd (including the playlist).
|
||||
.TP
|
||||
.B restore_paused <yes or no>
|
||||
Put MPD into pause mode instead of starting playback after startup.
|
||||
.TP
|
||||
.B user <username>
|
||||
This specifies the user that MPD will run as, if set. MPD should
|
||||
never run as root, and you may use this option to make MPD change its
|
||||
user id after initialization. Do not use this option if you start MPD
|
||||
as an unprivileged user.
|
||||
.TP
|
||||
.B port <port>
|
||||
This specifies the port that mpd listens on. The default is 6600.
|
||||
.TP
|
||||
.B log_level <default, secure, or verbose>
|
||||
This specifies how verbose logs are. "default" is minimal logging, "secure"
|
||||
reports from what address a connection is opened, and when it is closed, and
|
||||
"verbose" records excessive amounts of information for debugging purposes. The
|
||||
default is "default".
|
||||
.TP
|
||||
.B follow_outside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing outside the music dir.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B follow_inside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing inside the music dir,
|
||||
potentially adding duplicates to the database.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B zeroconf_enabled <yes or no>
|
||||
If yes, and MPD has been compiled with support for Avahi or Bonjour, service
|
||||
information will be published with Zeroconf. The default is yes.
|
||||
.TP
|
||||
.B zeroconf_name <name>
|
||||
If Zeroconf is enabled, this is the service name to publish. This name should
|
||||
be unique to your local network, but name collisions will be properly dealt
|
||||
with. The default is "Music Player @ %h", where %h will be replaced with the
|
||||
hostname of the machine running MPD.
|
||||
.TP
|
||||
.B audio_output
|
||||
See \fBDESCRIPTION\fP and the various \fBAUDIO OUTPUT PARAMETERS\fP sections
|
||||
for the format of this parameter. Multiple audio_output sections may be
|
||||
specified. If no audio_output section is specified, then MPD will scan for a
|
||||
usable audio output.
|
||||
.TP
|
||||
.B replaygain <off or album or track or auto>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain tags
|
||||
(see <\fBhttp://www.replaygain.org/\fP>). Setting this to "album" will adjust
|
||||
volume using the album's ReplayGain tags, while setting it to "track" will
|
||||
adjust it using the track ReplayGain tags. "auto" uses the track ReplayGain
|
||||
tags if random play is activated otherwise the album ReplayGain tags. Currently
|
||||
only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2 ReplayGain tags, not
|
||||
APEv2) are supported.
|
||||
.TP
|
||||
.B replaygain_preamp <\-15 to 15>
|
||||
This is the gain (in dB) applied to songs with ReplayGain tags.
|
||||
.TP
|
||||
.B volume_normalization <yes or no>
|
||||
If yes, mpd will normalize the volume of songs as they play. The default is no.
|
||||
.TP
|
||||
.B filesystem_charset <charset>
|
||||
This specifies the character set used for the filesystem. A list of supported
|
||||
character sets can be obtained by running "iconv \-l". The default is
|
||||
determined from the locale when the db was originally created.
|
||||
.TP
|
||||
.B save_absolute_paths_in_playlists <yes or no>
|
||||
This specifies whether relative or absolute paths for song filenames are used
|
||||
when saving playlists. The default is "no".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the whether to support automatic update of music database when
|
||||
files are changed in music_directory. The default is to disable autoupdate
|
||||
of database.
|
||||
.TP
|
||||
.B auto_update_depth <N>
|
||||
Limit the depth of the directories being watched, 0 means only watch
|
||||
the music directory itself. There is no limit by default.
|
||||
.TP
|
||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B type <type>
|
||||
This specifies the audio output type. See the list of supported outputs in mpd
|
||||
\-\-version for possible values.
|
||||
.TP
|
||||
.B name <name>
|
||||
This specifies a unique name for the audio output.
|
||||
.SH OPTIONAL AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B format <sample_rate:bits:channels>
|
||||
This specifies the sample rate, bits per sample, and number of channels of
|
||||
audio that is sent to the audio output device. See documentation for the
|
||||
\fBaudio_output_format\fP parameter for more details. The default is to use
|
||||
whatever audio format is passed to the audio output.
|
||||
Any of the three attributes may be an asterisk to specify that this
|
||||
attribute should not be enforced
|
||||
.TP
|
||||
.B replay_gain_handler <software, mixer or none>
|
||||
Specifies how replay gain is applied. The default is "software",
|
||||
which uses an internal software volume control. "mixer" uses the
|
||||
configured (hardware) mixer control. "none" disables replay gain on
|
||||
this audio output.
|
||||
.SH OPTIONAL ALSA OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B device <dev>
|
||||
This specifies the device to use for audio output. The default is "default".
|
||||
.TP
|
||||
.B mixer_type <hardware, software or none>
|
||||
Specifies which mixer should be used for this audio output: the
|
||||
hardware mixer (available for ALSA, OSS and PulseAudio), the software
|
||||
mixer or no mixer ("none"). By default, the hardware mixer is used
|
||||
for devices which support it, and none for the others.
|
||||
.TP
|
||||
.B mixer_device <mixer dev>
|
||||
This specifies which mixer to use. The default is "default". To use
|
||||
the second sound card in a system, use "hw:1".
|
||||
.TP
|
||||
.B mixer_control <mixer ctrl>
|
||||
This specifies which mixer control to use (sometimes referred to as
|
||||
the "device"). The default is "PCM". Use "amixer scontrols" to see
|
||||
the list of possible controls.
|
||||
.TP
|
||||
.B mixer_index <mixer index>
|
||||
A number identifying the index of the named mixer control. This is
|
||||
probably only useful if your alsa device has more than one
|
||||
identically\-named mixer control. The default is "0". Use "amixer
|
||||
scontrols" to see the list of controls with their indexes.
|
||||
.TP
|
||||
.B auto_resample <yes or no>
|
||||
Setting this to "no" disables ALSA's software resampling, if the
|
||||
hardware does not support a specific sample rate. This lets MPD do
|
||||
the resampling. "yes" is the default and allows ALSA to resample.
|
||||
.TP
|
||||
.B auto_channels <yes or no>
|
||||
Setting this to "no" disables ALSA's channel conversion, if the
|
||||
hardware does not support a specific number of channels. Default: "yes".
|
||||
.TP
|
||||
.B auto_format <yes or no>
|
||||
Setting this to "no" disables ALSA's sample format conversion, if the
|
||||
hardware does not support a specific sample format. Default: "yes".
|
||||
.TP
|
||||
.B buffer_time <time in microseconds>
|
||||
This sets the length of the hardware sample buffer in microseconds. Increasing
|
||||
it may help to reduce or eliminate skipping on certain setups. Most users do
|
||||
not need to change this. The default is 500000 microseconds (0.5 seconds).
|
||||
.TP
|
||||
.B period_time <time in microseconds>
|
||||
This sets the time between hardware sample transfers in microseconds.
|
||||
Increasing this can reduce CPU usage while lowering it can reduce underrun
|
||||
errors on bandwidth-limited devices. Some users have reported good results
|
||||
with this set to 50000, but not all devices support values this high. Most
|
||||
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
||||
or 5804 microseconds for CD-quality audio.
|
||||
.SH FILES
|
||||
.TP
|
||||
.BI ~/.mpdconf
|
||||
User configuration file.
|
||||
.TP
|
||||
.BI /etc/mpd.conf
|
||||
Global configuration file.
|
||||
.SH SEE ALSO
|
||||
mpd(1), mpc(1)
|
210
doc/mpd.conf.5.rst
Normal file
210
doc/mpd.conf.5.rst
Normal file
@@ -0,0 +1,210 @@
|
||||
========
|
||||
mpd.conf
|
||||
========
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
------------
|
||||
|
||||
:file:`mpd.conf` is the configuration file for :manpage:`mpd(1)`. If
|
||||
not specified on the command line, MPD first searches for it at
|
||||
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf` then at :file:`~/.mpdconf` then
|
||||
at :file:`~/.mpd/mpd.conf` and then in :file:`/etc/mpd.conf`.
|
||||
|
||||
Each line in the configuration file contains a setting name and its value, e.g.:
|
||||
|
||||
:code:`connection_timeout "5"`
|
||||
|
||||
For settings which specify a filesystem path, the tilde is expanded:
|
||||
|
||||
:code:`music_directory "~/Music"`
|
||||
|
||||
Some of the settings are grouped in blocks with curly braces, e.g. per-plugin settings:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
audio_output {
|
||||
type "alsa"
|
||||
name "My ALSA output"
|
||||
device "iec958:CARD=Intel,DEV=0"
|
||||
mixer_control "PCM"
|
||||
}
|
||||
|
||||
The :code:`include` directive can be used to include settings from
|
||||
another file; the given file name is relative to the current file:
|
||||
|
||||
:code:`include "other.conf"`
|
||||
|
||||
You can use include_optional instead if you want the included file to be
|
||||
optional; the directive will be ignored if the file does not exist:
|
||||
|
||||
:code:`include_optional "may_not_exist.conf"`
|
||||
|
||||
See :file:`docs/mpdconf.example` in the source tarball for an example
|
||||
configuration file.
|
||||
|
||||
This manual is not complete, it lists only the most important options.
|
||||
Please read the MPD user manual for a complete configuration guide:
|
||||
http://www.musicpd.org/doc/user/
|
||||
|
||||
|
||||
OPTIONAL PARAMETERS
|
||||
-------------------
|
||||
|
||||
db_file <file>
|
||||
This specifies where the db file will be stored.
|
||||
|
||||
log_file <file>
|
||||
This specifies where the log file should be located. The special value "syslog" makes MPD use the local syslog daemon.
|
||||
|
||||
sticker_file <file>
|
||||
The location of the sticker database. This is a database which manages
|
||||
dynamic information attached to songs.
|
||||
|
||||
pid_file <file>
|
||||
This specifies the file to save mpd's process ID in.
|
||||
|
||||
music_directory <directory>
|
||||
This specifies the directory where music is located. If you do not configure
|
||||
this, you can only play streams.
|
||||
|
||||
playlist_directory <directory>
|
||||
This specifies the directory where saved playlists are stored. If
|
||||
you do not configure this, you cannot save playlists.
|
||||
|
||||
state_file <file>
|
||||
This specifies if a state file is used and where it is located. The state of
|
||||
mpd will be saved to this file when mpd is terminated by a TERM signal or by
|
||||
the :program:`kill` command. When mpd is restarted, it will read the state file and
|
||||
restore the state of mpd (including the playlist).
|
||||
|
||||
restore_paused <yes or no>
|
||||
Put MPD into pause mode instead of starting playback after startup.
|
||||
|
||||
user <username>
|
||||
This specifies the user that MPD will run as, if set. MPD should never run
|
||||
as root, and you may use this option to make MPD change its user id after
|
||||
initialization. Do not use this option if you start MPD as an unprivileged
|
||||
user.
|
||||
|
||||
port <port>
|
||||
This specifies the port that mpd listens on. The default is 6600.
|
||||
|
||||
log_level <level>
|
||||
Suppress all messages below the given threshold. The following
|
||||
log levels are available:
|
||||
|
||||
- :samp:`error`: errors
|
||||
- :samp:`warning`: warnings
|
||||
- :samp:`notice`: interesting informational messages
|
||||
- :samp:`info`: unimportant informational messages
|
||||
- :samp:`verbose`: debug messages (for developers and for
|
||||
troubleshooting)
|
||||
|
||||
The default is :samp:`notice`.
|
||||
|
||||
follow_outside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing outside the music dir. You
|
||||
must recreate the database after changing this option. The default is "yes".
|
||||
|
||||
follow_inside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing inside the music dir,
|
||||
potentially adding duplicates to the database. You must recreate the
|
||||
database after changing this option. The default is "yes".
|
||||
|
||||
zeroconf_enabled <yes or no>
|
||||
If yes, and MPD has been compiled with support for Avahi or Bonjour, service
|
||||
information will be published with Zeroconf. The default is yes.
|
||||
|
||||
zeroconf_name <name>
|
||||
If Zeroconf is enabled, this is the service name to publish. This name should
|
||||
be unique to your local network, but name collisions will be properly dealt
|
||||
with. The default is "Music Player @ %h", where %h will be replaced with the
|
||||
hostname of the machine running MPD.
|
||||
|
||||
audio_output
|
||||
See DESCRIPTION and the various ``AUDIO OUTPUT PARAMETERS`` sections for the
|
||||
format of this parameter. Multiple audio_output sections may be specified. If
|
||||
no audio_output section is specified, then MPD will scan for a usable audio
|
||||
output.
|
||||
|
||||
replaygain <off or album or track or auto>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain
|
||||
tags (see https://wiki.hydrogenaud.io/index.php?title=Replaygain).
|
||||
Setting this to "album" will
|
||||
adjust volume using the album's ReplayGain tags, while setting it to "track"
|
||||
will adjust it using the track ReplayGain tags. "auto" uses the track
|
||||
ReplayGain tags if random play is activated otherwise the album ReplayGain
|
||||
tags. Currently only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2
|
||||
ReplayGain tags, not APEv2) are supported.
|
||||
|
||||
replaygain_preamp <-15 to 15>
|
||||
This is the gain (in dB) applied to songs with ReplayGain tags.
|
||||
|
||||
volume_normalization <yes or no>
|
||||
If yes, mpd will normalize the volume of songs as they play. The default is
|
||||
no.
|
||||
|
||||
filesystem_charset <charset>
|
||||
This specifies the character set used for the filesystem. A list of supported
|
||||
character sets can be obtained by running "iconv -l". The default is
|
||||
determined from the locale when the db was originally created.
|
||||
|
||||
save_absolute_paths_in_playlists <yes or no>
|
||||
This specifies whether relative or absolute paths for song filenames are used
|
||||
when saving playlists. The default is "no".
|
||||
|
||||
auto_update <yes or no>
|
||||
This specifies the whether to support automatic update of music database
|
||||
when files are changed in music_directory. The default is to disable
|
||||
autoupdate of database.
|
||||
|
||||
auto_update_depth <N>
|
||||
Limit the depth of the directories being watched, 0 means only watch the
|
||||
music directory itself. There is no limit by default.
|
||||
|
||||
REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
--------------------------------
|
||||
|
||||
type <type>
|
||||
This specifies the audio output type. See the list of supported outputs in
|
||||
``mpd --version`` for possible values.
|
||||
|
||||
name <name>
|
||||
This specifies a unique name for the audio output.
|
||||
|
||||
OPTIONAL AUDIO OUTPUT PARAMETERS
|
||||
--------------------------------
|
||||
|
||||
format <sample_rate:bits:channels>
|
||||
This specifies the sample rate, bits per sample, and number of channels of
|
||||
audio that is sent to the audio output device. See documentation for the
|
||||
``audio_output_format`` parameter for more details. The default is to use
|
||||
whatever audio format is passed to the audio output. Any of the three
|
||||
attributes may be an asterisk to specify that this attribute should not be
|
||||
enforced
|
||||
|
||||
replay_gain_handler <software, mixer or none>
|
||||
Specifies how replay gain is applied. The default is "software", which uses
|
||||
an internal software volume control. "mixer" uses the configured (hardware)
|
||||
mixer control. "none" disables replay gain on this audio output.
|
||||
|
||||
mixer_type <hardware, software or none>
|
||||
Specifies which mixer should be used for this audio output: the hardware
|
||||
mixer (available for ALSA, OSS and PulseAudio), the software mixer or no
|
||||
mixer ("none"). By default, the hardware mixer is used for devices which
|
||||
support it, and none for the others.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
|
||||
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
|
||||
|
||||
:file:`/etc/mpd.conf`
|
||||
Global configuration file.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`mpd(1)`, :manpage:`mpc(1)`
|
@@ -32,7 +32,7 @@
|
||||
# settings.
|
||||
#
|
||||
# The special value "syslog" makes MPD use the local syslog daemon. This
|
||||
# setting defaults to logging to syslog, otherwise logging is disabled.
|
||||
# setting defaults to logging to syslog.
|
||||
#
|
||||
#log_file "~/.mpd/log"
|
||||
#
|
||||
@@ -74,7 +74,7 @@
|
||||
#group "nogroup"
|
||||
#
|
||||
# This setting sets the address for the daemon to listen on. Careful attention
|
||||
# should be paid if this is assigned to anything other then the default, any.
|
||||
# should be paid if this is assigned to anything other than the default, any.
|
||||
# This setting can deny access to control of the daemon. Not effective if
|
||||
# systemd socket activiation is in use.
|
||||
#
|
||||
@@ -89,12 +89,11 @@
|
||||
#
|
||||
#port "6600"
|
||||
#
|
||||
# This setting controls the type of information which is logged. Available
|
||||
# setting arguments are "default", "secure" or "verbose". The "verbose" setting
|
||||
# argument is recommended for troubleshooting, though can quickly stretch
|
||||
# available resources on limited hardware storage.
|
||||
# Suppress all messages below the given threshold. Use "verbose" for
|
||||
# troubleshooting. Available setting arguments are "notice", "info", "verbose",
|
||||
# "warning" and "error".
|
||||
#
|
||||
#log_level "default"
|
||||
#log_level "notice"
|
||||
#
|
||||
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
|
||||
# of starting playback after startup.
|
||||
@@ -174,7 +173,18 @@
|
||||
|
||||
# Database #######################################################################
|
||||
#
|
||||
|
||||
# An example of a database section instead of the old 'db_file' setting.
|
||||
# It enables mounting other storages into the music directory.
|
||||
#
|
||||
#database {
|
||||
# plugin "simple"
|
||||
# path "~/.local/share/mpd/db
|
||||
# cache_directory "~/.local/share/mpd/cache"
|
||||
#}
|
||||
#
|
||||
# An example of database config for a sattelite setup
|
||||
#
|
||||
#music_directory "nfs://fileserver.local/srv/mp3"
|
||||
#database {
|
||||
# plugin "proxy"
|
||||
# host "other.mpd.host"
|
||||
@@ -183,7 +193,6 @@
|
||||
|
||||
# Input #######################################################################
|
||||
#
|
||||
|
||||
input {
|
||||
plugin "curl"
|
||||
# proxy "proxy.isp.com:8080"
|
||||
@@ -280,6 +289,7 @@ input {
|
||||
# name "My Pulse Output"
|
||||
## server "remote_server" # optional
|
||||
## sink "remote_server_sink" # optional
|
||||
## media_role "media_role" #optional
|
||||
#}
|
||||
#
|
||||
# An example of a winmm output (Windows multimedia API).
|
||||
@@ -293,6 +303,20 @@ input {
|
||||
## mixer_type "hardware" # optional
|
||||
#}
|
||||
#
|
||||
# An example of a wasapi output (Windows multimedia API).
|
||||
#
|
||||
#audio_output {
|
||||
# type "wasapi"
|
||||
# name "My WASAPI output"
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
# or
|
||||
## device "0" # optional
|
||||
## Exclusive mode blocks all other audio source, and get best audio quality without resampling.
|
||||
## exclusive "no" # optional
|
||||
## Enumerate all devices in log.
|
||||
## enumerate "no" # optional
|
||||
#}
|
||||
#
|
||||
# An example of an openal output.
|
||||
#
|
||||
#audio_output {
|
||||
@@ -348,7 +372,8 @@ input {
|
||||
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
|
||||
# chooses between "track" and "album" depending on the current state of
|
||||
# random playback. If random playback is enabled then "track" mode is used.
|
||||
# See <http://www.replaygain.org> for more details about ReplayGain.
|
||||
# See <https://wiki.hydrogenaud.io/index.php?title=Replaygain> for
|
||||
# more details about ReplayGain.
|
||||
# This setting is off by default.
|
||||
#
|
||||
#replaygain "album"
|
||||
|
540
doc/plugins.rst
540
doc/plugins.rst
File diff suppressed because it is too large
Load Diff
601
doc/protocol.rst
601
doc/protocol.rst
File diff suppressed because it is too large
Load Diff
501
doc/user.rst
501
doc/user.rst
@@ -44,7 +44,9 @@ ALSA is not available on Android; only the :ref:`OpenSL ES
|
||||
Compiling from source
|
||||
---------------------
|
||||
|
||||
Download the source tarball from the `MPD home page <https://musicpd.org>`_ and unpack it:
|
||||
`Download the source tarball <https://www.musicpd.org/download.html>`_
|
||||
and unpack it (or `clone the git repository
|
||||
<https://github.com/MusicPlayerDaemon/MPD>`_):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -53,8 +55,8 @@ Download the source tarball from the `MPD home page <https://musicpd.org>`_ and
|
||||
|
||||
In any case, you need:
|
||||
|
||||
* a C++14 compiler (e.g. gcc 6.0 or clang 3.9)
|
||||
* `Meson 0.47 <http://mesonbuild.com/>`__ and `Ninja
|
||||
* a C++17 compiler (e.g. GCC 8 or clang 7)
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* Boost 1.58
|
||||
* pkg-config
|
||||
@@ -62,15 +64,16 @@ In any case, you need:
|
||||
Each plugin usually needs a codec library, which you also need to
|
||||
install. Check the :doc:`plugins` for details about required libraries
|
||||
|
||||
For example, the following installs a fairly complete list of build dependencies on Debian Jessie:
|
||||
For example, the following installs a fairly complete list of build dependencies on Debian Buster:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
apt install g++ \
|
||||
apt install meson g++ \
|
||||
libpcre3-dev \
|
||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
||||
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
|
||||
libfluidsynth-dev libgme-dev libmikmod2-dev libmodplug-dev \
|
||||
libfluidsynth-dev libgme-dev libmikmod-dev libmodplug-dev \
|
||||
libmpcdec-dev libwavpack-dev libwildmidi-dev \
|
||||
libsidplay2-dev libsidutils-dev libresid-builder-dev \
|
||||
libavcodec-dev libavformat-dev \
|
||||
@@ -83,14 +86,16 @@ For example, the following installs a fairly complete list of build dependencies
|
||||
libpulse-dev libshout3-dev \
|
||||
libsndio-dev \
|
||||
libmpdclient-dev \
|
||||
libnfs-dev libsmbclient-dev \
|
||||
libnfs-dev \
|
||||
libupnp-dev \
|
||||
libavahi-client-dev \
|
||||
libsqlite3-dev \
|
||||
libsystemd-dev libwrap0-dev \
|
||||
libsystemd-dev \
|
||||
libgtest-dev \
|
||||
libboost-dev \
|
||||
libicu-dev
|
||||
libicu-dev \
|
||||
libchromaprint-dev \
|
||||
libgcrypt20-dev
|
||||
|
||||
|
||||
Now configure the source tree:
|
||||
@@ -136,6 +141,15 @@ Basically, there are two ways to compile :program:`MPD` for Windows:
|
||||
|
||||
This section is about the latter.
|
||||
|
||||
You need:
|
||||
|
||||
* `mingw-w64 <http://mingw-w64.org/doku.php>`__
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* cmake
|
||||
* pkg-config
|
||||
* quilt
|
||||
|
||||
Just like with the native build, unpack the :program:`MPD` source
|
||||
tarball and change into the directory. Then, instead of
|
||||
:program:`meson`, type:
|
||||
@@ -162,7 +176,12 @@ Compiling for Android
|
||||
You need:
|
||||
|
||||
* Android SDK
|
||||
* Android NDK
|
||||
* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* cmake
|
||||
* pkg-config
|
||||
* quilt
|
||||
|
||||
Just like with the native build, unpack the :program:`MPD` source
|
||||
tarball and change into the directory. Then, instead of
|
||||
@@ -182,47 +201,6 @@ ABI is the Android ABI to be built, e.g. ":code:`arm64-v8a`".
|
||||
|
||||
This downloads various library sources, and then configures and builds :program:`MPD`.
|
||||
|
||||
systemd socket activation
|
||||
-------------------------
|
||||
|
||||
Using systemd, you can launch :program:`MPD` on demand when the first client attempts to connect.
|
||||
|
||||
:program:`MPD` comes with two systemd unit files: a "service" unit and
|
||||
a "socket" unit. These will be installed to the directory specified
|
||||
with :code:`-Dsystemd_system_unit_dir=...`,
|
||||
e.g. :file:`/lib/systemd/system`.
|
||||
|
||||
To enable socket activation, type:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl enable mpd.socket
|
||||
systemctl start mpd.socket
|
||||
|
||||
In this configuration, :program:`MPD` will ignore the :ref:`listener
|
||||
settings <listeners>` (``bind_to_address`` and ``port``).
|
||||
|
||||
systemd user unit
|
||||
-----------------
|
||||
|
||||
You can launch :program:`MPD` as a systemd user unit. These will be
|
||||
installed to the directory specified with
|
||||
:code:`-Dsystemd_user_unit_dir=...`,
|
||||
e.g. :file:`/usr/lib/systemd/user` or
|
||||
:file:`$HOME/.local/share/systemd/user`.
|
||||
|
||||
Once the user unit is installed, you can start and stop :program:`MPD` like any other service:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl --user start mpd
|
||||
|
||||
To auto-start :program:`MPD` upon login, type:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl --user enable mpd
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
@@ -257,6 +235,13 @@ another file; the given file name is relative to the current file:
|
||||
|
||||
include "other.conf"
|
||||
|
||||
You can use :code:`include_optional` instead if you want the included file
|
||||
to be optional; the directive will be ignored if the file does not exist:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
include_optional "may_not_exist.conf"
|
||||
|
||||
Configuring the music directory
|
||||
-------------------------------
|
||||
|
||||
@@ -275,7 +260,9 @@ You can also use multiple storage plugins to assemble a virtual music directory
|
||||
Configuring database plugins
|
||||
----------------------------
|
||||
|
||||
If a music directory is configured, one database plugin is used. To configure this plugin, add a database block to :file:`mpd.conf`:
|
||||
If a music directory is configured, one database plugin is used. To
|
||||
configure this plugin, add a :code:`database` block to
|
||||
:file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -290,7 +277,9 @@ reference.
|
||||
Configuring neighbor plugins
|
||||
----------------------------
|
||||
|
||||
All neighbor plugins are disabled by default to avoid unwanted overhead. To enable (and configure) a plugin, add a neighbor block to :file:`mpd.conf`:
|
||||
All neighbor plugins are disabled by default to avoid unwanted
|
||||
overhead. To enable (and configure) a plugin, add a :code:`neighbor`
|
||||
block to :file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -303,7 +292,8 @@ More information can be found in the :ref:`neighbor_plugin` reference.
|
||||
Configuring input plugins
|
||||
-------------------------
|
||||
|
||||
To configure an input plugin, add a input block to :file:`mpd.conf`:
|
||||
To configure an input plugin, add an :code:`input` block to
|
||||
:file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -328,10 +318,49 @@ The following table lists the input options valid for all plugins:
|
||||
|
||||
More information can be found in the :ref:`input_plugins` reference.
|
||||
|
||||
.. _input_cache:
|
||||
|
||||
Configuring the Input Cache
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The input cache prefetches queued song files before they are going to
|
||||
be played. This has several advantages:
|
||||
|
||||
- risk of buffer underruns during playback is reduced because this
|
||||
decouples playback from disk (or network) I/O
|
||||
- bulk transfers may be faster and more energy efficient than loading
|
||||
small chunks on-the-fly
|
||||
- by prefetching several songs at a time, the hard disk can spin down
|
||||
for longer periods of time
|
||||
|
||||
This comes at a cost:
|
||||
|
||||
- memory usage
|
||||
- bulk transfers may reduce the performance of other applications
|
||||
which also want to access the disk (if the kernel's I/O scheduler
|
||||
isn't doing its job properly)
|
||||
|
||||
To enable the input cache, add an ``input_cache`` block to the
|
||||
configuration file:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
input_cache {
|
||||
size "1 GB"
|
||||
}
|
||||
|
||||
This allocates a cache of 1 GB. If the cache grows larger than that,
|
||||
older files will be evicted.
|
||||
|
||||
You can flush the cache at any time by sending ``SIGHUP`` to the
|
||||
:program:`MPD` process, see :ref:`signals`.
|
||||
|
||||
|
||||
Configuring decoder plugins
|
||||
---------------------------
|
||||
|
||||
Most decoder plugins do not need any special configuration. To configure a decoder, add a decoder block to :file:`mpd.conf`:
|
||||
Most decoder plugins do not need any special configuration. To
|
||||
configure a decoder, add a :code:`decoder` block to :file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -358,16 +387,21 @@ More information can be found in the :ref:`decoder_plugins` reference.
|
||||
Configuring encoder plugins
|
||||
---------------------------
|
||||
|
||||
Encoders are used by some of the output plugins (such as shout). The encoder settings are included in the audio_output section.
|
||||
Encoders are used by some of the output plugins (such as shout). The
|
||||
encoder settings are included in the ``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
More information can be found in the :ref:`encoder_plugins` reference.
|
||||
|
||||
|
||||
.. _config_audio_output:
|
||||
|
||||
Configuring audio outputs
|
||||
-------------------------
|
||||
|
||||
Audio outputs are devices which actually play the audio chunks produced by :program:`MPD`. You can configure any number of audio output devices, but there must be at least one. If none is configured, :program:`MPD` attempts to auto-detect. Usually, this works quite well with ALSA, OSS and on Mac OS X.
|
||||
|
||||
To configure an audio output manually, add one or more audio_output blocks to :file:`mpd.conf`:
|
||||
To configure an audio output manually, add one or more
|
||||
:code:`audio_output` blocks to :file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -390,29 +424,40 @@ The following table lists the audio_output options valid for all plugins:
|
||||
- The name of the plugin
|
||||
* - **name**
|
||||
- The name of the audio output. It is visible to the client. Some plugins also use it internally, e.g. as a name registered in the PULSE server.
|
||||
* - **format**
|
||||
- Always open the audio output with the specified audio format samplerate:bits:channels), regardless of the format of the input file. This is optional for most plugins.
|
||||
|
||||
Any of the three attributes may be an asterisk to specify that this attribute should not be enforced, example: 48000:16:*. *:*:* is equal to not having a format specification.
|
||||
|
||||
The following values are valid for bits: 8 (signed 8 bit integer samples), 16, 24 (signed 24 bit integer samples padded to 32 bit), 32 (signed 32 bit integer samples), f (32 bit floating point, -1.0 to 1.0), "dsd" means DSD (Direct Stream Digital). For DSD, there are special cases such as "dsd64", which allows you to omit the sample rate (e.g. dsd512:2 for stereo DSD512, i.e. 22.5792 MHz).
|
||||
|
||||
The sample rate is special for DSD: :program:`MPD` counts the number of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is 2822400 from :program:`MPD`'s point of view (44100*512/8).
|
||||
* - **enabed yes|no**
|
||||
* - **format samplerate:bits:channels**
|
||||
- Always open the audio output with the specified audio format, regardless of the format of the input file. This is optional for most plugins.
|
||||
See :ref:`audio_output_format` for a detailed description of the value.
|
||||
* - **enabled yes|no**
|
||||
- Specifies whether this audio output is enabled when :program:`MPD` is started. By default, all audio outputs are enabled. This is just the default setting when there is no state file; with a state file, the previous state is restored.
|
||||
* - **tags yes|no**
|
||||
- If set to no, then :program:`MPD` will not send tags to this output. This is only useful for output plugins that can receive tags, for example the httpd output plugin.
|
||||
* - **always_on yes|no**
|
||||
- If set to yes, then :program:`MPD` attempts to keep this audio output always open. This may be useful for streaming servers, when you don't want to disconnect all listeners even when playback is accidentally stopped.
|
||||
* - **mixer_type hardware|software|null|none**
|
||||
- Specifies which mixer should be used for this audio output: the hardware mixer (available for ALSA :ref:`alsa_plugin`, OSS :ref:`oss_plugin` and PulseAudio :ref:`pulse_plugin`), the software mixer, the "null" mixer (null; allows setting the volume, but with no effect; this can be used as a trick to implement an external mixer :ref:`external_mixer`) or no mixer (none). By default, the hardware mixer is used for devices which support it, and none for the others.
|
||||
- Specifies which mixer should be used for this audio output: the
|
||||
hardware mixer (available for ALSA :ref:`alsa_plugin`, OSS
|
||||
:ref:`oss_plugin` and PulseAudio :ref:`pulse_plugin`), the
|
||||
software mixer, the ":samp:`null`" mixer (allows setting the
|
||||
volume, but with no effect; this can be used as a trick to
|
||||
implement an external mixer, see :ref:`external_mixer`) or no mixer
|
||||
(:samp:`none`). By default, the hardware mixer is used for
|
||||
devices which support it, and none for the others.
|
||||
* - **filters "name,...**"
|
||||
- The specified configured filters are instantiated in the given
|
||||
order. Each filter name refers to a ``filter`` block, see
|
||||
:ref:`config_filter`.
|
||||
|
||||
More information can be found in the :ref:`output_plugins` reference.
|
||||
|
||||
|
||||
.. _config_filter:
|
||||
|
||||
Configuring filters
|
||||
-------------------
|
||||
|
||||
Filters are plugins which modify an audio stream.
|
||||
|
||||
To configure a filter, add a filter block to :file:`mpd.conf`:
|
||||
To configure a filter, add a :code:`filter` block to :file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -421,6 +466,9 @@ To configure a filter, add a filter block to :file:`mpd.conf`:
|
||||
name "software volume"
|
||||
}
|
||||
|
||||
Configured filters may then be added to the ``filters`` setting of an
|
||||
``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
The following table lists the filter options valid for all plugins:
|
||||
|
||||
.. list-table::
|
||||
@@ -434,12 +482,18 @@ The following table lists the filter options valid for all plugins:
|
||||
* - **name**
|
||||
- The name of the filter
|
||||
|
||||
More information can be found in the :ref:`filter_plugins` reference.
|
||||
|
||||
|
||||
Configuring playlist plugins
|
||||
----------------------------
|
||||
|
||||
Playlist plugins are used to load remote playlists (protocol commands load, listplaylist and listplaylistinfo). This is not related to :program:`MPD`'s playlist directory.
|
||||
Playlist plugins are used to load remote playlists (protocol commands
|
||||
load, listplaylist and listplaylistinfo). This is not related to
|
||||
:program:`MPD`'s :ref:`playlist directory <stored_playlists>`.
|
||||
|
||||
To configure a playlist plugin, add a playlist_plugin block to :file:`mpd.conf`:
|
||||
To configure a playlist plugin, add a :code:`playlist_plugin` block to
|
||||
:file:`mpd.conf`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -460,6 +514,11 @@ The following table lists the playlist_plugin options valid for all plugins:
|
||||
- The name of the plugin
|
||||
* - **enabled yes|no**
|
||||
- Allows you to disable a playlist plugin without recompiling. By default, all plugins are enabled.
|
||||
* - **as_directory yes|no**
|
||||
- With this option, a playlist file of this type is parsed during
|
||||
database update and converted to a virtual directory, allowing
|
||||
MPD clients to access individual entries. By default, this is
|
||||
only enabled for the :ref:`cue plugin <cue_playlist>`.
|
||||
|
||||
More information can be found in the :ref:`playlist_plugins`
|
||||
reference.
|
||||
@@ -467,13 +526,34 @@ reference.
|
||||
Audio Format Settings
|
||||
---------------------
|
||||
|
||||
Global Audio Format
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. _audio_output_format:
|
||||
|
||||
The setting audio_output_format forces :program:`MPD` to use one audio format for all outputs. Doing that is usually not a good idea. The values are the same as in format in the audio_output section.
|
||||
Global Audio Format
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The setting ``audio_output_format`` forces :program:`MPD` to use one
|
||||
audio format for all outputs. Doing that is usually not a good idea.
|
||||
|
||||
The value is specified as ``samplerate:bits:channels``.
|
||||
|
||||
Any of the three attributes may be an asterisk to specify that this
|
||||
attribute should not be enforced, example: ``48000:16:*``.
|
||||
``*:*:*`` is equal to not having a format specification.
|
||||
|
||||
The following values are valid for bits: ``8`` (signed 8 bit integer
|
||||
samples), ``16``, ``24`` (signed 24 bit integer samples padded to 32
|
||||
bit), ``32`` (signed 32 bit integer samples), ``f`` (32 bit floating
|
||||
point, -1.0 to 1.0), ``dsd`` means DSD (Direct Stream Digital). For
|
||||
DSD, there are special cases such as ``dsd64``, which allows you to
|
||||
omit the sample rate (e.g. ``dsd512:2`` for stereo DSD512,
|
||||
i.e. 22.5792 MHz).
|
||||
|
||||
The sample rate is special for DSD: :program:`MPD` counts the number
|
||||
of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is
|
||||
2822400 from :program:`MPD`'s point of view (44100*512/8).
|
||||
|
||||
Resampler
|
||||
~~~~~~~~~
|
||||
^^^^^^^^^
|
||||
|
||||
Sometimes, music needs to be resampled before it can be played; for example, CDs use a sample rate of 44,100 Hz while many cheap audio chips can only handle 48,000 Hz. Resampling reduces the quality and consumes a lot of CPU. There are different options, some of them optimized for high quality and others for low CPU usage, but you can't have both at the same time. Often, the resampler is the component that is responsible for most of :program:`MPD`'s CPU usage. Since :program:`MPD` comes with high quality defaults, it may appear that :program:`MPD` consumes more CPU than other software.
|
||||
|
||||
@@ -486,7 +566,7 @@ Client Connections
|
||||
.. _listeners:
|
||||
|
||||
Listeners
|
||||
~~~~~~~~~
|
||||
^^^^^^^^^
|
||||
|
||||
The setting :code:`bind_to_address` specifies which addresses
|
||||
:program:`MPD` listens on for connections from clients. It can be
|
||||
@@ -513,6 +593,12 @@ choice::
|
||||
|
||||
bind_to_address "/var/run/mpd/socket"
|
||||
|
||||
On Linux, local sockets can be bound to a name without a socket inode
|
||||
on the filesystem; MPD implements this by prepending ``@`` to the
|
||||
address::
|
||||
|
||||
bind_to_address "@mpd"
|
||||
|
||||
If no port is specified, the default port is 6600. This default can
|
||||
be changed with the port setting::
|
||||
|
||||
@@ -523,7 +609,7 @@ used.
|
||||
|
||||
|
||||
Permissions and Passwords
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, all clients are unauthenticated and have a full set of permissions. This can be restricted with the settings :code:`default_permissions` and :code:`password`.
|
||||
|
||||
@@ -542,7 +628,8 @@ By default, all clients are unauthenticated and have a full set of permissions.
|
||||
* - **control**
|
||||
- Allows all other player and playlist manipulations.
|
||||
* - **admin**
|
||||
- Allows database updates and allows shutting down :program:`MPD`.
|
||||
- Allows manipulating outputs, stickers and partitions,
|
||||
mounting/unmounting storage and shutting down :program:`MPD`.
|
||||
|
||||
:code:`local_permissions` may be used to assign other permissions to clients connecting on a local socket.
|
||||
|
||||
@@ -586,7 +673,7 @@ Other Settings
|
||||
Section :ref:`tags` contains a list of supported tags.
|
||||
|
||||
The State File
|
||||
~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The state file is a file where :program:`MPD` saves and restores its state (play queue, playback position etc.) to keep it persistent across restarts and reboots. It is an optional setting.
|
||||
|
||||
@@ -602,9 +689,11 @@ The State File
|
||||
- Specify the state file location. The parent directory must be writable by the :program:`MPD` user (+wx).
|
||||
* - **state_file_interval SECONDS**
|
||||
- Auto-save the state file this number of seconds after each state change. Defaults to 120 (2 minutes).
|
||||
* - **restore_paused yes|no**
|
||||
- If set to :samp:`yes`, then :program:`MPD` is put into pause mode instead of starting playback after startup. Default is :samp:`no`.
|
||||
|
||||
The Sticker Database
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
"Stickers" are pieces of information attached to songs. Some clients
|
||||
use them to store ratings and other volatile data. This feature
|
||||
@@ -621,7 +710,7 @@ requires :program:`SQLite`, compile-time configure option
|
||||
- The location of the sticker database.
|
||||
|
||||
Resource Limitations
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
These settings are various limitations to prevent :program:`MPD` from using too many resources (denial of service).
|
||||
|
||||
@@ -634,7 +723,7 @@ These settings are various limitations to prevent :program:`MPD` from using too
|
||||
* - **connection_timeout SECONDS**
|
||||
- If a client does not send any new data in this time period, the connection is closed. Clients waiting in "idle" mode are excluded from this. Default is 60.
|
||||
* - **max_connections NUMBER**
|
||||
- This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 5.
|
||||
- This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 100.
|
||||
* - **max_playlist_length NUMBER**
|
||||
- The maximum number of songs that can be in the playlist. Default is 16384.
|
||||
* - **max_command_list_size KBYTES**
|
||||
@@ -643,7 +732,7 @@ These settings are various limitations to prevent :program:`MPD` from using too
|
||||
- The maximum size of the output buffer to a client (maximum response size). Default is 8192 (8 MiB).
|
||||
|
||||
Buffer Settings
|
||||
~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Do not change these unless you know what you are doing.
|
||||
|
||||
@@ -653,11 +742,12 @@ Do not change these unless you know what you are doing.
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **audio_buffer_size KBYTES**
|
||||
- Adjust the size of the internal audio buffer. Default is 4096 (4 MiB).
|
||||
* - **audio_buffer_size SIZE**
|
||||
- Adjust the size of the internal audio buffer. Default is
|
||||
:samp:`4 MB` (4 MiB).
|
||||
|
||||
Zeroconf
|
||||
~~~~~~~~
|
||||
^^^^^^^^
|
||||
|
||||
If Zeroconf support (`Avahi <http://avahi.org/>`_ or Apple's Bonjour)
|
||||
was enabled at compile time with :code:`-Dzeroconf=...`,
|
||||
@@ -702,22 +792,24 @@ The :code:`music_directory` setting tells :program:`MPD` to read files from the
|
||||
|
||||
The database setting tells :program:`MPD` to pass all database queries on to the :program:`MPD` instance running on the file server (using the proxy plugin).
|
||||
|
||||
.. _realtime:
|
||||
|
||||
Real-Time Scheduling
|
||||
--------------------
|
||||
|
||||
On Linux, :program:`MPD` attempts to configure real-time scheduling for some threads that benefit from it.
|
||||
|
||||
This is only possible you allow :program:`MPD` to do it. This privilege is controlled by :envvar:`RLIMIT_RTPRIO` :envvar:`RLIMIT_RTTIME`. You can configure this privilege with :command:`ulimit` before launching :program:`MPD`:
|
||||
This is only possible if you allow :program:`MPD` to do it. This privilege is controlled by :envvar:`RLIMIT_RTPRIO` :envvar:`RLIMIT_RTTIME`. You can configure this privilege with :command:`ulimit` before launching :program:`MPD`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
ulimit -HS -r 50; mpd
|
||||
ulimit -HS -r 40; mpd
|
||||
|
||||
Or you can use the :command:`prlimit` program from the util-linux package:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
prlimit --rtprio=50 --rttime=unlimited mpd
|
||||
prlimit --rtprio=40 --rttime=unlimited mpd
|
||||
|
||||
The systemd service file shipped with :program:`MPD` comes with this setting.
|
||||
|
||||
@@ -735,22 +827,107 @@ You can verify whether the real-time scheduler is active with the ps command:
|
||||
PID TID CLS RTPRIO COMMAND
|
||||
16257 16257 TS - mpd
|
||||
16257 16258 TS - io
|
||||
16257 16259 FF 50 rtio
|
||||
16257 16259 FF 40 rtio
|
||||
16257 16260 TS - player
|
||||
16257 16261 TS - decoder
|
||||
16257 16262 FF 50 output:ALSA
|
||||
16257 16262 FF 40 output:ALSA
|
||||
16257 16263 IDL 0 update
|
||||
|
||||
The CLS column shows the CPU scheduler; TS is the normal scheduler; FF and RR are real-time schedulers. In this example, two threads use the real-time scheduler: the output thread and the rtio (real-time I/O) thread; these two are the important ones. The database update thread uses the idle scheduler ("IDL in ps), which only gets CPU when no other process needs it.
|
||||
|
||||
Note
|
||||
~~~~
|
||||
.. note::
|
||||
|
||||
There is a rumor that real-time scheduling improves audio quality. That is not true. All it does is reduce the probability of skipping (audio buffer xruns) when the computer is under heavy load.
|
||||
There is a rumor that real-time scheduling improves audio
|
||||
quality. That is not true. All it does is reduce the probability of
|
||||
skipping (audio buffer xruns) when the computer is under heavy
|
||||
load.
|
||||
|
||||
Using MPD
|
||||
*********
|
||||
|
||||
Starting and Stopping MPD
|
||||
-------------------------
|
||||
|
||||
The simplest (but not the best) way to start :program:`MPD` is to
|
||||
simply type::
|
||||
|
||||
mpd
|
||||
|
||||
This will start :program:`MPD` as a daemon process (which means it
|
||||
detaches from your terminal and continues to run in background). To
|
||||
stop it, send ``SIGTERM`` to the process; if you have configured a
|
||||
``pid_file``, you can use the ``--kill`` option::
|
||||
|
||||
mpd --kill
|
||||
|
||||
The best way to manage :program:`MPD` processes is to use a service
|
||||
manager such as :program:`systemd`.
|
||||
|
||||
systemd
|
||||
^^^^^^^
|
||||
|
||||
:program:`MPD` ships with :program:`systemd` service units.
|
||||
|
||||
If you have installed :program:`MPD` with your operating system's
|
||||
package manager, these are probably preinstalled, so you can start and
|
||||
stop :program:`MPD` this way (like any other service)::
|
||||
|
||||
systemctl start mpd
|
||||
systemctl stop mpd
|
||||
|
||||
systemd socket activation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Using systemd, you can launch :program:`MPD` on demand when the first client attempts to connect.
|
||||
|
||||
:program:`MPD` comes with two systemd unit files: a "service" unit and
|
||||
a "socket" unit. These will be installed to the directory specified
|
||||
with :code:`-Dsystemd_system_unit_dir=...`,
|
||||
e.g. :file:`/lib/systemd/system`.
|
||||
|
||||
To enable socket activation, type:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl enable mpd.socket
|
||||
systemctl start mpd.socket
|
||||
|
||||
In this configuration, :program:`MPD` will ignore the :ref:`listener
|
||||
settings <listeners>` (``bind_to_address`` and ``port``).
|
||||
|
||||
systemd user unit
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can launch :program:`MPD` as a systemd user unit. These will be
|
||||
installed to the directory specified with
|
||||
:code:`-Dsystemd_user_unit_dir=...`,
|
||||
e.g. :file:`/usr/lib/systemd/user` or
|
||||
:file:`$HOME/.local/share/systemd/user`.
|
||||
|
||||
Once the user unit is installed, you can start and stop :program:`MPD` like any other service:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl --user start mpd
|
||||
|
||||
To auto-start :program:`MPD` upon login, type:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
systemctl --user enable mpd
|
||||
|
||||
.. _signals:
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
:program:`MPD` understands the following UNIX signals:
|
||||
|
||||
- ``SIGTERM``, ``SIGINT``: shut down MPD
|
||||
- ``SIGHUP``: reopen log files (send this after log rotation) and
|
||||
flush caches (see :ref:`input_cache`)
|
||||
|
||||
|
||||
The client
|
||||
----------
|
||||
|
||||
@@ -774,7 +951,7 @@ Depending on the size of your music collection and the speed of the storage, thi
|
||||
To exclude a file from the update, create a file called :file:`.mpdignore` in its parent directory. Each line of that file may contain a list of shell wildcards. Matching files in the current directory and all subdirectories are excluded.
|
||||
|
||||
Mounting other storages into the music directory
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:program:`MPD` has various storage plugins of which multiple instances can be "mounted" into the music directory. This way, you can use local music, file servers and USB sticks at the same time. Example:
|
||||
|
||||
@@ -816,10 +993,16 @@ The queue
|
||||
|
||||
The queue (sometimes called "current playlist") is a list of songs to be played by :program:`MPD`. To play a song, add it to the queue and start playback. Most clients offer an interface to edit the queue.
|
||||
|
||||
.. _stored_playlists:
|
||||
|
||||
Stored Playlists
|
||||
----------------
|
||||
|
||||
Stored playlists are some kind of secondary playlists which can be created, saved, edited and deleted by the client. They are addressed by their names. Its contents can be loaded into the queue, to be played back. The playlist_directory setting specifies where those playlists are stored.
|
||||
Stored playlists are some kind of secondary playlists which can be
|
||||
created, saved, edited and deleted by the client. They are addressed
|
||||
by their names. Its contents can be loaded into the queue, to be
|
||||
played back. The :code:`playlist_directory` setting specifies where
|
||||
those playlists are stored.
|
||||
|
||||
Advanced usage
|
||||
**************
|
||||
@@ -836,7 +1019,7 @@ To verify if :program:`MPD` converts the audio format, enable verbose logging, a
|
||||
.. code-block:: none
|
||||
|
||||
decoder: audio_format=44100:24:2, seekable=true
|
||||
output: opened plugin=alsa name="An ALSA output"audio_format=44100:16:2
|
||||
output: opened plugin=alsa name="An ALSA output" audio_format=44100:16:2
|
||||
output: converting from 44100:24:2
|
||||
|
||||
This example shows that a 24 bit file is being played, but the sound chip cannot play 24 bit. It falls back to 16 bit, discarding 8 bit.
|
||||
@@ -859,24 +1042,54 @@ Obey the "format" row, which indicates that the current playback format is 16 bi
|
||||
Check list for bit-perfect playback:
|
||||
|
||||
* Use the ALSA output plugin.
|
||||
* Disable sound processing inside ALSA by configuring a "hardware" device (hw:0,0 or similar).
|
||||
* Don't use software volume (setting mixer_type).
|
||||
* Don't force :program:`MPD` to use a specific audio format (settings format, audio_output_format).
|
||||
* Disable sound processing inside ALSA by configuring a "hardware"
|
||||
device (:samp:`hw:0,0` or similar).
|
||||
* Don't use software volume (setting :code:`mixer_type`).
|
||||
* Don't force :program:`MPD` to use a specific audio format (settings
|
||||
:code:`format`, :ref:`audio_output_format <audio_output_format>`).
|
||||
* Verify that you are really doing bit-perfect playback using :program:`MPD`'s verbose log and :file:`/proc/asound/card*/pcm*p/sub*/hw_params`. Some DACs can also indicate the audio format.
|
||||
|
||||
.. _dsd:
|
||||
|
||||
Direct Stream Digital (DSD)
|
||||
---------------------------
|
||||
|
||||
DSD (`Direct Stream Digital <https://en.wikipedia.org/wiki/Direct_Stream_Digital>`_) is a digital format that stores audio as a sequence of single-bit values at a very high sampling rate.
|
||||
DSD (`Direct Stream Digital
|
||||
<https://en.wikipedia.org/wiki/Direct_Stream_Digital>`_) is a digital
|
||||
format that stores audio as a sequence of single-bit values at a very
|
||||
high sampling rate. It is the sample format used on `Super Audio CDs
|
||||
<https://en.wikipedia.org/wiki/Super_Audio_CD>`_.
|
||||
|
||||
:program:`MPD` understands the file formats dff and dsf. There are three ways to play back DSD:
|
||||
:program:`MPD` understands the file formats :ref:`DSDIFF
|
||||
<decoder_dsdiff>` and :ref:`DSF <decoder_dsf>`. There are three ways
|
||||
to play back DSD:
|
||||
|
||||
* Native DSD playback. Requires ALSA 1.0.27.1 or later, a sound driver/chip that supports DSD and of course a DAC that supports DSD.
|
||||
|
||||
* DoP (DSD over PCM) playback. This wraps DSD inside fake 24 bit PCM according to the DoP standard. Requires a DAC that supports DSD. No support from ALSA and the sound chip required (except for bit-perfect 24 bit PCM support).
|
||||
* Convert DSD to PCM on-the-fly.
|
||||
|
||||
Native DSD playback is used automatically if available. DoP is only used if enabled explicitly using the dop option, because there is no way for :program:`MPD` to find out whether the DAC supports it. DSD to PCM conversion is the fallback if DSD cannot be used directly.
|
||||
Native DSD playback is used automatically if available. DoP is only
|
||||
used if enabled explicitly using the :code:`dop` option, because there
|
||||
is no way for :program:`MPD` to find out whether the DAC supports
|
||||
it. DSD to PCM conversion is the fallback if DSD cannot be used
|
||||
directly.
|
||||
|
||||
ICY-MetaData
|
||||
------------
|
||||
|
||||
Some MP3 streams send information about the current song with a
|
||||
protocol named `"ICY-MetaData"
|
||||
<http://www.smackfu.com/stuff/programming/shoutcast.html>`_.
|
||||
:program:`MPD` makes its ``StreamTitle`` value available as ``Title``
|
||||
tag.
|
||||
|
||||
By default, :program:`MPD` assumes this tag is UTF-8-encoded. To tell
|
||||
:program:`MPD` to assume a different character set, specify it in the
|
||||
``charset`` URL fragment parameter, e.g.::
|
||||
|
||||
mpc add 'http://radio.example.com/stream#charset=cp1251'
|
||||
|
||||
|
||||
Client Hacks
|
||||
************
|
||||
@@ -902,47 +1115,79 @@ Sometimes, it is helpful to run :program:`MPD` in a terminal and follow what hap
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpd --stdout --no-daemon --verbose
|
||||
mpd --stderr --no-daemon --verbose
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
Getting Help
|
||||
~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The :program:`MPD` project runs a `forum <https://forum.musicpd.org/>`_ and an IRC channel (#mpd on Freenode) for requesting help. Visit the MPD help page for details on how to get help.
|
||||
The :program:`MPD` project runs a `forum <https://forum.musicpd.org/>`_ and an IRC channel (#mpd on Libera.Chat) for requesting help. Visit the MPD help page for details on how to get help.
|
||||
|
||||
Common Problems
|
||||
~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
1. Database
|
||||
^^^^^^^^^^^
|
||||
Startup
|
||||
"""""""
|
||||
|
||||
Question: I can't see my music in the MPD database!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
Error "could not get realtime scheduling"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
See :ref:`realtime`. You can safely ignore this, but you won't
|
||||
benefit from real-time scheduling. This only makes a difference if
|
||||
your computer runs programs other than MPD.
|
||||
|
||||
Error "Failed to initialize io_uring"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Linux specific: the io_uring subsystem could not be initialized. This
|
||||
is not a critical error - MPD will fall back to "classic" blocking
|
||||
disk I/O. You can safely ignore this error, but you won't benefit
|
||||
from io_uring's advantages.
|
||||
|
||||
* "Cannot allocate memory" usually means that your memlock limit
|
||||
(``ulimit -l`` in bash or ``LimitMEMLOCK`` in systemd) is too low.
|
||||
64 MB is a reasonable value for this limit.
|
||||
* Your Linux kernel might be too old and does not support io_uring.
|
||||
|
||||
Error "bind to '0.0.0.0:6600' failed (continuing anyway, because binding to '[::]:6600' succeeded)"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This happens on Linux when :file:`/proc/sys/net/ipv6/bindv6only` is
|
||||
disabled. MPD first binds to IPv6, and this automatically binds to
|
||||
IPv4 as well; after that, MPD binds to IPv4, but that fails. You can
|
||||
safely ignore this, because MPD works on both IPv4 and IPv6.
|
||||
|
||||
|
||||
Database
|
||||
""""""""
|
||||
|
||||
I can't see my music in the MPD database
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Check your :code:`music_directory` setting.
|
||||
* Does the MPD user have read permission on all music files, and read+execute permission on all music directories (and all of their parent directories)?
|
||||
* Did you update the database? (mpc update)
|
||||
* Did you enable all relevant decoder plugins at compile time? :command:`mpd --version` will tell you.
|
||||
|
||||
Question: MPD doesn't read ID3 tags!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
MPD doesn't read ID3 tags!
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* You probably compiled :program:`MPD` without libid3tag. :command:`mpd --version` will tell you.
|
||||
|
||||
2. Playback
|
||||
^^^^^^^^^^^
|
||||
Playback
|
||||
""""""""
|
||||
|
||||
Question: I can't hear music on my client!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
I can't hear music on my client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* That problem usually follows a misunderstanding of the nature of :program:`MPD`. :program:`MPD` is a remote-controlled music player, not a music distribution system. Usually, the speakers are connected to the box where :program:`MPD` runs, and the :program:`MPD` client only sends control commands, but the client does not actually play your music.
|
||||
|
||||
:program:`MPD` has output plugins which allow hearing music on a remote host (such as httpd), but that is not :program:`MPD`'s primary design goal.
|
||||
|
||||
Question: "Device or resource busy"
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
Error "Device or resource busy"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* This ALSA error means that another program uses your sound hardware exclusively. You can stop that program to allow :program:`MPD` to use it.
|
||||
|
||||
@@ -961,19 +1206,29 @@ Your bug report should contain:
|
||||
* be clear about what you expect MPD to do, and what is actually happening
|
||||
|
||||
MPD crashes
|
||||
~~~~~~~~~~~
|
||||
^^^^^^^^^^^
|
||||
|
||||
All :program:`MPD` crashes are bugs which must be fixed by a developer, and you should write a bug report. (Many crash bugs are caused by codec libraries used by :program:`MPD`, and then that library must be fixed; but in any case, the :program:`MPD` `bug tracker <https://github.com/MusicPlayerDaemon/MPD/issues>`_ is a good place to report it first if you don't know.)
|
||||
|
||||
A crash bug report needs to contain a "backtrace".
|
||||
|
||||
First of all, your :program:`MPD` executable must not be "stripped" (i.e. debug information deleted). The executables shipped with Linux distributions are usually stripped, but some have so-called "debug" packages (package mpd-dbg or mpd-dbgsym on Debian, mpd-debug on other distributions). Make sure this package is installed.
|
||||
First of all, your :program:`MPD` executable must not be "stripped"
|
||||
(i.e. debug information deleted). The executables shipped with Linux
|
||||
distributions are usually stripped, but some have so-called "debug"
|
||||
packages (package :file:`mpd-dbgsym` or :file:`mpd-dbg` on Debian,
|
||||
:file:`mpd-debug` on other distributions). Make sure this package is
|
||||
installed.
|
||||
|
||||
If you built :program:`MPD` from sources, please recompile with Meson
|
||||
option ":code:`--buildtype=debug -Db_ndebug=false`", because this will
|
||||
add more helpful information to the backtrace.
|
||||
|
||||
You can extract the backtrace from a core dump, or by running :program:`MPD` in a debugger, e.g.:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
gdb --args mpd --stdout --no-daemon --verbose
|
||||
gdb --args mpd --stderr --no-daemon --verbose
|
||||
run
|
||||
|
||||
As soon as you have reproduced the crash, type "bt" on the gdb command prompt. Copy the output to your bug report.
|
||||
As soon as you have reproduced the crash, type ":command:`bt`" on the
|
||||
gdb command prompt. Copy the output to your bug report.
|
||||
|
269
meson.build
269
meson.build
@@ -1,11 +1,18 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21',
|
||||
meson_version: '>= 0.47',
|
||||
version: '0.22.11',
|
||||
meson_version: '>= 0.49.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'cpp_std=c++14'
|
||||
'c_std=c11',
|
||||
'build.c_std=c11',
|
||||
'cpp_std=c++17',
|
||||
'build.cpp_std=c++17',
|
||||
'warning_level=3',
|
||||
|
||||
# This is only here to build subprojects as static libraries; MPD
|
||||
# itself doesn't ship any libraries.
|
||||
'default_library=static',
|
||||
],
|
||||
license: 'GPLv2+',
|
||||
)
|
||||
@@ -15,74 +22,100 @@ version_cxx = vcs_tag(input: 'src/GitVersion.cxx', output: 'GitVersion.cxx')
|
||||
compiler = meson.get_compiler('cpp')
|
||||
c_compiler = meson.get_compiler('c')
|
||||
|
||||
if compiler.get_id() == 'gcc' and compiler.version().version_compare('<8')
|
||||
warning('Your GCC version is too old. You need at least version 8.')
|
||||
elif compiler.get_id() == 'clang' and compiler.version().version_compare('<7')
|
||||
warning('Your clang version is too old. You need at least version 7.')
|
||||
endif
|
||||
|
||||
version_conf = configuration_data()
|
||||
version_conf.set_quoted('PACKAGE', meson.project_name())
|
||||
version_conf.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||
version_conf.set_quoted('VERSION', meson.project_version())
|
||||
version_conf.set_quoted('PROTOCOL_VERSION', '0.22.4')
|
||||
configure_file(output: 'Version.h', configuration: version_conf)
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set_quoted('PACKAGE', meson.project_name())
|
||||
conf.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||
conf.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||
conf.set_quoted('VERSION', meson.project_version())
|
||||
conf.set_quoted('PROTOCOL_VERSION', '0.21.0')
|
||||
conf.set_quoted('SYSTEM_CONFIG_FILE_LOCATION', join_paths(get_option('prefix'), get_option('sysconfdir'), 'mpd.conf'))
|
||||
|
||||
common_cppflags = [
|
||||
'-D_GNU_SOURCE',
|
||||
]
|
||||
|
||||
common_cflags = [
|
||||
]
|
||||
|
||||
common_cxxflags = [
|
||||
test_global_common_flags = [
|
||||
'-fvisibility=hidden',
|
||||
]
|
||||
|
||||
test_common_flags = [
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
|
||||
'-fvisibility=hidden',
|
||||
'-Wvla',
|
||||
'-Wdouble-promotion',
|
||||
|
||||
'-ffast-math',
|
||||
'-ftree-vectorize',
|
||||
]
|
||||
|
||||
test_global_cxxflags = test_global_common_flags + [
|
||||
]
|
||||
|
||||
test_global_cflags = test_global_common_flags + [
|
||||
]
|
||||
|
||||
test_cxxflags = test_common_flags + [
|
||||
'-fno-threadsafe-statics',
|
||||
'-fmerge-all-constants',
|
||||
|
||||
'-Wmissing-declarations',
|
||||
'-Wshadow',
|
||||
'-Wpointer-arith',
|
||||
'-Wcast-qual',
|
||||
'-Wcomma-subscript',
|
||||
'-Wextra-semi',
|
||||
'-Wmismatched-tags',
|
||||
'-Wmissing-declarations',
|
||||
'-Woverloaded-virtual',
|
||||
'-Wshadow',
|
||||
'-Wsign-promo',
|
||||
'-Wunused',
|
||||
'-Wvolatile',
|
||||
'-Wvirtual-inheritance',
|
||||
'-Wwrite-strings',
|
||||
'-Wsign-compare',
|
||||
|
||||
# a vtable without a dtor is just fine
|
||||
'-Wno-non-virtual-dtor',
|
||||
|
||||
# work around bogus GCC7 warning "mangled name for ... will change
|
||||
# in C++17 because the exception specification is part of a function
|
||||
# type"
|
||||
'-Wno-noexcept-type',
|
||||
# clang specific warning options:
|
||||
'-Wcomma',
|
||||
'-Wheader-hygiene',
|
||||
'-Winconsistent-missing-destructor-override',
|
||||
'-Wunreachable-code-aggressive',
|
||||
'-Wused-but-marked-unused',
|
||||
]
|
||||
|
||||
if compiler.get_id() == 'clang'
|
||||
# Workaround for clang bug
|
||||
# https://bugs.llvm.org/show_bug.cgi?id=32611
|
||||
test_cxxflags += '-funwind-tables'
|
||||
if compiler.get_id() != 'gcc' or compiler.version().version_compare('>=9')
|
||||
# The GCC 8 implementation of this flag is buggy: it complains even
|
||||
# if "final" is present, which implies "override".
|
||||
test_cxxflags += '-Wsuggest-override'
|
||||
endif
|
||||
|
||||
test_cflags = test_common_flags + [
|
||||
'-Wcast-qual',
|
||||
'-Wmissing-prototypes',
|
||||
'-Wshadow',
|
||||
'-Wpointer-arith',
|
||||
'-Wstrict-prototypes',
|
||||
'-Wcast-qual',
|
||||
'-Wwrite-strings',
|
||||
'-pedantic',
|
||||
]
|
||||
|
||||
test_ldflags = [
|
||||
# make relocations read-only (hardening)
|
||||
'-Wl,-z,relro',
|
||||
|
||||
# no lazy binding, please - not worth it for a daemon
|
||||
'-Wl,-z,now',
|
||||
]
|
||||
|
||||
if get_option('buildtype') != 'debug'
|
||||
test_cflags += [
|
||||
test_global_cxxflags += [
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
]
|
||||
test_global_cflags += [
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
]
|
||||
@@ -91,15 +124,27 @@ if get_option('buildtype') != 'debug'
|
||||
]
|
||||
endif
|
||||
|
||||
add_global_arguments(common_cxxflags + compiler.get_supported_arguments(test_cxxflags), language: 'cpp')
|
||||
add_global_arguments(common_cflags + c_compiler.get_supported_arguments(test_cflags), language: 'c')
|
||||
add_global_link_arguments(compiler.get_supported_link_arguments(test_ldflags), language: 'cpp')
|
||||
if get_option('fuzzer')
|
||||
fuzzer_flags = ['-fsanitize=fuzzer']
|
||||
if get_option('b_sanitize') == 'none'
|
||||
fuzzer_flags += ['-fsanitize=address,undefined']
|
||||
endif
|
||||
add_global_arguments(fuzzer_flags, language: 'cpp')
|
||||
add_global_arguments(fuzzer_flags, language: 'c')
|
||||
add_global_link_arguments(fuzzer_flags, language: 'cpp')
|
||||
endif
|
||||
|
||||
add_global_arguments(compiler.get_supported_arguments(test_global_cxxflags), language: 'cpp')
|
||||
add_global_arguments(c_compiler.get_supported_arguments(test_global_cflags), language: 'c')
|
||||
add_project_arguments(compiler.get_supported_arguments(test_cxxflags), language: 'cpp')
|
||||
add_project_arguments(c_compiler.get_supported_arguments(test_cflags), language: 'c')
|
||||
add_project_link_arguments(compiler.get_supported_link_arguments(test_ldflags), language: 'cpp')
|
||||
|
||||
is_linux = host_machine.system() == 'linux'
|
||||
is_android = get_option('android_ndk') != ''
|
||||
is_darwin = host_machine.system() == 'darwin'
|
||||
is_windows = host_machine.system() == 'windows'
|
||||
is_haiku = host_machine.system() == 'haiku' # TODO is this correct?
|
||||
is_haiku = host_machine.system() == 'haiku'
|
||||
|
||||
if is_android
|
||||
common_cppflags += '-DANDROID'
|
||||
@@ -107,10 +152,29 @@ endif
|
||||
|
||||
if is_windows
|
||||
common_cppflags += [
|
||||
'-DWIN32_LEAN_AND_MEAN',
|
||||
# enable Windows Vista APIs
|
||||
'-DWINVER=0x0600', '-D_WIN32_WINNT=0x0600',
|
||||
'-DSTRICT',
|
||||
|
||||
# enable Unicode support (TCHAR=wchar_t) in the Windows API (macro
|
||||
# "UNICODE) and the C library (macro "_UNICODE")
|
||||
'-DUNICODE', '-D_UNICODE',
|
||||
|
||||
# enable strict type checking in the Windows API headers
|
||||
'-DSTRICT',
|
||||
|
||||
# reduce header bloat by disabling obscure and obsolete Windows
|
||||
# APIs
|
||||
'-DWIN32_LEAN_AND_MEAN',
|
||||
|
||||
# disable more Windows APIs which are not used by MPD
|
||||
'-DNOGDI', '-DNOBITMAP', '-DNOCOMM',
|
||||
'-DNOUSER',
|
||||
|
||||
# reduce COM header bloat
|
||||
'-DCOM_NO_WINDOWS_H',
|
||||
|
||||
# disable Internet Explorer specific APIs
|
||||
'-D_WIN32_IE=0',
|
||||
]
|
||||
|
||||
subdir('win32')
|
||||
@@ -126,13 +190,17 @@ add_global_arguments(common_cppflags, language: 'cpp')
|
||||
enable_daemon = not is_windows and not is_android and get_option('daemon')
|
||||
conf.set('ENABLE_DAEMON', enable_daemon)
|
||||
|
||||
conf.set('HAVE_LOCALE_H', compiler.has_header('locale.h'))
|
||||
|
||||
conf.set('HAVE_GETPWNAM_R', compiler.has_function('getpwnam_r'))
|
||||
conf.set('HAVE_GETPWUID_R', compiler.has_function('getpwuid_r'))
|
||||
conf.set('HAVE_INITGROUPS', compiler.has_function('initgroups'))
|
||||
conf.set('HAVE_FNMATCH', compiler.has_function('fnmatch'))
|
||||
conf.set('HAVE_STRNDUP', compiler.has_function('strndup', prefix: '#define _GNU_SOURCE\n#include <string.h>'))
|
||||
|
||||
# Explicitly exclude Windows in this check because
|
||||
# https://github.com/mesonbuild/meson/issues/3672 (reported in 2018,
|
||||
# still not fixed in 2020) causes Meson to believe it exists, because
|
||||
# __builtin_strndup() exists (but strndup() still cannot be used).
|
||||
conf.set('HAVE_STRNDUP', not is_windows and compiler.has_function('strndup', prefix: '#define _GNU_SOURCE\n#include <string.h>'))
|
||||
|
||||
conf.set('HAVE_STRCASESTR', compiler.has_function('strcasestr'))
|
||||
|
||||
conf.set('HAVE_PRCTL', is_linux)
|
||||
@@ -172,11 +240,26 @@ inc = include_directories(
|
||||
)
|
||||
|
||||
boost_dep = dependency('boost', version: '>= 1.58')
|
||||
if boost_dep.version() == '1.67'
|
||||
# https://github.com/MusicPlayerDaemon/MPD/pull/384
|
||||
# https://github.com/boostorg/lockfree/commit/12726cda009a855073b9bedbdce57b6ce7763da2
|
||||
warning('Your Boost version 1.67 is known to be buggy, and the MPD build will fail. Please upgrade to Boost 1.68 or later.')
|
||||
endif
|
||||
|
||||
log = static_library(
|
||||
'log',
|
||||
'src/Log.cxx',
|
||||
'src/LogBackend.cxx',
|
||||
include_directories: inc,
|
||||
)
|
||||
|
||||
log_dep = declare_dependency(
|
||||
link_with: log,
|
||||
)
|
||||
|
||||
sources = [
|
||||
version_cxx,
|
||||
'src/Main.cxx',
|
||||
'src/protocol/Ack.cxx',
|
||||
'src/protocol/ArgParser.cxx',
|
||||
'src/protocol/Result.cxx',
|
||||
'src/command/CommandError.cxx',
|
||||
@@ -201,26 +284,25 @@ sources = [
|
||||
'src/decoder/DecoderPrint.cxx',
|
||||
'src/client/Listener.cxx',
|
||||
'src/client/Client.cxx',
|
||||
'src/client/ClientEvent.cxx',
|
||||
'src/client/ClientExpire.cxx',
|
||||
'src/client/ClientGlobal.cxx',
|
||||
'src/client/ClientIdle.cxx',
|
||||
'src/client/ClientList.cxx',
|
||||
'src/client/ClientNew.cxx',
|
||||
'src/client/ClientProcess.cxx',
|
||||
'src/client/ClientRead.cxx',
|
||||
'src/client/ClientWrite.cxx',
|
||||
'src/client/ClientMessage.cxx',
|
||||
'src/client/ClientSubscribe.cxx',
|
||||
'src/client/ClientFile.cxx',
|
||||
'src/client/Config.cxx',
|
||||
'src/client/Domain.cxx',
|
||||
'src/client/Event.cxx',
|
||||
'src/client/Expire.cxx',
|
||||
'src/client/Idle.cxx',
|
||||
'src/client/List.cxx',
|
||||
'src/client/New.cxx',
|
||||
'src/client/Process.cxx',
|
||||
'src/client/Read.cxx',
|
||||
'src/client/Write.cxx',
|
||||
'src/client/Message.cxx',
|
||||
'src/client/Subscribe.cxx',
|
||||
'src/client/File.cxx',
|
||||
'src/client/Response.cxx',
|
||||
'src/client/ThreadBackgroundCommand.cxx',
|
||||
'src/Listen.cxx',
|
||||
'src/LogInit.cxx',
|
||||
'src/LogBackend.cxx',
|
||||
'src/Log.cxx',
|
||||
'src/ls.cxx',
|
||||
'src/Instance.cxx',
|
||||
'src/win32/Win32Main.cxx',
|
||||
'src/MusicBuffer.cxx',
|
||||
'src/MusicPipe.cxx',
|
||||
'src/MusicChunk.cxx',
|
||||
@@ -262,11 +344,18 @@ sources = [
|
||||
'src/TagSave.cxx',
|
||||
'src/TagFile.cxx',
|
||||
'src/TagStream.cxx',
|
||||
'src/TagAny.cxx',
|
||||
'src/TimePrint.cxx',
|
||||
'src/mixer/Volume.cxx',
|
||||
'src/PlaylistFile.cxx',
|
||||
]
|
||||
|
||||
if is_windows
|
||||
sources += [
|
||||
'src/win32/Win32Main.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
if not is_android
|
||||
sources += [
|
||||
'src/CommandLine.cxx',
|
||||
@@ -275,6 +364,7 @@ if not is_android
|
||||
else
|
||||
sources += [
|
||||
'src/android/Context.cxx',
|
||||
'src/android/AudioManager.cxx',
|
||||
'src/android/Environment.cxx',
|
||||
'src/android/LogListener.cxx',
|
||||
]
|
||||
@@ -293,9 +383,16 @@ if enable_database
|
||||
endif
|
||||
|
||||
subdir('src/util')
|
||||
subdir('src/time')
|
||||
subdir('src/io')
|
||||
subdir('src/io/uring')
|
||||
subdir('src/system')
|
||||
subdir('src/thread')
|
||||
subdir('src/net')
|
||||
subdir('src/event')
|
||||
subdir('src/win32')
|
||||
|
||||
subdir('src/apple')
|
||||
|
||||
subdir('src/lib/dbus')
|
||||
subdir('src/lib/icu')
|
||||
@@ -303,13 +400,14 @@ subdir('src/lib/smbclient')
|
||||
subdir('src/lib/zlib')
|
||||
|
||||
subdir('src/lib/alsa')
|
||||
subdir('src/lib/chromaprint')
|
||||
subdir('src/lib/curl')
|
||||
subdir('src/lib/expat')
|
||||
subdir('src/lib/ffmpeg')
|
||||
subdir('src/lib/gcrypt')
|
||||
subdir('src/lib/wrap')
|
||||
subdir('src/lib/nfs')
|
||||
subdir('src/lib/oss')
|
||||
subdir('src/lib/pcre')
|
||||
subdir('src/lib/pulse')
|
||||
subdir('src/lib/sndio')
|
||||
subdir('src/lib/sqlite')
|
||||
@@ -317,9 +415,10 @@ subdir('src/lib/systemd')
|
||||
subdir('src/lib/upnp')
|
||||
subdir('src/lib/yajl')
|
||||
|
||||
subdir('src/lib/crypto')
|
||||
|
||||
subdir('src/fs')
|
||||
subdir('src/config')
|
||||
subdir('src/net')
|
||||
subdir('src/tag')
|
||||
subdir('src/pcm')
|
||||
subdir('src/neighbor')
|
||||
@@ -342,12 +441,19 @@ endif
|
||||
if sqlite_dep.found()
|
||||
sources += [
|
||||
'src/command/StickerCommands.cxx',
|
||||
'src/sticker/StickerDatabase.cxx',
|
||||
'src/sticker/StickerPrint.cxx',
|
||||
'src/sticker/Database.cxx',
|
||||
'src/sticker/Print.cxx',
|
||||
'src/sticker/SongSticker.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
if chromaprint_dep.found()
|
||||
sources += [
|
||||
'src/command/FingerprintCommands.cxx',
|
||||
'src/lib/chromaprint/DecoderClient.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
basic = static_library(
|
||||
'basic',
|
||||
'src/ReplayGainInfo.cxx',
|
||||
@@ -362,8 +468,10 @@ basic_dep = declare_dependency(
|
||||
|
||||
if enable_database
|
||||
subdir('src/storage')
|
||||
subdir('src/db')
|
||||
else
|
||||
storage_glue_dep = dependency('', required: false)
|
||||
endif
|
||||
subdir('src/db')
|
||||
|
||||
if neighbor_glue_dep.found()
|
||||
sources += 'src/command/NeighborCommands.cxx'
|
||||
@@ -372,8 +480,11 @@ endif
|
||||
if archive_glue_dep.found()
|
||||
sources += [
|
||||
'src/TagArchive.cxx',
|
||||
'src/db/update/Archive.cxx',
|
||||
]
|
||||
|
||||
if enable_database
|
||||
sources += ['src/db/update/Archive.cxx']
|
||||
endif
|
||||
endif
|
||||
|
||||
if is_windows
|
||||
@@ -385,6 +496,7 @@ more_deps = []
|
||||
if is_android
|
||||
subdir('src/java')
|
||||
target_type = 'shared_library'
|
||||
target_name = 'mpd'
|
||||
link_args += [
|
||||
'-Wl,--no-undefined,-shared,-Bsymbolic',
|
||||
'-llog',
|
||||
@@ -394,12 +506,20 @@ if is_android
|
||||
declare_dependency(sources: [classes_jar]),
|
||||
java_dep,
|
||||
]
|
||||
elif is_haiku
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd.nores'
|
||||
link_args += [
|
||||
'-lnetwork',
|
||||
'-lbe',
|
||||
]
|
||||
else
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd'
|
||||
endif
|
||||
|
||||
mpd = build_target(
|
||||
'mpd',
|
||||
target_name,
|
||||
sources,
|
||||
target_type: target_type,
|
||||
include_directories: inc,
|
||||
@@ -426,10 +546,11 @@ mpd = build_target(
|
||||
systemd_dep,
|
||||
sqlite_dep,
|
||||
zeroconf_dep,
|
||||
libwrap_dep,
|
||||
more_deps,
|
||||
chromaprint_dep,
|
||||
],
|
||||
link_args: link_args,
|
||||
build_by_default: not get_option('fuzzer'),
|
||||
install: not is_android and not is_haiku,
|
||||
)
|
||||
|
||||
@@ -439,6 +560,14 @@ endif
|
||||
|
||||
if is_haiku
|
||||
subdir('src/haiku')
|
||||
custom_target(
|
||||
'mpd',
|
||||
output: 'mpd',
|
||||
input: [mpd, rsrc],
|
||||
command: [addres, '@OUTPUT@', '@INPUT0@', '@INPUT1@'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
endif
|
||||
|
||||
configure_file(output: 'config.h', configuration: conf)
|
||||
@@ -457,10 +586,12 @@ install_data(
|
||||
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
|
||||
)
|
||||
|
||||
if get_option('documentation')
|
||||
subdir('doc')
|
||||
endif
|
||||
subdir('doc')
|
||||
|
||||
if get_option('test')
|
||||
subdir('test')
|
||||
endif
|
||||
|
||||
if get_option('fuzzer')
|
||||
subdir('test/fuzzer')
|
||||
endif
|
||||
|
@@ -1,9 +1,10 @@
|
||||
option('documentation', type: 'boolean', value: false, description: 'Build documentation')
|
||||
|
||||
option('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
|
||||
option('documentation', type: 'feature', description: 'Build documentation')
|
||||
option('html_manual', type: 'boolean', value: true, description: 'Build the HTML manual')
|
||||
option('manpages', type: 'boolean', value: true, description: 'Build manual pages')
|
||||
|
||||
option('syslog', type: 'feature', description: 'syslog support')
|
||||
option('inotify', type: 'boolean', value: true, description: 'inotify support (for automatic database update)')
|
||||
option('io_uring', type: 'feature', description: 'Linux io_uring support using liburing')
|
||||
|
||||
option('daemon', type: 'boolean', value: true, description: 'enable daemonization')
|
||||
option('systemd', type: 'feature', description: 'systemd support')
|
||||
@@ -11,6 +12,13 @@ option('systemd', type: 'feature', description: 'systemd support')
|
||||
option('systemd_system_unit_dir', type: 'string', description: 'systemd system service directory')
|
||||
option('systemd_user_unit_dir', type: 'string', description: 'systemd user service directory')
|
||||
|
||||
#
|
||||
# Options for developers
|
||||
#
|
||||
|
||||
option('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
|
||||
option('fuzzer', type: 'boolean', value: false, description: 'Build fuzzers (requires libFuzzer)')
|
||||
|
||||
#
|
||||
# Android
|
||||
#
|
||||
@@ -84,7 +92,11 @@ option('cdio_paranoia', type: 'feature', description: 'libcdio_paranoia input pl
|
||||
option('curl', type: 'feature', description: 'HTTP client using CURL')
|
||||
option('mms', type: 'feature', description: 'MMS protocol support using libmms')
|
||||
option('nfs', type: 'feature', description: 'NFS protocol support using libnfs')
|
||||
option('smbclient', type: 'feature', description: 'SMB support using libsmbclient')
|
||||
|
||||
# The "smbclient" plugin is disabled by default because libsmbclient
|
||||
# has a serious bug which crashes MPD very quickly:
|
||||
# https://bugzilla.samba.org/show_bug.cgi?id=11413
|
||||
option('smbclient', type: 'feature', value: 'disabled', description: 'SMB support using libsmbclient')
|
||||
|
||||
#
|
||||
# Commercial services
|
||||
@@ -92,7 +104,6 @@ option('smbclient', type: 'feature', description: 'SMB support using libsmbclien
|
||||
|
||||
option('qobuz', type: 'feature', description: 'Qobuz client')
|
||||
option('soundcloud', type: 'feature', description: 'SoundCloud client')
|
||||
option('tidal', type: 'feature', description: 'Tidal client')
|
||||
|
||||
#
|
||||
# Archive plugins
|
||||
@@ -128,12 +139,13 @@ option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
|
||||
option('opus', type: 'feature', description: 'Opus decoder plugin')
|
||||
option('sidplay', type: 'feature', description: 'C64 SID support via libsidplayfp or libsidplay2')
|
||||
option('sndfile', type: 'feature', description: 'libsndfile decoder plugin')
|
||||
option('tremor', type: 'feature', description: 'Fixed-point vorbis decoder plugin')
|
||||
option('vorbis', type: 'feature', description: 'Vorbis decoder plugin')
|
||||
option('wavpack', type: 'feature', description: 'WavPack decoder plugin')
|
||||
option('wildmidi', type: 'feature', description: 'WildMidi decoder plugin')
|
||||
|
||||
#
|
||||
# Decoder plugins
|
||||
# Encoder plugins
|
||||
#
|
||||
|
||||
option('vorbisenc', type: 'feature', description: 'Vorbis encoder plugin')
|
||||
@@ -175,7 +187,7 @@ option('dbus', type: 'feature', description: 'D-Bus support')
|
||||
option('expat', type: 'feature', description: 'Expat XML support')
|
||||
option('icu', type: 'feature', description: 'Use libicu for Unicode')
|
||||
option('iconv', type: 'feature', description: 'Use iconv() for character set conversion')
|
||||
option('libwrap', type: 'feature', description: 'libwrap support')
|
||||
option('pcre', type: 'feature', description: 'Enable regular expression support (using libpcre)')
|
||||
option('sqlite', type: 'feature', description: 'SQLite database support (for stickers)')
|
||||
option('yajl', type: 'feature', description: 'libyajl for YAML support')
|
||||
option('zlib', type: 'feature', description: 'zlib support (for database compression)')
|
||||
|
@@ -21,3 +21,8 @@ class BoostProject(Project):
|
||||
dest = os.path.join(includedir, 'boost')
|
||||
shutil.rmtree(dest, ignore_errors=True)
|
||||
shutil.copytree(os.path.join(src, 'boost'), dest)
|
||||
|
||||
# touch the boost/version.hpp file to ensure it's newer than
|
||||
# the downloaded Boost tarball, to avoid reinstalling Boost on
|
||||
# every run
|
||||
os.utime(os.path.join(toolchain.install_prefix, self.installed))
|
||||
|
45
python/build/cmake.py
Normal file
45
python/build/cmake.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import subprocess
|
||||
|
||||
from build.project import Project
|
||||
|
||||
def configure(toolchain, src, build, args=()):
|
||||
cross_args = []
|
||||
|
||||
if toolchain.is_windows:
|
||||
cross_args.append('-DCMAKE_SYSTEM_NAME=Windows')
|
||||
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
|
||||
|
||||
configure = [
|
||||
'cmake',
|
||||
src,
|
||||
|
||||
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
|
||||
'-DCMAKE_BUILD_TYPE=release',
|
||||
|
||||
'-DCMAKE_C_COMPILER=' + toolchain.cc,
|
||||
'-DCMAKE_CXX_COMPILER=' + toolchain.cxx,
|
||||
|
||||
'-DCMAKE_C_FLAGS=' + toolchain.cflags + ' ' + toolchain.cppflags,
|
||||
'-DCMAKE_CXX_FLAGS=' + toolchain.cxxflags + ' ' + toolchain.cppflags,
|
||||
|
||||
'-GNinja',
|
||||
] + cross_args + args
|
||||
|
||||
subprocess.check_call(configure, env=toolchain.env, cwd=build)
|
||||
|
||||
class CmakeProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
|
||||
def configure(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
configure(toolchain, src, build, self.configure_args)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
@@ -10,11 +10,6 @@ class FfmpegProject(Project):
|
||||
self.configure_args = configure_args
|
||||
self.cppflags = cppflags
|
||||
|
||||
def _filter_cflags(self, flags):
|
||||
# FFmpeg expects the GNU as syntax
|
||||
flags = flags.replace(' -integrated-as ', ' -no-integrated-as ')
|
||||
return flags
|
||||
|
||||
def build(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
@@ -36,8 +31,8 @@ class FfmpegProject(Project):
|
||||
'--cc=' + toolchain.cc,
|
||||
'--cxx=' + toolchain.cxx,
|
||||
'--nm=' + toolchain.nm,
|
||||
'--extra-cflags=' + self._filter_cflags(toolchain.cflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'--extra-cxxflags=' + self._filter_cflags(toolchain.cxxflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'--extra-cflags=' + toolchain.cflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'--extra-cxxflags=' + toolchain.cxxflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'--extra-ldflags=' + toolchain.ldflags,
|
||||
'--extra-libs=' + toolchain.libs,
|
||||
'--ar=' + toolchain.ar,
|
||||
|
47
python/build/jack.py
Normal file
47
python/build/jack.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os, shutil
|
||||
import re
|
||||
|
||||
from .project import Project
|
||||
|
||||
# This class installs just the public headers and a fake pkg-config
|
||||
# file which defines the macro "DYNAMIC_JACK". This tells MPD's JACK
|
||||
# output plugin to load the libjack64.dll dynamically using
|
||||
# LoadLibrary(). This kludge avoids the runtime DLL dependency for
|
||||
# users who don't use JACK, but still allows using the system JACK
|
||||
# client library.
|
||||
#
|
||||
# The problem with JACK is that it uses an extremely fragile shared
|
||||
# memory protocol to communicate with the daemon. One needs to use
|
||||
# daemon and client library from the same build. That's why we don't
|
||||
# build libjack statically here; it would probably not be compatible
|
||||
# with the user's JACK daemon.
|
||||
|
||||
class JackProject(Project):
|
||||
def __init__(self, url, md5, installed,
|
||||
**kwargs):
|
||||
m = re.match(r'.*/v([\d.]+)\.tar\.gz$', url)
|
||||
self.version = m.group(1)
|
||||
Project.__init__(self, url, md5, installed,
|
||||
name='jack2', version=self.version,
|
||||
base='jack2-' + self.version,
|
||||
**kwargs)
|
||||
|
||||
def build(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
|
||||
includes = ['jack.h', 'ringbuffer.h', 'systemdeps.h', 'transport.h', 'types.h', 'weakmacros.h']
|
||||
includedir = os.path.join(toolchain.install_prefix, 'include', 'jack')
|
||||
os.makedirs(includedir, exist_ok=True)
|
||||
|
||||
for i in includes:
|
||||
shutil.copyfile(os.path.join(src, 'common', 'jack', i),
|
||||
os.path.join(includedir, i))
|
||||
|
||||
with open(os.path.join(toolchain.install_prefix, 'lib', 'pkgconfig', 'jack.pc'), 'w') as f:
|
||||
print("prefix=" + toolchain.install_prefix, file=f)
|
||||
print("", file=f)
|
||||
print("Name: jack", file=f)
|
||||
print("Description: dummy", file=f)
|
||||
print("Version: " + self.version, file=f)
|
||||
print("Libs: ", file=f)
|
||||
print("Cflags: -DDYNAMIC_JACK", file=f)
|
@@ -4,19 +4,22 @@ from os.path import abspath
|
||||
from build.project import Project
|
||||
from build.zlib import ZlibProject
|
||||
from build.meson import MesonProject
|
||||
from build.cmake import CmakeProject
|
||||
from build.autotools import AutotoolsProject
|
||||
from build.ffmpeg import FfmpegProject
|
||||
from build.openssl import OpenSSLProject
|
||||
from build.boost import BoostProject
|
||||
from build.jack import JackProject
|
||||
|
||||
libmpdclient = MesonProject(
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.16.tar.xz',
|
||||
'fa6bdab67c0e0490302b38f00c27b4959735c3ec8aef7a88327adb1407654464',
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz',
|
||||
'158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189',
|
||||
'lib/libmpdclient.a',
|
||||
)
|
||||
|
||||
libogg = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz',
|
||||
'4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08',
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
|
||||
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
|
||||
'lib/libogg.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -24,8 +27,8 @@ libogg = AutotoolsProject(
|
||||
)
|
||||
|
||||
libvorbis = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
|
||||
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz',
|
||||
'b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b',
|
||||
'lib/libvorbis.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -38,8 +41,8 @@ libvorbis = AutotoolsProject(
|
||||
)
|
||||
|
||||
opus = AutotoolsProject(
|
||||
'https://archive.mozilla.org/pub/opus/opus-1.3.tar.gz',
|
||||
'4f3d69aefdf2dbaf9825408e452a8a414ffc60494c70633560700398820dc550',
|
||||
'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
|
||||
'65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
|
||||
'lib/libopus.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -52,8 +55,8 @@ opus = AutotoolsProject(
|
||||
)
|
||||
|
||||
flac = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.3.2.tar.xz',
|
||||
'91cfc3ed61dc40f47f050a109b08610667d73477af6ef36dcad31c31a4a8d53f',
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
|
||||
'213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
|
||||
'lib/libFLAC.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -111,9 +114,44 @@ liblame = AutotoolsProject(
|
||||
],
|
||||
)
|
||||
|
||||
libmodplug = AutotoolsProject(
|
||||
'https://downloads.sourceforge.net/modplug-xmms/libmodplug/0.8.9.0/libmodplug-0.8.9.0.tar.gz',
|
||||
'457ca5a6c179656d66c01505c0d95fafaead4329b9dbaa0f997d00a3508ad9de',
|
||||
'lib/libmodplug.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
)
|
||||
|
||||
wildmidi = CmakeProject(
|
||||
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
|
||||
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
|
||||
'lib/libWildMidi.a',
|
||||
[
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DWANT_PLAYER=OFF',
|
||||
'-DWANT_STATIC=ON',
|
||||
],
|
||||
base='wildmidi-wildmidi-0.4.3',
|
||||
name='wildmidi',
|
||||
version='0.4.3',
|
||||
)
|
||||
|
||||
gme = CmakeProject(
|
||||
'https://bitbucket.org/mpyne/game-music-emu/downloads/game-music-emu-0.6.3.tar.xz',
|
||||
'aba34e53ef0ec6a34b58b84e28bf8cfbccee6585cebca25333604c35db3e051d',
|
||||
'lib/libgme.a',
|
||||
[
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DENABLE_UBSAN=OFF',
|
||||
'-DZLIB_INCLUDE_DIR=OFF',
|
||||
'-DSDL2_DIR=OFF',
|
||||
],
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.0.2.tar.xz',
|
||||
'a95c0cc9eb990e94031d2183f2e6e444cc61c99f6f182d1575c433d62afb2f97',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.4.tar.xz',
|
||||
'06b10a183ce5371f915c6bb15b7b1fffbe046e8275099c96affc29e17645d909',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -340,9 +378,15 @@ ffmpeg = FfmpegProject(
|
||||
],
|
||||
)
|
||||
|
||||
openssl = OpenSSLProject(
|
||||
'https://www.openssl.org/source/openssl-3.0.0-beta2.tar.gz',
|
||||
'e76ab22879201b12f014393ee4becec7f264d8f6955b1036839128002868df71',
|
||||
'include/openssl/ossl_typ.h',
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.61.1.tar.xz',
|
||||
'3d5913d6a39bd22e68e34dff697fd6e4c3c81563f580c76fca2009315cd81891',
|
||||
'https://curl.se/download/curl-7.78.0.tar.xz',
|
||||
'be42766d5664a739c3974ee3dfbbcbe978a4ccb1fe628bb1d9b59ac79e445fb5',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -358,15 +402,23 @@ curl = AutotoolsProject(
|
||||
'--disable-manual',
|
||||
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
|
||||
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
|
||||
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
|
||||
'--disable-doh',
|
||||
'--disable-mime',
|
||||
'--disable-netrc',
|
||||
'--disable-progress-meter',
|
||||
'--disable-alt-svc',
|
||||
'--without-gnutls', '--without-nss', '--without-libssh2',
|
||||
|
||||
# native Windows SSL/TLS support, option ignored on non-Windows builds
|
||||
'--with-schannel',
|
||||
],
|
||||
|
||||
patches='src/lib/curl/patches',
|
||||
)
|
||||
|
||||
libexpat = AutotoolsProject(
|
||||
'https://github.com/libexpat/libexpat/releases/download/R_2_2_6/expat-2.2.6.tar.bz2',
|
||||
'17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2',
|
||||
'https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.bz2',
|
||||
'f1063084dc4302a427dabcca499c8312b3a32a29b7d2506653ecc8f950a9a237',
|
||||
'lib/libexpat.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -375,8 +427,8 @@ libexpat = AutotoolsProject(
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz',
|
||||
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5',
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
|
||||
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -387,12 +439,19 @@ libnfs = AutotoolsProject(
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-3.0.0',
|
||||
base='libnfs-libnfs-4.0.0',
|
||||
patches='src/lib/nfs/patches',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
jack = JackProject(
|
||||
'https://github.com/jackaudio/jack2/archive/v1.9.17.tar.gz',
|
||||
'38f674bbc57852a8eb3d9faa1f96a0912d26f7d5df14c11005ad499c8ae352f2',
|
||||
'lib/pkgconfig/jack.pc',
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2',
|
||||
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7',
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2',
|
||||
'f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
@@ -91,7 +91,12 @@ def configure(toolchain, src, build, args=()):
|
||||
'--cross-file', cross_file,
|
||||
] + args
|
||||
|
||||
subprocess.check_call(configure, env=toolchain.env)
|
||||
env = toolchain.env.copy()
|
||||
|
||||
# Meson 0.54 requires the BOOST_ROOT environment variable
|
||||
env['BOOST_ROOT'] = toolchain.install_prefix
|
||||
|
||||
subprocess.check_call(configure, env=env)
|
||||
|
||||
class MesonProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
|
61
python/build/openssl.py
Normal file
61
python/build/openssl.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import subprocess
|
||||
|
||||
from build.makeproject import MakeProject
|
||||
|
||||
class OpenSSLProject(MakeProject):
|
||||
def __init__(self, url, md5, installed,
|
||||
**kwargs):
|
||||
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
|
||||
|
||||
def get_make_args(self, toolchain):
|
||||
return MakeProject.get_make_args(self, toolchain) + [
|
||||
'CC=' + toolchain.cc,
|
||||
'CFLAGS=' + toolchain.cflags,
|
||||
'CPPFLAGS=' + toolchain.cppflags,
|
||||
'AR=' + toolchain.ar,
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
'build_libs',
|
||||
]
|
||||
|
||||
def get_make_install_args(self, toolchain):
|
||||
# OpenSSL's Makefile runs "ranlib" during installation
|
||||
return MakeProject.get_make_install_args(self, toolchain) + [
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
]
|
||||
|
||||
def build(self, toolchain):
|
||||
src = self.unpack(toolchain, out_of_tree=False)
|
||||
|
||||
# OpenSSL has a weird target architecture scheme with lots of
|
||||
# hard-coded architectures; this table translates between our
|
||||
# "toolchain_arch" (HOST_TRIPLET) and the OpenSSL target
|
||||
openssl_archs = {
|
||||
# not using "android-*" because those OpenSSL targets want
|
||||
# to know where the SDK is, but our own build scripts
|
||||
# prepared everything already to look like a regular Linux
|
||||
# build
|
||||
'arm-linux-androideabi': 'linux-generic32',
|
||||
'aarch64-linux-android': 'linux-aarch64',
|
||||
'i686-linux-android': 'linux-x86-clang',
|
||||
'x86_64-linux-android': 'linux-x86_64-clang',
|
||||
|
||||
# Kobo
|
||||
'arm-linux-gnueabihf': 'linux-generic32',
|
||||
|
||||
# Windows
|
||||
'i686-w64-mingw32': 'mingw',
|
||||
'x86_64-w64-mingw32': 'mingw64',
|
||||
}
|
||||
|
||||
openssl_arch = openssl_archs[toolchain.arch]
|
||||
|
||||
subprocess.check_call(['./Configure',
|
||||
'no-shared',
|
||||
'no-module', 'no-engine', 'no-static-engine',
|
||||
'no-async',
|
||||
'no-tests',
|
||||
'no-asm', # "asm" causes build failures on Windows
|
||||
openssl_arch,
|
||||
'--prefix=' + toolchain.install_prefix],
|
||||
cwd=src, env=toolchain.env)
|
||||
MakeProject.build(self, toolchain, src)
|
@@ -20,7 +20,7 @@ class Project:
|
||||
self.base = base
|
||||
|
||||
if name is None or version is None:
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)$', self.base)
|
||||
if name is None: name = m.group(1)
|
||||
if version is None: version = m.group(2)
|
||||
|
||||
|
@@ -7,5 +7,11 @@ def untar(tarball_path, parent_path, base):
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.makedirs(parent_path, exist_ok=True)
|
||||
try:
|
||||
subprocess.check_call(['/bin/tar', 'xfC', tarball_path, parent_path])
|
||||
except FileNotFoundError:
|
||||
import tarfile
|
||||
tar = tarfile.open(tarball_path)
|
||||
tar.extractall(path=parent_path)
|
||||
tar.close()
|
||||
return path
|
||||
|
@@ -18,5 +18,5 @@ class ZlibProject(Project):
|
||||
'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'),
|
||||
'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'),
|
||||
'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'),
|
||||
'SHARED_MODE=1'],
|
||||
],
|
||||
cwd=src, env=toolchain.env)
|
||||
|
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# This script verifies that every source includes config.h first.
|
||||
# This is very important for consistent Large File Support.
|
||||
#
|
||||
|
||||
def check_file(file)
|
||||
first = true
|
||||
file.each_line do |line|
|
||||
if line =~ /^\#include\s+(\S+)/ then
|
||||
if $1 == '"config.h"'
|
||||
unless first
|
||||
puts "#{file.path}: config.h included too late"
|
||||
end
|
||||
else
|
||||
if first
|
||||
puts "#{file.path}: config.h missing"
|
||||
end
|
||||
end
|
||||
first = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_path(path)
|
||||
File.open(path) do |file|
|
||||
check_file(file)
|
||||
end
|
||||
end
|
||||
|
||||
if ARGV.empty?
|
||||
Dir["src/*.c"].each do |path|
|
||||
check_path(path)
|
||||
end
|
||||
|
||||
Dir["src/*/*.c"].each do |path|
|
||||
check_path(path)
|
||||
end
|
||||
|
||||
Dir["test/*.c"].each do |path|
|
||||
check_path(path)
|
||||
end
|
||||
else
|
||||
ARGV.each do |path|
|
||||
check_path(path)
|
||||
end
|
||||
end
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
|
||||
constexpr double ToDoubleS() const {
|
||||
return double(count()) / 1000.;
|
||||
};
|
||||
}
|
||||
|
||||
constexpr bool IsZero() const {
|
||||
return count() == 0;
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
|
||||
constexpr double ToDoubleS() const {
|
||||
return double(count()) / 1000.;
|
||||
};
|
||||
}
|
||||
|
||||
constexpr bool IsZero() const {
|
||||
return count() == 0;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,15 +33,19 @@
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "playlist/PlaylistPlugin.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/Macros.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "io/uring/Features.h"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/OptionDef.hxx"
|
||||
#include "util/OptionParser.hxx"
|
||||
#include "Version.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "system/Error.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/Registry.hxx"
|
||||
@@ -55,6 +59,7 @@
|
||||
#include "neighbor/NeighborPlugin.hxx"
|
||||
#endif
|
||||
|
||||
#include "encoder/Features.h"
|
||||
#ifdef ENABLE_ENCODER
|
||||
#include "encoder/EncoderList.hxx"
|
||||
#include "encoder/EncoderPlugin.hxx"
|
||||
@@ -65,9 +70,6 @@
|
||||
#include "archive/ArchivePlugin.hxx"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
constexpr auto CONFIG_FILE_LOCATION = Path::FromFS(PATH_LITERAL("mpd\\mpd.conf"));
|
||||
@@ -77,7 +79,7 @@ constexpr auto USER_CONFIG_FILE_LOCATION1 = Path::FromFS(PATH_LITERAL(".mpdconf"
|
||||
constexpr auto USER_CONFIG_FILE_LOCATION2 = Path::FromFS(PATH_LITERAL(".mpd/mpd.conf"));
|
||||
constexpr auto USER_CONFIG_FILE_LOCATION_XDG = Path::FromFS(PATH_LITERAL("mpd/mpd.conf"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum Option {
|
||||
OPTION_KILL,
|
||||
@@ -105,20 +107,20 @@ static constexpr OptionDef option_defs[] = {
|
||||
|
||||
static constexpr Domain cmdline_domain("cmdline");
|
||||
|
||||
gcc_noreturn
|
||||
static void version(void)
|
||||
[[noreturn]]
|
||||
static void version()
|
||||
{
|
||||
printf("Music Player Daemon " VERSION " (%s)\n"
|
||||
printf("Music Player Daemon " VERSION " (%s)"
|
||||
"\n"
|
||||
"Copyright 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>\n"
|
||||
"Copyright 2008-2021 Max Kellermann <max.kellermann@gmail.com>\n"
|
||||
"This is free software; see the source for copying conditions. There is NO\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
||||
GIT_VERSION);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
"\n"
|
||||
"Database plugins:\n",
|
||||
GIT_VERSION);
|
||||
printf("\n"
|
||||
"Database plugins:\n");
|
||||
|
||||
for (auto i = database_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
@@ -129,18 +131,18 @@ static void version(void)
|
||||
for (auto i = storage_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
|
||||
printf("\n"
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
"\n"
|
||||
printf("\n"
|
||||
"Neighbor plugins:\n");
|
||||
for (auto i = neighbor_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
|
||||
printf("\n"
|
||||
#endif
|
||||
|
||||
printf("\n"
|
||||
"\n"
|
||||
"Decoders plugins:\n");
|
||||
|
||||
@@ -152,6 +154,10 @@ static void version(void)
|
||||
for (; *suffixes != nullptr; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
if (plugin.protocols != nullptr)
|
||||
for (const auto &i : plugin.protocols())
|
||||
printf(" %s", i.c_str());
|
||||
|
||||
printf("\n");
|
||||
});
|
||||
|
||||
@@ -202,6 +208,9 @@ static void version(void)
|
||||
"\n"
|
||||
"Input plugins:\n"
|
||||
" file"
|
||||
#ifdef HAVE_URING
|
||||
" io_uring"
|
||||
#endif
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
" archive"
|
||||
#endif
|
||||
@@ -255,7 +264,7 @@ static void version(void)
|
||||
#endif
|
||||
"\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void PrintOption(const OptionDef &opt)
|
||||
@@ -271,8 +280,8 @@ static void PrintOption(const OptionDef &opt)
|
||||
opt.GetDescription());
|
||||
}
|
||||
|
||||
gcc_noreturn
|
||||
static void help(void)
|
||||
[[noreturn]]
|
||||
static void help()
|
||||
{
|
||||
printf("Usage:\n"
|
||||
" mpd [OPTION...] [path/to/mpd.conf]\n"
|
||||
@@ -282,10 +291,10 @@ static void help(void)
|
||||
"Options:\n");
|
||||
|
||||
for (const auto &i : option_defs)
|
||||
if(i.HasDescription() == true) // hide hidden options from help print
|
||||
if(i.HasDescription()) // hide hidden options from help print
|
||||
PrintOption(i);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
class ConfigLoader
|
||||
@@ -296,7 +305,7 @@ public:
|
||||
explicit ConfigLoader(ConfigData &_config) noexcept
|
||||
:config(_config) {}
|
||||
|
||||
bool TryFile(const Path path);
|
||||
bool TryFile(Path path);
|
||||
bool TryFile(const AllocatedPath &base_path, Path path);
|
||||
};
|
||||
|
||||
@@ -380,17 +389,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
|
||||
|
||||
if (config_file != nullptr) {
|
||||
/* use specified configuration file */
|
||||
#ifdef _UNICODE
|
||||
wchar_t buffer[MAX_PATH];
|
||||
auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1,
|
||||
buffer, ARRAY_SIZE(buffer));
|
||||
if (result <= 0)
|
||||
throw MakeLastError("MultiByteToWideChar() failed");
|
||||
|
||||
ReadConfigFile(config, Path::FromFS(buffer));
|
||||
#else
|
||||
ReadConfigFile(config, Path::FromFS(config_file));
|
||||
#endif
|
||||
ReadConfigFile(config, FromNarrowPath(config_file));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,4 +19,4 @@
|
||||
|
||||
#include "GitVersion.hxx"
|
||||
|
||||
char GIT_VERSION[] = "@VCS_TAG@";
|
||||
const char GIT_VERSION[] = "@VCS_TAG@";
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,6 +20,6 @@
|
||||
#ifndef MPD_GIT_VERSION_HXX
|
||||
#define MPD_GIT_VERSION_HXX
|
||||
|
||||
extern char GIT_VERSION[];
|
||||
extern const char GIT_VERSION[];
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,18 +17,25 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "IcyMetaDataParser.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/AllocatedString.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static constexpr Domain icy_metadata_domain("icy_metadata");
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
|
||||
void
|
||||
IcyMetaDataParser::SetCharset(const char *charset)
|
||||
{
|
||||
icu_converter = IcuConverter::Create(charset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
IcyMetaDataParser::Reset() noexcept
|
||||
@@ -66,29 +73,40 @@ IcyMetaDataParser::Data(size_t length) noexcept
|
||||
}
|
||||
|
||||
static void
|
||||
icy_add_item(TagBuilder &tag, TagType type, const char *value) noexcept
|
||||
icy_add_item(TagBuilder &tag, TagType type, StringView value) noexcept
|
||||
{
|
||||
size_t length = strlen(value);
|
||||
|
||||
if (length >= 2 && value[0] == '\'' && value[length - 1] == '\'') {
|
||||
if (value.size >= 2 && value.front() == '\'' && value.back() == '\'') {
|
||||
/* strip the single quotes */
|
||||
++value;
|
||||
length -= 2;
|
||||
++value.data;
|
||||
value.size -= 2;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
tag.AddItem(type, {value, length});
|
||||
if (value.size > 0)
|
||||
tag.AddItem(type, value);
|
||||
}
|
||||
|
||||
static void
|
||||
icy_parse_tag_item(TagBuilder &tag,
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
const IcuConverter *icu_converter,
|
||||
#endif
|
||||
const char *name, const char *value) noexcept
|
||||
{
|
||||
if (strcmp(name, "StreamTitle") == 0)
|
||||
if (strcmp(name, "StreamTitle") == 0) {
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
if (icu_converter != nullptr) {
|
||||
try {
|
||||
icy_add_item(tag, TAG_TITLE,
|
||||
icu_converter->ToUTF8(value).c_str());
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
icy_add_item(tag, TAG_TITLE, value);
|
||||
else
|
||||
FormatDebug(icy_metadata_domain,
|
||||
"unknown icy-tag: '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +135,11 @@ find_end_quote(char *p, char *const end) noexcept
|
||||
}
|
||||
|
||||
static std::unique_ptr<Tag>
|
||||
icy_parse_tag(char *p, char *const end) noexcept
|
||||
icy_parse_tag(
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
const IcuConverter *icu_converter,
|
||||
#endif
|
||||
char *p, char *const end) noexcept
|
||||
{
|
||||
assert(p != nullptr);
|
||||
assert(end != nullptr);
|
||||
@@ -154,7 +176,11 @@ icy_parse_tag(char *p, char *const end) noexcept
|
||||
*quote = 0;
|
||||
p = quote + 1;
|
||||
|
||||
icy_parse_tag_item(tag, name, value);
|
||||
icy_parse_tag_item(tag,
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
icu_converter,
|
||||
#endif
|
||||
name, value);
|
||||
|
||||
char *semicolon = std::find(p, end, ';');
|
||||
if (semicolon == end)
|
||||
@@ -168,7 +194,7 @@ icy_parse_tag(char *p, char *const end) noexcept
|
||||
size_t
|
||||
IcyMetaDataParser::Meta(const void *data, size_t length) noexcept
|
||||
{
|
||||
const unsigned char *p = (const unsigned char *)data;
|
||||
const auto *p = (const unsigned char *)data;
|
||||
|
||||
assert(IsDefined());
|
||||
assert(data_rest == 0);
|
||||
@@ -209,7 +235,11 @@ IcyMetaDataParser::Meta(const void *data, size_t length) noexcept
|
||||
if (meta_position == meta_size) {
|
||||
/* parse */
|
||||
|
||||
tag = icy_parse_tag(meta_data, meta_data + meta_size);
|
||||
tag = icy_parse_tag(
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
icu_converter.get(),
|
||||
#endif
|
||||
meta_data, meta_data + meta_size);
|
||||
delete[] meta_data;
|
||||
|
||||
/* change back to normal data mode */
|
||||
@@ -224,7 +254,7 @@ IcyMetaDataParser::Meta(const void *data, size_t length) noexcept
|
||||
size_t
|
||||
IcyMetaDataParser::ParseInPlace(void *data, size_t length) noexcept
|
||||
{
|
||||
uint8_t *const dest0 = (uint8_t *)data;
|
||||
auto *const dest0 = (uint8_t *)data;
|
||||
uint8_t *dest = dest0;
|
||||
const uint8_t *src = dest0;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,18 +20,23 @@
|
||||
#ifndef MPD_ICY_META_DATA_PARSER_HXX
|
||||
#define MPD_ICY_META_DATA_PARSER_HXX
|
||||
|
||||
#include "lib/icu/Converter.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "config.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
class IcyMetaDataParser {
|
||||
size_t data_size = 0, data_rest;
|
||||
|
||||
size_t meta_size, meta_position;
|
||||
char *meta_data;
|
||||
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
std::unique_ptr<IcuConverter> icu_converter;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Tag> tag;
|
||||
|
||||
public:
|
||||
@@ -39,6 +44,13 @@ public:
|
||||
Reset();
|
||||
}
|
||||
|
||||
#ifdef HAVE_ICU_CONVERTER
|
||||
/**
|
||||
* Throws on error.
|
||||
*/
|
||||
void SetCharset(const char *charset);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize an enabled icy_metadata object with the specified
|
||||
* data_size (from the icy-metaint HTTP response header).
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,17 +22,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "Idle.hxx"
|
||||
#include "Main.hxx"
|
||||
#include "Instance.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
void
|
||||
idle_add(unsigned flags)
|
||||
{
|
||||
assert(flags != 0);
|
||||
|
||||
instance->EmitIdle(flags);
|
||||
global_instance->EmitIdle(flags);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,11 +22,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
static const char *const idle_names[] = {
|
||||
"database",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,25 +20,33 @@
|
||||
#include "config.h"
|
||||
#include "Instance.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "StateFile.hxx"
|
||||
#include "Stats.hxx"
|
||||
#include "client/List.hxx"
|
||||
#include "input/cache/Manager.hxx"
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
#include "RemoteTagCache.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseError.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
#include "neighbor/Glue.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "sticker/StickerDatabase.hxx"
|
||||
#include "sticker/Database.hxx"
|
||||
#include "sticker/SongSticker.hxx"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
|
||||
Instance::Instance()
|
||||
:rtio_thread(true),
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
@@ -48,7 +56,26 @@ Instance::Instance()
|
||||
{
|
||||
}
|
||||
|
||||
Instance::~Instance() noexcept = default;
|
||||
Instance::~Instance() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
delete update;
|
||||
|
||||
if (database != nullptr) {
|
||||
database->Close();
|
||||
database.reset();
|
||||
}
|
||||
|
||||
delete storage;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnStateModified() noexcept
|
||||
{
|
||||
if (state_file)
|
||||
state_file->CheckModified();
|
||||
}
|
||||
|
||||
Partition *
|
||||
Instance::FindPartition(const char *name) noexcept
|
||||
@@ -60,6 +87,20 @@ Instance::FindPartition(const char *name) noexcept
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Instance::DeletePartition(Partition &partition) noexcept
|
||||
{
|
||||
// TODO: use boost::intrusive::list to avoid this loop
|
||||
for (auto i = partitions.begin();; ++i) {
|
||||
assert(i != partitions.end());
|
||||
|
||||
if (&*i == &partition) {
|
||||
partitions.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
const Database &
|
||||
@@ -73,7 +114,7 @@ Instance::GetDatabaseOrThrow() const
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnDatabaseModified()
|
||||
Instance::OnDatabaseModified() noexcept
|
||||
{
|
||||
assert(database != nullptr);
|
||||
|
||||
@@ -86,15 +127,15 @@ Instance::OnDatabaseModified()
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnDatabaseSongRemoved(const char *uri)
|
||||
Instance::OnDatabaseSongRemoved(const char *uri) noexcept
|
||||
{
|
||||
assert(database != nullptr);
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
/* if the song has a sticker, remove it */
|
||||
if (sticker_enabled()) {
|
||||
if (HasStickerDatabase()) {
|
||||
try {
|
||||
sticker_song_delete(uri);
|
||||
sticker_song_delete(*sticker_database, uri);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
@@ -109,17 +150,15 @@ Instance::OnDatabaseSongRemoved(const char *uri)
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
|
||||
void
|
||||
Instance::FoundNeighbor(gcc_unused const NeighborInfo &info) noexcept
|
||||
Instance::FoundNeighbor([[maybe_unused]] const NeighborInfo &info) noexcept
|
||||
{
|
||||
for (auto &partition : partitions)
|
||||
partition.EmitIdle(IDLE_NEIGHBOR);
|
||||
EmitIdle(IDLE_NEIGHBOR);
|
||||
}
|
||||
|
||||
void
|
||||
Instance::LostNeighbor(gcc_unused const NeighborInfo &info) noexcept
|
||||
Instance::LostNeighbor([[maybe_unused]] const NeighborInfo &info) noexcept
|
||||
{
|
||||
for (auto &partition : partitions)
|
||||
partition.EmitIdle(IDLE_NEIGHBOR);
|
||||
EmitIdle(IDLE_NEIGHBOR);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -151,3 +190,18 @@ Instance::OnRemoteTag(const char *uri, const Tag &tag) noexcept
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
Instance::OnIdle(unsigned flags) noexcept
|
||||
{
|
||||
/* broadcast to all partitions */
|
||||
for (auto &partition : partitions)
|
||||
partition.EmitIdle(flags);
|
||||
}
|
||||
|
||||
void
|
||||
Instance::FlushCaches() noexcept
|
||||
{
|
||||
if (input_cache)
|
||||
input_cache->Flush();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_INSTANCE_HXX
|
||||
#define MPD_INSTANCE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "config.h"
|
||||
#include "event/Loop.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
#include "event/MaskMonitor.hxx"
|
||||
@@ -41,7 +41,7 @@ class NeighborGlue;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseListener.hxx"
|
||||
class Database;
|
||||
#include "db/Ptr.hxx"
|
||||
class Storage;
|
||||
class UpdateService;
|
||||
#endif
|
||||
@@ -53,6 +53,8 @@ class ClientList;
|
||||
struct Partition;
|
||||
class StateFile;
|
||||
class RemoteTagCache;
|
||||
class StickerDatabase;
|
||||
class InputCacheManager;
|
||||
|
||||
/**
|
||||
* A utility class which, when used as the first base class, ensures
|
||||
@@ -97,14 +99,20 @@ struct Instance final
|
||||
Systemd::Watchdog systemd_watchdog;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<InputCacheManager> input_cache;
|
||||
|
||||
/**
|
||||
* Monitor for global idle events to be broadcasted to all
|
||||
* partitions.
|
||||
*/
|
||||
MaskMonitor idle_monitor;
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
NeighborGlue *neighbors;
|
||||
std::unique_ptr<NeighborGlue> neighbors;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
Database *database;
|
||||
DatabasePtr database;
|
||||
|
||||
/**
|
||||
* This is really a #CompositeStorage. To avoid heavy include
|
||||
@@ -119,11 +127,15 @@ struct Instance final
|
||||
std::unique_ptr<RemoteTagCache> remote_tag_cache;
|
||||
#endif
|
||||
|
||||
ClientList *client_list;
|
||||
std::unique_ptr<ClientList> client_list;
|
||||
|
||||
std::list<Partition> partitions;
|
||||
|
||||
StateFile *state_file = nullptr;
|
||||
std::unique_ptr<StateFile> state_file;
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
std::unique_ptr<StickerDatabase> sticker_database;
|
||||
#endif
|
||||
|
||||
Instance();
|
||||
~Instance() noexcept;
|
||||
@@ -131,14 +143,27 @@ struct Instance final
|
||||
/**
|
||||
* Wrapper for EventLoop::Break(). Call to initiate shutdown.
|
||||
*/
|
||||
void Break() {
|
||||
void Break() noexcept {
|
||||
event_loop.Break();
|
||||
}
|
||||
|
||||
void EmitIdle(unsigned mask) {
|
||||
/**
|
||||
* Emit an "idle" event to all clients of all partitions.
|
||||
*
|
||||
* This method can be called from any thread.
|
||||
*/
|
||||
void EmitIdle(unsigned mask) noexcept {
|
||||
idle_monitor.OrMask(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the #Instance that the state has been modified, and
|
||||
* the #StateFile may need to be saved.
|
||||
*
|
||||
* This method must be called from the main thread.
|
||||
*/
|
||||
void OnStateModified() noexcept;
|
||||
|
||||
/**
|
||||
* Find a #Partition with the given name. Returns nullptr if
|
||||
* no such partition was found.
|
||||
@@ -146,8 +171,9 @@ struct Instance final
|
||||
gcc_pure
|
||||
Partition *FindPartition(const char *name) noexcept;
|
||||
|
||||
void DeletePartition(Partition &partition) noexcept;
|
||||
|
||||
void BeginShutdownPartitions() noexcept;
|
||||
void FinishShutdownPartitions() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
/**
|
||||
@@ -155,8 +181,8 @@ struct Instance final
|
||||
* if this MPD configuration has no database (no
|
||||
* music_directory was configured).
|
||||
*/
|
||||
Database *GetDatabase() {
|
||||
return database;
|
||||
Database *GetDatabase() noexcept {
|
||||
return database.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,9 +193,13 @@ struct Instance final
|
||||
const Database &GetDatabaseOrThrow() const;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
bool HasStickerDatabase() noexcept {
|
||||
return sticker_database != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BeginShutdownUpdate() noexcept;
|
||||
void FinishShutdownUpdate() noexcept;
|
||||
void ShutdownDatabase() noexcept;
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
void LookupRemoteTag(const char *uri) noexcept;
|
||||
@@ -179,10 +209,13 @@ struct Instance final
|
||||
}
|
||||
#endif
|
||||
|
||||
void FlushCaches() noexcept;
|
||||
|
||||
private:
|
||||
#ifdef ENABLE_DATABASE
|
||||
void OnDatabaseModified() override;
|
||||
void OnDatabaseSongRemoved(const char *uri) override;
|
||||
/* virtual methods from class DatabaseListener */
|
||||
void OnDatabaseModified() noexcept override;
|
||||
void OnDatabaseSongRemoved(const char *uri) noexcept override;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
@@ -197,7 +230,7 @@ private:
|
||||
#endif
|
||||
|
||||
/* callback for #idle_monitor */
|
||||
void OnIdle(unsigned mask);
|
||||
void OnIdle(unsigned mask) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -34,8 +34,6 @@
|
||||
#include "fs/XDG.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
#include <systemd/sd-daemon.h>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,13 +22,15 @@
|
||||
#include "client/Client.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
static LocatedUri
|
||||
LocateFileUri(const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
@@ -40,11 +42,13 @@ LocateFileUri(const char *uri, const Client *client
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (storage != nullptr) {
|
||||
const char *suffix = storage->MapToRelativeUTF8(uri);
|
||||
if (suffix != nullptr)
|
||||
const auto suffix = storage->MapToRelativeUTF8(uri);
|
||||
if (suffix.data() != nullptr)
|
||||
/* this path was relative to the music
|
||||
directory */
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE, suffix);
|
||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
||||
suffix.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -55,20 +59,34 @@ LocateFileUri(const char *uri, const Client *client
|
||||
}
|
||||
|
||||
static LocatedUri
|
||||
LocateAbsoluteUri(const char *uri
|
||||
LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
)
|
||||
{
|
||||
switch (kind) {
|
||||
case UriPluginKind::INPUT:
|
||||
case UriPluginKind::STORAGE: // TODO: separate check for storage plugins
|
||||
if (!uri_supported_scheme(uri))
|
||||
throw std::runtime_error("Unsupported URI scheme");
|
||||
break;
|
||||
|
||||
case UriPluginKind::PLAYLIST:
|
||||
/* for now, no validation for playlist URIs; this is
|
||||
more complicated because there are three ways to
|
||||
identify which plugin to use: URI scheme, filename
|
||||
suffix and MIME type */
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (storage != nullptr) {
|
||||
const char *suffix = storage->MapToRelativeUTF8(uri);
|
||||
if (suffix != nullptr)
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE, suffix);
|
||||
const auto suffix = storage->MapToRelativeUTF8(uri);
|
||||
if (suffix.data() != nullptr)
|
||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
||||
suffix.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -76,7 +94,8 @@ LocateAbsoluteUri(const char *uri
|
||||
}
|
||||
|
||||
LocatedUri
|
||||
LocateUri(const char *uri, const Client *client
|
||||
LocateUri(UriPluginKind kind,
|
||||
const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
@@ -100,7 +119,7 @@ LocateUri(const char *uri, const Client *client
|
||||
#endif
|
||||
);
|
||||
else if (uri_has_scheme(uri))
|
||||
return LocateAbsoluteUri(uri
|
||||
return LocateAbsoluteUri(kind, uri
|
||||
#ifdef ENABLE_DATABASE
|
||||
, storage
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,7 @@
|
||||
#ifndef MPD_LOCATE_URI_HXX
|
||||
#define MPD_LOCATE_URI_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "util/Compiler.h"
|
||||
#include "config.h"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -41,6 +40,12 @@ class Client;
|
||||
class Storage;
|
||||
#endif
|
||||
|
||||
enum class UriPluginKind {
|
||||
INPUT,
|
||||
STORAGE,
|
||||
PLAYLIST,
|
||||
};
|
||||
|
||||
struct LocatedUri {
|
||||
enum class Type {
|
||||
/**
|
||||
@@ -84,7 +89,8 @@ struct LocatedUri {
|
||||
* that feature is disabled if this parameter is nullptr
|
||||
*/
|
||||
LocatedUri
|
||||
LocateUri(const char *uri, const Client *client
|
||||
LocateUri(UriPluginKind kind,
|
||||
const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
|
110
src/Log.cxx
110
src/Log.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,165 +17,133 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "LogV.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/Exception.hxx"
|
||||
|
||||
#include <exception>
|
||||
#include <cerrno>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static constexpr Domain exception_domain("exception");
|
||||
|
||||
void
|
||||
LogFormatV(const Domain &domain, LogLevel level,
|
||||
const char *fmt, va_list ap) noexcept
|
||||
LogFormatV(LogLevel level, const Domain &domain,
|
||||
const char *fmt, std::va_list ap) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
Log(domain, level, msg);
|
||||
Log(level, domain, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept
|
||||
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, level, fmt, ap);
|
||||
LogFormatV(level, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, LogLevel::DEBUG, fmt, ap);
|
||||
LogFormatV(LogLevel::DEBUG, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, LogLevel::INFO, fmt, ap);
|
||||
LogFormatV(LogLevel::INFO, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatDefault(const Domain &domain, const char *fmt, ...) noexcept
|
||||
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, LogLevel::DEFAULT, fmt, ap);
|
||||
LogFormatV(LogLevel::NOTICE, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, LogLevel::WARNING, fmt, ap);
|
||||
LogFormatV(LogLevel::WARNING, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatError(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(domain, LogLevel::ERROR, fmt, ap);
|
||||
LogFormatV(LogLevel::ERROR, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception &e) noexcept
|
||||
Log(LogLevel level, const std::exception &e) noexcept
|
||||
{
|
||||
Log(exception_domain, LogLevel::ERROR, e.what());
|
||||
|
||||
try {
|
||||
std::rethrow_if_nested(e);
|
||||
} catch (const std::exception &nested) {
|
||||
LogError(nested, "nested");
|
||||
} catch (...) {
|
||||
Log(exception_domain, LogLevel::ERROR,
|
||||
"Unrecognized nested exception");
|
||||
}
|
||||
Log(level, exception_domain, GetFullMessage(e).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception &e, const char *msg) noexcept
|
||||
Log(LogLevel level, const std::exception &e, const char *msg) noexcept
|
||||
{
|
||||
FormatError(exception_domain, "%s: %s", msg, e.what());
|
||||
|
||||
try {
|
||||
std::rethrow_if_nested(e);
|
||||
} catch (const std::exception &nested) {
|
||||
LogError(nested);
|
||||
} catch (...) {
|
||||
Log(exception_domain, LogLevel::ERROR,
|
||||
"Unrecognized nested exception");
|
||||
}
|
||||
LogFormat(level, exception_domain, "%s: %s", msg, GetFullMessage(e).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
FormatError(const std::exception &e, const char *fmt, ...) noexcept
|
||||
LogFormat(LogLevel level, const std::exception &e, const char *fmt, ...) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
LogError(e, msg);
|
||||
Log(level, e, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception_ptr &ep) noexcept
|
||||
Log(LogLevel level, const std::exception_ptr &ep) noexcept
|
||||
{
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
} catch (...) {
|
||||
Log(exception_domain, LogLevel::ERROR,
|
||||
"Unrecognized exception");
|
||||
}
|
||||
Log(level, exception_domain, GetFullMessage(ep).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception_ptr &ep, const char *msg) noexcept
|
||||
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept
|
||||
{
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e, msg);
|
||||
} catch (...) {
|
||||
FormatError(exception_domain,
|
||||
"%s: Unrecognized exception", msg);
|
||||
}
|
||||
LogFormat(level, exception_domain, "%s: %s", msg,
|
||||
GetFullMessage(ep).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept
|
||||
LogFormat(LogLevel level, const std::exception_ptr &ep, const char *fmt, ...) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
LogError(ep, msg);
|
||||
Log(level, ep, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogErrno(const Domain &domain, int e, const char *msg) noexcept
|
||||
{
|
||||
LogFormat(domain, LogLevel::ERROR, "%s: %s", msg, strerror(e));
|
||||
LogFormat(LogLevel::ERROR, domain, "%s: %s", msg, strerror(e));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -185,7 +153,7 @@ LogErrno(const Domain &domain, const char *msg) noexcept
|
||||
}
|
||||
|
||||
static void
|
||||
FormatErrnoV(const Domain &domain, int e, const char *fmt, va_list ap) noexcept
|
||||
FormatErrnoV(const Domain &domain, int e, const char *fmt, std::va_list ap) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
@@ -196,7 +164,7 @@ FormatErrnoV(const Domain &domain, int e, const char *fmt, va_list ap) noexcept
|
||||
void
|
||||
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept
|
||||
{
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
FormatErrnoV(domain, e, fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -207,7 +175,7 @@ FormatErrno(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
const int e = errno;
|
||||
|
||||
va_list ap;
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
FormatErrnoV(domain, e, fmt, ap);
|
||||
va_end(ap);
|
||||
|
89
src/Log.hxx
89
src/Log.hxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -28,16 +28,38 @@
|
||||
class Domain;
|
||||
|
||||
void
|
||||
Log(const Domain &domain, LogLevel level, const char *msg) noexcept;
|
||||
Log(LogLevel level, const Domain &domain, const char *msg) noexcept;
|
||||
|
||||
gcc_printf(3,4)
|
||||
void
|
||||
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept;
|
||||
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e) noexcept;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e, const char *msg) noexcept;
|
||||
|
||||
gcc_printf(3,4)
|
||||
void
|
||||
LogFormat(LogLevel level, const std::exception &e,
|
||||
const char *fmt, ...) noexcept;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception_ptr &ep) noexcept;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept;
|
||||
|
||||
gcc_printf(3,4)
|
||||
void
|
||||
LogFormat(LogLevel level, const std::exception_ptr &ep,
|
||||
const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogDebug(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(domain, LogLevel::DEBUG, msg);
|
||||
Log(LogLevel::DEBUG, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
@@ -47,7 +69,7 @@ FormatDebug(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
static inline void
|
||||
LogInfo(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(domain, LogLevel::INFO, msg);
|
||||
Log(LogLevel::INFO, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
@@ -55,19 +77,19 @@ void
|
||||
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogDefault(const Domain &domain, const char *msg) noexcept
|
||||
LogNotice(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(domain, LogLevel::DEFAULT, msg);
|
||||
Log(LogLevel::NOTICE, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatDefault(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogWarning(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(domain, LogLevel::WARNING, msg);
|
||||
Log(LogLevel::WARNING, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
@@ -77,28 +99,47 @@ FormatWarning(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
static inline void
|
||||
LogError(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(domain, LogLevel::ERROR, msg);
|
||||
Log(LogLevel::ERROR, domain, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception &e) noexcept;
|
||||
inline void
|
||||
LogError(const std::exception &e) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, e);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception &e, const char *msg) noexcept;
|
||||
inline void
|
||||
LogError(const std::exception &e, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, e, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatError(const std::exception &e, const char *fmt, ...) noexcept;
|
||||
template<typename... Args>
|
||||
inline void
|
||||
FormatError(const std::exception &e, const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
LogFormat(LogLevel::ERROR, e, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception_ptr &ep) noexcept;
|
||||
inline void
|
||||
LogError(const std::exception_ptr &ep) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, ep);
|
||||
}
|
||||
|
||||
void
|
||||
LogError(const std::exception_ptr &ep, const char *msg) noexcept;
|
||||
inline void
|
||||
LogError(const std::exception_ptr &ep, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, ep, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept;
|
||||
template<typename... Args>
|
||||
inline void
|
||||
FormatError(const std::exception_ptr &ep,
|
||||
const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
LogFormat(LogLevel::ERROR, ep, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,13 +17,15 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "LogBackend.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "Version.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
@@ -45,7 +47,7 @@ ToAndroidLogLevel(LogLevel log_level) noexcept
|
||||
return ANDROID_LOG_DEBUG;
|
||||
|
||||
case LogLevel::INFO:
|
||||
case LogLevel::DEFAULT:
|
||||
case LogLevel::NOTICE:
|
||||
return ANDROID_LOG_INFO;
|
||||
|
||||
case LogLevel::WARNING:
|
||||
@@ -61,7 +63,7 @@ ToAndroidLogLevel(LogLevel log_level) noexcept
|
||||
|
||||
#else
|
||||
|
||||
static LogLevel log_threshold = LogLevel::INFO;
|
||||
static LogLevel log_threshold = LogLevel::NOTICE;
|
||||
|
||||
static bool enable_timestamp;
|
||||
|
||||
@@ -120,7 +122,7 @@ ToSysLogLevel(LogLevel log_level) noexcept
|
||||
case LogLevel::INFO:
|
||||
return LOG_INFO;
|
||||
|
||||
case LogLevel::DEFAULT:
|
||||
case LogLevel::NOTICE:
|
||||
return LOG_NOTICE;
|
||||
|
||||
case LogLevel::WARNING:
|
||||
@@ -176,7 +178,7 @@ FileLog(const Domain &domain, const char *message) noexcept
|
||||
#endif /* !ANDROID */
|
||||
|
||||
void
|
||||
Log(const Domain &domain, LogLevel level, const char *msg) noexcept
|
||||
Log(LogLevel level, const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ToAndroidLogLevel(level), "MPD",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,6 @@
|
||||
#ifndef MPD_LOG_BACKEND_HXX
|
||||
#define MPD_LOG_BACKEND_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "LogLevel.hxx"
|
||||
|
||||
void
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -28,20 +28,24 @@
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LOG_LEVEL_SECURE LogLevel::INFO
|
||||
|
||||
#define LOG_DATE_BUF_SIZE 16
|
||||
#define LOG_DATE_LEN (LOG_DATE_BUF_SIZE - 1)
|
||||
|
||||
gcc_unused
|
||||
[[maybe_unused]]
|
||||
static constexpr Domain log_domain("log");
|
||||
|
||||
#ifndef ANDROID
|
||||
@@ -59,7 +63,7 @@ static void redirect_logs(int fd)
|
||||
}
|
||||
|
||||
static int
|
||||
open_log_file(void)
|
||||
open_log_file()
|
||||
{
|
||||
assert(!out_path.IsNull());
|
||||
|
||||
@@ -89,23 +93,30 @@ log_init_file(int line)
|
||||
}
|
||||
|
||||
static inline LogLevel
|
||||
parse_log_level(const char *value, int line)
|
||||
parse_log_level(const char *value)
|
||||
{
|
||||
if (0 == strcmp(value, "default"))
|
||||
return LogLevel::DEFAULT;
|
||||
if (0 == strcmp(value, "secure"))
|
||||
return LOG_LEVEL_SECURE;
|
||||
else if (0 == strcmp(value, "verbose"))
|
||||
if (StringIsEqual(value, "notice") ||
|
||||
/* deprecated name: */
|
||||
StringIsEqual(value, "default"))
|
||||
return LogLevel::NOTICE;
|
||||
else if (StringIsEqual(value, "info") ||
|
||||
/* deprecated since MPD 0.22: */
|
||||
StringIsEqual(value, "secure"))
|
||||
return LogLevel::INFO;
|
||||
else if (StringIsEqual(value, "verbose"))
|
||||
return LogLevel::DEBUG;
|
||||
else if (StringIsEqual(value, "warning"))
|
||||
return LogLevel::WARNING;
|
||||
else if (StringIsEqual(value, "error"))
|
||||
return LogLevel::ERROR;
|
||||
else
|
||||
throw FormatRuntimeError("unknown log level \"%s\" at line %d",
|
||||
value, line);
|
||||
throw FormatRuntimeError("unknown log level \"%s\"", value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
log_early_init(bool verbose)
|
||||
log_early_init(bool verbose) noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
(void)verbose;
|
||||
@@ -128,9 +139,12 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
#else
|
||||
if (verbose)
|
||||
SetLogThreshold(LogLevel::DEBUG);
|
||||
else if (const auto ¶m = config.GetParam(ConfigOption::LOG_LEVEL))
|
||||
SetLogThreshold(parse_log_level(param->value.c_str(),
|
||||
param->line));
|
||||
else
|
||||
SetLogThreshold(config.With(ConfigOption::LOG_LEVEL, [](const char *s){
|
||||
return s != nullptr
|
||||
? parse_log_level(s)
|
||||
: LogLevel::NOTICE;
|
||||
}));
|
||||
|
||||
if (use_stdout) {
|
||||
out_fd = STDOUT_FILENO;
|
||||
@@ -139,11 +153,21 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
if (param == nullptr) {
|
||||
/* no configuration: default to syslog (if
|
||||
available) */
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
if (sd_booted() &&
|
||||
getenv("NOTIFY_SOCKET") != nullptr) {
|
||||
/* if MPD was started as a systemd
|
||||
service, default to journal (which
|
||||
is connected to fd=2) */
|
||||
out_fd = STDOUT_FILENO;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifndef HAVE_SYSLOG
|
||||
throw std::runtime_error("config parameter 'log_file' not found");
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG
|
||||
} else if (strcmp(param->value.c_str(), "syslog") == 0) {
|
||||
} else if (StringIsEqual(param->value.c_str(), "syslog")) {
|
||||
LogInitSysLog();
|
||||
#endif
|
||||
} else {
|
||||
@@ -157,7 +181,7 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
#ifndef ANDROID
|
||||
|
||||
static void
|
||||
close_log_files(void)
|
||||
close_log_files() noexcept
|
||||
{
|
||||
#ifdef HAVE_SYSLOG
|
||||
LogFinishSysLog();
|
||||
@@ -167,7 +191,7 @@ close_log_files(void)
|
||||
#endif
|
||||
|
||||
void
|
||||
log_deinit(void)
|
||||
log_deinit() noexcept
|
||||
{
|
||||
#ifndef ANDROID
|
||||
close_log_files();
|
||||
@@ -199,7 +223,8 @@ void setup_log_output()
|
||||
#endif
|
||||
}
|
||||
|
||||
int cycle_log_files(void)
|
||||
int
|
||||
cycle_log_files() noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
return 0;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -31,7 +31,7 @@ struct ConfigData;
|
||||
* @param verbose true when the program is started with --verbose
|
||||
*/
|
||||
void
|
||||
log_early_init(bool verbose);
|
||||
log_early_init(bool verbose) noexcept;
|
||||
|
||||
/**
|
||||
* Throws #std::runtime_error on error.
|
||||
@@ -40,12 +40,12 @@ void
|
||||
log_init(const ConfigData &config, bool verbose, bool use_stdout);
|
||||
|
||||
void
|
||||
log_deinit();
|
||||
log_deinit() noexcept;
|
||||
|
||||
void
|
||||
setup_log_output();
|
||||
|
||||
int
|
||||
cycle_log_files();
|
||||
cycle_log_files() noexcept;
|
||||
|
||||
#endif /* LOG_H */
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -42,7 +42,7 @@ enum class LogLevel {
|
||||
/**
|
||||
* Interesting informational message.
|
||||
*/
|
||||
DEFAULT,
|
||||
NOTICE,
|
||||
|
||||
/**
|
||||
* Warning: something may be wrong.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include "Log.hxx" // IWYU pragma: export
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <cstdarg>
|
||||
|
||||
void
|
||||
LogFormatV(const Domain &domain, LogLevel level,
|
||||
const char *fmt, va_list ap) noexcept;
|
||||
LogFormatV(LogLevel level, const Domain &domain,
|
||||
const char *fmt, std::va_list ap) noexcept;
|
||||
|
||||
#endif /* LOG_H */
|
||||
|
522
src/Main.cxx
522
src/Main.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,30 +27,30 @@
|
||||
#include "Mapper.hxx"
|
||||
#include "Permission.hxx"
|
||||
#include "Listen.hxx"
|
||||
#include "client/Listener.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "client/ClientList.hxx"
|
||||
#include "client/Config.hxx"
|
||||
#include "client/List.hxx"
|
||||
#include "command/AllCommands.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "tag/Config.hxx"
|
||||
#include "ReplayGainGlobal.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "LogInit.hxx"
|
||||
#include "input/Init.hxx"
|
||||
#include "input/cache/Config.hxx"
|
||||
#include "input/cache/Manager.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Config.hxx"
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "zeroconf/ZeroconfGlue.hxx"
|
||||
#include "decoder/DecoderList.hxx"
|
||||
#include "AudioParser.hxx"
|
||||
#include "pcm/PcmConvert.hxx"
|
||||
#include "pcm/AudioParser.hxx"
|
||||
#include "pcm/Convert.hxx"
|
||||
#include "unix/SignalHandlers.hxx"
|
||||
#include "thread/Slack.hxx"
|
||||
#include "net/Init.hxx"
|
||||
#include "lib/icu/Init.hxx"
|
||||
#include "config/File.hxx"
|
||||
#include "config/Check.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Param.hxx"
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "config/Defaults.hxx"
|
||||
#include "config/Option.hxx"
|
||||
#include "config/Domain.hxx"
|
||||
#include "config/Parser.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
|
||||
@@ -82,7 +83,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "sticker/StickerDatabase.hxx"
|
||||
#include "sticker/Database.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
@@ -95,6 +96,7 @@
|
||||
#include "android/Environment.hxx"
|
||||
#include "android/Context.hxx"
|
||||
#include "android/LogListener.hxx"
|
||||
#include "config/File.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "org_musicpd_Bridge.h"
|
||||
#endif
|
||||
@@ -107,14 +109,12 @@
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <climits>
|
||||
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#ifndef ANDROID
|
||||
#include <clocale>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
static constexpr size_t KILOBYTE = 1024;
|
||||
static constexpr size_t MEGABYTE = 1024 * KILOBYTE;
|
||||
|
||||
@@ -129,17 +129,14 @@ Context *context;
|
||||
LogListener *logListener;
|
||||
#endif
|
||||
|
||||
Instance *instance;
|
||||
Instance *global_instance;
|
||||
|
||||
struct Config {
|
||||
ReplayGainConfig replay_gain;
|
||||
};
|
||||
|
||||
static Config
|
||||
LoadConfig(const ConfigData &config)
|
||||
{
|
||||
return {LoadReplayGainConfig(config)};
|
||||
}
|
||||
explicit Config(const ConfigData &raw)
|
||||
:replay_gain(LoadReplayGainConfig(raw)) {}
|
||||
};
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
|
||||
@@ -160,20 +157,31 @@ glue_daemonize_init(const struct options *options,
|
||||
static void
|
||||
glue_mapper_init(const ConfigData &config)
|
||||
{
|
||||
mapper_init(config.GetPath(ConfigOption::PLAYLIST_DIR));
|
||||
auto playlist_directory = config.GetPath(ConfigOption::PLAYLIST_DIR);
|
||||
|
||||
#ifdef ANDROID
|
||||
/* if there is no explicit configuration, store playlists in
|
||||
"/sdcard/Android/data/org.musicpd/files/playlists" */
|
||||
if (playlist_directory.IsNull())
|
||||
playlist_directory = context->GetExternalFilesDir(Java::GetEnv(),
|
||||
"playlists");
|
||||
#endif
|
||||
|
||||
mapper_init(std::move(playlist_directory));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
static void
|
||||
InitStorage(const ConfigData &config, EventLoop &event_loop)
|
||||
InitStorage(Instance &instance, EventLoop &event_loop,
|
||||
const ConfigData &config)
|
||||
{
|
||||
auto storage = CreateConfiguredStorage(config, event_loop);
|
||||
if (storage == nullptr)
|
||||
return;
|
||||
|
||||
CompositeStorage *composite = new CompositeStorage();
|
||||
instance->storage = composite;
|
||||
auto *composite = new CompositeStorage();
|
||||
instance.storage = composite;
|
||||
composite->Mount("", std::move(storage));
|
||||
}
|
||||
|
||||
@@ -183,96 +191,97 @@ InitStorage(const ConfigData &config, EventLoop &event_loop)
|
||||
* process has been daemonized.
|
||||
*/
|
||||
static bool
|
||||
glue_db_init_and_load(const ConfigData &config)
|
||||
glue_db_init_and_load(Instance &instance, const ConfigData &config)
|
||||
{
|
||||
instance->database =
|
||||
CreateConfiguredDatabase(config, instance->event_loop,
|
||||
instance->io_thread.GetEventLoop(),
|
||||
*instance);
|
||||
if (instance->database == nullptr)
|
||||
auto db = CreateConfiguredDatabase(config, instance.event_loop,
|
||||
instance.io_thread.GetEventLoop(),
|
||||
instance);
|
||||
if (!db)
|
||||
return true;
|
||||
|
||||
if (instance->database->GetPlugin().flags & DatabasePlugin::FLAG_REQUIRE_STORAGE) {
|
||||
InitStorage(config, instance->io_thread.GetEventLoop());
|
||||
if (db->GetPlugin().RequireStorage()) {
|
||||
InitStorage(instance, instance.io_thread.GetEventLoop(),
|
||||
config);
|
||||
|
||||
if (instance->storage == nullptr) {
|
||||
delete instance->database;
|
||||
instance->database = nullptr;
|
||||
LogDefault(config_domain,
|
||||
if (instance.storage == nullptr) {
|
||||
LogNotice(config_domain,
|
||||
"Found database setting without "
|
||||
"music_directory - disabling database");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (IsStorageConfigured(config))
|
||||
LogDefault(config_domain,
|
||||
LogNotice(config_domain,
|
||||
"Ignoring the storage configuration "
|
||||
"because the database does not need it");
|
||||
}
|
||||
|
||||
try {
|
||||
instance->database->Open();
|
||||
db->Open();
|
||||
} catch (...) {
|
||||
std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
|
||||
}
|
||||
|
||||
if (!instance->database->IsPlugin(simple_db_plugin))
|
||||
instance.database = std::move(db);
|
||||
|
||||
auto *sdb = dynamic_cast<SimpleDatabase *>(instance.database.get());
|
||||
if (sdb == nullptr)
|
||||
return true;
|
||||
|
||||
SimpleDatabase &db = *(SimpleDatabase *)instance->database;
|
||||
instance->update = new UpdateService(config,
|
||||
instance->event_loop, db,
|
||||
static_cast<CompositeStorage &>(*instance->storage),
|
||||
*instance);
|
||||
instance.update = new UpdateService(config,
|
||||
instance.event_loop, *sdb,
|
||||
static_cast<CompositeStorage &>(*instance.storage),
|
||||
instance);
|
||||
|
||||
/* run database update after daemonization? */
|
||||
return db.FileExists();
|
||||
return sdb->FileExists();
|
||||
}
|
||||
|
||||
static bool
|
||||
InitDatabaseAndStorage(const ConfigData &config)
|
||||
InitDatabaseAndStorage(Instance &instance, const ConfigData &config)
|
||||
{
|
||||
const bool create_db = !glue_db_init_and_load(config);
|
||||
const bool create_db = !glue_db_init_and_load(instance, config);
|
||||
return create_db;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
|
||||
/**
|
||||
* Configure and initialize the sticker subsystem.
|
||||
*/
|
||||
static void
|
||||
glue_sticker_init(const ConfigData &config)
|
||||
static std::unique_ptr<StickerDatabase>
|
||||
LoadStickerDatabase(const ConfigData &config)
|
||||
{
|
||||
#ifdef ENABLE_SQLITE
|
||||
auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE);
|
||||
if (sticker_file.IsNull())
|
||||
return;
|
||||
return nullptr;
|
||||
|
||||
sticker_global_init(std::move(sticker_file));
|
||||
#else
|
||||
(void)config;
|
||||
#endif
|
||||
return std::make_unique<StickerDatabase>(std::move(sticker_file));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
glue_state_file_init(const ConfigData &raw_config)
|
||||
glue_state_file_init(Instance &instance, const ConfigData &raw_config)
|
||||
{
|
||||
StateFileConfig config(raw_config);
|
||||
if (!config.IsEnabled())
|
||||
return;
|
||||
|
||||
instance->state_file = new StateFile(std::move(config),
|
||||
instance->partitions.front(),
|
||||
instance->event_loop);
|
||||
instance->state_file->Read();
|
||||
instance.state_file = std::make_unique< StateFile>(std::move(config),
|
||||
instance.partitions.front(),
|
||||
instance.event_loop);
|
||||
instance.state_file->Read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the decoder and player core, including the music pipe.
|
||||
*/
|
||||
static void
|
||||
initialize_decoder_and_player(const ConfigData &config,
|
||||
initialize_decoder_and_player(Instance &instance,
|
||||
const ConfigData &config,
|
||||
const ReplayGainConfig &replay_gain_config)
|
||||
{
|
||||
const ConfigParam *param;
|
||||
@@ -280,20 +289,21 @@ initialize_decoder_and_player(const ConfigData &config,
|
||||
size_t buffer_size;
|
||||
param = config.GetParam(ConfigOption::AUDIO_BUFFER_SIZE);
|
||||
if (param != nullptr) {
|
||||
char *test;
|
||||
long tmp = strtol(param->value.c_str(), &test, 10);
|
||||
if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX)
|
||||
buffer_size = param->With([](const char *s){
|
||||
size_t result = ParseSize(s, KILOBYTE);
|
||||
if (result <= 0)
|
||||
throw FormatRuntimeError("buffer size \"%s\" is not a "
|
||||
"positive integer, line %i",
|
||||
param->value.c_str(), param->line);
|
||||
buffer_size = tmp * KILOBYTE;
|
||||
"positive integer", s);
|
||||
|
||||
if (buffer_size < MIN_BUFFER_SIZE) {
|
||||
if (result < MIN_BUFFER_SIZE) {
|
||||
FormatWarning(config_domain, "buffer size %lu is too small, using %lu bytes instead",
|
||||
(unsigned long)buffer_size,
|
||||
(unsigned long)result,
|
||||
(unsigned long)MIN_BUFFER_SIZE);
|
||||
buffer_size = MIN_BUFFER_SIZE;
|
||||
result = MIN_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
} else
|
||||
buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
@@ -307,35 +317,26 @@ initialize_decoder_and_player(const ConfigData &config,
|
||||
config.GetPositive(ConfigOption::MAX_PLAYLIST_LENGTH,
|
||||
DEFAULT_PLAYLIST_MAX_LENGTH);
|
||||
|
||||
AudioFormat configured_audio_format = AudioFormat::Undefined();
|
||||
param = config.GetParam(ConfigOption::AUDIO_OUTPUT_FORMAT);
|
||||
if (param != nullptr) {
|
||||
try {
|
||||
configured_audio_format = ParseAudioFormat(param->value.c_str(),
|
||||
true);
|
||||
} catch (...) {
|
||||
std::throw_with_nested(FormatRuntimeError("error parsing line %i",
|
||||
param->line));
|
||||
}
|
||||
}
|
||||
AudioFormat configured_audio_format = config.With(ConfigOption::AUDIO_OUTPUT_FORMAT, [](const char *s){
|
||||
if (s == nullptr)
|
||||
return AudioFormat::Undefined();
|
||||
|
||||
instance->partitions.emplace_back(*instance,
|
||||
return ParseAudioFormat(s, true);
|
||||
});
|
||||
|
||||
instance.partitions.emplace_back(instance,
|
||||
"default",
|
||||
max_length,
|
||||
buffered_chunks,
|
||||
configured_audio_format,
|
||||
replay_gain_config);
|
||||
auto &partition = instance->partitions.back();
|
||||
auto &partition = instance.partitions.back();
|
||||
|
||||
try {
|
||||
param = config.GetParam(ConfigOption::REPLAYGAIN);
|
||||
if (param != nullptr)
|
||||
partition.replay_gain_mode =
|
||||
FromString(param->value.c_str());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(FormatRuntimeError("Failed to parse line %i",
|
||||
param->line));
|
||||
}
|
||||
partition.replay_gain_mode = config.With(ConfigOption::REPLAYGAIN, [](const char *s){
|
||||
return s != nullptr
|
||||
? FromString(s)
|
||||
: ReplayGainMode::OFF;
|
||||
});
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -351,87 +352,25 @@ Instance::BeginShutdownUpdate() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::FinishShutdownUpdate() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
delete update;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::ShutdownDatabase() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (instance->database != nullptr) {
|
||||
instance->database->Close();
|
||||
delete instance->database;
|
||||
}
|
||||
|
||||
delete instance->storage;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::BeginShutdownPartitions() noexcept
|
||||
{
|
||||
for (auto &partition : partitions) {
|
||||
partition.pc.Kill();
|
||||
partition.listener.reset();
|
||||
partition.BeginShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::FinishShutdownPartitions() noexcept
|
||||
static inline void
|
||||
MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
{
|
||||
partitions.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnIdle(unsigned flags)
|
||||
{
|
||||
/* send "idle" notifications to all subscribed
|
||||
clients */
|
||||
client_list->IdleAdd(flags);
|
||||
|
||||
if (flags & (IDLE_PLAYLIST|IDLE_PLAYER|IDLE_MIXER|IDLE_OUTPUT) &&
|
||||
state_file != nullptr)
|
||||
state_file->CheckModified();
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return win32_main(argc, argv);
|
||||
#else
|
||||
return mpd_main(argc, argv);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
mpd_main_after_fork(const ConfigData &raw_config,
|
||||
const Config &config);
|
||||
|
||||
static inline int
|
||||
MainOrThrow(int argc, char *argv[])
|
||||
{
|
||||
struct options options;
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
daemonize_close_stdin();
|
||||
#endif
|
||||
|
||||
#ifndef ANDROID
|
||||
#ifdef HAVE_LOCALE_H
|
||||
/* initialize locale */
|
||||
setlocale(LC_CTYPE,"");
|
||||
setlocale(LC_COLLATE, "");
|
||||
#endif
|
||||
std::setlocale(LC_CTYPE,"");
|
||||
std::setlocale(LC_COLLATE, "");
|
||||
#endif
|
||||
|
||||
const ScopeIcuInit icu_init;
|
||||
@@ -441,25 +380,8 @@ MainOrThrow(int argc, char *argv[])
|
||||
const ODBus::ScopeInit dbus_init;
|
||||
#endif
|
||||
|
||||
ConfigData raw_config;
|
||||
|
||||
#ifdef ANDROID
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
const auto sdcard = Environment::getExternalStorageDirectory();
|
||||
if (!sdcard.IsNull()) {
|
||||
const auto config_path =
|
||||
sdcard / Path::FromFS("mpd.conf");
|
||||
if (FileExists(config_path))
|
||||
ReadConfigFile(raw_config, config_path);
|
||||
}
|
||||
#else
|
||||
ParseCommandLine(argc, argv, options, raw_config);
|
||||
#endif
|
||||
|
||||
InitPathParser(raw_config);
|
||||
const auto config = LoadConfig(raw_config);
|
||||
const Config config(raw_config);
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
glue_daemonize_init(&options, raw_config);
|
||||
@@ -469,31 +391,33 @@ MainOrThrow(int argc, char *argv[])
|
||||
|
||||
log_init(raw_config, options.verbose, options.log_stderr);
|
||||
|
||||
instance = new Instance();
|
||||
AtScopeExit() {
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
};
|
||||
Instance instance;
|
||||
global_instance = &instance;
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
instance->neighbors = new NeighborGlue();
|
||||
instance->neighbors->Init(raw_config,
|
||||
instance->io_thread.GetEventLoop(),
|
||||
*instance);
|
||||
instance.neighbors = std::make_unique<NeighborGlue>();
|
||||
instance.neighbors->Init(raw_config,
|
||||
instance.io_thread.GetEventLoop(),
|
||||
instance);
|
||||
|
||||
if (instance->neighbors->IsEmpty()) {
|
||||
delete instance->neighbors;
|
||||
instance->neighbors = nullptr;
|
||||
}
|
||||
if (instance.neighbors->IsEmpty())
|
||||
instance.neighbors.reset();
|
||||
#endif
|
||||
|
||||
const unsigned max_clients =
|
||||
raw_config.GetPositive(ConfigOption::MAX_CONN, 10);
|
||||
instance->client_list = new ClientList(max_clients);
|
||||
raw_config.GetPositive(ConfigOption::MAX_CONN, 100);
|
||||
instance.client_list = std::make_unique<ClientList>(max_clients);
|
||||
|
||||
initialize_decoder_and_player(raw_config, config.replay_gain);
|
||||
const auto *input_cache_config = raw_config.GetBlock(ConfigBlockOption::INPUT_CACHE);
|
||||
if (input_cache_config != nullptr) {
|
||||
const InputCacheConfig c(*input_cache_config);
|
||||
instance.input_cache = std::make_unique<InputCacheManager>(c);
|
||||
}
|
||||
|
||||
listen_global_init(raw_config, *instance->partitions.front().listener);
|
||||
initialize_decoder_and_player(instance,
|
||||
raw_config, config.replay_gain);
|
||||
|
||||
listen_global_init(raw_config, *instance.partitions.front().listener);
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
daemonize_set_user();
|
||||
@@ -501,61 +425,43 @@ MainOrThrow(int argc, char *argv[])
|
||||
AtScopeExit() { daemonize_finish(); };
|
||||
#endif
|
||||
|
||||
return mpd_main_after_fork(raw_config, config);
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
static inline
|
||||
#endif
|
||||
int mpd_main(int argc, char *argv[]) noexcept
|
||||
{
|
||||
AtScopeExit() { log_deinit(); };
|
||||
|
||||
try {
|
||||
return MainOrThrow(argc, argv);
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
{
|
||||
ConfigureFS(raw_config);
|
||||
AtScopeExit() { DeinitFS(); };
|
||||
|
||||
glue_mapper_init(raw_config);
|
||||
|
||||
initPermissions(raw_config);
|
||||
spl_global_init(raw_config);
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_init_all();
|
||||
const ScopeArchivePluginsInit archive_plugins_init;
|
||||
#endif
|
||||
|
||||
pcm_convert_global_init(raw_config);
|
||||
|
||||
decoder_plugin_init_all(raw_config);
|
||||
const ScopeDecoderPluginsInit decoder_plugins_init(raw_config);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
const bool create_db = InitDatabaseAndStorage(raw_config);
|
||||
const bool create_db = InitDatabaseAndStorage(instance, raw_config);
|
||||
#endif
|
||||
|
||||
glue_sticker_init(raw_config);
|
||||
#ifdef ENABLE_SQLITE
|
||||
instance.sticker_database = LoadStickerDatabase(raw_config);
|
||||
#endif
|
||||
|
||||
command_init();
|
||||
|
||||
for (auto &partition : instance->partitions) {
|
||||
partition.outputs.Configure(instance->rtio_thread.GetEventLoop(),
|
||||
for (auto &partition : instance.partitions) {
|
||||
partition.outputs.Configure(instance.rtio_thread.GetEventLoop(),
|
||||
raw_config,
|
||||
config.replay_gain,
|
||||
partition.pc);
|
||||
config.replay_gain);
|
||||
partition.UpdateEffectiveReplayGainMode();
|
||||
}
|
||||
|
||||
client_manager_init(raw_config);
|
||||
input_stream_global_init(raw_config,
|
||||
instance->io_thread.GetEventLoop());
|
||||
playlist_list_global_init(raw_config);
|
||||
const ScopeInputPluginsInit input_plugins_init(raw_config,
|
||||
instance.io_thread.GetEventLoop());
|
||||
|
||||
const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
daemonize_commit();
|
||||
@@ -564,37 +470,43 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
#ifndef ANDROID
|
||||
setup_log_output();
|
||||
|
||||
SignalHandlersInit(instance->event_loop);
|
||||
const ScopeSignalHandlersInit signal_handlers_init(instance);
|
||||
#endif
|
||||
|
||||
instance->io_thread.Start();
|
||||
instance->rtio_thread.Start();
|
||||
instance.io_thread.Start();
|
||||
instance.rtio_thread.Start();
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
if (instance->neighbors != nullptr)
|
||||
instance->neighbors->Open();
|
||||
if (instance.neighbors != nullptr)
|
||||
instance.neighbors->Open();
|
||||
|
||||
AtScopeExit(&instance) {
|
||||
if (instance.neighbors != nullptr)
|
||||
instance.neighbors->Close();
|
||||
};
|
||||
#endif
|
||||
|
||||
ZeroconfInit(raw_config, instance->event_loop);
|
||||
ZeroconfInit(raw_config, instance.event_loop);
|
||||
AtScopeExit() { ZeroconfDeinit(); };
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (create_db) {
|
||||
/* the database failed to load: recreate the
|
||||
database */
|
||||
instance->update->Enqueue("", true);
|
||||
instance.update->Enqueue("", true);
|
||||
}
|
||||
#endif
|
||||
|
||||
glue_state_file_init(raw_config);
|
||||
glue_state_file_init(instance, raw_config);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (raw_config.GetBool(ConfigOption::AUTO_UPDATE, false)) {
|
||||
#ifdef ENABLE_INOTIFY
|
||||
if (instance->storage != nullptr &&
|
||||
instance->update != nullptr)
|
||||
mpd_inotify_init(instance->event_loop,
|
||||
*instance->storage,
|
||||
*instance->update,
|
||||
if (instance.storage != nullptr &&
|
||||
instance.update != nullptr)
|
||||
mpd_inotify_init(instance.event_loop,
|
||||
*instance.storage,
|
||||
*instance.update,
|
||||
raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
|
||||
INT_MAX));
|
||||
#else
|
||||
@@ -608,7 +520,7 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
|
||||
/* enable all audio outputs (if not already done by
|
||||
playlist_state_restore() */
|
||||
for (auto &partition : instance->partitions)
|
||||
for (auto &partition : instance.partitions)
|
||||
partition.pc.LockUpdateAudio();
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -617,14 +529,14 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
|
||||
/* the MPD frontend does not care about timer slack; set it to
|
||||
a huge value to allow the kernel to reduce CPU wakeups */
|
||||
SetThreadTimerSlackMS(100);
|
||||
SetThreadTimerSlack(std::chrono::milliseconds(100));
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
sd_notify(0, "READY=1");
|
||||
#endif
|
||||
|
||||
/* run the main loop */
|
||||
instance->event_loop.Run();
|
||||
instance.event_loop.Run();
|
||||
|
||||
#ifdef _WIN32
|
||||
win32_app_stopping();
|
||||
@@ -632,84 +544,98 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
|
||||
/* cleanup */
|
||||
|
||||
instance->BeginShutdownUpdate();
|
||||
if (instance.state_file)
|
||||
instance.state_file->Write();
|
||||
|
||||
if (instance->state_file != nullptr) {
|
||||
instance->state_file->Write();
|
||||
delete instance->state_file;
|
||||
}
|
||||
|
||||
ZeroconfDeinit();
|
||||
|
||||
instance->BeginShutdownPartitions();
|
||||
|
||||
delete instance->client_list;
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
if (instance->neighbors != nullptr) {
|
||||
instance->neighbors->Close();
|
||||
delete instance->neighbors;
|
||||
}
|
||||
#endif
|
||||
|
||||
instance->FinishShutdownUpdate();
|
||||
instance->ShutdownDatabase();
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_finish();
|
||||
#endif
|
||||
|
||||
playlist_list_global_finish();
|
||||
input_stream_global_finish();
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
mapper_finish();
|
||||
#endif
|
||||
|
||||
DeinitFS();
|
||||
|
||||
instance->FinishShutdownPartitions();
|
||||
command_finish();
|
||||
decoder_plugin_deinit_all();
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_deinit_all();
|
||||
#endif
|
||||
instance->rtio_thread.Stop();
|
||||
instance->io_thread.Stop();
|
||||
#ifndef ANDROID
|
||||
SignalHandlersFinish();
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
instance.BeginShutdownUpdate();
|
||||
instance.BeginShutdownPartitions();
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
static void
|
||||
AndroidMain()
|
||||
{
|
||||
struct options options;
|
||||
ConfigData raw_config;
|
||||
|
||||
const auto sdcard = Environment::getExternalStorageDirectory();
|
||||
if (!sdcard.IsNull()) {
|
||||
const auto config_path =
|
||||
sdcard / Path::FromFS("mpd.conf");
|
||||
if (FileExists(config_path))
|
||||
ReadConfigFile(raw_config, config_path);
|
||||
}
|
||||
|
||||
MainConfigured(options, raw_config);
|
||||
}
|
||||
|
||||
gcc_visibility_default
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logListener)
|
||||
{
|
||||
Java::Init(env);
|
||||
Java::Object::Initialise(env);
|
||||
Java::File::Initialise(env);
|
||||
Environment::Initialise(env);
|
||||
AtScopeExit(env) { Environment::Deinitialise(env); };
|
||||
|
||||
context = new Context(env, _context);
|
||||
AtScopeExit() { delete context; };
|
||||
|
||||
if (_logListener != nullptr)
|
||||
logListener = new LogListener(env, _logListener);
|
||||
AtScopeExit() { delete logListener; };
|
||||
|
||||
mpd_main(0, nullptr);
|
||||
|
||||
delete logListener;
|
||||
delete context;
|
||||
Environment::Deinitialise(env);
|
||||
try {
|
||||
AndroidMain();
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
gcc_visibility_default
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
|
||||
{
|
||||
if (instance != nullptr)
|
||||
instance->Break();
|
||||
if (global_instance != nullptr)
|
||||
global_instance->Break();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
MainOrThrow(int argc, char *argv[])
|
||||
{
|
||||
struct options options;
|
||||
ConfigData raw_config;
|
||||
|
||||
ParseCommandLine(argc, argv, options, raw_config);
|
||||
|
||||
MainConfigured(options, raw_config);
|
||||
}
|
||||
|
||||
int mpd_main(int argc, char *argv[]) noexcept
|
||||
{
|
||||
AtScopeExit() { log_deinit(); };
|
||||
|
||||
try {
|
||||
MainOrThrow(argc, argv);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return win32_main(argc, argv);
|
||||
#else
|
||||
return mpd_main(argc, argv);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,18 +20,16 @@
|
||||
#ifndef MPD_MAIN_HXX
|
||||
#define MPD_MAIN_HXX
|
||||
|
||||
class EventLoop;
|
||||
class Context;
|
||||
struct Instance;
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "android/LogListener.hxx"
|
||||
|
||||
extern Context *context;
|
||||
extern class Context *context;
|
||||
extern LogListener *logListener;
|
||||
#endif
|
||||
|
||||
extern Instance *instance;
|
||||
extern Instance *global_instance;
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "Main.hxx"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
/**
|
||||
* The absolute path of the playlist directory encoded in the
|
||||
@@ -58,11 +58,6 @@ mapper_init(AllocatedPath &&_playlist_dir)
|
||||
mapper_set_playlist_dir(std::move(_playlist_dir));
|
||||
}
|
||||
|
||||
void
|
||||
mapper_finish() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
AllocatedPath
|
||||
@@ -71,10 +66,10 @@ map_uri_fs(const char *uri) noexcept
|
||||
assert(uri != nullptr);
|
||||
assert(*uri != '/');
|
||||
|
||||
if (instance->storage == nullptr)
|
||||
if (global_instance->storage == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const auto music_dir_fs = instance->storage->MapFS("");
|
||||
const auto music_dir_fs = global_instance->storage->MapFS("");
|
||||
if (music_dir_fs.IsNull())
|
||||
return nullptr;
|
||||
|
||||
@@ -89,10 +84,10 @@ std::string
|
||||
map_fs_to_utf8(Path path_fs) noexcept
|
||||
{
|
||||
if (path_fs.IsAbsolute()) {
|
||||
if (instance->storage == nullptr)
|
||||
if (global_instance->storage == nullptr)
|
||||
return std::string();
|
||||
|
||||
const auto music_dir_fs = instance->storage->MapFS("");
|
||||
const auto music_dir_fs = global_instance->storage->MapFS("");
|
||||
if (music_dir_fs.IsNull())
|
||||
return std::string();
|
||||
|
||||
@@ -124,7 +119,7 @@ map_spl_utf8_to_fs(const char *name) noexcept
|
||||
filename_utf8.append(PLAYLIST_FILE_SUFFIX);
|
||||
|
||||
const auto filename_fs =
|
||||
AllocatedPath::FromUTF8(filename_utf8.c_str());
|
||||
AllocatedPath::FromUTF8(filename_utf8);
|
||||
if (filename_fs.IsNull())
|
||||
return nullptr;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,9 +24,10 @@
|
||||
#ifndef MPD_MAPPER_HXX
|
||||
#define MPD_MAPPER_HXX
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/Compiler.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#define PLAYLIST_FILE_SUFFIX ".m3u"
|
||||
|
||||
@@ -36,9 +37,6 @@ class AllocatedPath;
|
||||
void
|
||||
mapper_init(AllocatedPath &&playlist_dir);
|
||||
|
||||
void
|
||||
mapper_finish() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,6 @@
|
||||
#ifndef MPD_MIX_RAMP_INFO_HXX
|
||||
#define MPD_MIX_RAMP_INFO_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <string>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,11 +17,10 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "MusicBuffer.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
MusicBuffer::MusicBuffer(unsigned num_chunks)
|
||||
:buffer(num_chunks) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,12 +17,11 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
MusicChunkInfo::MusicChunkInfo() noexcept = default;
|
||||
MusicChunkInfo::~MusicChunkInfo() noexcept = default;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,14 +26,13 @@
|
||||
#include "util/WritableBuffer.hxx"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "AudioFormat.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static constexpr size_t CHUNK_SIZE = 4096;
|
||||
|
||||
struct AudioFormat;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,7 +17,6 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "MusicChunkPtr.hxx"
|
||||
#include "MusicBuffer.hxx"
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,6 @@
|
||||
#ifndef MPD_MUSIC_CHUNK_PTR_HXX
|
||||
#define MPD_MUSIC_CHUNK_PTR_HXX
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct MusicChunk;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,10 +17,11 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "MusicPipe.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,11 +25,9 @@
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "AudioFormat.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* A queue of #MusicChunk objects. One party appends chunks at the
|
||||
* tail, and the other consumes them from the head.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,24 +20,33 @@
|
||||
#include "config.h"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "client/Listener.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "input/cache/Manager.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
static constexpr Domain cache_domain("cache");
|
||||
|
||||
Partition::Partition(Instance &_instance,
|
||||
const char *_name,
|
||||
unsigned max_length,
|
||||
unsigned buffer_chunks,
|
||||
AudioFormat configured_audio_format,
|
||||
const ReplayGainConfig &replay_gain_config)
|
||||
const ReplayGainConfig &replay_gain_config) noexcept
|
||||
:instance(_instance),
|
||||
name(_name),
|
||||
listener(new ClientListener(instance.event_loop, *this)),
|
||||
idle_monitor(instance.event_loop, BIND_THIS_METHOD(OnIdleMonitor)),
|
||||
global_events(instance.event_loop, BIND_THIS_METHOD(OnGlobalEvent)),
|
||||
playlist(max_length, *this),
|
||||
outputs(*this),
|
||||
pc(*this, outputs, buffer_chunks,
|
||||
outputs(pc, *this),
|
||||
pc(*this, outputs,
|
||||
instance.input_cache.get(),
|
||||
buffer_chunks,
|
||||
configured_audio_format, replay_gain_config)
|
||||
{
|
||||
UpdateEffectiveReplayGainMode();
|
||||
@@ -46,13 +55,51 @@ Partition::Partition(Instance &_instance,
|
||||
Partition::~Partition() noexcept = default;
|
||||
|
||||
void
|
||||
Partition::EmitIdle(unsigned mask)
|
||||
Partition::BeginShutdown() noexcept
|
||||
{
|
||||
instance.EmitIdle(mask);
|
||||
pc.Kill();
|
||||
listener.reset();
|
||||
}
|
||||
|
||||
static void
|
||||
PrefetchSong(InputCacheManager &cache, const char *uri) noexcept
|
||||
{
|
||||
if (cache.Contains(uri))
|
||||
return;
|
||||
|
||||
FormatDebug(cache_domain, "Prefetch '%s'", uri);
|
||||
|
||||
try {
|
||||
cache.Prefetch(uri);
|
||||
} catch (...) {
|
||||
FormatError(std::current_exception(),
|
||||
"Prefetch '%s' failed", uri);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
PrefetchSong(InputCacheManager &cache, const DetachedSong &song) noexcept
|
||||
{
|
||||
PrefetchSong(cache, song.GetURI());
|
||||
}
|
||||
|
||||
inline void
|
||||
Partition::PrefetchQueue() noexcept
|
||||
{
|
||||
if (!instance.input_cache)
|
||||
return;
|
||||
|
||||
auto &cache = *instance.input_cache;
|
||||
|
||||
int next = playlist.GetNextPosition();
|
||||
if (next >= 0)
|
||||
PrefetchSong(cache, playlist.queue.Get(next));
|
||||
|
||||
// TODO: prefetch more songs
|
||||
}
|
||||
|
||||
void
|
||||
Partition::UpdateEffectiveReplayGainMode()
|
||||
Partition::UpdateEffectiveReplayGainMode() noexcept
|
||||
{
|
||||
auto mode = replay_gain_mode;
|
||||
if (mode == ReplayGainMode::AUTO)
|
||||
@@ -68,7 +115,7 @@ Partition::UpdateEffectiveReplayGainMode()
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
const Database *
|
||||
Partition::GetDatabase() const
|
||||
Partition::GetDatabase() const noexcept
|
||||
{
|
||||
return instance.GetDatabase();
|
||||
}
|
||||
@@ -80,7 +127,7 @@ Partition::GetDatabaseOrThrow() const
|
||||
}
|
||||
|
||||
void
|
||||
Partition::DatabaseModified(const Database &db)
|
||||
Partition::DatabaseModified(const Database &db) noexcept
|
||||
{
|
||||
playlist.DatabaseModified(db);
|
||||
EmitIdle(IDLE_DATABASE);
|
||||
@@ -89,7 +136,7 @@ Partition::DatabaseModified(const Database &db)
|
||||
#endif
|
||||
|
||||
void
|
||||
Partition::TagModified()
|
||||
Partition::TagModified() noexcept
|
||||
{
|
||||
auto song = pc.LockReadTaggedSong();
|
||||
if (song)
|
||||
@@ -103,31 +150,35 @@ Partition::TagModified(const char *uri, const Tag &tag) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
Partition::SyncWithPlayer()
|
||||
Partition::SyncWithPlayer() noexcept
|
||||
{
|
||||
playlist.SyncWithPlayer(pc);
|
||||
|
||||
/* TODO: invoke this function in batches, to let the hard disk
|
||||
spin down in between */
|
||||
PrefetchQueue();
|
||||
}
|
||||
|
||||
void
|
||||
Partition::BorderPause()
|
||||
Partition::BorderPause() noexcept
|
||||
{
|
||||
playlist.BorderPause(pc);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnQueueModified()
|
||||
Partition::OnQueueModified() noexcept
|
||||
{
|
||||
EmitIdle(IDLE_PLAYLIST);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnQueueOptionsChanged()
|
||||
Partition::OnQueueOptionsChanged() noexcept
|
||||
{
|
||||
EmitIdle(IDLE_OPTIONS);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnQueueSongStarted()
|
||||
Partition::OnQueueSongStarted() noexcept
|
||||
{
|
||||
EmitIdle(IDLE_PLAYER);
|
||||
}
|
||||
@@ -151,7 +202,7 @@ Partition::OnBorderPause() noexcept
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
|
||||
Partition::OnMixerVolumeChanged(Mixer &, int) noexcept
|
||||
{
|
||||
InvalidateHardwareVolume();
|
||||
|
||||
@@ -160,7 +211,19 @@ Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnGlobalEvent(unsigned mask)
|
||||
Partition::OnIdleMonitor(unsigned mask) noexcept
|
||||
{
|
||||
/* send "idle" notifications to all subscribed
|
||||
clients */
|
||||
for (auto &client : clients)
|
||||
client.IdleAdd(mask);
|
||||
|
||||
if (mask & (IDLE_PLAYLIST|IDLE_PLAYER|IDLE_MIXER|IDLE_OUTPUT))
|
||||
instance.OnStateModified();
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnGlobalEvent(unsigned mask) noexcept
|
||||
{
|
||||
if ((mask & SYNC_WITH_PLAYER) != 0)
|
||||
SyncWithPlayer();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,9 @@
|
||||
#include "ReplayGainMode.hxx"
|
||||
#include "SingleMode.hxx"
|
||||
#include "Chrono.hxx"
|
||||
#include "util/Compiler.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -39,6 +41,7 @@ struct Instance;
|
||||
class MultipleOutputs;
|
||||
class SongLoader;
|
||||
class ClientListener;
|
||||
class Client;
|
||||
|
||||
/**
|
||||
* A partition of the Music Player Daemon. It is a separate unit with
|
||||
@@ -55,6 +58,16 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
|
||||
std::unique_ptr<ClientListener> listener;
|
||||
|
||||
boost::intrusive::list<Client,
|
||||
boost::intrusive::base_hook<boost::intrusive::list_base_hook<boost::intrusive::tag<Partition>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>>>,
|
||||
boost::intrusive::constant_time_size<false>> clients;
|
||||
|
||||
/**
|
||||
* Monitor for idle events local to this partition.
|
||||
*/
|
||||
MaskMonitor idle_monitor;
|
||||
|
||||
MaskMonitor global_events;
|
||||
|
||||
struct playlist playlist;
|
||||
@@ -70,17 +83,34 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
unsigned max_length,
|
||||
unsigned buffer_chunks,
|
||||
AudioFormat configured_audio_format,
|
||||
const ReplayGainConfig &replay_gain_config);
|
||||
const ReplayGainConfig &replay_gain_config) noexcept;
|
||||
|
||||
~Partition() noexcept;
|
||||
|
||||
void EmitGlobalEvent(unsigned mask) {
|
||||
void BeginShutdown() noexcept;
|
||||
|
||||
void EmitGlobalEvent(unsigned mask) noexcept {
|
||||
global_events.OrMask(mask);
|
||||
}
|
||||
|
||||
void EmitIdle(unsigned mask);
|
||||
/**
|
||||
* Emit an "idle" event to all clients of this partition.
|
||||
*
|
||||
* This method can be called from any thread.
|
||||
*/
|
||||
void EmitIdle(unsigned mask) noexcept {
|
||||
idle_monitor.OrMask(mask);
|
||||
}
|
||||
|
||||
void ClearQueue() {
|
||||
/**
|
||||
* Populate the #InputCacheManager with soon-to-be-played song
|
||||
* files.
|
||||
*
|
||||
* Errors will be logged.
|
||||
*/
|
||||
void PrefetchQueue() noexcept;
|
||||
|
||||
void ClearQueue() noexcept {
|
||||
playlist.Clear(pc);
|
||||
}
|
||||
|
||||
@@ -107,11 +137,11 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
playlist.DeleteRange(pc, start, end);
|
||||
}
|
||||
|
||||
void StaleSong(const char *uri) {
|
||||
void StaleSong(const char *uri) noexcept {
|
||||
playlist.StaleSong(pc, uri);
|
||||
}
|
||||
|
||||
void Shuffle(unsigned start, unsigned end) {
|
||||
void Shuffle(unsigned start, unsigned end) noexcept {
|
||||
playlist.Shuffle(pc, start, end);
|
||||
}
|
||||
|
||||
@@ -141,7 +171,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
playlist.SetPriorityId(pc, song_id, priority);
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
void Stop() noexcept {
|
||||
playlist.Stop(pc);
|
||||
}
|
||||
|
||||
@@ -173,27 +203,27 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
playlist.SeekCurrent(pc, seek_time, relative);
|
||||
}
|
||||
|
||||
void SetRepeat(bool new_value) {
|
||||
void SetRepeat(bool new_value) noexcept {
|
||||
playlist.SetRepeat(pc, new_value);
|
||||
}
|
||||
|
||||
bool GetRandom() const {
|
||||
bool GetRandom() const noexcept {
|
||||
return playlist.GetRandom();
|
||||
}
|
||||
|
||||
void SetRandom(bool new_value) {
|
||||
void SetRandom(bool new_value) noexcept {
|
||||
playlist.SetRandom(pc, new_value);
|
||||
}
|
||||
|
||||
void SetSingle(SingleMode new_value) {
|
||||
void SetSingle(SingleMode new_value) noexcept {
|
||||
playlist.SetSingle(pc, new_value);
|
||||
}
|
||||
|
||||
void SetConsume(bool new_value) {
|
||||
void SetConsume(bool new_value) noexcept {
|
||||
playlist.SetConsume(new_value);
|
||||
}
|
||||
|
||||
void SetReplayGainMode(ReplayGainMode mode) {
|
||||
void SetReplayGainMode(ReplayGainMode mode) noexcept {
|
||||
replay_gain_mode = mode;
|
||||
UpdateEffectiveReplayGainMode();
|
||||
}
|
||||
@@ -202,7 +232,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
* Publishes the effective #ReplayGainMode to all subsystems.
|
||||
* #ReplayGainMode::AUTO is substituted.
|
||||
*/
|
||||
void UpdateEffectiveReplayGainMode();
|
||||
void UpdateEffectiveReplayGainMode() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
/**
|
||||
@@ -210,7 +240,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
* if this MPD configuration has no database (no
|
||||
* music_directory was configured).
|
||||
*/
|
||||
const Database *GetDatabase() const;
|
||||
const Database *GetDatabase() const noexcept;
|
||||
|
||||
const Database &GetDatabaseOrThrow() const;
|
||||
|
||||
@@ -218,14 +248,14 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
* The database has been modified. Propagate the change to
|
||||
* all subsystems.
|
||||
*/
|
||||
void DatabaseModified(const Database &db);
|
||||
void DatabaseModified(const Database &db) noexcept;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A tag in the play queue has been modified by the player
|
||||
* thread. Propagate the change to all subsystems.
|
||||
*/
|
||||
void TagModified();
|
||||
void TagModified() noexcept;
|
||||
|
||||
/**
|
||||
* The tag of the given song has been modified. Propagate the
|
||||
@@ -236,19 +266,19 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
/**
|
||||
* Synchronize the player with the play queue.
|
||||
*/
|
||||
void SyncWithPlayer();
|
||||
void SyncWithPlayer() noexcept;
|
||||
|
||||
/**
|
||||
* Border pause has just been enabled. Change single mode to off
|
||||
* if it was one-shot.
|
||||
*/
|
||||
void BorderPause();
|
||||
void BorderPause() noexcept;
|
||||
|
||||
private:
|
||||
/* virtual methods from class QueueListener */
|
||||
void OnQueueModified() override;
|
||||
void OnQueueOptionsChanged() override;
|
||||
void OnQueueSongStarted() override;
|
||||
void OnQueueModified() noexcept override;
|
||||
void OnQueueOptionsChanged() noexcept override;
|
||||
void OnQueueSongStarted() noexcept override;
|
||||
|
||||
/* virtual methods from class PlayerListener */
|
||||
void OnPlayerSync() noexcept override;
|
||||
@@ -256,10 +286,13 @@ private:
|
||||
void OnBorderPause() noexcept override;
|
||||
|
||||
/* virtual methods from class MixerListener */
|
||||
void OnMixerVolumeChanged(Mixer &mixer, int volume) override;
|
||||
void OnMixerVolumeChanged(Mixer &mixer, int volume) noexcept override;
|
||||
|
||||
/* callback for #idle_monitor */
|
||||
void OnIdleMonitor(unsigned mask) noexcept;
|
||||
|
||||
/* callback for #global_events */
|
||||
void OnGlobalEvent(unsigned mask);
|
||||
void OnGlobalEvent(unsigned mask) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,14 +22,15 @@
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Option.hxx"
|
||||
#include "util/IterableSplitString.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
|
||||
static constexpr char PERMISSION_PASSWORD_CHAR = '@';
|
||||
static constexpr char PERMISSION_SEPARATOR = ',';
|
||||
@@ -54,35 +55,25 @@ static unsigned local_permissions;
|
||||
#endif
|
||||
|
||||
static unsigned
|
||||
ParsePermission(const char *p)
|
||||
ParsePermission(StringView s)
|
||||
{
|
||||
for (auto i = permission_names; i->name != nullptr; ++i)
|
||||
if (strcmp(p, i->name) == 0)
|
||||
if (s.Equals(i->name))
|
||||
return i->value;
|
||||
|
||||
throw FormatRuntimeError("unknown permission \"%s\"", p);
|
||||
throw FormatRuntimeError("unknown permission \"%.*s\"",
|
||||
int(s.size), s.data);
|
||||
}
|
||||
|
||||
static unsigned parsePermissions(const char *string)
|
||||
{
|
||||
assert(string != nullptr);
|
||||
|
||||
const char *const end = string + strlen(string);
|
||||
|
||||
unsigned permission = 0;
|
||||
while (true) {
|
||||
const char *comma = std::find(string, end,
|
||||
PERMISSION_SEPARATOR);
|
||||
if (comma > string) {
|
||||
const std::string name(string, comma);
|
||||
permission |= ParsePermission(name.c_str());
|
||||
}
|
||||
|
||||
if (comma == end)
|
||||
break;
|
||||
|
||||
string = comma + 1;
|
||||
}
|
||||
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
|
||||
if (!i.empty())
|
||||
permission |= ParsePermission(i);
|
||||
|
||||
return permission;
|
||||
}
|
||||
@@ -90,48 +81,44 @@ static unsigned parsePermissions(const char *string)
|
||||
void
|
||||
initPermissions(const ConfigData &config)
|
||||
{
|
||||
unsigned permission;
|
||||
|
||||
permission_default = PERMISSION_READ | PERMISSION_ADD |
|
||||
PERMISSION_CONTROL | PERMISSION_ADMIN;
|
||||
|
||||
for (const auto ¶m : config.GetParamList(ConfigOption::PASSWORD)) {
|
||||
permission_default = 0;
|
||||
|
||||
const char *separator = strchr(param.value.c_str(),
|
||||
param.With([](const char *value){
|
||||
const char *separator = std::strchr(value,
|
||||
PERMISSION_PASSWORD_CHAR);
|
||||
|
||||
if (separator == NULL)
|
||||
throw FormatRuntimeError("\"%c\" not found in password string "
|
||||
"\"%s\", line %i",
|
||||
PERMISSION_PASSWORD_CHAR,
|
||||
param.value.c_str(),
|
||||
param.line);
|
||||
if (separator == nullptr)
|
||||
throw FormatRuntimeError("\"%c\" not found in password string",
|
||||
PERMISSION_PASSWORD_CHAR);
|
||||
|
||||
std::string password(param.value.c_str(), separator);
|
||||
|
||||
permission = parsePermissions(separator + 1);
|
||||
std::string password(value, separator);
|
||||
|
||||
unsigned permission = parsePermissions(separator + 1);
|
||||
permission_passwords.insert(std::make_pair(std::move(password),
|
||||
permission));
|
||||
});
|
||||
}
|
||||
|
||||
const ConfigParam *param;
|
||||
param = config.GetParam(ConfigOption::DEFAULT_PERMS);
|
||||
|
||||
if (param)
|
||||
permission_default = parsePermissions(param->value.c_str());
|
||||
config.With(ConfigOption::DEFAULT_PERMS, [](const char *value){
|
||||
if (value != nullptr)
|
||||
permission_default = parsePermissions(value);
|
||||
});
|
||||
|
||||
#ifdef HAVE_UN
|
||||
param = config.GetParam(ConfigOption::LOCAL_PERMISSIONS);
|
||||
if (param != nullptr)
|
||||
local_permissions = parsePermissions(param->value.c_str());
|
||||
else
|
||||
local_permissions = permission_default;
|
||||
local_permissions = config.With(ConfigOption::LOCAL_PERMISSIONS, [](const char *value){
|
||||
return value != nullptr
|
||||
? parsePermissions(value)
|
||||
: permission_default;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
int getPermissionFromPassword(char const* password, unsigned* permission)
|
||||
int
|
||||
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
|
||||
{
|
||||
auto i = permission_passwords.find(password);
|
||||
if (i == permission_passwords.end())
|
||||
@@ -141,7 +128,8 @@ int getPermissionFromPassword(char const* password, unsigned* permission)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned getDefaultPermissions(void)
|
||||
unsigned
|
||||
getDefaultPermissions() noexcept
|
||||
{
|
||||
return permission_default;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_PERMISSION_HXX
|
||||
#define MPD_PERMISSION_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "config.h"
|
||||
|
||||
struct ConfigData;
|
||||
|
||||
@@ -30,10 +30,11 @@ static constexpr unsigned PERMISSION_ADD = 2;
|
||||
static constexpr unsigned PERMISSION_CONTROL = 4;
|
||||
static constexpr unsigned PERMISSION_ADMIN = 8;
|
||||
|
||||
int getPermissionFromPassword(char const* password, unsigned* permission);
|
||||
int
|
||||
getPermissionFromPassword(const char *password, unsigned *permission) noexcept;
|
||||
|
||||
unsigned
|
||||
getDefaultPermissions();
|
||||
getDefaultPermissions() noexcept;
|
||||
|
||||
#ifdef HAVE_UN
|
||||
unsigned
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,16 +17,16 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistDatabase.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
@@ -50,8 +50,8 @@ playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
|
||||
const char *value;
|
||||
|
||||
while ((line = file.ReadLine()) != nullptr &&
|
||||
strcmp(line, "playlist_end") != 0) {
|
||||
colon = strchr(line, ':');
|
||||
std::strcmp(line, "playlist_end") != 0) {
|
||||
colon = std::strchr(line, ':');
|
||||
if (colon == nullptr || colon == line)
|
||||
throw FormatRuntimeError("unknown line in db: %s",
|
||||
line);
|
||||
@@ -59,7 +59,7 @@ playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
|
||||
*colon++ = 0;
|
||||
value = StripLeft(colon);
|
||||
|
||||
if (strcmp(line, "mtime") == 0)
|
||||
if (std::strcmp(line, "mtime") == 0)
|
||||
pm.mtime = std::chrono::system_clock::from_time_t(strtol(value, nullptr, 10));
|
||||
else
|
||||
throw FormatRuntimeError("unknown line in db: %s",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,6 @@
|
||||
#ifndef MPD_PLAYLIST_DATABASE_HXX
|
||||
#define MPD_PLAYLIST_DATABASE_HXX
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#define PLAYLIST_META_BEGIN "playlist_begin: "
|
||||
|
||||
class PlaylistVector;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,7 +17,6 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -39,15 +39,11 @@
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
#include "util/Macros.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
static const char PLAYLIST_COMMENT = '#';
|
||||
|
||||
@@ -84,9 +80,9 @@ spl_valid_name(const char *name_utf8)
|
||||
* filenames isn't going to happen, either.
|
||||
*/
|
||||
|
||||
return strchr(name_utf8, '/') == nullptr &&
|
||||
strchr(name_utf8, '\n') == nullptr &&
|
||||
strchr(name_utf8, '\r') == nullptr;
|
||||
return std::strchr(name_utf8, '/') == nullptr &&
|
||||
std::strchr(name_utf8, '\n') == nullptr &&
|
||||
std::strchr(name_utf8, '\r') == nullptr;
|
||||
}
|
||||
|
||||
static const AllocatedPath &
|
||||
@@ -134,7 +130,9 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
||||
const auto *const name_fs_end =
|
||||
FindStringSuffix(name_fs_str,
|
||||
PATH_LITERAL(PLAYLIST_FILE_SUFFIX));
|
||||
if (name_fs_end == nullptr)
|
||||
if (name_fs_end == nullptr ||
|
||||
/* no empty playlist names (raw file name = ".m3u") */
|
||||
name_fs_end == name_fs_str)
|
||||
return false;
|
||||
|
||||
FileInfo fi;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,17 +17,11 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistPrint.hxx"
|
||||
#include "PlaylistFile.hxx"
|
||||
#include "PlaylistError.hxx"
|
||||
#include "queue/Playlist.hxx"
|
||||
#include "queue/QueuePrint.hxx"
|
||||
#include "SongPrint.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "client/Response.hxx"
|
||||
|
||||
#define SONG_FILE "file: "
|
||||
#define SONG_TIME "Time: "
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_PLAYLIST_PRINT_HXX
|
||||
#define MPD_PLAYLIST_PRINT_HXX
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
struct playlist;
|
||||
class SongFilter;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,9 +30,7 @@
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
|
||||
#include <exception>
|
||||
#include "util/UriExtract.hxx"
|
||||
|
||||
static void
|
||||
playlist_print_path(BufferedOutputStream &os, const Path path)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user