Compare commits
2032 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
508e522188 | ||
![]() |
b1b630a4cc | ||
![]() |
c60d374fc8 | ||
![]() |
de4fd4c059 | ||
![]() |
95d8b30864 | ||
![]() |
eb94f409d5 | ||
![]() |
93d91936b5 | ||
![]() |
2220383d83 | ||
![]() |
3231706628 | ||
![]() |
ca4e53859d | ||
![]() |
61120d2059 | ||
![]() |
cc1822810f | ||
![]() |
a21c6884f2 | ||
![]() |
2700eed08d | ||
![]() |
ec2badbedd | ||
![]() |
054a7557fa | ||
![]() |
977a4570d9 | ||
![]() |
6c2077eb7c | ||
![]() |
1d436b3c86 | ||
![]() |
f86b14bfc5 | ||
![]() |
ec5be91ff6 | ||
![]() |
a7a9490a0c | ||
![]() |
c0d6008781 | ||
![]() |
9f62824e98 | ||
![]() |
b824ba3299 | ||
![]() |
59c4f9a089 | ||
![]() |
c673528cff | ||
![]() |
321f01b95c | ||
![]() |
e88667e01c | ||
![]() |
fb96907b52 | ||
![]() |
09ece26200 | ||
![]() |
0c6d22fe47 | ||
![]() |
c563eb81a3 | ||
![]() |
e864a0dd05 | ||
![]() |
42a05bc904 | ||
![]() |
4722175049 | ||
![]() |
3a901098e9 | ||
![]() |
a66097129d | ||
![]() |
eed4e40ec6 | ||
![]() |
6de57b36c7 | ||
![]() |
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 |
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
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,3 +6,6 @@
|
||||
/output/
|
||||
|
||||
__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:
|
||||
|
||||
|
388
NEWS
388
NEWS
@@ -1,3 +1,391 @@
|
||||
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
|
||||
|
@@ -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
|
||||
|
@@ -2,18 +2,26 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="29"
|
||||
android:versionName="0.21.7">
|
||||
android:versionCode="57"
|
||||
android:versionName="0.22.9">
|
||||
|
||||
<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" />
|
||||
|
@@ -25,25 +25,32 @@ android_abis = {
|
||||
'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 -march=armv7-a -mfpu=vfpv3-d16 -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',
|
||||
'toolchain_arch': 'x86_64',
|
||||
'llvm_triple': 'x86_64-linux-android',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -76,37 +83,33 @@ 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 + ' -gcc-toolchain ' + toolchain_path
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
# required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments
|
||||
common_flags += ' -fno-addrsig'
|
||||
|
||||
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')
|
||||
@@ -114,15 +117,11 @@ class AndroidNdkToolchain:
|
||||
|
||||
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 +129,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 +168,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() {
|
||||
|
||||
|
@@ -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,7 +38,7 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21.7'
|
||||
version = '0.22.9'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
@@ -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,6 +1,13 @@
|
||||
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 = 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',
|
||||
@@ -15,19 +22,16 @@ sphinx_output = custom_target(
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('manpages')
|
||||
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=*',
|
||||
],
|
||||
'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)`
|
@@ -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"
|
||||
|
473
doc/plugins.rst
473
doc/plugins.rst
@@ -4,10 +4,10 @@ Plugin reference
|
||||
.. _database_plugins:
|
||||
|
||||
Database plugins
|
||||
----------------
|
||||
================
|
||||
|
||||
simple
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
The default plugin. Stores a copy of the database in memory. A file is used for permanent storage.
|
||||
|
||||
@@ -25,9 +25,9 @@ The default plugin. Stores a copy of the database in memory. A file is used for
|
||||
- Compress the database file using gzip? Enabled by default (if built with zlib).
|
||||
|
||||
proxy
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Provides access to the database of another :program:`MPD` instance using libmpdclient. This is useful when you run mount the music directory via NFS/SMB, and the file server already runs a :program:`MPD` instance. Only the file server needs to update the database.
|
||||
Provides access to the database of another :program:`MPD` instance using libmpdclient. This is useful when you mount the music directory via NFS/SMB, and the file server already runs a :program:`MPD` instance. Only the file server needs to update the database.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
@@ -42,46 +42,51 @@ Provides access to the database of another :program:`MPD` instance using libmpdc
|
||||
* - **password**
|
||||
- The password used to log in to the "master" :program:`MPD` instance.
|
||||
* - **keepalive yes|no**
|
||||
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expensive of a very small amount of additional network traffic. Disabled by default.
|
||||
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expense of a very small amount of additional network traffic. Disabled by default.
|
||||
|
||||
upnp
|
||||
~~~~
|
||||
----
|
||||
|
||||
Provides access to UPnP media servers.
|
||||
|
||||
Storage plugins
|
||||
---------------
|
||||
===============
|
||||
|
||||
local
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
The default plugin which gives :program:`MPD` access to local files. It is used when music_directory refers to a local directory.
|
||||
|
||||
curl
|
||||
~~~~
|
||||
----
|
||||
|
||||
A WebDAV client using libcurl. It is used when :code:`music_directory` contains a http:// or https:// URI, for example :samp:`https://the.server/dav/`.
|
||||
A WebDAV client using libcurl. It is used when :code:`music_directory`
|
||||
contains a ``http://`` or ``https://`` URI, for example
|
||||
:samp:`https://the.server/dav/`.
|
||||
|
||||
smbclient
|
||||
~~~~~~~~~
|
||||
---------
|
||||
|
||||
Load music files from a SMB/CIFS server. It is used when :code:`music_directory` contains a smb:// URI, for example :samp:`smb://myfileserver/Music`.
|
||||
Load music files from a SMB/CIFS server. It is used when
|
||||
:code:`music_directory` contains a ``smb://`` URI, for example
|
||||
:samp:`smb://myfileserver/Music`.
|
||||
|
||||
Note that :file:`libsmbclient` has a serious bug which causes MPD to
|
||||
crash, and therefore this plugin is disabled by default and should not
|
||||
be used until the bug is fixed:
|
||||
https://bugzilla.samba.org/show_bug.cgi?id=11413
|
||||
|
||||
nfs
|
||||
~~~
|
||||
---
|
||||
|
||||
Load music files from a NFS server. It is used when :code:`music_directory` contains a nfs:// URI according to RFC2224, for example :samp:`nfs://servername/path`.
|
||||
Load music files from a NFS server. It is used when
|
||||
:code:`music_directory` contains a ``nfs://`` URI according to
|
||||
RFC2224, for example :samp:`nfs://servername/path`.
|
||||
|
||||
This plugin uses libnfs, which supports only NFS version 3. Since :program:`MPD` is not allowed to bind to "privileged ports", the NFS server needs to enable the "insecure" setting; example :file:`/etc/exports`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
/srv/mp3 192.168.1.55(ro,insecure)
|
||||
|
||||
Don't fear: "insecure" does not mean that your NFS server is insecure. A few decades ago, people thought the concept of "privileged ports" would make network services "secure", which was a fallacy. The absence of this obsolete "security" measure means little.
|
||||
See :ref:`input_nfs` for more information.
|
||||
|
||||
udisks
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Mount file systems (e.g. USB sticks or other removable media) using
|
||||
the udisks2 daemon via D-Bus. To obtain a valid udisks2 URI, consult
|
||||
@@ -106,42 +111,67 @@ MPD user.
|
||||
.. _neighbor_plugin:
|
||||
|
||||
Neighbor plugins
|
||||
----------------
|
||||
================
|
||||
|
||||
smbclient
|
||||
~~~~~~~~~
|
||||
---------
|
||||
|
||||
Provides a list of SMB/CIFS servers on the local network.
|
||||
|
||||
udisks
|
||||
~~~~~~
|
||||
Queries the udisks2 daemon via D-Bus and obtain a list of file systems (e.g. USB sticks or other removable media).
|
||||
------
|
||||
|
||||
Queries the udisks2 daemon via D-Bus and obtains a list of file systems (e.g. USB sticks or other removable media).
|
||||
|
||||
upnp
|
||||
~~~~
|
||||
----
|
||||
|
||||
Provides a list of UPnP servers on the local network.
|
||||
|
||||
.. _input_plugins:
|
||||
|
||||
Input plugins
|
||||
-------------
|
||||
=============
|
||||
|
||||
alsa
|
||||
~~~~
|
||||
----
|
||||
|
||||
Allows :program:`MPD` on Linux to play audio directly from a soundcard using the scheme alsa://. Audio is formatted as 44.1 kHz 16-bit stereo (CD format). Examples:
|
||||
Allows :program:`MPD` on Linux to play audio directly from a soundcard using the scheme alsa://. Audio is by default formatted as 48 kHz 16-bit stereo, but this default can be overidden by a config file setting or by the URI. Examples:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add alsa:// plays audio from device hw:0,0
|
||||
mpc add alsa:// plays audio from device default
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add alsa://hw:1,0 plays audio from device hw:1,0 cdio_paranoia
|
||||
mpc add alsa://hw:1,0 plays audio from device hw:1,0
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add alsa://hw:1,0?format=44100:16:2 plays audio from device hw:1,0 sampling 16-bit stereo at 44.1kHz.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **default_device NAME**
|
||||
- The alsa device id to use when none is specified in the URI.
|
||||
* - **default_format F**
|
||||
- The sampling rate, size and channels to use. Wildcards are not allowed.
|
||||
|
||||
Example - "44100:16:2"
|
||||
|
||||
* - **auto_resample yes|no**
|
||||
- If set to no, then libasound will not attempt to resample. In this case, the user is responsible for ensuring that the requested sample rate can be produced natively by the device, otherwise an error will occur.
|
||||
* - **auto_channels yes|no**
|
||||
- If set to no, then libasound will not attempt to convert between different channel numbers. The user must ensure that the device supports the requested channels when sampling.
|
||||
* - **auto_format yes|no**
|
||||
- If set to no, then libasound will not attempt to convert between different sample formats (16 bit, 24 bit, floating point, ...). Again the user must ensure that the requested format is available natively from the device.
|
||||
|
||||
cdio_paranoia
|
||||
~~~~~~~~~~~~~
|
||||
-------------
|
||||
|
||||
Plays audio CDs using libcdio. The URI has the form: "cdda://[DEVICE][/TRACK]". The simplest form cdda:// plays the whole disc in the default drive.
|
||||
|
||||
@@ -157,11 +187,14 @@ Plays audio CDs using libcdio. The URI has the form: "cdda://[DEVICE][/TRACK]".
|
||||
- Request CDParanoia cap the extraction speed to Nx normal CD audio rotation speed, keeping the drive quiet.
|
||||
|
||||
curl
|
||||
~~~~
|
||||
----
|
||||
|
||||
Opens remote files or streams over HTTP using libcurl.
|
||||
|
||||
Note that unless overridden by the below settings (e.g. by setting them to a blank value), general curl configuration from environment variables such as http_proxy or specified in :file:`~/.curlrc` will be in effect.
|
||||
Note that unless overridden by the below settings (e.g. by setting
|
||||
them to a blank value), general curl configuration from environment
|
||||
variables such as ``http_proxy`` or specified in :file:`~/.curlrc`
|
||||
will be in effect.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
@@ -179,44 +212,67 @@ Note that unless overridden by the below settings (e.g. by setting them to a bla
|
||||
- Verify the certificate's name against host? `More information <http://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html>`_.
|
||||
|
||||
ffmpeg
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Access to various network protocols implemented by the FFmpeg library: gopher://, rtp://, rtsp://, rtmp://, rtmpt://, rtmps://
|
||||
Access to various network protocols implemented by the FFmpeg library:
|
||||
``gopher://``, ``rtp://``, ``rtsp://``, ``rtmp://``, ``rtmpt://``,
|
||||
``rtmps://``
|
||||
|
||||
file
|
||||
~~~~
|
||||
----
|
||||
|
||||
Opens local files
|
||||
|
||||
mms
|
||||
~~~
|
||||
---
|
||||
|
||||
Plays streams with the MMS protocol using `libmms <https://launchpad.net/libmms>`_.
|
||||
|
||||
nfs
|
||||
~~~
|
||||
.. _input_nfs:
|
||||
|
||||
Allows :program:`MPD` to access files on NFSv3 servers without actually mounting them (i.e. in userspace, without help from the kernel's VFS layer). All URIs with the nfs:// scheme are used according to RFC2224. Example:
|
||||
nfs
|
||||
---
|
||||
|
||||
Allows :program:`MPD` to access files on NFS servers without actually
|
||||
mounting them (i.e. with :program:`libnfs` in userspace, without help
|
||||
from the kernel's VFS layer). All URIs with the ``nfs://`` scheme are
|
||||
used according to RFC2224. Example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add nfs://servername/path/filename.ogg
|
||||
|
||||
Note that this usually requires enabling the "insecure" flag in the server's /etc/exports file, because :program:`MPD` cannot bind to so-called "privileged" ports. Don't fear: this will not make your file server insecure; the flag was named in a time long ago when privileged ports were thought to be meaningful for security. By today's standards, NFSv3 is not secure at all, and if you believe it is, you're already doomed.
|
||||
This plugin uses :program:`libnfs`, which supports only NFS version 3.
|
||||
Since :program:`MPD` is not allowed to bind to so-called "privileged
|
||||
ports", the NFS server needs to enable the ``insecure`` setting;
|
||||
example :file:`/etc/exports`:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
/srv/mp3 192.168.1.55(ro,insecure)
|
||||
|
||||
Don't fear: this will not make your file server insecure; the flag was
|
||||
named a time long ago when privileged ports were thought to be
|
||||
meaningful for security. By today's standards, NFSv3 is not secure at
|
||||
all, and if you believe it is, you're already doomed.
|
||||
|
||||
smbclient
|
||||
~~~~~~~~~
|
||||
---------
|
||||
|
||||
Allows :program:`MPD` to access files on SMB/CIFS servers (e.g. Samba or Microsoft Windows). All URIs with the smb:// scheme are used. Example:
|
||||
Allows :program:`MPD` to access files on SMB/CIFS servers (e.g. Samba
|
||||
or Microsoft Windows). All URIs with the ``smb://`` scheme are
|
||||
used. Example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add smb://servername/sharename/filename.ogg
|
||||
mpc add smb://username:password@servername/sharename/filename.ogg
|
||||
|
||||
qobuz
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Play songs from the commercial streaming service Qobuz. It plays URLs in the form qobuz://track/ID, e.g.:
|
||||
Play songs from the commercial streaming service Qobuz. It plays URLs
|
||||
in the form ``qobuz://track/ID``, e.g.:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -240,9 +296,16 @@ Play songs from the commercial streaming service Qobuz. It plays URLs in the for
|
||||
- The `Qobuz format identifier <https://github.com/Qobuz/api-documentation/blob/master/endpoints/track/getFileUrl.md#parameters>`_, i.e. a number which chooses the format and quality to be requested from Qobuz. The default is "5" (320 kbit/s MP3).
|
||||
|
||||
tidal
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Play songs from the commercial streaming service `Tidal <http://tidal.com/>`_. It plays URLs in the form tidal://track/ID, e.g.:
|
||||
Play songs from the commercial streaming service `Tidal
|
||||
<http://tidal.com/>`_. It plays URLs in the form ``tidal://track/ID``,
|
||||
e.g.:
|
||||
|
||||
.. warning::
|
||||
|
||||
This plugin is currently defunct because Tidal has changed the
|
||||
protocol and decided not to share documentation.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -266,10 +329,10 @@ Play songs from the commercial streaming service `Tidal <http://tidal.com/>`_. I
|
||||
.. _decoder_plugins:
|
||||
|
||||
Decoder plugins
|
||||
---------------
|
||||
===============
|
||||
|
||||
adplug
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Decodes AdLib files using libadplug.
|
||||
|
||||
@@ -283,17 +346,19 @@ Decodes AdLib files using libadplug.
|
||||
- The sample rate that shall be synthesized by the plugin. Defaults to 48000.
|
||||
|
||||
audiofile
|
||||
~~~~~~~~~
|
||||
---------
|
||||
|
||||
Decodes WAV and AIFF files using libaudiofile.
|
||||
|
||||
faad
|
||||
~~~~
|
||||
----
|
||||
|
||||
Decodes AAC files using libfaad.
|
||||
|
||||
.. _decoder_ffmpeg:
|
||||
|
||||
ffmpeg
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Decodes various codecs using FFmpeg.
|
||||
|
||||
@@ -309,14 +374,18 @@ Decodes various codecs using FFmpeg.
|
||||
- Sets the FFmpeg muxer option probesize, which specifies probing size in bytes, i.e. the size of the data to analyze to get stream information. The `FFmpeg formats documentation <https://ffmpeg.org/ffmpeg-formats.html>`_ has more information.
|
||||
|
||||
flac
|
||||
~~~~
|
||||
----
|
||||
|
||||
Decodes FLAC files using libFLAC.
|
||||
|
||||
dsdiff
|
||||
~~~~~~
|
||||
.. _decoder_dsdiff:
|
||||
|
||||
Decodes DFF files containing DSDIFF data (e.g. SACD rips).
|
||||
dsdiff
|
||||
------
|
||||
|
||||
Decodes DSDIFF (`Direct Stream Digital Interchange File Format
|
||||
<http://www.sonicstudio.com/pdf/dsd/DSDIFF_1.5_Spec.pdf>`_) files
|
||||
(:file:`*.dff`). These contain :ref:`DSD <dsd>` instead of PCM.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
@@ -327,13 +396,17 @@ Decodes DFF files containing DSDIFF data (e.g. SACD rips).
|
||||
* - **lsbitfirst yes|no**
|
||||
- Decode the least significant bit first. Default is no.
|
||||
|
||||
dsf
|
||||
~~~
|
||||
.. _decoder_dsf:
|
||||
|
||||
Decodes DSF files containing DSDIFF data (e.g. SACD rips).
|
||||
dsf
|
||||
---
|
||||
|
||||
Decodes DSF
|
||||
(<https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf>)
|
||||
files (:file:`*.dsf`). These contain :ref:`DSD <dsd>` instead of PCM.
|
||||
|
||||
fluidsynth
|
||||
~~~~~~~~~~
|
||||
----------
|
||||
|
||||
MIDI decoder based on `FluidSynth <http://www.fluidsynth.org/>`_.
|
||||
|
||||
@@ -349,7 +422,7 @@ MIDI decoder based on `FluidSynth <http://www.fluidsynth.org/>`_.
|
||||
- The absolute path of the soundfont file. Defaults to :file:`/usr/share/sounds/sf2/FluidR3_GM.sf2`.
|
||||
|
||||
gme
|
||||
~~~
|
||||
---
|
||||
|
||||
Video game music file emulator based on `game-music-emu <https://bitbucket.org/mpyne/game-music-emu/wiki/Home>`_.
|
||||
|
||||
@@ -361,37 +434,29 @@ Video game music file emulator based on `game-music-emu <https://bitbucket.org/m
|
||||
- Description
|
||||
* - **accuracy yes|no**
|
||||
- Enable more accurate sound emulation.
|
||||
* - **default_fade**
|
||||
- The default fade-out time, in seconds. Used by songs that don't specify their own fade-out time.
|
||||
|
||||
hybrid_dsd
|
||||
~~~~~~~~~~
|
||||
----------
|
||||
|
||||
`Hybrid-DSD
|
||||
<http://dsdmaster.blogspot.de/p/bitperfect-introduces-hybrid-dsd-file.html>`_
|
||||
is a MP4 container file (:file:`*.m4a`) which contains both ALAC and
|
||||
is an MP4 container file (:file:`*.m4a`) which contains both ALAC and
|
||||
DSD data. It is disabled by default, and works only if you explicitly
|
||||
enable it. Without this plugin, the ALAC parts gets handled by the
|
||||
`FFmpeg decoder plugin
|
||||
<https://www.musicpd.org/doc/user/decoder_plugins.html#ffmpeg_decoder>`_. This
|
||||
:ref:`FFmpeg decoder plugin <decoder_ffmpeg>`. This
|
||||
plugin should be enabled only if you have a bit-perfect playback path
|
||||
to a DSD-capable DAC; for everybody else, playing back the ALAC copy
|
||||
of the file is better.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **gapless yes|no**
|
||||
- This specifies whether to support gapless playback of MP3s which have the necessary headers. Useful if your MP3s have headers with incorrect information. If you have such MP3s, it is highly recommended that you fix them using `vbrfix <http://www.willwap.co.uk/Programs/vbrfix.php>`_ instead of disabling gapless MP3 playback. The default is to support gapless MP3 playback.
|
||||
|
||||
mad
|
||||
~~~
|
||||
---
|
||||
|
||||
Decodes MP3 files using `libmad <http://www.underbit.com/products/mad/>`_.
|
||||
|
||||
mikmod
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Module player based on `MikMod <http://mikmod.sourceforge.net/>`_.
|
||||
|
||||
@@ -407,7 +472,7 @@ Module player based on `MikMod <http://mikmod.sourceforge.net/>`_.
|
||||
- Sets the sample rate generated by libmikmod. Default is 44100.
|
||||
|
||||
modplug
|
||||
~~~~~~~
|
||||
-------
|
||||
|
||||
Module player based on MODPlug.
|
||||
|
||||
@@ -421,27 +486,29 @@ Module player based on MODPlug.
|
||||
- Number of times to loop the module if it uses backward loops. Default is 0 which prevents looping. -1 loops forever.
|
||||
|
||||
mpcdec
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Decodes Musepack files using `libmpcdec <http://www.musepack.net/>`_.
|
||||
|
||||
mpg123
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Decodes MP3 files using `libmpg123 <http://www.mpg123.de/>`_.
|
||||
Decodes MP3 files using `libmpg123 <http://www.mpg123.de/>`_. Currently, this
|
||||
decoder does not support streams (e.g. archived files, remote files over HTTP,
|
||||
...), only regular local files.
|
||||
|
||||
opus
|
||||
~~~~
|
||||
----
|
||||
|
||||
Decodes Opus files using `libopus <http://www.opus-codec.org/>`_.
|
||||
|
||||
pcm
|
||||
~~~
|
||||
---
|
||||
|
||||
Read raw PCM samples. It understands the "audio/L16" MIME type with parameters "rate" and "channels" according to RFC 2586. It also understands the MPD-specific MIME type "audio/x-mpd-float".
|
||||
Reads raw PCM samples. It understands the "audio/L16" MIME type with parameters "rate" and "channels" according to RFC 2586. It also understands the MPD-specific MIME type "audio/x-mpd-float".
|
||||
|
||||
sidplay
|
||||
~~~~~~~
|
||||
-------
|
||||
|
||||
C64 SID decoder based on `libsidplayfp <https://sourceforge.net/projects/sidplay-residfp/>`_ or `libsidplay2 <https://sourceforge.net/projects/sidplay2/>`_.
|
||||
|
||||
@@ -452,9 +519,11 @@ C64 SID decoder based on `libsidplayfp <https://sourceforge.net/projects/sidplay
|
||||
* - Setting
|
||||
- Description
|
||||
* - **songlength_database PATH**
|
||||
- Location of your songlengths file, as distributed with the HVSC. The sidplay plugin checks this for matching MD5 fingerprints. See http://www.hvsc.c64.org/download/C64Music/DOCUMENTS/Songlengths.faq.
|
||||
- Location of your songlengths file, as distributed with the HVSC. The sidplay plugin checks this for matching MD5 fingerprints. See http://www.hvsc.c64.org/download/C64Music/DOCUMENTS/Songlengths.faq. New songlength format support requires libsidplayfp 2.0 or later.
|
||||
* - **default_songlength SECONDS**
|
||||
- This is the default playing time in seconds for songs not in the songlength database, or in case you're not using a database. A value of 0 means play indefinitely.
|
||||
* - **default_genre GENRE**
|
||||
- Optional default genre for SID songs.
|
||||
* - **filter yes|no**
|
||||
- Turns the SID filter emulation on or off.
|
||||
* - **kernal**
|
||||
@@ -463,23 +532,23 @@ C64 SID decoder based on `libsidplayfp <https://sourceforge.net/projects/sidplay
|
||||
- Only libsidplayfp. Absolute path to basic rom image file.
|
||||
|
||||
sndfile
|
||||
~~~~~~~
|
||||
-------
|
||||
|
||||
Decodes WAV and AIFF files using `libsndfile <http://www.mega-nerd.com/libsndfile/>`_.
|
||||
|
||||
|
||||
vorbis
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Decodes Ogg-Vorbis files using `libvorbis <http://www.xiph.org/ogg/vorbis/>`_.
|
||||
|
||||
wavpack
|
||||
~~~~~~~
|
||||
-------
|
||||
|
||||
Decodes WavPack files using `libwavpack <http://www.wavpack.com/>`_.
|
||||
|
||||
wildmidi
|
||||
~~~~~~~~
|
||||
--------
|
||||
|
||||
MIDI decoder based on `libwildmidi <http://www.mindwerks.net/projects/wildmidi/>`_.
|
||||
|
||||
@@ -495,10 +564,11 @@ MIDI decoder based on `libwildmidi <http://www.mindwerks.net/projects/wildmidi/>
|
||||
.. _encoder_plugins:
|
||||
|
||||
Encoder plugins
|
||||
---------------
|
||||
===============
|
||||
|
||||
flac
|
||||
~~~~
|
||||
----
|
||||
|
||||
Encodes into `FLAC <https://xiph.org/flac/>`_ (lossless).
|
||||
|
||||
.. list-table::
|
||||
@@ -511,7 +581,7 @@ Encodes into `FLAC <https://xiph.org/flac/>`_ (lossless).
|
||||
- Sets the libFLAC compression level. The levels range from 0 (fastest, least compression) to 8 (slowest, most compression).
|
||||
|
||||
lame
|
||||
~~~~
|
||||
----
|
||||
|
||||
Encodes into MP3 using the `LAME <http://lame.sourceforge.net/>`_ library.
|
||||
|
||||
@@ -527,12 +597,12 @@ Encodes into MP3 using the `LAME <http://lame.sourceforge.net/>`_ library.
|
||||
- Sets the bit rate in kilobit per second. Cannot be used with quality.
|
||||
|
||||
null
|
||||
~~~~
|
||||
----
|
||||
|
||||
Does not encode anything, passes the input PCM data as-is.
|
||||
|
||||
shine
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Encodes into MP3 using the `Shine <https://github.com/savonet/shine>`_ library.
|
||||
|
||||
@@ -546,7 +616,7 @@ Encodes into MP3 using the `Shine <https://github.com/savonet/shine>`_ library.
|
||||
- Sets the bit rate in kilobit per second.
|
||||
|
||||
twolame
|
||||
~~~~~~~
|
||||
-------
|
||||
|
||||
Encodes into MP2 using the `TwoLAME <http://www.twolame.org/>`_ library.
|
||||
|
||||
@@ -562,7 +632,7 @@ Encodes into MP2 using the `TwoLAME <http://www.twolame.org/>`_ library.
|
||||
- Sets the bit rate in kilobit per second. Cannot be used with quality.
|
||||
|
||||
opus
|
||||
~~~~
|
||||
----
|
||||
|
||||
Encodes into `Ogg Opus <http://www.opus-codec.org/>`_.
|
||||
|
||||
@@ -584,7 +654,7 @@ Encodes into `Ogg Opus <http://www.opus-codec.org/>`_.
|
||||
.. _vorbis_plugin:
|
||||
|
||||
vorbis
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
Encodes into `Ogg Vorbis <http://www.vorbis.com/>`_.
|
||||
|
||||
@@ -600,13 +670,13 @@ Encodes into `Ogg Vorbis <http://www.vorbis.com/>`_.
|
||||
- Sets the bit rate in kilobit per second. Cannot be used with quality.
|
||||
|
||||
wave
|
||||
~~~~
|
||||
----
|
||||
Encodes into WAV (lossless).
|
||||
|
||||
.. _resampler_plugins:
|
||||
|
||||
Resampler plugins
|
||||
-----------------
|
||||
=================
|
||||
|
||||
The resampler can be configured in a block named resampler, for example:
|
||||
|
||||
@@ -629,12 +699,12 @@ The following table lists the resampler options valid for all plugins:
|
||||
- The name of the plugin.
|
||||
|
||||
internal
|
||||
~~~~~~~~
|
||||
--------
|
||||
|
||||
A resampler built into :program:`MPD`. Its quality is very poor, but its CPU usage is low. This is the fallback if :program:`MPD` was compiled without an external resampler.
|
||||
|
||||
libsamplerate
|
||||
~~~~~~~~~~~~~
|
||||
-------------
|
||||
|
||||
A resampler using `libsamplerate <http://www.mega-nerd.com/SRC/>`_ a.k.a. Secret Rabbit Code (SRC).
|
||||
|
||||
@@ -645,7 +715,7 @@ A resampler using `libsamplerate <http://www.mega-nerd.com/SRC/>`_ a.k.a. Secret
|
||||
* - Name
|
||||
- Description
|
||||
* - **type**
|
||||
- The interpolator type. See below for a list of known types.
|
||||
- The interpolator type. Defaults to :samp:`2`. See below for a list of known types.
|
||||
|
||||
The following converter types are provided by libsamplerate:
|
||||
|
||||
@@ -667,7 +737,7 @@ The following converter types are provided by libsamplerate:
|
||||
- Linear interpolator, very fast, poor quality.
|
||||
|
||||
soxr
|
||||
~~~~
|
||||
----
|
||||
|
||||
A resampler using `libsoxr <http://sourceforge.net/projects/soxr/>`_, the SoX Resampler library
|
||||
|
||||
@@ -689,16 +759,39 @@ Valid quality values for libsoxr:
|
||||
* "medium"
|
||||
* "low"
|
||||
* "quick"
|
||||
* "custom"
|
||||
|
||||
If the quality is set to custom also the following settings are available:
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Description
|
||||
* - **precision**
|
||||
- The precision in bits. Valid values 16,20,24,28 and 32 bits.
|
||||
* - **phase_response**
|
||||
- Between the 0-100, Where 0=MINIMUM_PHASE and 50=LINEAR_PHASE.
|
||||
* - **passband_end**
|
||||
- The % of source bandwidth where to start filtering. Typical between the 90-99.7.
|
||||
* - **stopband_begin**
|
||||
- The % of the source bandwidth Where the anti aliasing filter start. Value 100+.
|
||||
* - **attenuation**
|
||||
- Reduction in dB's to prevent clipping from the resampling process.
|
||||
* - **flags**
|
||||
- Bitmask with additional option see soxr documentation for specific flags.
|
||||
|
||||
|
||||
.. _output_plugins:
|
||||
|
||||
Output plugins
|
||||
--------------
|
||||
==============
|
||||
|
||||
.. _alsa_plugin:
|
||||
|
||||
alsa
|
||||
~~~~
|
||||
----
|
||||
|
||||
The `Advanced Linux Sound Architecture (ALSA) <http://www.alsa-project.org/>`_ plugin uses libasound. It is recommended if you are using Linux.
|
||||
|
||||
@@ -757,7 +850,7 @@ The following attributes can be configured at runtime using the outputset comman
|
||||
|
||||
|
||||
ao
|
||||
~~
|
||||
--
|
||||
The ao plugin uses the portable `libao <https://www.xiph.org/ao/>`_ library. Use only if there is no native plugin for your operating system.
|
||||
|
||||
.. list-table::
|
||||
@@ -774,7 +867,8 @@ The ao plugin uses the portable `libao <https://www.xiph.org/ao/>`_ library. Use
|
||||
- This specifies how many bytes to write to the audio device at once. This parameter is to work around a bug in older versions of libao on sound cards with very small buffers. The default is 1024.
|
||||
|
||||
sndio
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
The sndio plugin uses the `sndio <http://www.sndio.org/>`_ library. It should normally be used on OpenBSD.
|
||||
|
||||
.. list-table::
|
||||
@@ -789,7 +883,7 @@ The sndio plugin uses the `sndio <http://www.sndio.org/>`_ library. It should no
|
||||
- Set the application buffer time in milliseconds.
|
||||
|
||||
fifo
|
||||
~~~~
|
||||
----
|
||||
|
||||
The fifo plugin writes raw PCM data to a FIFO (First In, First Out) file. The data can be read by another program.
|
||||
|
||||
@@ -803,7 +897,7 @@ The fifo plugin writes raw PCM data to a FIFO (First In, First Out) file. The da
|
||||
- This specifies the path of the FIFO to write to. Must be an absolute path. If the path does not exist, it will be created when MPD is started, and removed when MPD is stopped. The FIFO will be created with the same user and group as MPD is running as. Default permissions can be modified by using the builtin shell command umask. If a FIFO already exists at the specified path it will be reused, and will not be removed when MPD is stopped. You can use the "mkfifo" command to create this, and then you may modify the permissions to your liking.
|
||||
|
||||
haiku
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Use the SoundPlayer API on the Haiku operating system.
|
||||
|
||||
@@ -812,9 +906,14 @@ removed soon, unless there is a new maintainer.
|
||||
|
||||
|
||||
jack
|
||||
~~~~
|
||||
----
|
||||
|
||||
The jack plugin connects to a `JACK server <http://jackaudio.org/>`_.
|
||||
|
||||
On Windows, this plugin loads :file:`libjack64.dll` at runtime. This
|
||||
means you need to `download and install the JACK windows build
|
||||
<https://jackaudio.org/downloads/>`_.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
@@ -831,11 +930,16 @@ The jack plugin connects to a `JACK server <http://jackaudio.org/>`_.
|
||||
- The names of the JACK source ports to be created. By default, the ports "left" and "right" are created. To use more ports, you have to tweak this option.
|
||||
* - **destination_ports A,B**
|
||||
- The names of the JACK destination ports to connect to.
|
||||
* - **auto_destination_ports yes|no**
|
||||
- If set to *yes*, then MPD will automatically create connections between the send ports of
|
||||
MPD and receive ports of the first sound card; if set to *no*, then MPD will only create
|
||||
connections to the contents of *destination_ports* if it is set. Enabled by default.
|
||||
* - **ringbuffer_size NBYTES**
|
||||
- Sets the size of the ring buffer for each channel. Do not configure this value unless you know what you're doing.
|
||||
|
||||
httpd
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
The httpd plugin creates a HTTP server, similar to `ShoutCast <http://www.shoutcast.com/>`_ / `IceCast <http://icecast.org/>`_. HTTP streaming clients like mplayer, VLC, and mpv can connect to it.
|
||||
|
||||
It is highly recommended to configure a fixed format, because a stream cannot switch its audio format on-the-fly when the song changes.
|
||||
@@ -856,7 +960,8 @@ It is highly recommended to configure a fixed format, because a stream cannot sw
|
||||
- Sets a limit, number of concurrent clients. When set to 0 no limit will apply.
|
||||
|
||||
null
|
||||
~~~~
|
||||
----
|
||||
|
||||
The null plugin does nothing. It discards everything sent to it.
|
||||
|
||||
.. list-table::
|
||||
@@ -871,7 +976,8 @@ The null plugin does nothing. It discards everything sent to it.
|
||||
.. _oss_plugin:
|
||||
|
||||
oss
|
||||
~~~
|
||||
---
|
||||
|
||||
The "Open Sound System" plugin is supported on most Unix platforms.
|
||||
|
||||
On Linux, OSS has been superseded by ALSA. Use the ALSA output plugin :ref:`alsa_plugin` instead of this one on Linux.
|
||||
@@ -899,7 +1005,7 @@ The according hardware mixer plugin understands the following settings:
|
||||
- Choose a mixer control, defaulting to PCM.
|
||||
|
||||
openal
|
||||
~~~~~~
|
||||
------
|
||||
The "OpenAL" plugin uses `libopenal <http://kcat.strangesoft.net/openal.html>`_. It is supported on many platforms. Use only if there is no native plugin for your operating system.
|
||||
|
||||
.. list-table::
|
||||
@@ -912,7 +1018,7 @@ The "OpenAL" plugin uses `libopenal <http://kcat.strangesoft.net/openal.html>`_.
|
||||
- Sets the device which should be used. This can be any valid OpenAL device name. If not specified, then libopenal will choose a default device.
|
||||
|
||||
osx
|
||||
~~~
|
||||
---
|
||||
The "Mac OS X" plugin uses Apple's CoreAudio API.
|
||||
|
||||
.. list-table::
|
||||
@@ -933,7 +1039,7 @@ The "Mac OS X" plugin uses Apple's CoreAudio API.
|
||||
The channel map may not refer to outputs that do not exist according to the format. If the format is "*:*:1" (mono) and you have a four-channel sound card then "-1,-1,0,0" (dual mono output on the second pair of sound card outputs) is a valid channel map but "-1,-1,0,1" is not because the second channel ('1') does not exist when the output is mono.
|
||||
|
||||
pipe
|
||||
~~~~
|
||||
----
|
||||
|
||||
The pipe plugin starts a program and writes raw PCM data into its standard input.
|
||||
|
||||
@@ -949,7 +1055,7 @@ The pipe plugin starts a program and writes raw PCM data into its standard input
|
||||
.. _pulse_plugin:
|
||||
|
||||
pulse
|
||||
~~~~~
|
||||
-----
|
||||
The pulse plugin connects to a `PulseAudio <http://www.freedesktop.org/wiki/Software/PulseAudio/>`_ server. Requires libpulse.
|
||||
|
||||
.. list-table::
|
||||
@@ -962,11 +1068,13 @@ The pulse plugin connects to a `PulseAudio <http://www.freedesktop.org/wiki/Soft
|
||||
- Sets the host name of the PulseAudio server. By default, :program:`MPD` connects to the local PulseAudio server.
|
||||
* - **sink NAME**
|
||||
- Specifies the name of the PulseAudio sink :program:`MPD` should play on.
|
||||
* - **media_role ROLE**
|
||||
- Specifies a custom media role that :program:`MPD` reports to PulseAudio. Default is "music". (optional).
|
||||
* - **scale_volume FACTOR**
|
||||
- Specifies a linear scaling coefficient (ranging from 0.5 to 5.0) to apply when adjusting volume through :program:`MPD`. For example, chosing a factor equal to ``"0.7"`` means that setting the volume to 100 in :program:`MPD` will set the PulseAudio volume to 70%, and a factor equal to ``"3.5"`` means that volume 100 in :program:`MPD` corresponds to a 350% PulseAudio volume.
|
||||
|
||||
recorder
|
||||
~~~~~~~~
|
||||
--------
|
||||
The recorder plugin writes the audio played by :program:`MPD` to a file. This may be useful for recording radio streams.
|
||||
|
||||
.. list-table::
|
||||
@@ -978,13 +1086,13 @@ The recorder plugin writes the audio played by :program:`MPD` to a file. This ma
|
||||
* - **path P**
|
||||
- Write to this file.
|
||||
* - **format_path P**
|
||||
- An alternative to path which provides a format string referring to tag values. The special tag iso8601 emits the current date and time in `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ format (UTC). Every time a new song starts or a new tag gets received from a radio station, a new file is opened. If the format does not render a file name, nothing is recorded. A tag name enclosed in percent signs ('%') is replaced with the tag value. Example: :file:`~/.mpd/recorder/%artist% - %title%.ogg`. Square brackets can be used to group a substring. If none of the tags referred in the group can be found, the whole group is omitted. Example: [~/.mpd/recorder/[%artist% - ]%title%.ogg] (this omits the dash when no artist tag exists; if title also doesn't exist, no file is written). The operators "|" (logical "or") and "&" (logical "and") can be used to select portions of the format string depending on the existing tag values. Example: ~/.mpd/recorder/[%title%|%name%].ogg (use the "name" tag if no title exists)
|
||||
- An alternative to path which provides a format string referring to tag values. The special tag iso8601 emits the current date and time in `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ format (UTC). Every time a new song starts or a new tag gets received from a radio station, a new file is opened. If the format does not render a file name, nothing is recorded. A tag name enclosed in percent signs ('%') is replaced with the tag value. Example: :file:`-/.mpd/recorder/%artist% - %title%.ogg`. Square brackets can be used to group a substring. If none of the tags referred in the group can be found, the whole group is omitted. Example: [-/.mpd/recorder/[%artist% - ]%title%.ogg] (this omits the dash when no artist tag exists; if title also doesn't exist, no file is written). The operators "|" (logical "or") and "&" (logical "and") can be used to select portions of the format string depending on the existing tag values. Example: -/.mpd/recorder/[%title%|%name%].ogg (use the "name" tag if no title exists)
|
||||
* - **encoder NAME**
|
||||
- Chooses an encoder plugin. A list of encoder plugins can be found in the encoder plugin reference :ref:`encoder_plugins`.
|
||||
|
||||
|
||||
shout
|
||||
~~~~~
|
||||
-----
|
||||
The shout plugin connects to a ShoutCast or IceCast server using libshout. It forwards tags to this server.
|
||||
|
||||
You must set a format.
|
||||
@@ -1028,15 +1136,16 @@ You must set a format.
|
||||
.. _sles_output:
|
||||
|
||||
sles
|
||||
~~~~
|
||||
----
|
||||
|
||||
Plugin using the `OpenSL ES <https://www.khronos.org/opensles/>`__
|
||||
audio API. Its primary use is local playback on Android, where
|
||||
:ref:`ALSA <alsa_plugin>` is not available.
|
||||
:ref:`ALSA <alsa_plugin>` is not available. It supports 16 bit and
|
||||
floating point samples.
|
||||
|
||||
|
||||
solaris
|
||||
~~~~~~~
|
||||
-------
|
||||
The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
|
||||
|
||||
.. list-table::
|
||||
@@ -1049,25 +1158,74 @@ The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
|
||||
- Sets the path of the audio device, defaults to /dev/audio.
|
||||
|
||||
|
||||
wasapi
|
||||
------
|
||||
|
||||
The `Windows Audio Session API <https://docs.microsoft.com/en-us/windows/win32/coreaudio/wasapi>`_ plugin uses WASAPI, which is supported started from Windows Vista. It is recommended if you are using Windows.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **device NAME**
|
||||
- Sets the device which should be used. This can be any valid audio device name, or index number. The default value is "", which makes WASAPI choose the default output device.
|
||||
* - **enumerate yes|no**
|
||||
- Enumerate all devices in log while playing started. Useful for device configuration. The default value is "no".
|
||||
* - **exclusive yes|no**
|
||||
- Exclusive mode blocks all other audio source, and get best audio quality without resampling. Stopping playing release the exclusive control of the output device. The default value is "no".
|
||||
* - **dop yes|no**
|
||||
- Enable DSD over PCM. Require exclusive mode. The default value is "no".
|
||||
|
||||
|
||||
.. _filter_plugins:
|
||||
|
||||
Filter plugins
|
||||
--------------
|
||||
==============
|
||||
|
||||
ffmpeg
|
||||
------
|
||||
|
||||
Configures a FFmpeg filter graph.
|
||||
|
||||
This plugin requires building with ``libavfilter`` (FFmpeg).
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **graph "..."**
|
||||
- Specifies the ``libavfilter`` graph; read the `FFmpeg
|
||||
documentation
|
||||
<https://libav.org/documentation/libavfilter.html#Filtergraph-syntax-1>`_
|
||||
for details
|
||||
|
||||
|
||||
hdcd
|
||||
----
|
||||
|
||||
Decode `HDCD
|
||||
<https://en.wikipedia.org/wiki/High_Definition_Compatible_Digital>`_.
|
||||
|
||||
This plugin requires building with ``libavfilter`` (FFmpeg).
|
||||
|
||||
normalize
|
||||
~~~~~~~~~
|
||||
---------
|
||||
|
||||
Normalize the volume during playback (at the expensve of quality).
|
||||
Normalize the volume during playback (at the expense of quality).
|
||||
|
||||
|
||||
null
|
||||
~~~~
|
||||
----
|
||||
|
||||
A no-op filter. Audio data is returned as-is.
|
||||
|
||||
|
||||
route
|
||||
~~~~~
|
||||
-----
|
||||
|
||||
Reroute channels.
|
||||
|
||||
@@ -1084,43 +1242,46 @@ Reroute channels.
|
||||
.. _playlist_plugins:
|
||||
|
||||
Playlist plugins
|
||||
----------------
|
||||
================
|
||||
|
||||
asx
|
||||
~~~
|
||||
---
|
||||
|
||||
Reads .asx playlist files.
|
||||
Reads :file:`.asx` playlist files.
|
||||
|
||||
.. _cue_playlist:
|
||||
|
||||
cue
|
||||
~~~
|
||||
Reads .cue files.
|
||||
---
|
||||
Reads :file:`.cue` files.
|
||||
|
||||
embcue
|
||||
~~~~~~
|
||||
Reads CUE sheets from the "CUESHEET" tag of song files.
|
||||
------
|
||||
Reads CUE sheets from the ``CUESHEET`` tag of song files.
|
||||
|
||||
m3u
|
||||
~~~
|
||||
Reads .m3u playlist files.
|
||||
---
|
||||
Reads :file:`.m3u` playlist files.
|
||||
|
||||
extm3u
|
||||
~~~~~~
|
||||
Reads extended .m3u playlist files.
|
||||
------
|
||||
Reads extended :file:`.m3u` playlist files.
|
||||
|
||||
flac
|
||||
~~~~
|
||||
----
|
||||
Reads the cuesheet metablock from a FLAC file.
|
||||
|
||||
pls
|
||||
~~~
|
||||
Reads .pls playlist files.
|
||||
---
|
||||
Reads :file:`.pls` playlist files.
|
||||
|
||||
rss
|
||||
~~~
|
||||
Reads music links from .rss files.
|
||||
---
|
||||
Reads music links from :file:`.rss` files.
|
||||
|
||||
soundcloud
|
||||
~~~~~~~~~~
|
||||
----------
|
||||
|
||||
Download playlist from SoundCloud. It accepts URIs starting with soundcloud://.
|
||||
|
||||
.. list-table::
|
||||
@@ -1133,5 +1294,21 @@ Download playlist from SoundCloud. It accepts URIs starting with soundcloud://.
|
||||
- An API key to access the SoundCloud servers.
|
||||
|
||||
xspf
|
||||
~~~~
|
||||
----
|
||||
Reads XSPF playlist files.
|
||||
|
||||
|
||||
Archive plugins
|
||||
===============
|
||||
|
||||
bz2
|
||||
---
|
||||
Allows to load single bzip2 compressed files using `libbz2 <https://www.sourceware.org/bzip2/>`_. Does not support seeking.
|
||||
|
||||
zzip
|
||||
----
|
||||
Allows to load music files from ZIP archives using `zziplib <http://zziplib.sourceforge.net/>`_.
|
||||
|
||||
iso
|
||||
---
|
||||
Allows to load music files from ISO 9660 images using `libcdio <https://www.gnu.org/software/libcdio/>`_.
|
||||
|
397
doc/protocol.rst
397
doc/protocol.rst
File diff suppressed because it is too large
Load Diff
397
doc/user.rst
397
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,7 +55,7 @@ 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)
|
||||
* 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
|
||||
@@ -62,16 +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 \
|
||||
@@ -84,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 \
|
||||
libgtest-dev \
|
||||
libboost-dev \
|
||||
libicu-dev
|
||||
libicu-dev \
|
||||
libchromaprint-dev \
|
||||
libgcrypt20-dev
|
||||
|
||||
|
||||
Now configure the source tree:
|
||||
@@ -137,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:
|
||||
@@ -163,7 +176,12 @@ Compiling for Android
|
||||
You need:
|
||||
|
||||
* Android SDK
|
||||
* Android NDK
|
||||
* `Android NDK r22 <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
|
||||
@@ -183,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
|
||||
*************
|
||||
|
||||
@@ -258,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
|
||||
-------------------------------
|
||||
|
||||
@@ -334,6 +318,44 @@ 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
|
||||
---------------------------
|
||||
|
||||
@@ -402,15 +424,10 @@ 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.
|
||||
@@ -422,7 +439,7 @@ The following table lists the audio_output options valid for all plugins:
|
||||
: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 :ref:`external_mixer`) or no mixer
|
||||
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,...**"
|
||||
@@ -497,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.
|
||||
@@ -504,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.
|
||||
|
||||
@@ -523,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
|
||||
@@ -566,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`.
|
||||
|
||||
@@ -629,7 +672,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.
|
||||
|
||||
@@ -645,9 +688,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
|
||||
@@ -664,7 +709,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).
|
||||
|
||||
@@ -677,7 +722,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**
|
||||
@@ -686,7 +731,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.
|
||||
|
||||
@@ -696,11 +741,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=...`,
|
||||
@@ -745,22 +791,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.
|
||||
|
||||
@@ -778,22 +826,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
|
||||
----------
|
||||
|
||||
@@ -817,7 +950,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:
|
||||
|
||||
@@ -912,15 +1045,23 @@ Check list for bit-perfect playback:
|
||||
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`, :code:`audio_output_format`).
|
||||
: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.
|
||||
|
||||
@@ -933,6 +1074,22 @@ 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
|
||||
************
|
||||
|
||||
@@ -957,47 +1114,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.
|
||||
|
||||
@@ -1016,7 +1205,7 @@ 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.)
|
||||
|
||||
@@ -1037,7 +1226,7 @@ You can extract the backtrace from a core dump, or by running :program:`MPD` in
|
||||
|
||||
.. 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 ":command:`bt`" on the
|
||||
|
236
meson.build
236
meson.build
@@ -1,11 +1,18 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.7',
|
||||
version: '0.22.9',
|
||||
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.6')
|
||||
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,9 +124,21 @@ 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') != ''
|
||||
@@ -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)
|
||||
@@ -178,10 +246,20 @@ if boost_dep.version() == '1.67'
|
||||
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',
|
||||
@@ -206,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',
|
||||
@@ -267,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',
|
||||
@@ -280,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',
|
||||
]
|
||||
@@ -298,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')
|
||||
@@ -308,6 +400,7 @@ 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')
|
||||
@@ -322,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')
|
||||
@@ -347,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',
|
||||
@@ -367,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'
|
||||
@@ -377,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
|
||||
@@ -441,8 +547,10 @@ mpd = build_target(
|
||||
sqlite_dep,
|
||||
zeroconf_dep,
|
||||
more_deps,
|
||||
chromaprint_dep,
|
||||
],
|
||||
link_args: link_args,
|
||||
build_by_default: not get_option('fuzzer'),
|
||||
install: not is_android and not is_haiku,
|
||||
)
|
||||
|
||||
@@ -478,10 +586,12 @@ install_data(
|
||||
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
|
||||
)
|
||||
|
||||
if get_option('documentation')
|
||||
subdir('doc')
|
||||
endif
|
||||
|
||||
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
|
||||
|
@@ -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.1.3.tar.xz',
|
||||
'0c3020452880581a8face91595b239198078645e7d7184273b8bcc7758beb63d',
|
||||
'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-alpha16.tar.gz',
|
||||
'08ce8244b59d75f40f91170dfcb012bf25309cdcb1fef9502e39d694f883d1d1',
|
||||
'include/openssl/ossl_typ.h',
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.64.1.tar.xz',
|
||||
'9252332a7f871ce37bfa7f78bdd0a0e3924d8187cc27cb57c76c9474a7168fb3',
|
||||
'https://curl.se/download/curl-7.76.1.tar.xz',
|
||||
'64bb5288c39f0840c07d077e30d9052e1cbb9fa6c2dc52523824cc859e679145',
|
||||
'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',
|
||||
@@ -388,11 +440,18 @@ libnfs = AutotoolsProject(
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
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.69.0/boost_1_69_0.tar.bz2',
|
||||
'8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406',
|
||||
'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=[],
|
||||
|
55
python/build/openssl.py
Normal file
55
python/build/openssl.py
Normal file
@@ -0,0 +1,55 @@
|
||||
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 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\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
|
||||
|
@@ -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
|
||||
@@ -18,16 +18,24 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -65,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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,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);
|
||||
@@ -153,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)
|
||||
@@ -167,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);
|
||||
@@ -208,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 */
|
||||
@@ -223,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
|
||||
@@ -26,12 +26,12 @@
|
||||
#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
|
||||
@@ -25,7 +25,7 @@
|
||||
#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,12 +20,15 @@
|
||||
#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
|
||||
@@ -34,14 +37,16 @@
|
||||
#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
|
||||
@@ -65,6 +70,13 @@ Instance::~Instance() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnStateModified() noexcept
|
||||
{
|
||||
if (state_file)
|
||||
state_file->CheckModified();
|
||||
}
|
||||
|
||||
Partition *
|
||||
Instance::FindPartition(const char *name) noexcept
|
||||
{
|
||||
@@ -75,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 &
|
||||
@@ -88,7 +114,7 @@ Instance::GetDatabaseOrThrow() const
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnDatabaseModified()
|
||||
Instance::OnDatabaseModified() noexcept
|
||||
{
|
||||
assert(database != nullptr);
|
||||
|
||||
@@ -101,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 (...) {
|
||||
}
|
||||
}
|
||||
@@ -124,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
|
||||
@@ -166,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
|
||||
@@ -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,10 +99,16 @@ 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
|
||||
@@ -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,6 +171,8 @@ struct Instance final
|
||||
gcc_pure
|
||||
Partition *FindPartition(const char *name) noexcept;
|
||||
|
||||
void DeletePartition(Partition &partition) noexcept;
|
||||
|
||||
void BeginShutdownPartitions() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
@@ -154,7 +181,7 @@ struct Instance final
|
||||
* if this MPD configuration has no database (no
|
||||
* music_directory was configured).
|
||||
*/
|
||||
Database *GetDatabase() {
|
||||
Database *GetDatabase() noexcept {
|
||||
return database.get();
|
||||
}
|
||||
|
||||
@@ -166,6 +193,12 @@ struct Instance final
|
||||
const Database &GetDatabaseOrThrow() const;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
bool HasStickerDatabase() noexcept {
|
||||
return sticker_database != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BeginShutdownUpdate() noexcept;
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
@@ -176,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
|
||||
@@ -194,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
|
||||
@@ -21,7 +21,6 @@
|
||||
#define MPD_LOCATE_URI_HXX
|
||||
|
||||
#include "config.h"
|
||||
#include "util/Compiler.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
|
||||
|
109
src/Log.cxx
109
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
|
||||
@@ -19,162 +19,131 @@
|
||||
|
||||
#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
|
||||
@@ -184,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);
|
||||
@@ -195,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);
|
||||
@@ -206,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
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "Log.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "Version.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
#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
|
||||
|
@@ -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,24 +28,24 @@
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#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
|
||||
@@ -63,7 +63,7 @@ static void redirect_logs(int fd)
|
||||
}
|
||||
|
||||
static int
|
||||
open_log_file(void)
|
||||
open_log_file()
|
||||
{
|
||||
assert(!out_path.IsNull());
|
||||
|
||||
@@ -93,17 +93,24 @@ 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
|
||||
@@ -132,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;
|
||||
@@ -157,7 +167,7 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
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 {
|
||||
|
@@ -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
|
||||
@@ -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 */
|
||||
|
437
src/Main.cxx
437
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
|
||||
|
||||
@@ -166,14 +163,15 @@ glue_mapper_init(const ConfigData &config)
|
||||
#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,26 +181,27 @@ 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)
|
||||
{
|
||||
auto db = CreateConfiguredDatabase(config, instance->event_loop,
|
||||
instance->io_thread.GetEventLoop(),
|
||||
*instance);
|
||||
auto db = CreateConfiguredDatabase(config, instance.event_loop,
|
||||
instance.io_thread.GetEventLoop(),
|
||||
instance);
|
||||
if (!db)
|
||||
return true;
|
||||
|
||||
if (db->GetPlugin().RequireStorage()) {
|
||||
InitStorage(config, instance->io_thread.GetEventLoop());
|
||||
InitStorage(instance, instance.io_thread.GetEventLoop(),
|
||||
config);
|
||||
|
||||
if (instance->storage == 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");
|
||||
}
|
||||
@@ -213,65 +212,66 @@ glue_db_init_and_load(const ConfigData &config)
|
||||
std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
|
||||
}
|
||||
|
||||
instance->database = std::move(db);
|
||||
instance.database = std::move(db);
|
||||
|
||||
auto *sdb = dynamic_cast<SimpleDatabase *>(instance->database.get());
|
||||
auto *sdb = dynamic_cast<SimpleDatabase *>(instance.database.get());
|
||||
if (sdb == nullptr)
|
||||
return true;
|
||||
|
||||
instance->update = new UpdateService(config,
|
||||
instance->event_loop, *sdb,
|
||||
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 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;
|
||||
@@ -279,20 +279,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;
|
||||
|
||||
@@ -306,35 +307,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
|
||||
@@ -354,56 +346,21 @@ inline void
|
||||
Instance::BeginShutdownPartitions() noexcept
|
||||
{
|
||||
for (auto &partition : partitions) {
|
||||
partition.pc.Kill();
|
||||
partition.listener.reset();
|
||||
partition.BeginShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnIdle(unsigned flags)
|
||||
static inline void
|
||||
MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
{
|
||||
/* 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;
|
||||
@@ -413,25 +370,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);
|
||||
@@ -441,31 +381,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();
|
||||
@@ -473,27 +415,6 @@ 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(); };
|
||||
|
||||
@@ -510,24 +431,26 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &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);
|
||||
const ScopeInputPluginsInit input_plugins_init(raw_config,
|
||||
instance->io_thread.GetEventLoop());
|
||||
instance.io_thread.GetEventLoop());
|
||||
|
||||
const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
@@ -537,37 +460,43 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
#ifndef ANDROID
|
||||
setup_log_output();
|
||||
|
||||
const ScopeSignalHandlersInit signal_handlers_init(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
|
||||
@@ -581,7 +510,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
|
||||
@@ -590,14 +519,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();
|
||||
@@ -605,60 +534,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
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_finish();
|
||||
#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
|
||||
@@ -66,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;
|
||||
|
||||
@@ -84,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();
|
||||
|
||||
@@ -119,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
|
||||
|
@@ -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,7 +20,7 @@
|
||||
#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
|
||||
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
|
@@ -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,6 +20,8 @@
|
||||
#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,9 +30,10 @@
|
||||
#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>
|
||||
|
||||
@@ -40,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
|
||||
@@ -56,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;
|
||||
@@ -71,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);
|
||||
}
|
||||
|
||||
@@ -108,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);
|
||||
}
|
||||
|
||||
@@ -142,7 +171,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
playlist.SetPriorityId(pc, song_id, priority);
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
void Stop() noexcept {
|
||||
playlist.Stop(pc);
|
||||
}
|
||||
|
||||
@@ -174,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();
|
||||
}
|
||||
@@ -203,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
|
||||
/**
|
||||
@@ -211,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;
|
||||
|
||||
@@ -219,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
|
||||
@@ -237,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;
|
||||
@@ -257,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
|
||||
@@ -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
|
||||
@@ -21,11 +21,12 @@
|
||||
#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
|
||||
@@ -49,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);
|
||||
@@ -58,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
|
||||
|
@@ -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
|
||||
@@ -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 &
|
||||
|
@@ -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,6 @@
|
||||
#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
|
||||
|
@@ -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,10 +27,20 @@
|
||||
* that this plugin is unavailable. It will be disabled, and MPD can
|
||||
* continue initialization.
|
||||
*/
|
||||
class PluginUnavailable final : public std::runtime_error {
|
||||
class PluginUnavailable : public std::runtime_error {
|
||||
public:
|
||||
explicit PluginUnavailable(const char *msg)
|
||||
:std::runtime_error(msg) {}
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Like #PluginUnavailable, but denotes that the plugin is not
|
||||
* available because it was not explicitly enabled in the
|
||||
* configuration. The message may describe the necessary steps to
|
||||
* enable it.
|
||||
*/
|
||||
class PluginUnconfigured : public PluginUnavailable {
|
||||
public:
|
||||
using PluginUnavailable::PluginUnavailable;
|
||||
};
|
||||
|
||||
#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
|
||||
@@ -42,9 +42,9 @@ RemoteTagCache::Lookup(const std::string &uri) noexcept
|
||||
std::unique_lock<Mutex> lock(mutex);
|
||||
|
||||
KeyMap::insert_commit_data hint;
|
||||
auto result = map.insert_check(uri, Item::Hash(), Item::Equal(), hint);
|
||||
if (result.second) {
|
||||
auto *item = new Item(*this, uri);
|
||||
auto [tag, value] = map.insert_check(uri, Item::Hash(), Item::Equal(), hint);
|
||||
if (value) {
|
||||
auto item = new Item(*this, uri);
|
||||
map.insert_commit(*item, hint);
|
||||
waiting_list.push_back(*item);
|
||||
lock.unlock();
|
||||
@@ -70,15 +70,13 @@ RemoteTagCache::Lookup(const std::string &uri) noexcept
|
||||
ItemResolved(*item);
|
||||
return;
|
||||
}
|
||||
} else if (result.first->scanner) {
|
||||
} else if (tag->scanner) {
|
||||
/* already scanning this one - no-op */
|
||||
} else {
|
||||
/* already finished: re-invoke the handler */
|
||||
|
||||
auto &item = *result.first;
|
||||
|
||||
idle_list.erase(waiting_list.iterator_to(item));
|
||||
invoke_list.push_back(item);
|
||||
idle_list.erase(waiting_list.iterator_to(*tag));
|
||||
invoke_list.push_back(*tag);
|
||||
|
||||
ScheduleInvokeHandlers();
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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,13 +19,12 @@
|
||||
|
||||
#include "ReplayGainGlobal.hxx"
|
||||
#include "ReplayGainConfig.hxx"
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
static float
|
||||
ParsePreamp(const char *s)
|
||||
@@ -33,25 +32,14 @@ ParsePreamp(const char *s)
|
||||
assert(s != nullptr);
|
||||
|
||||
char *endptr;
|
||||
float f = strtod(s, &endptr);
|
||||
float f = std::strtof(s, &endptr);
|
||||
if (endptr == s || *endptr != '\0')
|
||||
throw std::invalid_argument("Not a numeric value");
|
||||
|
||||
if (f < -15 || f > 15)
|
||||
if (f < -15.0f || f > 15.0f)
|
||||
throw std::invalid_argument("Number must be between -15 and 15");
|
||||
|
||||
return pow(10, f / 20.0);
|
||||
}
|
||||
|
||||
static float
|
||||
ParsePreamp(const ConfigParam &p)
|
||||
{
|
||||
try {
|
||||
return ParsePreamp(p.value.c_str());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(FormatRuntimeError("Failed to parse line %i",
|
||||
p.line));
|
||||
}
|
||||
return std::pow(10.0f, f / 20.0f);
|
||||
}
|
||||
|
||||
ReplayGainConfig
|
||||
@@ -59,13 +47,17 @@ LoadReplayGainConfig(const ConfigData &config)
|
||||
{
|
||||
ReplayGainConfig replay_gain_config;
|
||||
|
||||
const auto *param = config.GetParam(ConfigOption::REPLAYGAIN_PREAMP);
|
||||
if (param)
|
||||
replay_gain_config.preamp = ParsePreamp(*param);
|
||||
replay_gain_config.preamp = config.With(ConfigOption::REPLAYGAIN_PREAMP, [](const char *s){
|
||||
return s != nullptr
|
||||
? ParsePreamp(s)
|
||||
: 1.0f;
|
||||
});
|
||||
|
||||
param = config.GetParam(ConfigOption::REPLAYGAIN_MISSING_PREAMP);
|
||||
if (param)
|
||||
replay_gain_config.missing_preamp = ParsePreamp(*param);
|
||||
replay_gain_config.missing_preamp = config.With(ConfigOption::REPLAYGAIN_MISSING_PREAMP, [](const char *s){
|
||||
return s != nullptr
|
||||
? ParsePreamp(s)
|
||||
: 1.0f;
|
||||
});
|
||||
|
||||
replay_gain_config.limit = config.GetBool(ConfigOption::REPLAYGAIN_LIMIT,
|
||||
ReplayGainConfig::DEFAULT_LIMIT);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user