Compare commits
1088 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b7fdff46f2 | ||
![]() |
e16109330d | ||
![]() |
72621531e0 | ||
![]() |
0a48146efc | ||
![]() |
0c4bf12bfd | ||
![]() |
b8e0855ef3 | ||
![]() |
6467502b9d | ||
![]() |
15b67f20e5 | ||
![]() |
0825179f00 | ||
![]() |
97211d0aad | ||
![]() |
029c499bfa | ||
![]() |
0ba867ec16 | ||
![]() |
866d147122 | ||
![]() |
32851d1bc7 | ||
![]() |
78257408b4 | ||
![]() |
f447b7615e | ||
![]() |
1f780b7209 | ||
![]() |
04bf8a6b1a | ||
![]() |
c4c64854d4 | ||
![]() |
17562dc90b | ||
![]() |
7b24316734 | ||
![]() |
5fab107fd3 | ||
![]() |
f31920e092 | ||
![]() |
eb111a10e7 | ||
![]() |
80b09360c6 | ||
![]() |
5ccf78855d | ||
![]() |
fd5a3b5880 | ||
![]() |
6120c1360c | ||
![]() |
a8087dc12c | ||
![]() |
070c03dbf7 | ||
![]() |
0a9bec3754 | ||
![]() |
fff25ac753 | ||
![]() |
4f1e79b6b8 | ||
![]() |
aa9933c0b5 | ||
![]() |
0697d1f859 | ||
![]() |
df033fa4aa | ||
![]() |
b941a7df83 | ||
![]() |
31151cec3c | ||
![]() |
07e8c338df | ||
![]() |
b22d7218aa | ||
![]() |
d5be8c74b0 | ||
![]() |
c112cb60da | ||
![]() |
677fa4f9bc | ||
![]() |
907af2ad02 | ||
![]() |
6a2e7bbc02 | ||
![]() |
771c46032f | ||
![]() |
85611aa456 | ||
![]() |
466b5cb08d | ||
![]() |
3f2f3251cb | ||
![]() |
8ae85f3991 | ||
![]() |
781fe4ff28 | ||
![]() |
163c59128e | ||
![]() |
608896571c | ||
![]() |
2e5ca1cbd2 | ||
![]() |
680fb51c37 | ||
![]() |
77d74b404e | ||
![]() |
a636d2127a | ||
![]() |
9a766f4cd9 | ||
![]() |
8ad17d25ef | ||
![]() |
46d00dd85f | ||
![]() |
ec52b13449 | ||
![]() |
cf6ca1b0ba | ||
![]() |
37bd6de658 | ||
![]() |
f7622ca332 | ||
![]() |
b82b56970b | ||
![]() |
e4eb5b79c9 | ||
![]() |
1cfea20b22 | ||
![]() |
efa3ffa8d8 | ||
![]() |
1b8c94d6b9 | ||
![]() |
cd5c1f3f45 | ||
![]() |
9200fa6d06 | ||
![]() |
1bbe9896f6 | ||
![]() |
2d8847f428 | ||
![]() |
72f6e018e7 | ||
![]() |
2fbbd540bb | ||
![]() |
18f64b5fb7 | ||
![]() |
d2a8b1e8a5 | ||
![]() |
184e8eca7c | ||
![]() |
0a8886704a | ||
![]() |
0712314d23 | ||
![]() |
f8cbba1850 | ||
![]() |
635ec3ce37 | ||
![]() |
8e71130e8a | ||
![]() |
ed7baf3ae1 | ||
![]() |
1e159af2ce | ||
![]() |
dffed6e393 | ||
![]() |
bf656af555 | ||
![]() |
d2b8852d19 | ||
![]() |
7d4de71899 | ||
![]() |
e1c16d78e4 | ||
![]() |
a49b49cba7 | ||
![]() |
f510564d9d | ||
![]() |
1c4b484a56 | ||
![]() |
b394d8d059 | ||
![]() |
a15c1c71d5 | ||
![]() |
8d679e7e00 | ||
![]() |
2b30ac2351 | ||
![]() |
1c97793b49 | ||
![]() |
4dae8b41da | ||
![]() |
be8ed2f59e | ||
![]() |
08491fcd86 | ||
![]() |
abed633fcb | ||
![]() |
db2a9cb6d5 | ||
![]() |
7caeb3b0d8 | ||
![]() |
08500be239 | ||
![]() |
3ef7d8fecb | ||
![]() |
ffde7223b9 | ||
![]() |
4e84fa4a00 | ||
![]() |
78e49928b6 | ||
![]() |
c0bcfe244c | ||
![]() |
e63ecd81ec | ||
![]() |
c47a858d15 | ||
![]() |
076c9a0dd9 | ||
![]() |
3993176b76 | ||
![]() |
16cad48641 | ||
![]() |
7a6d0c2efc | ||
![]() |
f6035f2dda | ||
![]() |
c34a1e29de | ||
![]() |
41a69027c2 | ||
![]() |
711c614528 | ||
![]() |
6acb240f69 | ||
![]() |
45f3dd8b7a | ||
![]() |
acc1bd6297 | ||
![]() |
49ed9dae34 | ||
![]() |
cf554d306d | ||
![]() |
ef24cfa523 | ||
![]() |
5d35983298 | ||
![]() |
2dacb36789 | ||
![]() |
57a1403f08 | ||
![]() |
bad3283182 | ||
![]() |
6ed4aff4d3 | ||
![]() |
e525465592 | ||
![]() |
10782f4c84 | ||
![]() |
2a02576d6d | ||
![]() |
9ea1578a97 | ||
![]() |
520028dcfc | ||
![]() |
e98cef06c7 | ||
![]() |
aef0535c55 | ||
![]() |
6b1d0cb01d | ||
![]() |
f23ecf00da | ||
![]() |
a1c1e26875 | ||
![]() |
410b8711f2 | ||
![]() |
6acf81d5ae | ||
![]() |
4eb56d844e | ||
![]() |
5faf6d061f | ||
![]() |
d5a9f6d79d | ||
![]() |
2699889342 | ||
![]() |
e4f933361e | ||
![]() |
6f278977e9 | ||
![]() |
4f2f705dca | ||
![]() |
f31e38145d | ||
![]() |
0231622169 | ||
![]() |
937423dbcf | ||
![]() |
40483d8478 | ||
![]() |
6ec5089cc9 | ||
![]() |
15f419e1cb | ||
![]() |
bdd8c34c67 | ||
![]() |
c9a9248c9f | ||
![]() |
31f7fede30 | ||
![]() |
917fe549b0 | ||
![]() |
8e430e55af | ||
![]() |
9e61bda592 | ||
![]() |
56997290d7 | ||
![]() |
d2f84f3df8 | ||
![]() |
9da28e5c73 | ||
![]() |
d1f9b06f84 | ||
![]() |
f9f3306db9 | ||
![]() |
19d19cd737 | ||
![]() |
b1175acb59 | ||
![]() |
672278e5fd | ||
![]() |
da155f8822 | ||
![]() |
4026ef63b6 | ||
![]() |
b282682ba5 | ||
![]() |
ad00926e1b | ||
![]() |
0b774df375 | ||
![]() |
53ffcf455c | ||
![]() |
9ca64d5fb3 | ||
![]() |
bd79354f32 | ||
![]() |
49dcac5c21 | ||
![]() |
38a4b0d8d5 | ||
![]() |
a224225e48 | ||
![]() |
7d7fe756b3 | ||
![]() |
1cb7fe12ff | ||
![]() |
8a29805767 | ||
![]() |
94c196108d | ||
![]() |
263d1ba002 | ||
![]() |
2dba06dc34 | ||
![]() |
811860c3b4 | ||
![]() |
8439119e24 | ||
![]() |
b5b40d8235 | ||
![]() |
b904f8af03 | ||
![]() |
ebfbb74f9e | ||
![]() |
7b4225aa1f | ||
![]() |
71a5311b06 | ||
![]() |
a62a35e1db | ||
![]() |
ca2439f595 | ||
![]() |
f9a0db716a | ||
![]() |
34aa67ea87 | ||
![]() |
18be8c3318 | ||
![]() |
1d7a8f992f | ||
![]() |
da1783cdff | ||
![]() |
20d74bb07e | ||
![]() |
0f7a0b04ca | ||
![]() |
40c6a214e3 | ||
![]() |
cfe024ea13 | ||
![]() |
993d85125e | ||
![]() |
bedcf1cce5 | ||
![]() |
30e3ef4c8e | ||
![]() |
4c5fea96e4 | ||
![]() |
46600931e4 | ||
![]() |
a2387210bf | ||
![]() |
d7e7adb496 | ||
![]() |
45354a421c | ||
![]() |
9fc3c60910 | ||
![]() |
1976003e91 | ||
![]() |
488afc47d4 | ||
![]() |
017814adc7 | ||
![]() |
7f94af8b2c | ||
![]() |
09d74f05c3 | ||
![]() |
1af8694ef6 | ||
![]() |
b8eb9b466a | ||
![]() |
bd9e449b69 | ||
![]() |
f3d67115d7 | ||
![]() |
ee6603ed38 | ||
![]() |
0dacde32f2 | ||
![]() |
528e05f025 | ||
![]() |
269583f5dd | ||
![]() |
7c9f4f7e4f | ||
![]() |
00fd692eba | ||
![]() |
668c3782b2 | ||
![]() |
1e0af2dadf | ||
![]() |
4ea2ea2a52 | ||
![]() |
8a243e6e28 | ||
![]() |
d33aa01000 | ||
![]() |
bd893e6336 | ||
![]() |
64c39af556 | ||
![]() |
04eb911a51 | ||
![]() |
351b39e0c5 | ||
![]() |
3b6d4e6673 | ||
![]() |
e8f328d8ad | ||
![]() |
5f5b5f63af | ||
![]() |
ad6e303047 | ||
![]() |
b0e9538855 | ||
![]() |
694debd4cc | ||
![]() |
0f56ddb805 | ||
![]() |
dde77ec6bd | ||
![]() |
5d73eda115 | ||
![]() |
1985786ed2 | ||
![]() |
8e0d39ae94 | ||
![]() |
1761fb14af | ||
![]() |
ef2fc4e6f6 | ||
![]() |
b979245d6c | ||
![]() |
a74b07728e | ||
![]() |
7d69cbbda7 | ||
![]() |
955502f881 | ||
![]() |
dee5d1b87b | ||
![]() |
d42342e0ba | ||
![]() |
8da3f8c6a7 | ||
![]() |
c8c553c75c | ||
![]() |
c97aabe43a | ||
![]() |
17b0ac75ca | ||
![]() |
bde64a13e2 | ||
![]() |
96875921b7 | ||
![]() |
551c941b5a | ||
![]() |
624c77ab43 | ||
![]() |
ba13b4b5d6 | ||
![]() |
4b2d9e544c | ||
![]() |
97c43954e8 | ||
![]() |
b77acd35f7 | ||
![]() |
4873159872 | ||
![]() |
968624035c | ||
![]() |
b838bf3109 | ||
![]() |
4d1ce7023b | ||
![]() |
52577ac87a | ||
![]() |
9fa3984a2f | ||
![]() |
239698cb5a | ||
![]() |
e55de6e9f0 | ||
![]() |
cfaf2ed03c | ||
![]() |
6015960871 | ||
![]() |
26328cc915 | ||
![]() |
cd512f0b40 | ||
![]() |
be14dd59a8 | ||
![]() |
97e5787ff7 | ||
![]() |
6975d3ca4b | ||
![]() |
cdca27e6bb | ||
![]() |
5355335f19 | ||
![]() |
5b775ca5b4 | ||
![]() |
ea95da3b1a | ||
![]() |
57687779be | ||
![]() |
64fa76c568 | ||
![]() |
19a44076cf | ||
![]() |
809a18913a | ||
![]() |
5eab2d96f4 | ||
![]() |
716784f632 | ||
![]() |
d39b11ba5d | ||
![]() |
b29a43b4d7 | ||
![]() |
f60a42e0b6 | ||
![]() |
85b0029ba2 | ||
![]() |
0e0f46a1e0 | ||
![]() |
6f539cfcd6 | ||
![]() |
0185d58a2b | ||
![]() |
eb630ca655 | ||
![]() |
d7df5e1c90 | ||
![]() |
e4e4576a39 | ||
![]() |
18628bf89e | ||
![]() |
2052b461af | ||
![]() |
5019bdcd52 | ||
![]() |
8be0bcbdb9 | ||
![]() |
af72a22ed8 | ||
![]() |
6ed9668fea | ||
![]() |
175d2c6d29 | ||
![]() |
f789451007 | ||
![]() |
36680607d0 | ||
![]() |
fc54877c6b | ||
![]() |
6af7be4a45 | ||
![]() |
ab487b9a99 | ||
![]() |
ac59ec34f9 | ||
![]() |
82da57b7ce | ||
![]() |
aa6dac9bd2 | ||
![]() |
220d2bf026 | ||
![]() |
9ef1cf15a9 | ||
![]() |
679b3bc00f | ||
![]() |
1f9e32c35e | ||
![]() |
36410daaa4 | ||
![]() |
38bfef7d0b | ||
![]() |
724754f16c | ||
![]() |
4d32454697 | ||
![]() |
4db882f666 | ||
![]() |
a83bf97b98 | ||
![]() |
262e1957b7 | ||
![]() |
792411384d | ||
![]() |
78b0ff83e8 | ||
![]() |
23613355f3 | ||
![]() |
0d97eba7a5 | ||
![]() |
18efda719e | ||
![]() |
42239a30eb | ||
![]() |
a26bf261a9 | ||
![]() |
c692286c67 | ||
![]() |
43a9dc7082 | ||
![]() |
6f64fa070d | ||
![]() |
dc5b9d989b | ||
![]() |
9e407f5989 | ||
![]() |
fec6aac0f1 | ||
![]() |
541c31c879 | ||
![]() |
4ee0a06e18 | ||
![]() |
3775766605 | ||
![]() |
38e24208f6 | ||
![]() |
fbaedf2262 | ||
![]() |
8f3341cefb | ||
![]() |
4ec4bab3a9 | ||
![]() |
6d567bcd35 | ||
![]() |
4f75eb9bfe | ||
![]() |
d2bd12822f | ||
![]() |
363d9f0180 | ||
![]() |
db0682a469 | ||
![]() |
7a6823dcdf | ||
![]() |
bce144a232 | ||
![]() |
0cef84cac6 | ||
![]() |
56c0733b42 | ||
![]() |
0b0acb3981 | ||
![]() |
1375dcc4ec | ||
![]() |
6aeb0e335b | ||
![]() |
c1e2537851 | ||
![]() |
8c690fb737 | ||
![]() |
dad1c21b59 | ||
![]() |
dd10b2bd61 | ||
![]() |
48c7c540df | ||
![]() |
281270cd2a | ||
![]() |
02502514f6 | ||
![]() |
1bc02123f9 | ||
![]() |
3488a47c41 | ||
![]() |
fd82d67678 | ||
![]() |
e66c12105b | ||
![]() |
8a9d678bac | ||
![]() |
dbe12a6b90 | ||
![]() |
0440c41cba | ||
![]() |
a9c704b76e | ||
![]() |
d3a680cc87 | ||
![]() |
62fc4d5cf4 | ||
![]() |
0cca1b138c | ||
![]() |
14465be847 | ||
![]() |
0e49de867d | ||
![]() |
f2e4529707 | ||
![]() |
d3576a1b71 | ||
![]() |
96707c0426 | ||
![]() |
3547fc7e61 | ||
![]() |
466a05bc52 | ||
![]() |
6de4064cca | ||
![]() |
bcf0fdd3a8 | ||
![]() |
a8f05a7efc | ||
![]() |
c64a3b5dbb | ||
![]() |
16c38c438f | ||
![]() |
48cc4a6ced | ||
![]() |
a169a05e41 | ||
![]() |
a6cb3139db | ||
![]() |
239a83324e | ||
![]() |
8efa5c7641 | ||
![]() |
28e7be248f | ||
![]() |
e016cc8940 | ||
![]() |
34f636ffc3 | ||
![]() |
a134f692bf | ||
![]() |
d747576793 | ||
![]() |
d9578f6427 | ||
![]() |
b2cec7a0a3 | ||
![]() |
85db2d6704 | ||
![]() |
22ebb2bdd5 | ||
![]() |
e108568082 | ||
![]() |
360381e65d | ||
![]() |
3ead778664 | ||
![]() |
4fc08e39b4 | ||
![]() |
c3f9b38c97 | ||
![]() |
dbb18a401b | ||
![]() |
e1e41708af | ||
![]() |
a2bdac571a | ||
![]() |
638dfc3981 | ||
![]() |
7c09e44ad4 | ||
![]() |
365b798f33 | ||
![]() |
6f51d910ee | ||
![]() |
1215818572 | ||
![]() |
87fa6bca54 | ||
![]() |
c3226a3195 | ||
![]() |
51671af5a4 | ||
![]() |
2908f6565b | ||
![]() |
514ed33a02 | ||
![]() |
a0334d1d94 | ||
![]() |
bfed47b82d | ||
![]() |
8c51440057 | ||
![]() |
018858ec97 | ||
![]() |
3c1988b68f | ||
![]() |
5452428d69 | ||
![]() |
d6bf6e161a | ||
![]() |
a71b76bb3c | ||
![]() |
c1429500b2 | ||
![]() |
0f02bbc2fe | ||
![]() |
b885f358a5 | ||
![]() |
423f2df5e0 | ||
![]() |
0122dc8452 | ||
![]() |
650a30d794 | ||
![]() |
1dc71f383a | ||
![]() |
95ad1b0cc6 | ||
![]() |
52f46b94e9 | ||
![]() |
e07e0bc9c1 | ||
![]() |
4a1c231734 | ||
![]() |
fd0e958e95 | ||
![]() |
3d814115c8 | ||
![]() |
ca726a0110 | ||
![]() |
e01710cbd1 | ||
![]() |
c87a4a7d08 | ||
![]() |
b59170b702 | ||
![]() |
a237db556a | ||
![]() |
285ba54fe5 | ||
![]() |
ee86434a89 | ||
![]() |
95d5efbfe6 | ||
![]() |
c33f206ce8 | ||
![]() |
2d95ac2e94 | ||
![]() |
f58c14a74a | ||
![]() |
a52ce7bb7b | ||
![]() |
16d187b7ed | ||
![]() |
296ec4d07c | ||
![]() |
6e58fd1583 | ||
![]() |
c5fec4ac2a | ||
![]() |
fe2ca1ddef | ||
![]() |
e960626804 | ||
![]() |
7dd2dce6ad | ||
![]() |
a7ba10423d | ||
![]() |
6dfebf7df9 | ||
![]() |
4bcdcca7f5 | ||
![]() |
c08a8581ee | ||
![]() |
8f1e7385b7 | ||
![]() |
25354b9d8c | ||
![]() |
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 | ||
![]() |
ee720064a7 | ||
![]() |
e1fe9ebcd6 | ||
![]() |
e1b62fb90d | ||
![]() |
93016ac6ab | ||
![]() |
fc20a1f10a | ||
![]() |
a4257e51d5 | ||
![]() |
2f2b3f1cdc | ||
![]() |
2ff6a9ad2b | ||
![]() |
17d4873b60 | ||
![]() |
8b41c4f384 | ||
![]() |
17f7098e27 | ||
![]() |
9ff790b7bb | ||
![]() |
ebc1fe2821 | ||
![]() |
bc2988144e | ||
![]() |
b1a9958c66 | ||
![]() |
e6a81bb95c | ||
![]() |
9521c1ad58 | ||
![]() |
6d65cc48d7 | ||
![]() |
681956a963 | ||
![]() |
052f64d648 | ||
![]() |
afe621c25c | ||
![]() |
422cf5f182 | ||
![]() |
637cf8a039 | ||
![]() |
2011a6e2ee | ||
![]() |
d54830de12 | ||
![]() |
a7e7312cca | ||
![]() |
6b83fc6b57 | ||
![]() |
74f9e07151 | ||
![]() |
82a61ab3be | ||
![]() |
54c1794cee | ||
![]() |
c962a6be76 | ||
![]() |
922c4bf3f0 | ||
![]() |
932756efce | ||
![]() |
7838265482 | ||
![]() |
b14b0e5634 | ||
![]() |
4d2d0e7bb8 | ||
![]() |
44378b7dbe | ||
![]() |
ef1acb4e2f | ||
![]() |
da642b2890 | ||
![]() |
6f77af20d0 | ||
![]() |
010f65a1d6 | ||
![]() |
c46f97454a | ||
![]() |
844dbd2ec5 | ||
![]() |
db7caa2dac | ||
![]() |
2974737746 | ||
![]() |
b1d7567226 | ||
![]() |
5103eb3039 | ||
![]() |
0cccdcf9b2 | ||
![]() |
22b840c2f1 | ||
![]() |
ed1a995bff | ||
![]() |
0f39dc1edb | ||
![]() |
dc9103befe | ||
![]() |
67760f5283 | ||
![]() |
99405a4c93 | ||
![]() |
b833c5d2c7 | ||
![]() |
bca5d79f88 | ||
![]() |
6e1c8edf09 | ||
![]() |
32b7b2e2fa | ||
![]() |
cfb7f8ab84 | ||
![]() |
d4bbb8c851 | ||
![]() |
8d80280ab9 | ||
![]() |
c95e3dc065 | ||
![]() |
428f769c38 | ||
![]() |
133c8834df | ||
![]() |
99217593bf | ||
![]() |
1c6e4a2b18 | ||
![]() |
a6eb264770 | ||
![]() |
f5f296b13a | ||
![]() |
0091c4e12b | ||
![]() |
80172e17ac | ||
![]() |
2d96b05403 | ||
![]() |
ec0c1f0d02 | ||
![]() |
00a520a4c3 | ||
![]() |
946b3c1f80 | ||
![]() |
a0dc398f36 | ||
![]() |
b54d2d984a | ||
![]() |
4ab73f9de9 | ||
![]() |
5ebe23e4bb | ||
![]() |
aa227cded1 | ||
![]() |
e406bdbb80 | ||
![]() |
1048f23680 | ||
![]() |
8fe8f09027 | ||
![]() |
78670c0941 | ||
![]() |
34f735890e | ||
![]() |
f08810b202 | ||
![]() |
7a68775e6c | ||
![]() |
e4fccc85c8 | ||
![]() |
2efa142ec9 | ||
![]() |
29b49dd630 | ||
![]() |
9d6bf7e720 | ||
![]() |
5f34508aae | ||
![]() |
2d8ecd561b | ||
![]() |
2059195ae9 | ||
![]() |
d89856f77b | ||
![]() |
975d5be046 | ||
![]() |
b01ef1b9a6 | ||
![]() |
ceb76b6a82 | ||
![]() |
a7e697b588 | ||
![]() |
3ecd918442 | ||
![]() |
4fbdb3a2d5 | ||
![]() |
0157643667 | ||
![]() |
fe741bd767 | ||
![]() |
06b9bdba2c | ||
![]() |
bd0aa74bdd | ||
![]() |
47461df59c | ||
![]() |
04d5588fe5 | ||
![]() |
40d061621b | ||
![]() |
a312629aad | ||
![]() |
d527d4b530 | ||
![]() |
978d2638d8 | ||
![]() |
cfcafdf822 | ||
![]() |
07865d0707 | ||
![]() |
1ac16516a1 | ||
![]() |
75e8795e3f | ||
![]() |
4912466d50 | ||
![]() |
664674913e | ||
![]() |
31e3658823 | ||
![]() |
abd416735d | ||
![]() |
6090bd2095 | ||
![]() |
1777592ec0 | ||
![]() |
8e8fbe14b1 | ||
![]() |
a8a39b6a38 | ||
![]() |
f84cb6de5e | ||
![]() |
dfc67c45c7 | ||
![]() |
e875da5d38 | ||
![]() |
9b9522e3f5 | ||
![]() |
87963685fb | ||
![]() |
0405a57f26 | ||
![]() |
f29c69d6a9 | ||
![]() |
7ec4de841e | ||
![]() |
1f08d2d03c | ||
![]() |
c1a695d1ac | ||
![]() |
ec05056e38 | ||
![]() |
c0b9339d31 | ||
![]() |
6eba621045 | ||
![]() |
a9ad8fa505 | ||
![]() |
00a1731085 | ||
![]() |
6e3da00874 | ||
![]() |
38df01b266 | ||
![]() |
c729f16dcd | ||
![]() |
81d0c04ed4 | ||
![]() |
0924b63e10 | ||
![]() |
ce6afe9379 | ||
![]() |
6f04b2230a | ||
![]() |
8d90b831e1 | ||
![]() |
d4710604c4 | ||
![]() |
85427826aa | ||
![]() |
9c8da03c5c | ||
![]() |
85adefd9a4 | ||
![]() |
22804cfbe8 | ||
![]() |
25e0a90402 | ||
![]() |
8a4b88a59d | ||
![]() |
d2371af120 | ||
![]() |
aa2e1bb310 | ||
![]() |
6153fca4fc | ||
![]() |
f090af0a22 | ||
![]() |
58f420fdca | ||
![]() |
ded2b31fbc | ||
![]() |
75c8d2235b | ||
![]() |
f679961564 | ||
![]() |
938728820b | ||
![]() |
80531ef8d8 | ||
![]() |
a91fba6a3d | ||
![]() |
471c37be59 | ||
![]() |
157ddcbab1 | ||
![]() |
ab160aa359 | ||
![]() |
ecc07e4e98 | ||
![]() |
f8be403c34 | ||
![]() |
28a5cdf319 | ||
![]() |
6b1d264b35 | ||
![]() |
a6c10e9a1c | ||
![]() |
19a46064e9 | ||
![]() |
b57eeaa720 | ||
![]() |
ad059d5804 | ||
![]() |
6e1940e930 | ||
![]() |
103194e32d | ||
![]() |
cbc830fd65 | ||
![]() |
481c330c17 | ||
![]() |
7ef489e057 | ||
![]() |
d9e5d5ff5b | ||
![]() |
ca02fb7782 | ||
![]() |
d4d06da2f8 | ||
![]() |
efde78db77 | ||
![]() |
f1b8bcd6b2 | ||
![]() |
c2bc3704e1 | ||
![]() |
def120aca4 | ||
![]() |
6d2b09ac2b | ||
![]() |
98a9f81d61 | ||
![]() |
78b43a9930 | ||
![]() |
f5460b35a3 | ||
![]() |
3456b1e50d | ||
![]() |
fe6abe1750 | ||
![]() |
6cdb3ff21e | ||
![]() |
01af2778ab | ||
![]() |
ad03c70753 | ||
![]() |
7fe0095fa7 | ||
![]() |
a4b236348f | ||
![]() |
da5ff779c6 | ||
![]() |
e7da5b104d | ||
![]() |
aa40aae5bd | ||
![]() |
4be76f3c8f | ||
![]() |
c58c53293c | ||
![]() |
8695a2806a | ||
![]() |
a59f1b21a6 | ||
![]() |
5a16e3ffa3 | ||
![]() |
d1957b83c8 | ||
![]() |
1b4fd74575 | ||
![]() |
def962b6cb | ||
![]() |
e802f1f61a | ||
![]() |
271b287356 | ||
![]() |
2a30acd99c | ||
![]() |
a8e70f18fd | ||
![]() |
ddd9f20a0b | ||
![]() |
f4a5d671fe | ||
![]() |
c72006dbcc | ||
![]() |
06fe30e2bd | ||
![]() |
08e76815ba | ||
![]() |
33ac3eb551 | ||
![]() |
d56a51cb5e | ||
![]() |
9e2d09dabc | ||
![]() |
2719f62feb | ||
![]() |
065a0c09f8 | ||
![]() |
04731fb7cc | ||
![]() |
12ff5a547f | ||
![]() |
9b2eb74f95 | ||
![]() |
84084baa65 | ||
![]() |
3bc45fbf68 | ||
![]() |
36168a24f5 | ||
![]() |
5e67443a1a | ||
![]() |
17858143b3 | ||
![]() |
c44a7b2705 | ||
![]() |
0ded23591b | ||
![]() |
c1a7aa652d | ||
![]() |
8d47f51399 | ||
![]() |
a81c9bfb81 | ||
![]() |
1caf57644f | ||
![]() |
c70b63c183 | ||
![]() |
1b89b4ef83 | ||
![]() |
234cedd6c6 | ||
![]() |
8279cafd6d | ||
![]() |
5b946e9d95 | ||
![]() |
b46ca50dcc | ||
![]() |
a0d76c3be9 | ||
![]() |
995aafe9cc | ||
![]() |
6e33566cee | ||
![]() |
3b3c1d466d | ||
![]() |
056ab199ab | ||
![]() |
eea0e084af | ||
![]() |
fa82f558be | ||
![]() |
6b555b7017 | ||
![]() |
dafba203e7 | ||
![]() |
a5d382348e | ||
![]() |
74396448df | ||
![]() |
168d6257b4 | ||
![]() |
014c2a82bd | ||
![]() |
1afa33c3c7 | ||
![]() |
3a7c9c7c84 | ||
![]() |
6d08e761c8 | ||
![]() |
fee282f49c | ||
![]() |
07d2bc6898 | ||
![]() |
9551166f27 | ||
![]() |
2a8c420cff | ||
![]() |
ec1e04a65d | ||
![]() |
97a2122f41 | ||
![]() |
3825175bfc | ||
![]() |
68f4be323c | ||
![]() |
4949cd98f3 | ||
![]() |
a14ce4c7cb | ||
![]() |
85a5b7dec4 | ||
![]() |
153d464ce8 | ||
![]() |
83391e2bd9 | ||
![]() |
594dfe572b | ||
![]() |
906e82f600 | ||
![]() |
bcb7e954e9 | ||
![]() |
866c87c65e | ||
![]() |
4ba36d7cb9 | ||
![]() |
13f8a912e3 | ||
![]() |
51f110a990 | ||
![]() |
17eae74c1c | ||
![]() |
cd4b673b6c | ||
![]() |
0d606c743b | ||
![]() |
ec0d3ac95d | ||
![]() |
81ea749248 | ||
![]() |
e99f6b5b38 | ||
![]() |
74b2fc7fdc | ||
![]() |
216f62ea14 | ||
![]() |
b7d0001390 | ||
![]() |
687788e4d3 | ||
![]() |
e009ad1a72 | ||
![]() |
abbd980671 | ||
![]() |
5348f8c9c8 | ||
![]() |
5a4ebf8291 | ||
![]() |
7ae3664c91 | ||
![]() |
1caed3e390 | ||
![]() |
937da63ba6 | ||
![]() |
1f312b2e42 | ||
![]() |
1e3089ffb7 | ||
![]() |
5d7ff150dd | ||
![]() |
c767501c12 | ||
![]() |
7adb907a55 | ||
![]() |
00602d28a4 | ||
![]() |
7a56837141 | ||
![]() |
ed1caffc79 | ||
![]() |
65473b5113 | ||
![]() |
178d115ccb | ||
![]() |
af2896547a | ||
![]() |
10e5b0759c | ||
![]() |
0a81e462db | ||
![]() |
5cbbe8ae2e | ||
![]() |
00fafa16c7 | ||
![]() |
cea8db7eaa | ||
![]() |
b56c0e69e4 | ||
![]() |
b27e82e4a9 | ||
![]() |
ad48834469 | ||
![]() |
9d6b5e2ba1 | ||
![]() |
33ba190bec | ||
![]() |
3783350d25 | ||
![]() |
173405a343 | ||
![]() |
7bc1c9925b | ||
![]() |
ce4c69dd95 | ||
![]() |
8eea825462 | ||
![]() |
49e1ce7c43 | ||
![]() |
618f94f589 | ||
![]() |
ad2c22844c | ||
![]() |
b8df851414 | ||
![]() |
a584141cae | ||
![]() |
4e88f95f94 | ||
![]() |
790e540c19 | ||
![]() |
16074c565f | ||
![]() |
2a1dd55b11 | ||
![]() |
be20f760ab | ||
![]() |
8050394003 | ||
![]() |
ff8b5bc61b | ||
![]() |
ef8797821f | ||
![]() |
5f2797e7cc | ||
![]() |
e286702f4c | ||
![]() |
c58aaf545f | ||
![]() |
990f2dc1cf | ||
![]() |
774b4313f2 | ||
![]() |
1ecbc2ff0f | ||
![]() |
fd8e38f8cd | ||
![]() |
e86d4db55c | ||
![]() |
d9583faf06 | ||
![]() |
2788cf9330 | ||
![]() |
9420c74101 | ||
![]() |
b1bef9c21d | ||
![]() |
5b0ef7ea98 | ||
![]() |
ab53c414bc | ||
![]() |
d547ace749 | ||
![]() |
b47e0cffdd | ||
![]() |
3af35aee9e | ||
![]() |
02314ac7dd | ||
![]() |
e7c4e87ac4 | ||
![]() |
de58bfbb7f | ||
![]() |
0dda4c06b1 | ||
![]() |
79fd6143ec | ||
![]() |
8f89e3f7f4 | ||
![]() |
fc01d11b8d | ||
![]() |
0c28d8dcbe | ||
![]() |
764eaadd25 | ||
![]() |
273771ffec | ||
![]() |
32ce9ce919 | ||
![]() |
34a070f5a6 | ||
![]() |
92bfdffa42 | ||
![]() |
ac4975cd7a | ||
![]() |
fbbbfb9668 | ||
![]() |
38b41fc3fd | ||
![]() |
394f69bee1 | ||
![]() |
ba5531f9dd | ||
![]() |
eb9f5339b6 | ||
![]() |
60d19b2380 | ||
![]() |
a9714e73c8 | ||
![]() |
004d6a3b66 | ||
![]() |
3e79e62c17 | ||
![]() |
27b69330f4 | ||
![]() |
6b50b67339 | ||
![]() |
51ca775a1c | ||
![]() |
1092882f38 | ||
![]() |
8a7986c3bf | ||
![]() |
a99bc91eb0 | ||
![]() |
c7bd8c663d | ||
![]() |
f6c65cba58 | ||
![]() |
f849b07766 | ||
![]() |
071d3c71d8 | ||
![]() |
afbcac9fb1 | ||
![]() |
51e5b56b3a | ||
![]() |
bb07fd42ce | ||
![]() |
bab626c325 | ||
![]() |
2a9131498f | ||
![]() |
35a232105e | ||
![]() |
19dd1a25d7 | ||
![]() |
53396c0e50 | ||
![]() |
0b8208fe7f | ||
![]() |
2da3cff1e8 | ||
![]() |
0c965d0573 | ||
![]() |
77c14692c9 | ||
![]() |
226eb26300 | ||
![]() |
2d606fa989 | ||
![]() |
7a0342c8bb | ||
![]() |
42c9d765cf | ||
![]() |
a8a80ee689 | ||
![]() |
f9bdb4b0b8 | ||
![]() |
9332527872 | ||
![]() |
84f772357e | ||
![]() |
f2b9785a67 | ||
![]() |
eeaec99c59 | ||
![]() |
b0002e3b73 | ||
![]() |
27c589da97 | ||
![]() |
6484af472b | ||
![]() |
92a218b7a9 | ||
![]() |
d69a1f98af | ||
![]() |
23a6f62ea3 | ||
![]() |
e0d3ca71b3 | ||
![]() |
4f40b9f7cf | ||
![]() |
bb009daf66 | ||
![]() |
3d276d50b4 | ||
![]() |
b1b731340e | ||
![]() |
b9b02b4ff2 | ||
![]() |
ab5d23da11 | ||
![]() |
0554fe3652 | ||
![]() |
b0282fe36f | ||
![]() |
69b45e693b | ||
![]() |
9e97acc28d | ||
![]() |
b1e446a931 | ||
![]() |
938319cd44 | ||
![]() |
fee29001fa | ||
![]() |
6d894a1806 | ||
![]() |
f1fc5d79ca | ||
![]() |
0fd2c74a66 | ||
![]() |
bb99cf37e3 | ||
![]() |
dc432f3ffa | ||
![]() |
37710195ca | ||
![]() |
7b9295ff99 | ||
![]() |
5f61d440eb | ||
![]() |
6bc73a9ebe | ||
![]() |
1195eb266e | ||
![]() |
3562a3e51e | ||
![]() |
7c47fe746c | ||
![]() |
65a1c4a016 | ||
![]() |
46418d0f2d | ||
![]() |
bbfa6fe632 | ||
![]() |
bf97d13d0b | ||
![]() |
b5673b6333 | ||
![]() |
ee802867df | ||
![]() |
ecaa51e322 | ||
![]() |
0779333064 | ||
![]() |
6f1a4a73b7 | ||
![]() |
945ed2610a | ||
![]() |
ad585e179f | ||
![]() |
8348a1ec8f | ||
![]() |
c18e00daa4 | ||
![]() |
418ba96334 | ||
![]() |
a60e782959 | ||
![]() |
8bab5733d7 | ||
![]() |
e3270dfd68 | ||
![]() |
a14997ffb8 | ||
![]() |
dd94f97572 | ||
![]() |
7d502fb448 | ||
![]() |
3ac87bbcda | ||
![]() |
f64799622d | ||
![]() |
6f0ad2b6c5 | ||
![]() |
b5750afb24 | ||
![]() |
442dd5e955 | ||
![]() |
172c2ae1aa | ||
![]() |
d7fcaf33b9 | ||
![]() |
6a65b4c305 | ||
![]() |
4f0e0af319 | ||
![]() |
cb382b1e7d | ||
![]() |
a163beee69 | ||
![]() |
31268ad7cd | ||
![]() |
a0d43dd87f | ||
![]() |
1db533c8cf | ||
![]() |
78ee663660 | ||
![]() |
c32a809d38 | ||
![]() |
871bf3b88f | ||
![]() |
08360e401d | ||
![]() |
b611b1824a | ||
![]() |
1473d8474f | ||
![]() |
0ecc3394c3 | ||
![]() |
725985379a | ||
![]() |
8849b9b62c | ||
![]() |
caa2611ad5 | ||
![]() |
f8ff597963 | ||
![]() |
ff6e434caf | ||
![]() |
95bb12880d | ||
![]() |
257196664a | ||
![]() |
643bf95366 | ||
![]() |
36a187da39 | ||
![]() |
fec80f2835 | ||
![]() |
4e47653cf6 | ||
![]() |
c13004f985 | ||
![]() |
baba41e304 | ||
![]() |
d87e09a8b4 | ||
![]() |
33146ac353 | ||
![]() |
1406144210 | ||
![]() |
bb6ab67175 | ||
![]() |
ed3d8222d6 | ||
![]() |
bb20af8f20 | ||
![]() |
9355ec44e0 | ||
![]() |
c63bd323ce | ||
![]() |
55db7105c5 | ||
![]() |
1c079e554b | ||
![]() |
48afb68f3a | ||
![]() |
21f409d5e2 | ||
![]() |
521e573be9 | ||
![]() |
abf9ae2dd9 | ||
![]() |
9f013f7de4 | ||
![]() |
7fc04fd5cd | ||
![]() |
7901b04c78 | ||
![]() |
653eea5840 | ||
![]() |
5a4055fb08 | ||
![]() |
4d68a12f03 | ||
![]() |
0e951da64b | ||
![]() |
38dab040b3 | ||
![]() |
e9f6af61f9 | ||
![]() |
b06c4e2711 | ||
![]() |
1686f4e857 | ||
![]() |
9f57732af2 | ||
![]() |
329382c1da | ||
![]() |
fadc03df21 | ||
![]() |
54ee0e28ab | ||
![]() |
92fc53ebef | ||
![]() |
7e7a1613cf | ||
![]() |
f73c4643ef | ||
![]() |
8b94e8f260 | ||
![]() |
41bc17a27f | ||
![]() |
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 |
.travis.ymlAUTHORSNEWSREADME.md
android
doc
client.rstconf.pydeveloper.rstindex.rstmeson.buildmpd.1.rstmpd.conf.5.rstmpdconf.exampleplugins.rstprotocol.rstuser.rst
meson.buildmeson_options.txtpython/build
autotools.pyboost.pycmake.pyffmpeg.pyjack.pylibs.pymakeproject.pymeson.pyopenssl.pyproject.pyverify.pyzlib.py
src
BulkEdit.hxxChrono.hxxCommandLine.cxxCommandLine.hxxGitVersion.cxxGitVersion.hxxIcyMetaDataParser.cxxIcyMetaDataParser.hxxIdle.cxxIdle.hxxIdleFlags.cxxIdleFlags.hxxInstance.cxxInstance.hxxListen.cxxListen.hxxLocateUri.cxxLocateUri.hxxLog.cxxLog.hxxLogBackend.cxxLogBackend.hxxLogInit.cxxLogInit.hxxLogLevel.hxxMain.cxxMain.hxxMapper.cxxMapper.hxxMixRampInfo.hxxMusicBuffer.cxxMusicBuffer.hxxMusicChunk.cxxMusicChunk.hxxMusicChunkPtr.cxxMusicChunkPtr.hxxMusicPipe.cxxMusicPipe.hxxPartition.cxxPartition.hxxPermission.cxxPermission.hxxPlaylistDatabase.cxxPlaylistDatabase.hxxPlaylistError.cxxPlaylistError.hxxPlaylistFile.cxxPlaylistFile.hxxPlaylistPrint.cxxPlaylistPrint.hxxPlaylistSave.cxxPlaylistSave.hxxPluginUnavailable.hxxRemoteTagCache.cxxRemoteTagCache.hxxRemoteTagCacheHandler.hxxReplayGainConfig.hxxReplayGainGlobal.cxxReplayGainGlobal.hxxReplayGainInfo.cxxReplayGainInfo.hxxReplayGainMode.cxxReplayGainMode.hxxSingleMode.cxxSingleMode.hxxSongLoader.cxxSongLoader.hxxSongPrint.cxxSongPrint.hxxSongSave.cxxSongSave.hxxSongUpdate.cxxStateFile.cxxStateFile.hxxStateFileConfig.cxxStateFileConfig.hxxStats.cxxStats.hxxTagAny.cxxTagAny.hxxTagArchive.cxxTagArchive.hxxTagFile.cxxTagFile.hxxTagPrint.cxxTagPrint.hxxTagSave.cxxTagSave.hxxTagStream.cxxTagStream.hxxTimePrint.cxxTimePrint.hxxls.cxxls.hxx
android
AudioManager.cxxAudioManager.hxxContext.cxxContext.hxxEnvironment.cxxEnvironment.hxxLogListener.cxxLogListener.hxx
archive
ArchiveFile.hxxArchiveList.cxxArchiveList.hxxArchivePlugin.cxxArchivePlugin.hxxArchiveVisitor.hxxmeson.build
plugins
client
BackgroundCommand.hxxClient.cxxClient.hxxConfig.cxxConfig.hxxDomain.cxxDomain.hxxEvent.cxxExpire.cxxFile.cxxIdle.cxxList.cxxList.hxxListener.cxxListener.hxxMessage.cxxMessage.hxxNew.cxxProcess.cxxRead.cxxResponse.cxxResponse.hxxSubscribe.cxxThreadBackgroundCommand.cxxThreadBackgroundCommand.hxxWrite.cxx
command
AllCommands.cxxAllCommands.hxxClientCommands.cxxClientCommands.hxxCommandError.cxxCommandError.hxxCommandListBuilder.cxxCommandListBuilder.hxxCommandResult.hxxDatabaseCommands.cxxDatabaseCommands.hxxFileCommands.cxxFileCommands.hxxFingerprintCommands.cxxFingerprintCommands.hxxMessageCommands.cxxMessageCommands.hxxNeighborCommands.cxxNeighborCommands.hxxOtherCommands.cxxOtherCommands.hxxOutputCommands.cxxOutputCommands.hxxPartitionCommands.cxxPartitionCommands.hxxPlayerCommands.cxxPlayerCommands.hxxPlaylistCommands.cxxPlaylistCommands.hxxPositionArg.cxxPositionArg.hxxQueueCommands.cxxQueueCommands.hxxRequest.hxxStickerCommands.cxxStickerCommands.hxxStorageCommands.cxxStorageCommands.hxxTagCommands.cxxTagCommands.hxx
config
Block.cxxBlock.hxxCheck.cxxCheck.hxxData.cxxData.hxxDefaults.hxxDomain.cxxDomain.hxxFile.cxxFile.hxxMigrate.cxxMigrate.hxxNet.cxxNet.hxxOption.hxxParam.cxxParam.hxxParser.cxxParser.hxxPath.cxxPath.hxxTemplates.cxxTemplates.hxx
db
Configured.cxxConfigured.hxxCount.cxxCount.hxxDatabaseError.hxxDatabaseGlue.cxxDatabaseGlue.hxxDatabaseListener.hxxDatabaseLock.cxxDatabaseLock.hxxDatabasePlaylist.cxxDatabasePlaylist.hxxDatabasePlugin.hxxDatabasePrint.cxxDatabasePrint.hxxDatabaseQueue.cxxDatabaseQueue.hxxDatabaseSong.cxxDatabaseSong.hxxHelpers.cxxHelpers.hxxInterface.hxxLightDirectory.hxxPlaylistInfo.hxxPlaylistVector.cxxPlaylistVector.hxxPtr.hxxRegistry.cxxRegistry.hxxSelection.cxxSelection.hxxStats.hxxUniqueTags.cxxUniqueTags.hxxUri.hxxVHelper.cxxVHelper.hxxVisitor.hxxmeson.build
plugins
ProxyDatabasePlugin.cxxProxyDatabasePlugin.hxxmeson.build
simple
DatabaseSave.cxxDatabaseSave.hxxDirectory.cxxDirectory.hxxDirectorySave.cxxDirectorySave.hxxExportedSong.hxxMount.cxxMount.hxxPrefixedLightSong.hxxPtr.hxxSimpleDatabasePlugin.cxxSimpleDatabasePlugin.hxxSong.cxxSong.hxxSongSort.cxxSongSort.hxx
upnp
update
Archive.cxxConfig.cxxConfig.hxxContainer.cxxEditor.cxxEditor.hxxExcludeList.cxxExcludeList.hxxInotifyDomain.cxxInotifyDomain.hxxInotifyQueue.cxxInotifyQueue.hxxInotifySource.cxxInotifySource.hxxInotifyUpdate.cxxInotifyUpdate.hxxPlaylist.cxxQueue.cxxQueue.hxxRemove.cxxRemove.hxxService.cxxService.hxxSpecialDirectory.cxxUpdateDomain.cxxUpdateDomain.hxxUpdateIO.cxxUpdateIO.hxxUpdateSong.cxxVirtualDirectory.cxxWalk.cxxWalk.hxx
decoder
Bridge.cxxBridge.hxxClient.hxxCommand.hxxControl.cxxControl.hxxDecoderAPI.cxxDecoderAPI.hxxDecoderBuffer.cxxDecoderBuffer.hxxDecoderList.cxxDecoderList.hxxDecoderPlugin.cxxDecoderPlugin.hxxDecoderPrint.cxxDecoderPrint.hxxDomain.cxxDomain.hxxReader.cxxReader.hxxThread.cxxmeson.build
plugins
AdPlugDecoderPlugin.cxxAdPlugDecoderPlugin.hAudiofileDecoderPlugin.cxxAudiofileDecoderPlugin.hxxDsdLib.cxxDsdLib.hxxDsdiffDecoderPlugin.cxxDsdiffDecoderPlugin.hxxDsfDecoderPlugin.cxxDsfDecoderPlugin.hxxFaadDecoderPlugin.cxxFaadDecoderPlugin.hxxFfmpegDecoderPlugin.cxxFfmpegDecoderPlugin.hxxFfmpegIo.cxxFfmpegIo.hxxFfmpegMetaData.cxxFfmpegMetaData.hxxFlacCommon.cxxFlacCommon.hxxFlacDecoderPlugin.cxxFlacDecoderPlugin.hFlacDomain.cxxFlacDomain.hxxFlacInput.cxxFlacInput.hxxFlacPcm.cxxFlacPcm.hxxFlacStreamDecoder.hxxFluidsynthDecoderPlugin.cxxFluidsynthDecoderPlugin.hxxGmeDecoderPlugin.cxxGmeDecoderPlugin.hxxHybridDsdDecoderPlugin.cxxHybridDsdDecoderPlugin.hxxMadDecoderPlugin.cxxMadDecoderPlugin.hxxMikmodDecoderPlugin.cxxMikmodDecoderPlugin.hxxModCommon.cxxModCommon.hxxModplugDecoderPlugin.cxxModplugDecoderPlugin.hxxMpcdecDecoderPlugin.cxxMpcdecDecoderPlugin.hxxMpg123DecoderPlugin.cxxMpg123DecoderPlugin.hxxOggCodec.cxxOggCodec.hxxOggDecoder.cxxOggDecoder.hxxOpenmptDecoderPlugin.cxxOpenmptDecoderPlugin.hxxOpusDecoderPlugin.cxxOpusDecoderPlugin.hOpusDomain.cxxOpusDomain.hxxOpusHead.cxxOpusHead.hxxOpusReader.hxxOpusTags.cxxOpusTags.hxxPcmDecoderPlugin.cxxPcmDecoderPlugin.hxxSidplayDecoderPlugin.cxxSidplayDecoderPlugin.hxxSndfileDecoderPlugin.cxxSndfileDecoderPlugin.hxxVorbisDecoderPlugin.cxxVorbisDecoderPlugin.hVorbisDomain.cxxVorbisDomain.hxxWavpackDecoderPlugin.cxxWavpackDecoderPlugin.hxxWildmidiDecoderPlugin.cxxWildmidiDecoderPlugin.hxxmeson.build
encoder
Configured.cxxConfigured.hxxEncoderAPI.hxxEncoderInterface.hxxEncoderList.cxxEncoderList.hxxEncoderPlugin.hxxToOutputStream.cxxToOutputStream.hxx
plugins
FlacEncoderPlugin.cxxFlacEncoderPlugin.hxxLameEncoderPlugin.cxxLameEncoderPlugin.hxxNullEncoderPlugin.cxxNullEncoderPlugin.hxxOggEncoder.hxxOpusEncoderPlugin.cxxOpusEncoderPlugin.hxxShineEncoderPlugin.cxxShineEncoderPlugin.hxxTwolameEncoderPlugin.cxxTwolameEncoderPlugin.hxxVorbisEncoderPlugin.cxxVorbisEncoderPlugin.hxxWaveEncoderPlugin.cxxWaveEncoderPlugin.hxxmeson.build
event
Backend.hxxBackendEvents.hxxBufferedSocket.cxxBufferedSocket.hxxCall.cxxCall.hxxChrono.hxxCoarseTimerEvent.cxxCoarseTimerEvent.hxxDeferEvent.cxxDeferEvent.hxxEpollBackend.hxxEpollEvents.hxxFarTimerEvent.hxxFineTimerEvent.cxxFineTimerEvent.hxxFullyBufferedSocket.cxxFullyBufferedSocket.hxxIdleEvent.cxxIdleEvent.hxxInjectEvent.cxxInjectEvent.hxxLoop.cxxLoop.hxxMaskMonitor.cxxMaskMonitor.hxxMultiSocketMonitor.cxxMultiSocketMonitor.hxxPipeEvent.hxxPollBackend.cxxPollBackend.hxxPollEvents.hxxPollResultGeneric.hxxServerSocket.cxxServerSocket.hxxSignalMonitor.cxxSignalMonitor.hxxSocketEvent.cxxSocketEvent.hxxSocketMonitor.hxxThread.cxxThread.hxxTimerEvent.hxxTimerList.cxxTimerList.hxxTimerWheel.cxxTimerWheel.hxxUringManager.cxxUringManager.hxxWakeFD.hxxWinSelectBackend.cxxWinSelectBackend.hxxWinSelectEvents.hxxmeson.build
filter
Factory.cxxFactory.hxxFilter.cxxFilter.hxxFilterPlugin.hxxLoadChain.cxxLoadChain.hxxLoadOne.cxxLoadOne.hxxNullFilter.hxxObserver.cxxObserver.hxxPrepared.hxxRegistry.cxxRegistry.hxx
plugins
AutoConvertFilterPlugin.cxxAutoConvertFilterPlugin.hxxChainFilterPlugin.cxxChainFilterPlugin.hxxConvertFilterPlugin.cxxConvertFilterPlugin.hxxFfmpegFilter.cxxFfmpegFilter.hxxFfmpegFilterPlugin.cxxFfmpegFilterPlugin.hxxHdcdFilterPlugin.cxxHdcdFilterPlugin.hxxNormalizeFilterPlugin.cxxNormalizeFilterPlugin.hxxNullFilterPlugin.cxxNullFilterPlugin.hxxReplayGainFilterPlugin.cxxReplayGainFilterPlugin.hxxRouteFilterPlugin.cxxRouteFilterPlugin.hxxTwoFilters.cxxTwoFilters.hxxVolumeFilterPlugin.cxxVolumeFilterPlugin.hxxmeson.build
fs
AllocatedPath.cxxAllocatedPath.hxxCharset.cxxCharset.hxxCheckFile.cxxCheckFile.hxxConfig.cxxConfig.hxxDirectoryReader.cxxDirectoryReader.hxxDomain.cxxDomain.hxxFeatures.hxxFileInfo.hxxFileSystem.cxxFileSystem.hxxGlob.cxxGlob.hxxLimits.hxxList.cxxList.hxxLookupFile.cxxLookupFile.hxxNarrowPath.cxxNarrowPath.hxxPath.cxxPath.hxxPath2.cxxStandardDirectory.cxxStandardDirectory.hxxTraits.cxxTraits.hxxXDG.hxx
io
AutoGunzipReader.cxxAutoGunzipReader.hxxBufferedOutputStream.cxxBufferedOutputStream.hxxBufferedReader.hxxFileOutputStream.cxxFileOutputStream.hxxFileReader.hxxGunzipReader.hxxPeekReader.cxxPeekReader.hxxReader.hxxStdioOutputStream.hxxTextFile.cxxTextFile.hxx
meson.buildinput
AsyncInputStream.cxxAsyncInputStream.hxxBufferedInputStream.cxxBufferedInputStream.hxxBufferingInputStream.cxxBufferingInputStream.hxxCondHandler.hxxError.cxxError.hxxFailingInputStream.hxxHandler.hxxIcyInputStream.cxxIcyInputStream.hxxInit.cxxInit.hxxInputPlugin.cxxInputPlugin.hxxInputStream.cxxInputStream.hxxLastInputStream.cxxLastInputStream.hxxLocalOpen.cxxLocalOpen.hxxMaybeBufferedInputStream.cxxMaybeBufferedInputStream.hxxOffset.hxxOpen.cxxProxyInputStream.cxxProxyInputStream.hxxPtr.hxxReader.cxxReader.hxxRegistry.cxxRegistry.hxxRemoteTagScanner.hxxRewindInputStream.cxxRewindInputStream.hxxScanTags.cxxScanTags.hxxTextInputStream.cxxTextInputStream.hxxThreadInputStream.cxxThreadInputStream.hxx
cache
meson.buildplugins
AlsaInputPlugin.cxxAlsaInputPlugin.hxxArchiveInputPlugin.cxxArchiveInputPlugin.hxxCdioParanoiaInputPlugin.cxxCdioParanoiaInputPlugin.hxxCurlInputPlugin.cxxCurlInputPlugin.hxxFfmpegInputPlugin.cxxFfmpegInputPlugin.hxxFileInputPlugin.cxxFileInputPlugin.hxxMmsInputPlugin.cxxMmsInputPlugin.hxxNfsInputPlugin.cxxNfsInputPlugin.hxxQobuzClient.cxxQobuzClient.hxxQobuzErrorParser.cxxQobuzErrorParser.hxxQobuzInputPlugin.cxxQobuzInputPlugin.hxxQobuzLoginRequest.cxxQobuzLoginRequest.hxxQobuzSession.hxxQobuzTagScanner.cxxQobuzTagScanner.hxxQobuzTrackRequest.cxxQobuzTrackRequest.hxxSmbclientInputPlugin.cxxSmbclientInputPlugin.hxxTidalError.hxxTidalErrorParser.cxxTidalErrorParser.hxxTidalInputPlugin.cxxTidalLoginRequest.cxxTidalLoginRequest.hxxTidalSessionManager.cxxTidalSessionManager.hxxTidalTagScanner.cxxTidalTagScanner.hxxTidalTrackRequest.cxxTidalTrackRequest.hxxUringInputPlugin.cxxUringInputPlugin.hxxmeson.build
io
java
Class.hxxException.cxxException.hxxFile.cxxFile.hxxGlobal.cxxGlobal.hxxObject.hxxRef.hxxString.cxxString.hxx
lib
alsa
AllowedFormat.cxxAllowedFormat.hxxFormat.hxxHwSetup.cxxHwSetup.hxxNonBlock.cxxNonBlock.hxxPeriodBuffer.hxxVersion.cxxVersion.hxxmeson.build
cdio
chromaprint
crypto
curl
Easy.hxxError.hxxForm.cxxGlobal.cxxGlobal.hxxHandler.hxxMulti.hxxRequest.cxxRequest.hxxVersion.hxxmeson.build
patches
dbus
AppendIter.hxxError.hxxFilterHelper.cxxFilterHelper.hxxGlue.cxxGlue.hxxObjectManager.hxxReadIter.hxxTypes.hxxUDisks2.cxxUDisks2.hxxValues.hxxWatch.cxxWatch.hxxmeson.build
expat
ffmpeg
Buffer.hxxChannelLayout.hxxCodec.hxxDetectFilterFormat.cxxDetectFilterFormat.hxxDomain.cxxDomain.hxxError.cxxError.hxxFilter.cxxFilter.hxxFormat.hxxFrame.hxxIOContext.hxxInit.cxxInit.hxxInterleave.cxxInterleave.hxxLogCallback.cxxLogCallback.hxxLogError.cxxLogError.hxxSampleFormat.hxxTime.hxxmeson.build
fmt
gcrypt
icu
CaseFold.cxxCaseFold.hxxCollate.cxxCollate.hxxCompare.cxxCompare.hxxConverter.cxxConverter.hxxInit.cxxInit.hxxUtil.cxxUtil.hxxWin32.cxxWin32.hxxmeson.build
jack
nfs
Base.cxxBase.hxxBlocking.cxxBlocking.hxxCallback.hxxCancellable.hxxConnection.cxxConnection.hxxFileReader.cxxFileReader.hxxGlue.cxxGlue.hxxLease.hxxManager.cxxManager.hxxmeson.build
patches
pcre
pipewire
pulse
smbclient
sqlite
systemd
upnp
Action.hxxCallback.hxxClientInit.cxxClientInit.hxxCompat.hxxContentDirectoryService.cxxContentDirectoryService.hxxDevice.cxxDevice.hxxDiscovery.cxxDiscovery.hxxInit.cxxInit.hxxUniqueIxml.hxxUtil.cxxUtil.hxxixmlwrap.cxxixmlwrap.hxxmeson.build
xiph
FlacAudioFormat.hxxFlacIOHandle.cxxFlacIOHandle.hxxFlacMetadataChain.cxxFlacMetadataChain.hxxFlacMetadataIterator.hxxFlacStreamMetadata.cxxFlacStreamMetadata.hxxOggFind.cxxOggFind.hxxOggPacket.cxxOggPacket.hxxOggPage.hxxOggStreamState.hxxOggSyncState.cxxOggSyncState.hxxOggVisitor.cxxOggVisitor.hxxScanVorbisComment.cxxScanVorbisComment.hxxVorbisComment.hxxVorbisComments.cxxVorbisComments.hxxVorbisPicture.cxxVorbisPicture.hxxXiphTags.cxxXiphTags.hxxmeson.build
yajl
zlib
mixer
Listener.hxxMixerAll.cxxMixerControl.cxxMixerControl.hxxMixerInternal.hxxMixerList.hxxMixerPlugin.hxxMixerType.cxxMixerType.hxxVolume.cxxVolume.hxxmeson.build
plugins
AlsaMixerPlugin.cxxAndroidMixerPlugin.cxxHaikuMixerPlugin.cxxNullMixerPlugin.cxxOSXMixerPlugin.cxxOssMixerPlugin.cxxPipeWireMixerPlugin.cxxPipeWireMixerPlugin.hxxPulseMixerPlugin.cxxPulseMixerPlugin.hxxSndioMixerPlugin.cxxSoftwareMixerPlugin.cxxSoftwareMixerPlugin.hxxWasapiMixerPlugin.cxxWinmmMixerPlugin.cxxmeson.build
neighbor
net
AddressInfo.cxxAddressInfo.hxxAllocatedSocketAddress.cxxAllocatedSocketAddress.hxxFeatures.hxxHostParser.hxxIPv4Address.cxxIPv4Address.hxxIPv6Address.cxxIPv6Address.hxxInit.hxxResolver.cxxResolver.hxxSocketAddress.cxxSocketAddress.hxxSocketDescriptor.cxxSocketDescriptor.hxxSocketError.cxxSocketError.hxxSocketUtil.cxxSocketUtil.hxxStaticSocketAddress.hxxToString.cxxToString.hxxUniqueSocketDescriptor.hxx
open.houtput
Client.hxxControl.cxxControl.hxxDefaults.cxxDefaults.hxxDomain.cxxDomain.hxxError.hxxFiltered.cxxFiltered.hxxFinish.cxxInit.cxxInterface.cxxInterface.hxxMultipleOutputs.cxxMultipleOutputs.hxxOutputAPI.hxxOutputCommand.cxxOutputCommand.hxxOutputPlugin.cxxOutputPlugin.hxxPrint.cxxPrint.hxxRegistry.cxxRegistry.hxxSharedPipeConsumer.cxxSharedPipeConsumer.hxxSource.cxxSource.hxxState.cxxState.hxxThread.cxxTimer.cxxTimer.hxxmeson.build
plugins
AlsaOutputPlugin.cxxAlsaOutputPlugin.hxxAoOutputPlugin.cxxAoOutputPlugin.hxxFifoOutputPlugin.cxxFifoOutputPlugin.hxxHaikuOutputPlugin.cxxHaikuOutputPlugin.hxxJackOutputPlugin.cxxJackOutputPlugin.hxxNullOutputPlugin.cxxNullOutputPlugin.hxxOSXOutputPlugin.cxxOSXOutputPlugin.hxxOpenALOutputPlugin.cxxOpenALOutputPlugin.hxxOssOutputPlugin.cxxOssOutputPlugin.hxxPipeOutputPlugin.cxxPipeOutputPlugin.hxxPipeWireOutputPlugin.cxxPipeWireOutputPlugin.hxxPulseOutputPlugin.cxxPulseOutputPlugin.hxxRecorderOutputPlugin.cxxRecorderOutputPlugin.hxxShoutOutputPlugin.cxxShoutOutputPlugin.hxxSndioOutputPlugin.cxxSndioOutputPlugin.hxxSolarisOutputPlugin.cxxSolarisOutputPlugin.hxxWasapiOutputPlugin.cxxWinmmOutputPlugin.cxxWinmmOutputPlugin.hxx
httpd
HttpdClient.cxxHttpdClient.hxxHttpdInternal.hxxHttpdOutputPlugin.cxxHttpdOutputPlugin.hxxIcyMetaDataServer.cxxIcyMetaDataServer.hxxPage.hxx
meson.buildsles
snapcast
Chunk.hxxClient.cxxClient.hxxInternal.hxxProtocol.hxxSnapcastOutputPlugin.cxxSnapcastOutputPlugin.hxxTimestamp.hxx
wasapi
pcm
AudioFormat.cxxAudioFormat.hxxAudioParser.cxxAudioParser.hxxBuffer.cxxBuffer.hxxChannelDefs.hxxChannelsConverter.cxxChannelsConverter.hxxCheckAudioFormat.cxxCheckAudioFormat.hxxClamp.hxxConfiguredResampler.cxxConfiguredResampler.hxxConvert.cxxConvert.hxxDither.cxxDither.hxxDop.cxxDop.hxxDsd16.cxxDsd16.hxxDsd32.cxxDsd32.hxxExport.cxxExport.hxxFallbackResampler.cxxFallbackResampler.hxxFloatConvert.hxxFormatConverter.cxxFormatConverter.hxxGlueResampler.cxxGlueResampler.hxxInterleave.cxxInterleave.hxxLibsamplerateResampler.cxxLibsamplerateResampler.hxxMix.cxxMix.hxxNeon.hxxOrder.cxxOrder.hxxPack.cxxPack.hxxPcmChannels.cxxPcmChannels.hxxPcmDsd.cxxPcmDsd.hxxPcmFormat.cxxPcmFormat.hxxPrng.hxxResampler.hxxRestBuffer.hxxSampleFormat.cxxSampleFormat.hxxShiftConvert.hxxSilence.cxxSilence.hxxSoxrResampler.cxxSoxrResampler.hxxTraits.hxxVolume.cxxVolume.hxxmeson.build
player
playlist
MemorySongEnumerator.cxxMemorySongEnumerator.hxxPlaylistAny.cxxPlaylistAny.hxxPlaylistMapper.cxxPlaylistMapper.hxxPlaylistPlugin.cxxPlaylistPlugin.hxxPlaylistQueue.cxxPlaylistQueue.hxxPlaylistRegistry.cxxPlaylistRegistry.hxxPlaylistSong.cxxPlaylistSong.hxxPlaylistStream.cxxPlaylistStream.hxxPrint.cxxPrint.hxxSongEnumerator.hxx
cue
plugins
AsxPlaylistPlugin.cxxAsxPlaylistPlugin.hxxCuePlaylistPlugin.cxxCuePlaylistPlugin.hxxEmbeddedCuePlaylistPlugin.cxxEmbeddedCuePlaylistPlugin.hxxExtM3uPlaylistPlugin.cxxExtM3uPlaylistPlugin.hxxFlacPlaylistPlugin.cxxFlacPlaylistPlugin.hxxM3uPlaylistPlugin.cxxM3uPlaylistPlugin.hxxPlsPlaylistPlugin.cxxPlsPlaylistPlugin.hxxRssPlaylistPlugin.cxxRssPlaylistPlugin.hxxSoundCloudPlaylistPlugin.cxxSoundCloudPlaylistPlugin.hxxXspfPlaylistPlugin.cxxXspfPlaylistPlugin.hxxmeson.build
protocol
queue
IdTable.hxxListener.hxxPlaylist.cxxPlaylist.hxxPlaylistControl.cxxPlaylistEdit.cxxPlaylistState.cxxPlaylistState.hxxPlaylistTag.cxxPlaylistUpdate.cxxQueue.cxxQueue.hxxQueuePrint.cxxQueuePrint.hxxQueueSave.cxxQueueSave.hxx
song
AndSongFilter.cxxAndSongFilter.hxxAudioFormatSongFilter.cxxAudioFormatSongFilter.hxxBaseSongFilter.cxxBaseSongFilter.hxxDetachedSong.cxxDetachedSong.hxxEscape.cxxEscape.hxxFilter.cxxFilter.hxxISongFilter.hxxLightSong.cxxLightSong.hxxModifiedSinceSongFilter.cxxModifiedSinceSongFilter.hxxNotSongFilter.hxxOptimizeFilter.cxxOptimizeFilter.hxxStringFilter.cxxStringFilter.hxxTagSongFilter.cxxTagSongFilter.hxxUriSongFilter.cxxUriSongFilter.hxx
sticker
storage
CompositeStorage.cxxCompositeStorage.hxxConfigured.cxxConfigured.hxxFileInfo.hxxMemoryDirectoryReader.cxxMemoryDirectoryReader.hxxRegistry.cxxRegistry.hxxStorageInterface.cxxStorageInterface.hxxStoragePlugin.cxxStoragePlugin.hxxStorageState.cxxStorageState.hxxmeson.build
plugins
system
Clock.cxxClock.hxxError.hxxEventFD.hxxEventPipe.cxxEventPipe.hxxFatalError.cxxFatalError.hxxKernelVersion.cxxKernelVersion.hxxPeriodClock.hxxmeson.build
tag
Aiff.cxxAiff.hxxApeLoader.cxxApeLoader.hxxApeReplayGain.cxxApeReplayGain.hxxApeTag.cxxApeTag.hxxBuilder.cxxBuilder.hxxConfig.cxxConfig.hxxFallback.hxxFixString.cxxFixString.hxxFormat.cxxFormat.hxxGenParseName.cxxGeneric.cxxGeneric.hxxHandler.cxxHandler.hxxId3Load.cxxId3Load.hxxId3MusicBrainz.cxxId3MusicBrainz.hxxId3Picture.cxxId3Picture.hxxId3ReplayGain.cxxId3ReplayGain.hxxId3Scan.cxxId3Scan.hxxId3String.hxxId3Unique.hxxItem.hxxMask.hxxMixRamp.cxxMixRamp.hxxNames.cParseName.cxxParseName.hxxPool.cxxPool.hxxReplayGain.cxxReplayGain.hxxRiffFormat.hxxRiffId3.cxxRiffId3.hxxRva2.cxxRva2.hxxSettings.cxxSettings.hxxTable.cxxTable.hxxTag.cxxTag.hxxType.hVisitFallback.hxxVorbisComment.cxxVorbisComment.hxxmeson.build
thread
CriticalSection.hxxFuture.hxxId.hxxName.hxxSafeSingleton.hxxSlack.hxxThread.cxxThread.hxxWindowsCond.hxxWindowsFuture.hxx
time
Calendar.hxxClockCache.hxxConvert.cxxConvert.hxxFileTime.hxxISO8601.cxxISO8601.hxxMath.cxxMath.hxxSystemClock.hxxmeson.build
unix
util
Alloc.cxxAlloc.hxxAllocatedArray.hxxAllocatedString.hxxBindMethod.hxxBitReverse.cxxBitReverse.hxxByteOrder.hxxByteReverse.cxxByteReverse.hxxCast.hxxCompiler.hConstBuffer.hxxDereferenceIterator.hxxDivideString.cxxDivideString.hxxException.hxxForeignFifoBuffer.hxxFormatString.cxxFormatString.hxxHexFormat.hxxHugeAllocator.cxxHugeAllocator.hxxIntrusiveList.hxxIterableSplitString.hxxLazyRandomEngine.cxxLazyRandomEngine.hxxMimeType.cxxMimeType.hxxOffsetPointer.hxxOptionDef.hxxOptionParser.cxxOptionParser.hxxPeakBuffer.cxxPeakBuffer.hxxRuntimeError.hxxSerial.cxxSerial.hxxSliceBuffer.hxxStringAPI.hxxStringBuffer.hxxStringCompare.cxxStringCompare.hxxStringPointer.hxxStringStrip.cxxStringStrip.hxxStringUtil.cxxStringUtil.hxxStringView.hxxTemplateString.hxxUTF8.cxxUTF8.hxxUriExtract.cxxUriExtract.hxxUriRelative.cxxUriRelative.hxxUriUtil.cxxUriUtil.hxxVarSize.hxxWStringAPI.hxxWStringCompare.hxxWStringView.hxxWritableBuffer.hxxformat.cformat.hmeson.build
win32
Com.hxxComHeapPtr.hxxComPtr.hxxComWorker.cxxComWorker.hxxHResult.cxxHResult.hxxPropVariant.cxxPropVariant.hxxWin32Main.cxxWinEvent.cxxWinEvent.hxxmeson.build
zeroconf
subprojects
test
ConfigGlue.hxxContainerScan.cxxDumpDatabase.cxxDumpDecoderClient.cxxDumpDecoderClient.hxxMakeTag.hxxNullMixerListener.hxxParseSongFilter.cxxReadApeTags.cxxRunChromaprint.cxxRunCurl.cxxRunZeroconf.cxxShutdownHandler.cxxShutdownHandler.hxxTestAudioFormat.cxxTestLookupFile.cxxTestMimeType.cxxTestTagSongFilter.cxxTestUriExtract.cxxWriteFile.cxxdump_playlist.cxxdump_rva2.cxxdump_text_file.cxx
fuzzer
meson.buildread_conf.cxxread_mixer.cxxread_tags.cxxrun_convert.cxxrun_decoder.cxxrun_encoder.cxxrun_filter.cxxrun_gunzip.cxxrun_gzip.cxxrun_inotify.cxxrun_input.cxxrun_neighbor_explorer.cxxrun_normalize.cxxrun_output.cxxrun_resolver.cxxrun_storage.cxxsoftware_volume.cxxtest_mixramp.cxxtest_pcm_channels.cxxtest_pcm_dither.cxxtest_pcm_export.cxxtest_pcm_format.cxxtest_pcm_interleave.cxxtest_pcm_mix.cxxtest_pcm_pack.cxxtest_pcm_util.hxxtest_pcm_volume.cxxtest_vorbis_encoder.cxxtime
util
TestCircularBuffer.cxxTestDivideString.cxxTestException.cxxTestIntrusiveList.cxxTestMimeType.cxxTestSplitString.cxxTestTemplateString.cxxTestUriExtract.cxxTestUriQueryParser.cxxTestUriRelative.cxxTestUriUtil.cxxmeson.buildtest_byte_reverse.cxx
visit_archive.cxxwin32
68
.travis.yml
68
.travis.yml
@@ -2,70 +2,40 @@ language: cpp
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Ubuntu Bionic (18.04) with GCC 7
|
||||
# Ubuntu Focal (20.04) with GCC 9.3
|
||||
- os: linux
|
||||
dist: bionic
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
- python3.6
|
||||
- python3-urllib3
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
|
||||
- libfmt-dev
|
||||
|
||||
# Ubuntu Bionic (18.04) with GCC 7 on big-endian
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on big-endian
|
||||
- os: linux
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
- python3.6
|
||||
- python3-urllib3
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
|
||||
- libfmt-dev
|
||||
|
||||
# Ubuntu Bionic (18.04) with GCC 7 on ARM64
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on ARM64
|
||||
- os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
- python3.6
|
||||
- python3-urllib3
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
|
||||
- libfmt-dev
|
||||
|
||||
# Ubuntu Trusty (16.04) with GCC 8
|
||||
- os: linux
|
||||
@@ -75,7 +45,7 @@ jobs:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
|
||||
- sourceline: 'ppa:ricotz/toolchain'
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-8
|
||||
@@ -94,12 +64,14 @@ jobs:
|
||||
- MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode10.3
|
||||
osx_image: xcode11.6
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- meson
|
||||
- fmt
|
||||
- googletest
|
||||
- icu4c
|
||||
- ffmpeg
|
||||
- libnfs
|
||||
@@ -117,7 +89,6 @@ jobs:
|
||||
- faad2
|
||||
- wavpack
|
||||
- libmpdclient
|
||||
update: true
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
||||
|
||||
@@ -134,13 +105,6 @@ before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
|
||||
install:
|
||||
# C++14
|
||||
|
||||
# Work around "Target /usr/local/lib/libgtest.a is a symlink
|
||||
# belonging to nss. You can unlink it" during gtest install
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew unlink nss
|
||||
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew install 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-2020 The Music Player Daemon Project
|
||||
Copyright 2003-2021 The Music Player Daemon Project
|
||||
|
||||
The following people have contributed code to MPD:
|
||||
|
||||
|
193
NEWS
193
NEWS
@@ -1,3 +1,196 @@
|
||||
ver 0.23.2 (2021/10/22)
|
||||
* protocol
|
||||
- fix "albumart" timeout bug
|
||||
* input
|
||||
- nfs: fix playback bug
|
||||
* output
|
||||
- pipewire: send artist and title to PipeWire
|
||||
- pipewire: DSD support
|
||||
* neighbor
|
||||
- mention failed plugin name in error message
|
||||
* player
|
||||
- fix cross-fade regression
|
||||
* fix crash with libfmt versions older than 7
|
||||
|
||||
ver 0.23.1 (2021/10/19)
|
||||
* protocol
|
||||
- use decimal notation instead of scientific notation
|
||||
- "load" supports relative positions
|
||||
* output
|
||||
- emit "mixer" idle event when replay gain changes volume
|
||||
- pipewire: emit "mixer" idle events on external volume change
|
||||
- pipewire: attempt to change the graph sample rate
|
||||
- snapcast: fix time stamp bug which caused "Failed to get chunk"
|
||||
* fix libfmt linker problems
|
||||
* fix broken password authentication
|
||||
|
||||
ver 0.23 (2021/10/14)
|
||||
* protocol
|
||||
- new command "getvol"
|
||||
- show the audio format in "playlistinfo"
|
||||
- support "listfiles" with arbitrary storage plugins
|
||||
- support relative positions in "addid"
|
||||
- fix relative positions in "move" and "moveid"
|
||||
- add "position" parameter to "findadd" and "searchadd"
|
||||
- add position parameter to "load"
|
||||
* database
|
||||
- proxy: require MPD 0.20 or later
|
||||
- proxy: require libmpdclient 2.11 or later
|
||||
- proxy: split search into chunks to avoid exceeding the output buffer
|
||||
- simple: add option to hide CUE target songs
|
||||
- upnp: support libnpupnp instead of libupnp
|
||||
* archive
|
||||
- zzip, iso9660: ignore file names which are invalid UTF-8
|
||||
* decoder
|
||||
- openmpt: new plugin
|
||||
- wavpack: fix WVC file support
|
||||
* player
|
||||
- do not cross-fade songs shorter than 20 seconds
|
||||
* output
|
||||
- oss: support DSD over PCM
|
||||
- pipewire: new plugin
|
||||
- snapcast: new plugin
|
||||
* tags
|
||||
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
|
||||
* split permission "player" from "control"
|
||||
* add option "host_permissions"
|
||||
* new build-time dependency: libfmt
|
||||
|
||||
ver 0.22.11 (2021/08/24)
|
||||
* protocol
|
||||
- fix "albumart" crash
|
||||
* filter
|
||||
- ffmpeg: pass "channel_layout" instead of "channels" to buffersrc
|
||||
- ffmpeg: fix "av_buffersink_get_frame() failed: Resource temporarily unavailable"
|
||||
- ffmpeg: support double-precision samples (by converting to single precision)
|
||||
* Android
|
||||
- build with NDK r23
|
||||
- playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists"
|
||||
|
||||
ver 0.22.10 (2021/08/06)
|
||||
* protocol
|
||||
- support "albumart" for virtual tracks in CUE sheets
|
||||
* database
|
||||
- simple: fix crash bug
|
||||
- simple: fix absolute paths in CUE "as_directory" entries
|
||||
- simple: prune CUE entries from database for non-existent songs
|
||||
* input
|
||||
- curl: fix crash bug after stream with Icy metadata was closed by peer
|
||||
- tidal: remove defunct unmaintained plugin
|
||||
* tags
|
||||
- fix crash caused by bug in TagBuilder and a few potential reference leaks
|
||||
* output
|
||||
- httpd: fix missing tag after seeking into a new song
|
||||
- oss: fix channel order of multi-channel files
|
||||
* mixer
|
||||
- alsa: fix yet more rounding errors
|
||||
|
||||
ver 0.22.9 (2021/06/23)
|
||||
* database
|
||||
- simple: load all .mpdignore files of all parent directories
|
||||
* tags
|
||||
- fix "readcomments" and "readpicture" on remote files with ID3 tags
|
||||
* decoder
|
||||
- ffmpeg: support the tags "sort_album", "album-sort", "artist-sort"
|
||||
- ffmpeg: fix build failure with FFmpeg 3.4
|
||||
* Android
|
||||
- fix auto-start on boot in Android 8 or later
|
||||
* Windows
|
||||
- fix build failure with SQLite
|
||||
|
||||
ver 0.22.8 (2021/05/22)
|
||||
* fix crash bug in "albumart" command (0.22.7 regression)
|
||||
|
||||
ver 0.22.7 (2021/05/19)
|
||||
* protocol
|
||||
- don't use glibc extension to parse time stamps
|
||||
- optimize the "albumart" command
|
||||
* input
|
||||
- curl: send user/password in the first request, save one roundtrip
|
||||
* decoder
|
||||
- ffmpeg: fix build problem with FFmpeg 3.4
|
||||
- gme: support RSN files
|
||||
* storage
|
||||
- curl: don't use glibc extension
|
||||
* database
|
||||
- simple: fix database corruption bug
|
||||
* output
|
||||
- fix crash when pausing with multiple partitions
|
||||
- jack: enable on Windows
|
||||
- httpd: send header "Access-Control-Allow-Origin: *"
|
||||
- wasapi: add algorithm for finding usable audio format
|
||||
- wasapi: use default device only if none was configured
|
||||
- wasapi: add DoP support
|
||||
|
||||
ver 0.22.6 (2021/02/16)
|
||||
* fix missing tags on songs in queue
|
||||
|
||||
ver 0.22.5 (2021/02/15)
|
||||
* protocol
|
||||
- error for malformed ranges instead of ignoring silently
|
||||
- better error message for open-ended range with "move"
|
||||
* database
|
||||
- simple: fix missing CUE sheet metadata in "addid" command
|
||||
* tags
|
||||
- id: translate TPE3 to Conductor, not Performer
|
||||
* archive
|
||||
- iso9660: another fix for unaligned reads
|
||||
* output
|
||||
- httpd: error handling on Windows improved
|
||||
- pulse: fix deadlock with "always_on"
|
||||
* Windows:
|
||||
- enable https:// support (via Schannel)
|
||||
* Android
|
||||
- work around "Permission denied" on mpd.conf
|
||||
|
||||
ver 0.22.4 (2021/01/21)
|
||||
* protocol
|
||||
- add command "binarylimit" to allow larger chunk sizes
|
||||
- fix "readpicture" on 32 bit machines
|
||||
- show duration and tags of songs in virtual playlist (CUE) folders
|
||||
* storage
|
||||
- curl: fix several WebDAV protocol bugs
|
||||
* decoder
|
||||
- dsdiff: apply padding to odd-sized chunks
|
||||
* filter
|
||||
- ffmpeg: detect the output sample format
|
||||
* output
|
||||
- moveoutput: fix always_on and tag lost on move
|
||||
* Android
|
||||
- enable https:// support (via OpenSSL)
|
||||
|
||||
ver 0.22.3 (2020/11/06)
|
||||
* playlist
|
||||
- add option "as_directory", making CUE file expansion optional
|
||||
* storage
|
||||
- curl: fix crash bug
|
||||
* filter
|
||||
- fix garbage after "Audio format not supported by filter" message
|
||||
- ffmpeg: support planar output
|
||||
- ffmpeg: support sample formats other than 16 bit
|
||||
|
||||
ver 0.22.2 (2020/10/28)
|
||||
* database
|
||||
- simple: purge songs and virtual directories for unavailable plugins
|
||||
on update
|
||||
* input
|
||||
- qobuz/tidal: fix protocol errors due to newlines in error messages
|
||||
- smbclient: disable by default due to libsmbclient crash bug
|
||||
* playlist
|
||||
- soundcloud: fix protocol errors due to newlines in error messages
|
||||
* state_file: save on shutdown
|
||||
|
||||
ver 0.22.1 (2020/10/17)
|
||||
* decoder
|
||||
- opus: apply the OpusHead output gain even if there is no EBU R128 tag
|
||||
- opus: fix track/album ReplayGain fallback
|
||||
* output
|
||||
- alsa: don't deadlock when the ALSA driver is buggy
|
||||
- jack, pulse: reduce the delay when stopping or pausing playback
|
||||
* playlist
|
||||
- cue: fix two crash bugs
|
||||
* state_file: fix the state_file_interval setting
|
||||
|
||||
ver 0.22 (2020/09/23)
|
||||
* protocol
|
||||
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
|
||||
|
@@ -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,10 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="50"
|
||||
android:versionName="0.22">
|
||||
android:versionCode="62"
|
||||
android:versionName="0.23.2">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
@@ -17,8 +17,11 @@
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:banner="@drawable/icon"
|
||||
android:label="@string/app_name">
|
||||
@@ -40,6 +43,7 @@
|
||||
<receiver android:name=".Receiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.HEADSET_PLUG" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name=".Main" android:process=":main"/>
|
||||
|
@@ -24,15 +24,13 @@ android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'toolchain_arch': 'arm-linux-androideabi',
|
||||
'llvm_triple': 'armv7-linux-androideabi',
|
||||
'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
|
||||
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'toolchain_arch': 'aarch64-linux-android',
|
||||
'llvm_triple': 'aarch64-linux-android',
|
||||
'cflags': '-fpic',
|
||||
},
|
||||
@@ -40,7 +38,6 @@ android_abis = {
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'toolchain_arch': 'x86',
|
||||
'llvm_triple': 'i686-linux-android',
|
||||
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
@@ -48,7 +45,6 @@ android_abis = {
|
||||
'x86_64': {
|
||||
'arch': 'x86_64-linux-android',
|
||||
'ndk_arch': 'x86_64',
|
||||
'toolchain_arch': 'x86_64',
|
||||
'llvm_triple': 'x86_64-linux-android',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
@@ -84,36 +80,29 @@ class AndroidNdkToolchain:
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '21'
|
||||
|
||||
# select the NDK compiler
|
||||
gcc_version = '4.9'
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
self.arch = arch
|
||||
self.actual_arch = arch
|
||||
self.install_prefix = install_prefix
|
||||
|
||||
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'] + android_api_level
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
self.cc = os.path.join(llvm_bin, 'clang')
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
|
||||
common_flags += ' -target ' + llvm_triple
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
# 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')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
self.ar = os.path.join(llvm_bin, 'llvm-ar')
|
||||
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
|
||||
self.nm = os.path.join(llvm_bin, 'llvm-nm')
|
||||
self.strip = os.path.join(llvm_bin, 'llvm-strip')
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
@@ -151,10 +140,7 @@ class AndroidNdkToolchain:
|
||||
# default one on the build host
|
||||
import shutil
|
||||
bin_dir = os.path.join(install_prefix, 'bin')
|
||||
try:
|
||||
os.makedirs(bin_dir)
|
||||
except:
|
||||
pass
|
||||
os.makedirs(bin_dir, exist_ok=True)
|
||||
self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
|
||||
os.path.join(bin_dir, 'pkg-config'))
|
||||
self.env['PKG_CONFIG'] = self.pkg_config
|
||||
@@ -164,7 +150,6 @@ from build.libs import *
|
||||
thirdparty_libs = [
|
||||
libmpdclient,
|
||||
libogg,
|
||||
libvorbis,
|
||||
opus,
|
||||
flac,
|
||||
libid3tag,
|
||||
@@ -172,8 +157,8 @@ thirdparty_libs = [
|
||||
wildmidi,
|
||||
gme,
|
||||
ffmpeg,
|
||||
openssl,
|
||||
curl,
|
||||
libexpat,
|
||||
libnfs,
|
||||
boost,
|
||||
]
|
||||
|
@@ -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-23'
|
||||
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)
|
||||
|
@@ -23,6 +23,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/checkbox_wakelock" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/pause_on_headphones_disconnect"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/checkbox_pause_on_headphones_disconnect" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
|
@@ -8,4 +8,5 @@
|
||||
<string name="toggle_button_run_off">MPD is not running</string>
|
||||
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
|
||||
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
|
||||
<string name="checkbox_pause_on_headphones_disconnect">Pause MPD when headphones disconnect</string>
|
||||
</resources>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,4 +33,5 @@ public class Bridge {
|
||||
|
||||
public static native void run(Context context, LogListener logListener);
|
||||
public static native void shutdown();
|
||||
public static native void pause();
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ interface IMain
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
void setPauseOnHeadphonesDisconnect(boolean enabled);
|
||||
void setWakelockEnabled(boolean enabled);
|
||||
boolean isRunning();
|
||||
void registerCallback(IMainCallback cb);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,9 +24,13 @@ import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
@@ -55,6 +59,7 @@ public class Main extends Service implements Runnable {
|
||||
private String mError = null;
|
||||
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
|
||||
private final IBinder mBinder = new MainStub(this);
|
||||
private boolean mPauseOnHeadphonesDisconnect = false;
|
||||
private PowerManager.WakeLock mWakelock = null;
|
||||
|
||||
static class MainStub extends IMain.Stub {
|
||||
@@ -68,6 +73,9 @@ public class Main extends Service implements Runnable {
|
||||
public void stop() {
|
||||
mService.stop();
|
||||
}
|
||||
public void setPauseOnHeadphonesDisconnect(boolean enabled) {
|
||||
mService.setPauseOnHeadphonesDisconnect(enabled);
|
||||
}
|
||||
public void setWakelockEnabled(boolean enabled) {
|
||||
mService.setWakelockEnabled(enabled);
|
||||
}
|
||||
@@ -191,6 +199,28 @@ public class Main extends Service implements Runnable {
|
||||
if (mThread != null)
|
||||
return;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_HEADSET_PLUG);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!mPauseOnHeadphonesDisconnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
|
||||
if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0)
|
||||
pause();
|
||||
} else {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO))
|
||||
pause();
|
||||
}
|
||||
}
|
||||
}, filter);
|
||||
|
||||
final Intent mainIntent = new Intent(this, Settings.class);
|
||||
mainIntent.setAction("android.intent.action.MAIN");
|
||||
mainIntent.addCategory("android.intent.category.LAUNCHER");
|
||||
@@ -241,6 +271,21 @@ public class Main extends Service implements Runnable {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void pause() {
|
||||
if (mThread != null) {
|
||||
if (mThread.isAlive()) {
|
||||
synchronized (this) {
|
||||
if (mStatus == MAIN_STATUS_STARTED)
|
||||
Bridge.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setPauseOnHeadphonesDisconnect(boolean enabled) {
|
||||
mPauseOnHeadphonesDisconnect = enabled;
|
||||
}
|
||||
|
||||
private void setWakelockEnabled(boolean enabled) {
|
||||
if (enabled && mWakelock == null) {
|
||||
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
|
||||
@@ -368,6 +413,19 @@ public class Main extends Service implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setPauseOnHeadphonesDisconnect(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.setPauseOnHeadphonesDisconnect(enabled);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setWakelockEnabled(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
@@ -414,6 +472,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-2020 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
|
||||
@@ -59,6 +59,7 @@ public class Settings extends Activity {
|
||||
public static class Preferences {
|
||||
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
|
||||
public static final String KEY_WAKELOCK ="wakelock";
|
||||
public static final String KEY_PAUSE_ON_HEADPHONES_DISCONNECT ="pause_on_headphones_disconnect";
|
||||
|
||||
public static SharedPreferences get(Context context) {
|
||||
return context.getSharedPreferences(TAG, MODE_PRIVATE);
|
||||
@@ -154,6 +155,9 @@ public class Settings extends Activity {
|
||||
if (Preferences.getBoolean(Settings.this,
|
||||
Preferences.KEY_WAKELOCK, false))
|
||||
mClient.setWakelockEnabled(true);
|
||||
if (Preferences.getBoolean(Settings.this,
|
||||
Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
|
||||
mClient.setPauseOnHeadphonesDisconnect(true);
|
||||
} else {
|
||||
mClient.stop();
|
||||
}
|
||||
@@ -179,6 +183,15 @@ public class Settings extends Activity {
|
||||
}
|
||||
};
|
||||
|
||||
private final OnCheckedChangeListener mOnPauseOnHeadphonesDisconnectChangeListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Preferences.putBoolean(Settings.this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, isChecked);
|
||||
if (mClient != null && mClient.isRunning())
|
||||
mClient.setPauseOnHeadphonesDisconnect(isChecked);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
/* TODO: this sure is the wrong place to request
|
||||
@@ -211,6 +224,11 @@ public class Settings extends Activity {
|
||||
if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false))
|
||||
checkbox.setChecked(true);
|
||||
|
||||
checkbox = (CheckBox) findViewById(R.id.pause_on_headphones_disconnect);
|
||||
checkbox.setOnCheckedChangeListener(mOnPauseOnHeadphonesDisconnectChangeListener);
|
||||
if (Preferences.getBoolean(this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
|
||||
checkbox.setChecked(true);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
|
66
doc/client.rst
Normal file
66
doc/client.rst
Normal file
@@ -0,0 +1,66 @@
|
||||
Client Developer's Manual
|
||||
#########################
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
MPD is a music player without a user interface. The user interface
|
||||
will be provided by independent clients, which connect to MPD over
|
||||
socket connections (TCP or local sockets).
|
||||
|
||||
This chapter describes how to develop a client.
|
||||
|
||||
Before you develop a new client, consider joining an existing client
|
||||
project. There are many clients, but few are mature; we need fewer,
|
||||
but better clients.
|
||||
|
||||
Client Libraries
|
||||
****************
|
||||
|
||||
There are many libraries which help with connecting to MPD. If you
|
||||
develop a MPD client, use a library instead of reinventing the wheel.
|
||||
The MPD website has a list of libraries: https://www.musicpd.org/libs/
|
||||
|
||||
|
||||
Connecting to MPD
|
||||
*****************
|
||||
|
||||
Do not hard-code your client to connect to ``localhost:6600``.
|
||||
Instead, use the defaults of the client library. For example, with
|
||||
:program:`libmpdclient`, don't do::
|
||||
|
||||
c = mpd_connection_new("localhost", 6600, 30000);
|
||||
|
||||
Instead, do::
|
||||
|
||||
c = mpd_connection_new(NULL, 0, 0);
|
||||
|
||||
This way, the library can choose the best defaults, maybe derived from
|
||||
environment variables, so all MPD clients use the same settings.
|
||||
|
||||
If you need to reimplement those defaults (or if you are developing a
|
||||
client library), this is a good set of addresses to attempt to connect
|
||||
to:
|
||||
|
||||
- if the environment variable :envvar:`MPD_HOST` is set:
|
||||
``$MPD_HOST:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
|
||||
- if the environment variable :envvar:`XDG_RUNTIME_DIR` is set:
|
||||
``$XDG_RUNTIME_DIR/mpd/socket``
|
||||
- :file:`/run/mpd/socket`
|
||||
- ``localhost:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
|
||||
|
||||
Environment Variables
|
||||
*********************
|
||||
|
||||
The following environment variables should be obeyed by all clients
|
||||
(preferably by the client library):
|
||||
|
||||
- :envvar:`MPD_HOST`: the host (or local socket path) to connect to;
|
||||
on Linux, this may start with a ``@`` to connect to an abstract
|
||||
socket. To use a password with MPD, set :envvar:`MPD_HOST` to
|
||||
``password@host`` (then abstract socket requires double ``@``:
|
||||
``password@@socket``).
|
||||
- :envvar:`MPD_PORT`: the port number; defaults to 6600.
|
||||
- :envvar:`MPD_TIMEOUT`: timeout for connecting to MPD and for waiting
|
||||
for MPD's response in seconds. A good default is 30 seconds.
|
||||
|
@@ -30,7 +30,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Music Player Daemon'
|
||||
copyright = '2003-2020 The Music Player Daemon Project'
|
||||
copyright = '2003-2021 The Music Player Daemon Project'
|
||||
author = 'Max Kellermann'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
@@ -38,9 +38,9 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.22'
|
||||
version = '0.23.2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
#release = version + '~git'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@@ -68,11 +68,11 @@ 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.21.x``) where only bug
|
||||
- the "stable" branch (currently called ``v0.22.x``) where only bug
|
||||
fixes are merged.
|
||||
|
||||
Once :program:`MPD` 0.22 is released, a new branch called ``v0.22.x``
|
||||
will be created for 0.22 bug-fix releases; after that, ``v0.21.x``
|
||||
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
|
||||
|
@@ -8,6 +8,7 @@ Music Player Daemon
|
||||
user
|
||||
plugins
|
||||
developer
|
||||
client
|
||||
protocol
|
||||
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
if not get_option('html_manual')
|
||||
if not get_option('html_manual') and not get_option('manpages')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
@@ -14,6 +14,7 @@ if get_option('html_manual')
|
||||
input: [
|
||||
'index.rst', 'user.rst', 'developer.rst',
|
||||
'plugins.rst',
|
||||
'client.rst',
|
||||
'protocol.rst',
|
||||
'conf.py',
|
||||
],
|
||||
@@ -22,20 +23,6 @@ if get_option('html_manual')
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
|
||||
)
|
||||
|
||||
custom_target(
|
||||
'upload',
|
||||
input: sphinx_output,
|
||||
output: 'upload',
|
||||
build_always_stale: true,
|
||||
command: [
|
||||
'rsync', '-vpruz', '--delete', meson.current_build_dir() + '/',
|
||||
'www.musicpd.org:/var/www/mpd/doc/',
|
||||
'--chmod=Dug+rwx,Do+rx,Fug+rw,Fo+r',
|
||||
'--include=html', '--include=html/**',
|
||||
'--exclude=*',
|
||||
],
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('manpages')
|
||||
|
@@ -13,7 +13,7 @@ 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 ``/etc/mpd.conf`` or uses ``CONF_FILE``.
|
||||
then ``~/.mpdconf`` then ``~/.mpd/mpd.conf`` then ``/etc/mpd.conf`` or uses ``CONF_FILE``.
|
||||
|
||||
Read more about MPD at http://www.musicpd.org/
|
||||
|
||||
@@ -53,8 +53,8 @@ OPTIONS
|
||||
FILES
|
||||
-----
|
||||
|
||||
:file:`~/.mpdconf`
|
||||
User configuration file.
|
||||
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
|
||||
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
|
||||
|
||||
:file:`/etc/mpd.conf`
|
||||
Global configuration file.
|
||||
|
@@ -11,24 +11,34 @@ 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`.
|
||||
|
||||
Lines beginning with a :samp:`#` 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::
|
||||
Each line in the configuration file contains a setting name and its value, e.g.:
|
||||
|
||||
parameter "value"
|
||||
:code:`connection_timeout "5"`
|
||||
|
||||
The exception to this rule is the audio_output parameter, which is of
|
||||
the form::
|
||||
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 {
|
||||
parameter1 "value"
|
||||
parameter2 "value"
|
||||
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:
|
||||
|
||||
Parameters that take a file or directory as an argument should use absolute paths.
|
||||
: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.
|
||||
@@ -38,7 +48,7 @@ Please read the MPD user manual for a complete configuration guide:
|
||||
http://www.musicpd.org/doc/user/
|
||||
|
||||
|
||||
REQUIRED PARAMETERS
|
||||
OPTIONAL PARAMETERS
|
||||
-------------------
|
||||
|
||||
db_file <file>
|
||||
@@ -47,9 +57,6 @@ db_file <file>
|
||||
log_file <file>
|
||||
This specifies where the log file should be located. The special value "syslog" makes MPD use the local syslog daemon.
|
||||
|
||||
OPTIONAL PARAMETERS
|
||||
-------------------
|
||||
|
||||
sticker_file <file>
|
||||
The location of the sticker database. This is a database which manages
|
||||
dynamic information attached to songs.
|
||||
@@ -83,7 +90,7 @@ user <username>
|
||||
port <port>
|
||||
This specifies the port that mpd listens on. The default is 6600.
|
||||
|
||||
log_level <default, secure, or verbose>
|
||||
log_level <level>
|
||||
Suppress all messages below the given threshold. The following
|
||||
log levels are available:
|
||||
|
||||
@@ -123,7 +130,8 @@ audio_output
|
||||
|
||||
replaygain <off or album or track or auto>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain
|
||||
tags (see http://www.replaygain.org/). Setting this to "album" will
|
||||
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
|
||||
@@ -190,8 +198,8 @@ mixer_type <hardware, software or none>
|
||||
FILES
|
||||
-----
|
||||
|
||||
:file:`~/.mpdconf`
|
||||
User configuration file.
|
||||
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
|
||||
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
|
||||
|
||||
:file:`/etc/mpd.conf`
|
||||
Global configuration file.
|
||||
|
@@ -5,7 +5,7 @@
|
||||
# Files and directories #######################################################
|
||||
#
|
||||
# This setting controls the top directory which MPD will search to discover the
|
||||
# available audio files and add them to the daemon's online database. This
|
||||
# available audio files and add them to the daemon's online database. This
|
||||
# setting defaults to the XDG directory, otherwise the music directory will be
|
||||
# be disabled and audio files will only be accepted over ipc socket (using
|
||||
# file:// protocol) or streaming files over an accepted protocol.
|
||||
@@ -13,20 +13,20 @@
|
||||
#music_directory "~/music"
|
||||
#
|
||||
# This setting sets the MPD internal playlist directory. The purpose of this
|
||||
# directory is storage for playlists created by MPD. The server will use
|
||||
# directory is storage for playlists created by MPD. The server will use
|
||||
# playlist files not created by the server but only if they are in the MPD
|
||||
# format. This setting defaults to playlist saving being disabled.
|
||||
#
|
||||
#playlist_directory "~/.mpd/playlists"
|
||||
#
|
||||
# This setting sets the location of the MPD database. This file is used to
|
||||
# load the database at server start up and store the database while the
|
||||
# load the database at server start up and store the database while the
|
||||
# server is not up. This setting defaults to disabled which will allow
|
||||
# MPD to accept files over ipc socket (using file:// protocol) or streaming
|
||||
# files over an accepted protocol.
|
||||
#
|
||||
#db_file "~/.mpd/database"
|
||||
#
|
||||
#
|
||||
# These settings are the locations for the daemon log files for the daemon.
|
||||
# These logs are great for troubleshooting, depending on your log_level
|
||||
# settings.
|
||||
@@ -44,7 +44,7 @@
|
||||
#
|
||||
# This setting sets the location of the file which contains information about
|
||||
# most variables to get MPD back into the same general shape it was in before
|
||||
# it was brought down. This setting is disabled by default and the server
|
||||
# it was brought down. This setting is disabled by default and the server
|
||||
# state will be reset on server start up.
|
||||
#
|
||||
#state_file "~/.mpd/state"
|
||||
@@ -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.
|
||||
#
|
||||
@@ -90,7 +90,8 @@
|
||||
#port "6600"
|
||||
#
|
||||
# Suppress all messages below the given threshold. Use "verbose" for
|
||||
# troubleshooting.
|
||||
# troubleshooting. Available setting arguments are "notice", "info", "verbose",
|
||||
# "warning" and "error".
|
||||
#
|
||||
#log_level "notice"
|
||||
#
|
||||
@@ -113,7 +114,7 @@
|
||||
# the other supported tags:
|
||||
#metadata_to_use "+comment"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
# music_directory are changed.
|
||||
#
|
||||
#auto_update "yes"
|
||||
@@ -128,7 +129,7 @@
|
||||
|
||||
# Symbolic link behavior ######################################################
|
||||
#
|
||||
# If this setting is set to "yes", MPD will discover audio files by following
|
||||
# If this setting is set to "yes", MPD will discover audio files by following
|
||||
# symbolic links outside of the configured music_directory.
|
||||
#
|
||||
#follow_outside_symlinks "yes"
|
||||
@@ -163,7 +164,7 @@
|
||||
#
|
||||
#password "password@read,add,control,admin"
|
||||
#
|
||||
# This setting specifies the permissions a user has who has not yet logged in.
|
||||
# This setting specifies the permissions a user has who has not yet logged in.
|
||||
#
|
||||
#default_permissions "read,add,control,admin"
|
||||
#
|
||||
@@ -172,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"
|
||||
@@ -181,7 +193,6 @@
|
||||
|
||||
# Input #######################################################################
|
||||
#
|
||||
|
||||
input {
|
||||
plugin "curl"
|
||||
# proxy "proxy.isp.com:8080"
|
||||
@@ -194,8 +205,8 @@ input {
|
||||
|
||||
# Audio Output ################################################################
|
||||
#
|
||||
# MPD supports various audio output types, as well as playing through multiple
|
||||
# audio outputs at the same time, through multiple audio_output settings
|
||||
# MPD supports various audio output types, as well as playing through multiple
|
||||
# audio outputs at the same time, through multiple audio_output settings
|
||||
# blocks. Setting this block is optional, though the server will only attempt
|
||||
# autodetection for one sound card.
|
||||
#
|
||||
@@ -361,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"
|
||||
@@ -386,7 +398,7 @@ input {
|
||||
#replaygain_limit "yes"
|
||||
#
|
||||
# This setting enables on-the-fly normalization volume adjustment. This will
|
||||
# result in the volume of all playing audio to be adjusted so the output has
|
||||
# result in the volume of all playing audio to be adjusted so the output has
|
||||
# equal "loudness". This setting is disabled by default.
|
||||
#
|
||||
#volume_normalization "no"
|
||||
@@ -395,7 +407,7 @@ input {
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
# may need to modify this setting.
|
||||
#
|
||||
#filesystem_charset "UTF-8"
|
||||
|
188
doc/plugins.rst
188
doc/plugins.rst
@@ -23,11 +23,23 @@ The default plugin. Stores a copy of the database in memory. A file is used for
|
||||
- The path of the cache directory for additional storages mounted at runtime. This setting is necessary for the **mount** protocol command.
|
||||
* - **compress yes|no**
|
||||
- Compress the database file using gzip? Enabled by default (if built with zlib).
|
||||
* - **hide_playlist_targets yes|no**
|
||||
- Hide songs which are referenced by playlists? Thas is,
|
||||
playlist files which are represented in the database as virtual
|
||||
directories (playlist plugin setting ``as_directory``). This
|
||||
option is enabled by default and avoids duplicate songs; one
|
||||
copy for the original file, and another copy in the virtual
|
||||
directory of a CUE file referring to it.
|
||||
|
||||
proxy
|
||||
-----
|
||||
|
||||
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.
|
||||
Provides access to the database of another :program:`MPD` instance
|
||||
using `libmpdclient
|
||||
<https://www.musicpd.org/libs/libmpdclient/>`_. This is useful when
|
||||
you mount the music directory via NFS/SMB, and the file server already
|
||||
runs a :program:`MPD` (0.20 or newer) instance. Only the file server
|
||||
needs to update the database.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
@@ -71,6 +83,11 @@ 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
|
||||
---
|
||||
|
||||
@@ -205,6 +222,8 @@ will be in effect.
|
||||
- Verify the peer's SSL certificate? `More information <http://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html>`_.
|
||||
* - **verify_host yes|no**
|
||||
- Verify the certificate's name against host? `More information <http://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html>`_.
|
||||
* - **cacert**
|
||||
- Set path to Certificate Authority (CA) bundle `More information <https://curl.se/libcurl/c/CURLOPT_CAINFO.html>`_.
|
||||
|
||||
ffmpeg
|
||||
------
|
||||
@@ -290,37 +309,6 @@ in the form ``qobuz://track/ID``, e.g.:
|
||||
* - **format_id N**
|
||||
- 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.:
|
||||
|
||||
.. warning::
|
||||
|
||||
This plugin is currently defunct because Tidal has changed the
|
||||
protocol and decided not to share documentation.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mpc add tidal://track/59727857
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **token TOKEN**
|
||||
- The Tidal application token. Since Tidal is unwilling to assign a token to MPD, this needs to be reverse-engineered from another (approved) Tidal client.
|
||||
* - **username USERNAME**
|
||||
- The Tidal user name.
|
||||
* - **password PASSWORD**
|
||||
- The Tidal password.
|
||||
* - **audioquality Q**
|
||||
- The Tidal "audioquality" parameter. Possible values: HI_RES, LOSSLESS, HIGH, LOW. Default is HIGH.
|
||||
|
||||
.. _decoder_plugins:
|
||||
|
||||
Decoder plugins
|
||||
@@ -373,10 +361,14 @@ flac
|
||||
|
||||
Decodes FLAC files using libFLAC.
|
||||
|
||||
.. _decoder_dsdiff:
|
||||
|
||||
dsdiff
|
||||
------
|
||||
|
||||
Decodes DFF files containing DSDIFF data (e.g. SACD rips).
|
||||
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
|
||||
@@ -387,10 +379,14 @@ Decodes DFF files containing DSDIFF data (e.g. SACD rips).
|
||||
* - **lsbitfirst yes|no**
|
||||
- Decode the least significant bit first. Default is no.
|
||||
|
||||
.. _decoder_dsf:
|
||||
|
||||
dsf
|
||||
---
|
||||
|
||||
Decodes DSF files containing DSDIFF data (e.g. SACD rips).
|
||||
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
|
||||
----------
|
||||
@@ -469,9 +465,39 @@ Module player based on MODPlug.
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **resampling_mode nearest|linear|spline|fir**
|
||||
- Sets the resampling mode. "nearest" disables interpolation (good for chiptunes). "linear" makes modplug use linear interpolation (fast, good quality). "spline" makes modplug use cubic spline interpolation (high quality). "fir" makes modplug use 8-tap fir filter (extremely high quality). Defaults to "fir".
|
||||
* - **loop_count**
|
||||
- Number of times to loop the module if it uses backward loops. Default is 0 which prevents looping. -1 loops forever.
|
||||
|
||||
openmpt
|
||||
-------
|
||||
|
||||
Module player based on `libopenmpt <https://lib.openmpt.org>`_.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **repeat_count**
|
||||
- Set how many times the module repeats. -1: repeat forever. 0: play once, repeat zero times (the default). n>0: play once and repeat n times after that.
|
||||
* - **stereo_separation**
|
||||
- Sets the stereo separation. The supported value range is [0,200]. Defaults to 100.
|
||||
* - **interpolation_filter 0|1|2|4|8**
|
||||
- Sets the interpolation filter. 0: internal default. 1: no interpolation (zero order hold). 2: linear interpolation. 4: cubic interpolation. 8: windowed sinc with 8 taps. Defaults to 0.
|
||||
* - **override_mptm_interp_filter yes|no**
|
||||
- If `interpolation_filter` has been changed, setting this to yes will force all MPTM modules to use that interpolation filter. If set to no, MPTM modules will play with their own interpolation filter regardless of the value of `interpolation_filter`. Defaults to no.
|
||||
* - **volume_ramping**
|
||||
- Sets the amount of volume ramping done by the libopenmpt mixer. The default value is -1, which indicates a recommended default value. The meaningful value range is [-1..10]. A value of 0 completely disables volume ramping. This might cause clicks in sound output. Higher values imply slower/softer volume ramps.
|
||||
* - **sync_samples yes|no**
|
||||
- Syncs sample playback when seeking. Defaults to yes.
|
||||
* - **emulate_amiga yes|no**
|
||||
- Enables the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. Defaults to yes.
|
||||
* - **emulate_amiga_type**
|
||||
- Configures the filter type to use for the Amiga resampler. Supported values are: "auto": Filter type is chosen by the library and might change. This is the default. "a500": Amiga A500 filter. "a1200": Amiga A1200 filter. "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. Defaults to "auto". Requires libopenmpt 0.5 or higher.
|
||||
|
||||
mpcdec
|
||||
------
|
||||
|
||||
@@ -566,6 +592,10 @@ Encodes into `FLAC <https://xiph.org/flac/>`_ (lossless).
|
||||
- Description
|
||||
* - **compression**
|
||||
- Sets the libFLAC compression level. The levels range from 0 (fastest, least compression) to 8 (slowest, most compression).
|
||||
* - **oggflac yes|no**
|
||||
- Configures if the stream should be Ogg FLAC versus native FLAC. Defaults to "no" (use native FLAC).
|
||||
* - **oggchaining yes|no**
|
||||
- Configures if the stream should use Ogg Chaining for in-stream metadata. Defaults to "no". Setting this to "yes" also enables Ogg FLAC.
|
||||
|
||||
lame
|
||||
----
|
||||
@@ -630,11 +660,15 @@ Encodes into `Ogg Opus <http://www.opus-codec.org/>`_.
|
||||
* - Setting
|
||||
- Description
|
||||
* - **bitrate**
|
||||
- Sets the data rate in bit per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
|
||||
- Sets the data rate in bits per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
|
||||
* - **complexity**
|
||||
- Sets the `Opus complexity <https://wiki.xiph.org/OpusFAQ#What_is_the_complexity_of_Opus.3F>`_.
|
||||
* - **signal**
|
||||
- Sets the Opus signal type. Valid values are "auto" (the default), "voice" and "music".
|
||||
* - **vbr yes|no|constrained**
|
||||
- Sets the vbr mode. Setting to "yes" (default) enables variable bitrate, "no" forces constant bitrate and frame sizes, "constrained" uses constant bitrate analogous to CBR in AAC and MP3.
|
||||
* - **packet_loss**
|
||||
- Sets the expected packet loss percentage. This value can be increased from the default "0" for a more redundant stream at the expense of quality.
|
||||
* - **opustags yes|no**
|
||||
- Configures how metadata is interleaved into the stream. If set to yes, then metadata is inserted using ogg stream chaining, as specified in :rfc:`7845`. If set to no (the default), then ogg stream chaining is avoided and other output-dependent method is used, if available.
|
||||
|
||||
@@ -702,7 +736,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:
|
||||
|
||||
@@ -750,6 +784,10 @@ Valid quality values for libsoxr:
|
||||
|
||||
If the quality is set to custom also the following settings are available:
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Description
|
||||
* - **precision**
|
||||
@@ -893,6 +931,10 @@ 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
|
||||
@@ -969,6 +1011,8 @@ On Linux, OSS has been superseded by ALSA. Use the ALSA output plugin :ref:`alsa
|
||||
- Description
|
||||
* - **device PATH**
|
||||
- Sets the path of the PCM device. If not specified, then MPD will attempt to open /dev/sound/dsp and /dev/dsp.
|
||||
* - **dop yes|no**
|
||||
- If set to yes, then DSD over PCM according to the `DoP standard <http://dsd-guide.com/dop-open-standard>`_ is enabled. This wraps DSD samples in fake 24 bit PCM, and is understood by some DSD capable products, but may be harmful to other hardware. Therefore, the default is no and you can enable the option at your own risk.
|
||||
|
||||
The according hardware mixer plugin understands the following settings:
|
||||
|
||||
@@ -1031,6 +1075,28 @@ The pipe plugin starts a program and writes raw PCM data into its standard input
|
||||
* - **command CMD**
|
||||
- This command is invoked with the shell.
|
||||
|
||||
pipewire
|
||||
--------
|
||||
|
||||
Connect to a `PipeWire <https://pipewire.org/>`_ server. Requires
|
||||
``libpipewire``.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **target NAME**
|
||||
- Link to the given target. If not specified, let the PipeWire
|
||||
manager select a target. To get a list of available targets,
|
||||
type ``pw-cli dump short Node``
|
||||
* - **remote NAME**
|
||||
- The name of the remote to connect to. The default is
|
||||
``pipewire-0``.
|
||||
* - **dsd yes|no**
|
||||
- Enable DSD playback. This requires PipeWire 0.38.
|
||||
|
||||
.. _pulse_plugin:
|
||||
|
||||
pulse
|
||||
@@ -1086,8 +1152,6 @@ You must set a format.
|
||||
- Sets the host name of the `ShoutCast <http://www.shoutcast.com/>`_ / `IceCast <http://icecast.org/>`_ server.
|
||||
* - **port PORTNUMBER**
|
||||
- Connect to this port number on the specified host.
|
||||
* - **timeout SECONDS**
|
||||
- Set the timeout for the shout connection in seconds. Defaults to 2 seconds.
|
||||
* - **protocol icecast2|icecast1|shoutcast**
|
||||
- Specifies the protocol that wil be used to connect to the server. The default is "icecast2".
|
||||
* - **tls disabled|auto|auto_no_plain|rfc2818|rfc2817**
|
||||
@@ -1123,6 +1187,34 @@ audio API. Its primary use is local playback on Android, where
|
||||
floating point samples.
|
||||
|
||||
|
||||
snapcast
|
||||
--------
|
||||
|
||||
Snapcast is a multiroom client-server audio player. This plugin
|
||||
allows MPD to act as a `Snapcast
|
||||
<https://github.com/badaix/snapcast>`__ server. Snapcast clients
|
||||
connect to it and receive audio data from MPD.
|
||||
|
||||
You must set a format.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **port P**
|
||||
- Binds the Snapcast server to the specified port. The default
|
||||
port is :samp:`1704`.
|
||||
* - **bind_to_address ADDR**
|
||||
- Binds the Snapcast server to the specified address. Multiple
|
||||
addresses in parallel are not supported. The default is to
|
||||
bind on all addresses on port :samp:`1704`.
|
||||
* - **zeroconf yes|no**
|
||||
- Publish the Snapcast server as service type ``_snapcast._tcp``
|
||||
via Zeroconf (Avahi or Bonjour). Default is :samp:`yes`.
|
||||
|
||||
|
||||
solaris
|
||||
-------
|
||||
The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
|
||||
@@ -1154,6 +1246,8 @@ The `Windows Audio Session API <https://docs.microsoft.com/en-us/windows/win32/c
|
||||
- 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:
|
||||
@@ -1177,7 +1271,7 @@ This plugin requires building with ``libavfilter`` (FFmpeg).
|
||||
* - **graph "..."**
|
||||
- Specifies the ``libavfilter`` graph; read the `FFmpeg
|
||||
documentation
|
||||
<https://libav.org/documentation/libavfilter.html#Filtergraph-syntax-1>`_
|
||||
<https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-syntax-1>`_
|
||||
for details
|
||||
|
||||
|
||||
@@ -1224,23 +1318,25 @@ 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
|
||||
----
|
||||
@@ -1248,11 +1344,11 @@ 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
|
||||
----------
|
||||
|
286
doc/protocol.rst
286
doc/protocol.rst
@@ -69,11 +69,14 @@ that, the specified number of bytes of binary data follows, then a
|
||||
newline, and finally the ``OK`` line.
|
||||
|
||||
If the object to be transmitted is large, the server may choose a
|
||||
reasonable chunk size and transmit only a portion. Usually, the
|
||||
response also contains a ``size`` line which specifies the total
|
||||
(uncropped) size, and the command usually has a way to specify an
|
||||
offset into the object; this way, the client can copy the whole file
|
||||
without blocking the connection for too long.
|
||||
reasonable chunk size and transmit only a portion. The maximum chunk
|
||||
size can be changed by clients with the :ref:`binarylimit
|
||||
<command_binarylimit>` command.
|
||||
|
||||
Usually, the response also contains a ``size`` line which specifies
|
||||
the total (uncropped) size, and the command usually has a way to
|
||||
specify an offset into the object; this way, the client can copy the
|
||||
whole file without blocking the connection for too long.
|
||||
|
||||
Example::
|
||||
|
||||
@@ -281,11 +284,17 @@ The following tags are supported by :program:`MPD`:
|
||||
* **name**: 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.
|
||||
* **genre**: the music genre.
|
||||
* **date**: the song's release date. This is usually a 4-digit year.
|
||||
* **originaldate**: the song's original release date.
|
||||
* **composer**: the artist who composed the song.
|
||||
* **composersort**: same as composer, but for sorting.
|
||||
* **performer**: the artist who performed the song.
|
||||
* **conductor**: the conductor who conducted the song.
|
||||
* **work**: `"a work is a distinct intellectual or artistic creation,
|
||||
which can be expressed in the form of one or more audio recordings" <https://musicbrainz.org/doc/Work>`_
|
||||
* **ensemble**: the ensemble performing this song, e.g. "Wiener Philharmoniker".
|
||||
* **movement**: name of the movement, e.g. "Andante con moto".
|
||||
* **movementnumber**: movement number, e.g. "2" or "II".
|
||||
* **location**: location of the recording, e.g. "Royal Albert Hall".
|
||||
* **grouping**: "used if the sound belongs to a larger category of
|
||||
sounds/music" (`from the IDv2.4.0 TIT1 description
|
||||
<http://id3.org/id3v2.4.0-frames>`_).
|
||||
@@ -383,10 +392,14 @@ Command reference
|
||||
Querying :program:`MPD`'s status
|
||||
================================
|
||||
|
||||
.. _command_clearerror:
|
||||
|
||||
:command:`clearerror`
|
||||
Clears the current error message in status (this is also
|
||||
accomplished by any command that starts playback).
|
||||
|
||||
.. _command_currentsong:
|
||||
|
||||
:command:`currentsong`
|
||||
Displays the song info of the current song (same song that
|
||||
is identified in status). Information about the current song
|
||||
@@ -407,7 +420,9 @@ Querying :program:`MPD`'s status
|
||||
- ``update``: a database update has started or finished. If the database was modified during the update, the ``database`` event is also emitted.
|
||||
- ``stored_playlist``: a stored playlist has been modified, renamed, created or deleted
|
||||
- ``playlist``: the queue (i.e. the current playlist) has been modified
|
||||
- ``player``: the player has been started, stopped or seeked
|
||||
- ``player``: the player has been started, stopped or seeked or
|
||||
tags of the currently playing song have changed (e.g. received
|
||||
from stream)
|
||||
- ``mixer``: the volume has been changed
|
||||
- ``output``: an audio output has been added, removed or modified (e.g. renamed, enabled or disabled)
|
||||
- ``options``: options like repeat, random, crossfade, replay gain
|
||||
@@ -477,6 +492,8 @@ Querying :program:`MPD`'s status
|
||||
:program:`MPD` versions used to have a "magic" value for
|
||||
"unknown", e.g. ":samp:`volume: -1`".
|
||||
|
||||
.. _command_stats:
|
||||
|
||||
:command:`stats`
|
||||
Displays statistics.
|
||||
|
||||
@@ -492,17 +509,25 @@ Querying :program:`MPD`'s status
|
||||
Playback options
|
||||
================
|
||||
|
||||
.. _command_consume:
|
||||
|
||||
:command:`consume {STATE}` [#since_0_15]_
|
||||
Sets consume state to ``STATE``,
|
||||
``STATE`` should be 0 or 1.
|
||||
When consume is activated, each song played is removed from playlist.
|
||||
|
||||
.. _command_crossfade:
|
||||
|
||||
:command:`crossfade {SECONDS}`
|
||||
Sets crossfading between songs.
|
||||
|
||||
.. _command_mixrampdb:
|
||||
|
||||
:command:`mixrampdb {deciBels}`
|
||||
Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See http://sourceforge.net/projects/mixramp
|
||||
|
||||
.. _command_mixrampdelay:
|
||||
|
||||
:command:`mixrampdelay {SECONDS}`
|
||||
Additional time subtracted from the overlap calculated by mixrampdb. A value of "nan" disables MixRamp overlapping and falls back to crossfading.
|
||||
|
||||
@@ -512,6 +537,8 @@ Playback options
|
||||
Sets random state to ``STATE``,
|
||||
``STATE`` should be 0 or 1.
|
||||
|
||||
.. _command_repeat:
|
||||
|
||||
:command:`repeat {STATE}`
|
||||
Sets repeat state to ``STATE``,
|
||||
``STATE`` should be 0 or 1.
|
||||
@@ -522,12 +549,28 @@ Playback options
|
||||
Sets volume to ``VOL``, the range of
|
||||
volume is 0-100.
|
||||
|
||||
.. _command_getvol:
|
||||
|
||||
:command:`getvol`
|
||||
|
||||
Read the volume. The result is a ``volume:`` line like in
|
||||
:ref:`status <command_status>`. If there is no mixer, MPD will
|
||||
emit an empty response. Example::
|
||||
|
||||
getvol
|
||||
volume: 42
|
||||
OK
|
||||
|
||||
.. _command_single:
|
||||
|
||||
:command:`single {STATE}` [#since_0_15]_
|
||||
Sets single state to ``STATE``,
|
||||
``STATE`` should be ``0``, ``1`` or ``oneshot`` [#since_0_20]_.
|
||||
``STATE`` should be ``0``, ``1`` or ``oneshot`` [#since_0_21]_.
|
||||
When single is activated, playback is stopped after current song, or
|
||||
song is repeated if the 'repeat' mode is enabled.
|
||||
|
||||
.. _command_replay_gain_mode:
|
||||
|
||||
:command:`replay_gain_mode {MODE}` [#since_0_16]_
|
||||
Sets the replay gain mode. One of
|
||||
``off``,
|
||||
@@ -541,11 +584,15 @@ Playback options
|
||||
This command triggers the
|
||||
``options`` idle event.
|
||||
|
||||
.. _command_replay_gain_status:
|
||||
|
||||
:command:`replay_gain_status`
|
||||
Prints replay gain options. Currently, only the
|
||||
variable ``replay_gain_mode`` is
|
||||
returned.
|
||||
|
||||
.. _command_volume:
|
||||
|
||||
:command:`volume {CHANGE}`
|
||||
Changes volume by amount ``CHANGE``.
|
||||
Deprecated, use :ref:`setvol <command_setvol>` instead.
|
||||
@@ -553,41 +600,59 @@ Playback options
|
||||
Controlling playback
|
||||
====================
|
||||
|
||||
.. _command_next:
|
||||
|
||||
:command:`next`
|
||||
Plays next song in the playlist.
|
||||
|
||||
.. _command_pause:
|
||||
|
||||
:command:`pause {STATE}`
|
||||
Pause or resume playback. Pass :samp:`1` to pause playback or
|
||||
:samp:`0` to resume playback. Without the parameter, the pause
|
||||
state is toggled.
|
||||
|
||||
.. _command_play:
|
||||
|
||||
:command:`play [SONGPOS]`
|
||||
Begins playing the playlist at song number
|
||||
``SONGPOS``.
|
||||
|
||||
.. _command_playid:
|
||||
|
||||
:command:`playid [SONGID]`
|
||||
Begins playing the playlist at song
|
||||
``SONGID``.
|
||||
|
||||
.. _command_previous:
|
||||
|
||||
:command:`previous`
|
||||
Plays previous song in the playlist.
|
||||
|
||||
.. _command_seek:
|
||||
|
||||
:command:`seek {SONGPOS} {TIME}`
|
||||
Seeks to the position ``TIME`` (in
|
||||
seconds; fractions allowed) of entry
|
||||
``SONGPOS`` in the playlist.
|
||||
|
||||
.. _command_seekid:
|
||||
|
||||
:command:`seekid {SONGID} {TIME}`
|
||||
Seeks to the position ``TIME`` (in
|
||||
seconds; fractions allowed) of song
|
||||
``SONGID``.
|
||||
|
||||
.. _command_seekcur:
|
||||
|
||||
:command:`seekcur {TIME}`
|
||||
Seeks to the position ``TIME`` (in
|
||||
seconds; fractions allowed) within the current song. If
|
||||
prefixed by ``+`` or ``-``, then the time is relative to the
|
||||
current playing position.
|
||||
|
||||
.. _command_stop:
|
||||
|
||||
:command:`stop`
|
||||
Stops playing.
|
||||
|
||||
@@ -622,11 +687,20 @@ client can always be sure the correct song is being used.
|
||||
Many commands come in two flavors, one for each address type.
|
||||
Whenever possible, ids should be used.
|
||||
|
||||
.. _command_add:
|
||||
|
||||
:command:`add {URI}`
|
||||
Adds the file ``URI`` to the playlist
|
||||
(directories add recursively). ``URI``
|
||||
can also be a single file.
|
||||
|
||||
Clients that are connected via local socket may add arbitrary
|
||||
local files (URI is an absolute path). Example::
|
||||
|
||||
add "/home/foo/Music/bar.ogg"
|
||||
|
||||
.. _command_addid:
|
||||
|
||||
:command:`addid {URI} [POSITION]`
|
||||
Adds a song to the playlist (non-recursive) and returns the
|
||||
song id. ``URI`` is always a single file or URL. For example::
|
||||
@@ -635,6 +709,15 @@ Whenever possible, ids should be used.
|
||||
Id: 999
|
||||
OK
|
||||
|
||||
If the second parameter is given, then the song is inserted at the
|
||||
specified position. If the parameter starts with ``+`` or ``-``,
|
||||
then it is relative to the current song; e.g. ``+0`` inserts right
|
||||
after the current song and ``-0`` inserts right before the current
|
||||
song (i.e. zero songs between the current song and the newly added
|
||||
song).
|
||||
|
||||
.. _command_clear:
|
||||
|
||||
:command:`clear`
|
||||
Clears the queue.
|
||||
|
||||
@@ -643,21 +726,37 @@ Whenever possible, ids should be used.
|
||||
:command:`delete [{POS} | {START:END}]`
|
||||
Deletes a song from the playlist.
|
||||
|
||||
.. _command_deleteid:
|
||||
|
||||
:command:`deleteid {SONGID}`
|
||||
Deletes the song ``SONGID`` from the
|
||||
playlist
|
||||
|
||||
.. _command_move:
|
||||
|
||||
:command:`move [{FROM} | {START:END}] {TO}`
|
||||
Moves the song at ``FROM`` or range of songs
|
||||
at ``START:END`` [#since_0_15]_ to ``TO``
|
||||
in the playlist.
|
||||
|
||||
If ``TO`` starts with ``+`` or ``-``, then it is relative to the
|
||||
current song; e.g. ``+0`` moves to right after the current song
|
||||
and ``-0`` moves to right before the current song (i.e. zero songs
|
||||
between the current song and the moved range).
|
||||
|
||||
.. _command_moveid:
|
||||
|
||||
:command:`moveid {FROM} {TO}`
|
||||
Moves the song with ``FROM`` (songid) to
|
||||
``TO`` (playlist index) in the
|
||||
playlist. If ``TO`` is negative, it
|
||||
is relative to the current song in the playlist (if
|
||||
there is one).
|
||||
playlist.
|
||||
|
||||
If ``TO`` starts with ``+`` or ``-``, then it is relative to the
|
||||
current song; e.g. ``+0`` moves to right after the current song
|
||||
and ``-0`` moves to right before the current song (i.e. zero songs
|
||||
between the current song and the moved song).
|
||||
|
||||
.. _command_playlist:
|
||||
|
||||
:command:`playlist`
|
||||
|
||||
@@ -666,10 +765,14 @@ Whenever possible, ids should be used.
|
||||
Do not use this, instead use :ref:`playlistinfo
|
||||
<command_playlistinfo>`.
|
||||
|
||||
:command:`playlistfind {TAG} {NEEDLE}`
|
||||
.. _command_playlistfind:
|
||||
|
||||
:command:`playlistfind {FILTER}`
|
||||
Finds songs in the queue with strict
|
||||
matching.
|
||||
|
||||
.. _command_playlistid:
|
||||
|
||||
:command:`playlistid {SONGID}`
|
||||
Displays a list of songs in the playlist.
|
||||
``SONGID`` is optional and specifies a
|
||||
@@ -683,10 +786,14 @@ Whenever possible, ids should be used.
|
||||
``SONGPOS`` or the range of songs
|
||||
``START:END`` [#since_0_15]_
|
||||
|
||||
:command:`playlistsearch {TAG} {NEEDLE}`
|
||||
.. _command_playlistsearch:
|
||||
|
||||
:command:`playlistsearch {FILTER}`
|
||||
Searches case-insensitively for partial matches in the
|
||||
queue.
|
||||
|
||||
.. _command_plchanges:
|
||||
|
||||
:command:`plchanges {VERSION} [START:END]`
|
||||
Displays changed songs currently in the playlist since
|
||||
``VERSION``. Start and end positions may
|
||||
@@ -696,6 +803,8 @@ Whenever possible, ids should be used.
|
||||
To detect songs that were deleted at the end of the
|
||||
playlist, use playlistlength returned by status command.
|
||||
|
||||
.. _command_plchangesposid:
|
||||
|
||||
:command:`plchangesposid {VERSION} [START:END]`
|
||||
Displays changed songs currently in the playlist since
|
||||
``VERSION``. This function only
|
||||
@@ -721,6 +830,8 @@ Whenever possible, ids should be used.
|
||||
Same as :ref:`priod <command_prio>`,
|
||||
but address the songs with their id.
|
||||
|
||||
.. _command_rangeid:
|
||||
|
||||
:command:`rangeid {ID} {START:END}` [#since_0_19]_
|
||||
Since :program:`MPD`
|
||||
0.19 Specifies the portion of the
|
||||
@@ -731,19 +842,27 @@ Whenever possible, ids should be used.
|
||||
range, play everything". A song that is currently
|
||||
playing cannot be manipulated this way.
|
||||
|
||||
.. _command_shuffle:
|
||||
|
||||
:command:`shuffle [START:END]`
|
||||
Shuffles the queue.
|
||||
``START:END`` is optional and specifies
|
||||
a range of songs.
|
||||
|
||||
.. _command_swap:
|
||||
|
||||
:command:`swap {SONG1} {SONG2}`
|
||||
Swaps the positions of ``SONG1`` and
|
||||
``SONG2``.
|
||||
|
||||
.. _command_swapid:
|
||||
|
||||
:command:`swapid {SONG1} {SONG2}`
|
||||
Swaps the positions of ``SONG1`` and
|
||||
``SONG2`` (both song ids).
|
||||
|
||||
.. _command_addtagid:
|
||||
|
||||
:command:`addtagid {SONGID} {TAG} {VALUE}`
|
||||
Adds a tag to the specified song. Editing song tags is
|
||||
only possible for remote songs. This change is
|
||||
@@ -751,6 +870,8 @@ Whenever possible, ids should be used.
|
||||
the server, and the data is gone when the song gets
|
||||
removed from the queue.
|
||||
|
||||
.. _command_cleartagid:
|
||||
|
||||
:command:`cleartagid {SONGID} [TAG]`
|
||||
Removes tags from the specified song. If
|
||||
``TAG`` is not specified, then all tag
|
||||
@@ -772,14 +893,20 @@ playlists in arbitrary location (absolute path including the suffix;
|
||||
allowed only for clients that are connected via local socket), or
|
||||
remote playlists (absolute URI with a supported scheme).
|
||||
|
||||
.. _command_listplaylist:
|
||||
|
||||
:command:`listplaylist {NAME}`
|
||||
Lists the songs in the playlist. Playlist plugins are
|
||||
supported.
|
||||
|
||||
.. _command_listplaylistinfo:
|
||||
|
||||
:command:`listplaylistinfo {NAME}`
|
||||
Lists the songs with metadata in the playlist. Playlist
|
||||
plugins are supported.
|
||||
|
||||
.. _command_listplaylists:
|
||||
|
||||
:command:`listplaylists`
|
||||
Prints a list of the playlist directory.
|
||||
After each playlist name the server sends its last
|
||||
@@ -790,32 +917,50 @@ remote playlists (absolute URI with a supported scheme).
|
||||
|
||||
.. _command_load:
|
||||
|
||||
:command:`load {NAME} [START:END]`
|
||||
:command:`load {NAME} [START:END] [POSITION]`
|
||||
Loads the playlist into the current queue. Playlist
|
||||
plugins are supported. A range may be specified to load
|
||||
only a part of the playlist.
|
||||
|
||||
The ``POSITION`` parameter specifies where the songs will be
|
||||
inserted into the queue; it can be relative as described in
|
||||
:ref:`addid <command_addid>`. (This requires specifying the range
|
||||
as well; the special value `0:` can be used if the whole playlist
|
||||
shall be loaded at a certain queue position.)
|
||||
|
||||
.. _command_playlistadd:
|
||||
|
||||
:command:`playlistadd {NAME} {URI}`
|
||||
Adds ``URI`` to the playlist
|
||||
`NAME.m3u`.
|
||||
`NAME.m3u` will be created if it does
|
||||
not exist.
|
||||
|
||||
.. _command_playlistclear:
|
||||
|
||||
:command:`playlistclear {NAME}`
|
||||
Clears the playlist `NAME.m3u`.
|
||||
|
||||
.. _command_playlistdelete:
|
||||
|
||||
:command:`playlistdelete {NAME} {SONGPOS}`
|
||||
Deletes ``SONGPOS`` from the
|
||||
playlist `NAME.m3u`.
|
||||
|
||||
.. _command_playlistmove:
|
||||
|
||||
:command:`playlistmove {NAME} {FROM} {TO}`
|
||||
Moves the song at position ``FROM`` in
|
||||
the playlist `NAME.m3u` to the
|
||||
position ``TO``.
|
||||
|
||||
.. _command_rename:
|
||||
|
||||
:command:`rename {NAME} {NEW_NAME}`
|
||||
Renames the playlist `NAME.m3u` to `NEW_NAME.m3u`.
|
||||
|
||||
.. _command_rm:
|
||||
|
||||
:command:`rm {NAME}`
|
||||
Removes the playlist `NAME.m3u` from
|
||||
the playlist directory.
|
||||
@@ -829,6 +974,8 @@ remote playlists (absolute URI with a supported scheme).
|
||||
The music database
|
||||
==================
|
||||
|
||||
.. _command_albumart:
|
||||
|
||||
:command:`albumart {URI} {OFFSET}`
|
||||
Locate album art for the given song and return a chunk of an album
|
||||
art image file at offset ``OFFSET``.
|
||||
@@ -850,6 +997,8 @@ The music database
|
||||
<8192 bytes>
|
||||
OK
|
||||
|
||||
.. _command_count:
|
||||
|
||||
:command:`count {FILTER} [group {GROUPTYPE}]`
|
||||
Count the number of songs and their total playtime in
|
||||
the database matching ``FILTER`` (see
|
||||
@@ -872,6 +1021,8 @@ The music database
|
||||
don't have this group tag. It exists only if at least one such song is
|
||||
found.
|
||||
|
||||
.. _command_getfingerprint:
|
||||
|
||||
:command:`getfingerprint {URI}`
|
||||
|
||||
Calculate the song's audio fingerprint. Example (abbreviated fingerprint)::
|
||||
@@ -909,11 +1060,11 @@ The music database
|
||||
|
||||
.. _command_findadd:
|
||||
|
||||
:command:`findadd {FILTER} [sort {TYPE}] [window {START:END}]`
|
||||
:command:`findadd {FILTER} [sort {TYPE}] [window {START:END}] [position POS]`
|
||||
Search the database for songs matching
|
||||
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
|
||||
the queue. Parameters have the same meaning as for
|
||||
:ref:`find <command_find>`.
|
||||
:ref:`find <command_find>` and :ref:`searchadd <command_searchadd>`.
|
||||
|
||||
.. _command_list:
|
||||
|
||||
@@ -962,6 +1113,8 @@ The music database
|
||||
:program:`MPD` whenever you need
|
||||
something.
|
||||
|
||||
.. _command_listfiles:
|
||||
|
||||
:command:`listfiles {URI}`
|
||||
Lists the contents of the directory
|
||||
``URI``, including files are not
|
||||
@@ -998,6 +1151,8 @@ The music database
|
||||
use this command to read the tags of an arbitrary local
|
||||
file (URI is an absolute path).
|
||||
|
||||
.. _command_readcomments:
|
||||
|
||||
:command:`readcomments {URI}`
|
||||
Read "comments" (i.e. key-value pairs) from the file
|
||||
specified by "URI". This "URI" can be a path relative
|
||||
@@ -1014,6 +1169,8 @@ The music database
|
||||
decoder plugins support it. For example, on Ogg files,
|
||||
this lists the Vorbis comments.
|
||||
|
||||
.. _command_readpicture:
|
||||
|
||||
:command:`readpicture {URI} {OFFSET}`
|
||||
Locate a picture for the given song and return a chunk of the
|
||||
image file at offset ``OFFSET``. This is usually implemented by
|
||||
@@ -1040,7 +1197,7 @@ The music database
|
||||
|
||||
.. _command_search:
|
||||
|
||||
:command:`search {FILTER} [sort {TYPE}] [window {START:END}]`
|
||||
:command:`search {FILTER} [sort {TYPE}] [window {START:END}]
|
||||
Search the database for songs matching
|
||||
``FILTER`` (see :ref:`Filters <filter_syntax>`). Parameters
|
||||
have the same meaning as for :ref:`find <command_find>`,
|
||||
@@ -1048,13 +1205,18 @@ The music database
|
||||
|
||||
.. _command_searchadd:
|
||||
|
||||
:command:`searchadd {FILTER} [sort {TYPE}] [window {START:END}]`
|
||||
:command:`searchadd {FILTER} [sort {TYPE}] [window {START:END}] [position POS]`
|
||||
Search the database for songs matching
|
||||
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
|
||||
the queue.
|
||||
|
||||
Parameters have the same meaning as for :ref:`search <command_search>`.
|
||||
|
||||
The ``position`` parameter specifies where the songs will be
|
||||
inserted.
|
||||
|
||||
.. _command_searchaddpl:
|
||||
|
||||
:command:`searchaddpl {NAME} {FILTER} [sort {TYPE}] [window {START:END}]`
|
||||
Search the database for songs matching
|
||||
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
|
||||
@@ -1080,6 +1242,8 @@ The music database
|
||||
job id in the :ref:`status <command_status>`
|
||||
response.
|
||||
|
||||
.. _command_rescan:
|
||||
|
||||
:command:`rescan [URI]`
|
||||
Same as :ref:`update <command_update>`,
|
||||
but also rescans unmodified files.
|
||||
@@ -1106,11 +1270,15 @@ only inside the :program:`MPD` process.
|
||||
|
||||
mount foo nfs://192.168.1.4/export/mp3
|
||||
|
||||
.. _command_unmount:
|
||||
|
||||
:command:`unmount {PATH}`
|
||||
Unmounts the specified path. Example::
|
||||
|
||||
unmount foo
|
||||
|
||||
.. _command_listmounts:
|
||||
|
||||
:command:`listmounts`
|
||||
Queries a list of all mounts. By default, this contains
|
||||
just the configured ``music_directory``.
|
||||
@@ -1123,6 +1291,8 @@ only inside the :program:`MPD` process.
|
||||
storage: nfs://192.168.1.4/export/mp3
|
||||
OK
|
||||
|
||||
.. _command_listneighbors:
|
||||
|
||||
:command:`listneighbors`
|
||||
Queries a list of "neighbors" (e.g. accessible file
|
||||
servers on the local net). Items on that list may be
|
||||
@@ -1158,28 +1328,40 @@ Objects which may have stickers are addressed by their object
|
||||
type ("song" for song objects) and their URI (the path within
|
||||
the database for songs).
|
||||
|
||||
.. _command_sticker_get:
|
||||
|
||||
:command:`sticker get {TYPE} {URI} {NAME}`
|
||||
Reads a sticker value for the specified object.
|
||||
|
||||
.. _command_sticker_set:
|
||||
|
||||
:command:`sticker set {TYPE} {URI} {NAME} {VALUE}`
|
||||
Adds a sticker value to the specified object. If a
|
||||
sticker item with that name already exists, it is
|
||||
replaced.
|
||||
|
||||
.. _command_sticker_delete:
|
||||
|
||||
:command:`sticker delete {TYPE} {URI} [NAME]`
|
||||
Deletes a sticker value from the specified object. If
|
||||
you do not specify a sticker name, all sticker values
|
||||
are deleted.
|
||||
|
||||
.. _command_sticker_list:
|
||||
|
||||
:command:`sticker list {TYPE} {URI}`
|
||||
Lists the stickers for the specified object.
|
||||
|
||||
.. _command_sticker_find:
|
||||
|
||||
:command:`sticker find {TYPE} {URI} {NAME}`
|
||||
Searches the sticker database for stickers with the
|
||||
specified name, below the specified directory (URI).
|
||||
For each matching song, it prints the URI and that one
|
||||
sticker's value.
|
||||
|
||||
.. _command_sticker_find_value:
|
||||
|
||||
:command:`sticker find {TYPE} {URI} {NAME} = {VALUE}`
|
||||
Searches for stickers with the given value.
|
||||
|
||||
@@ -1189,6 +1371,8 @@ the database for songs).
|
||||
Connection settings
|
||||
===================
|
||||
|
||||
.. _command_close:
|
||||
|
||||
:command:`close`
|
||||
Closes the connection to :program:`MPD`.
|
||||
:program:`MPD` will try to send the
|
||||
@@ -1199,6 +1383,8 @@ Connection settings
|
||||
Clients should not use this command; instead, they should just
|
||||
close the socket.
|
||||
|
||||
.. _command_kill:
|
||||
|
||||
:command:`kill`
|
||||
Kills :program:`MPD`.
|
||||
|
||||
@@ -1206,14 +1392,31 @@ Connection settings
|
||||
instead, or better: let your service manager handle :program:`MPD`
|
||||
shutdown (e.g. :command:`systemctl stop mpd`).
|
||||
|
||||
.. _command_password:
|
||||
|
||||
:command:`password {PASSWORD}`
|
||||
This is used for authentication with the server.
|
||||
``PASSWORD`` is simply the plaintext
|
||||
password.
|
||||
|
||||
.. _command_ping:
|
||||
|
||||
:command:`ping`
|
||||
Does nothing but return "OK".
|
||||
|
||||
.. _command_binarylimit:
|
||||
|
||||
:command:`binarylimit SIZE` [#since_0_22_4]_
|
||||
|
||||
Set the maximum :ref:`binary response <binary>` size for the
|
||||
current connection to the specified number of bytes.
|
||||
|
||||
A bigger value means less overhead for transmitting large
|
||||
entities, but it also means that the connection is blocked for a
|
||||
longer time.
|
||||
|
||||
.. _command_tagtypes:
|
||||
|
||||
:command:`tagtypes`
|
||||
Shows a list of available tag types. It is an
|
||||
intersection of the ``metadata_to_use``
|
||||
@@ -1226,21 +1429,29 @@ Connection settings
|
||||
``tagtypes`` sub commands configure this
|
||||
list.
|
||||
|
||||
.. _command_tagtypes_disable:
|
||||
|
||||
:command:`tagtypes disable {NAME...}`
|
||||
Remove one or more tags from the list of tag types the
|
||||
client is interested in. These will be omitted from
|
||||
responses to this client.
|
||||
|
||||
.. _command_tagtypes_enable:
|
||||
|
||||
:command:`tagtypes enable {NAME...}`
|
||||
Re-enable one or more tags from the list of tag types
|
||||
for this client. These will no longer be hidden from
|
||||
responses to this client.
|
||||
|
||||
.. _command_tagtypes_clear:
|
||||
|
||||
:command:`tagtypes clear`
|
||||
Clear the list of tag types this client is interested
|
||||
in. This means that :program:`MPD` will
|
||||
not send any tags to this client.
|
||||
|
||||
.. _command_tagtypes_all:
|
||||
|
||||
:command:`tagtypes all`
|
||||
Announce that this client is interested in all tag
|
||||
types. This is the default setting for new clients.
|
||||
@@ -1255,34 +1466,50 @@ These commands allow a client to inspect and manage
|
||||
MPD process: it has separate queue, player and outputs. A
|
||||
client is assigned to one partition at a time.
|
||||
|
||||
.. _command_partition:
|
||||
|
||||
:command:`partition {NAME}`
|
||||
Switch the client to a different partition.
|
||||
|
||||
.. _command_listpartitions:
|
||||
|
||||
:command:`listpartitions`
|
||||
Print a list of partitions. Each partition starts with
|
||||
a ``partition`` keyword and the
|
||||
partition's name, followed by information about the
|
||||
partition.
|
||||
|
||||
.. _command_newpartition:
|
||||
|
||||
:command:`newpartition {NAME}`
|
||||
Create a new partition.
|
||||
|
||||
.. _command_delpartition:
|
||||
|
||||
:command:`delpartition {NAME}`
|
||||
Delete a partition. The partition must be empty (no connected
|
||||
clients and no outputs).
|
||||
|
||||
.. _command_moveoutput:
|
||||
|
||||
:command:`moveoutput {OUTPUTNAME}`
|
||||
Move an output to the current partition.
|
||||
|
||||
Audio output devices
|
||||
====================
|
||||
|
||||
.. _command_disableoutput:
|
||||
|
||||
:command:`disableoutput {ID}`
|
||||
Turns an output off.
|
||||
|
||||
.. _command_enableoutput:
|
||||
|
||||
:command:`enableoutput {ID}`
|
||||
Turns an output on.
|
||||
|
||||
.. _command_toggleoutput:
|
||||
|
||||
:command:`toggleoutput {ID}`
|
||||
Turns an output on or off, depending on the current
|
||||
state.
|
||||
@@ -1291,7 +1518,7 @@ Audio output devices
|
||||
|
||||
:command:`outputs`
|
||||
Shows information about all outputs.
|
||||
|
||||
|
||||
::
|
||||
|
||||
outputid: 0
|
||||
@@ -1307,6 +1534,8 @@ Audio output devices
|
||||
- ``outputname``: Name of the output. It can be any.
|
||||
- ``outputenabled``: Status of the output. 0 if disabled, 1 if enabled.
|
||||
|
||||
.. _command_outputset:
|
||||
|
||||
:command:`outputset {ID} {NAME} {VALUE}`
|
||||
Set a runtime attribute. These are specific to the
|
||||
output plugin, and supported values are usually printed
|
||||
@@ -1316,6 +1545,8 @@ Audio output devices
|
||||
Reflection
|
||||
==========
|
||||
|
||||
.. _command_config:
|
||||
|
||||
:command:`config`
|
||||
Dumps configuration values that may be interesting for
|
||||
the client. This command is only permitted to "local"
|
||||
@@ -1325,16 +1556,24 @@ Reflection
|
||||
|
||||
- ``music_directory``: The absolute path of the music directory.
|
||||
|
||||
.. _command_commands:
|
||||
|
||||
:command:`commands`
|
||||
Shows which commands the current user has access to.
|
||||
|
||||
.. _command_notcommands:
|
||||
|
||||
:command:`notcommands`
|
||||
Shows which commands the current user does not have
|
||||
access to.
|
||||
|
||||
.. _command_urlhandlers:
|
||||
|
||||
:command:`urlhandlers`
|
||||
Gets a list of available URL handlers.
|
||||
|
||||
.. _command_decoders:
|
||||
|
||||
:command:`decoders`
|
||||
Print a list of decoder plugins, followed by their
|
||||
supported suffixes and MIME types. Example response::
|
||||
@@ -1366,12 +1605,16 @@ idle event.
|
||||
If your MPD instance has multiple partitions, note that
|
||||
client-to-client messages are local to the current partition.
|
||||
|
||||
.. _command_subscribe:
|
||||
|
||||
:command:`subscribe {NAME}`
|
||||
Subscribe to a channel. The channel is created if it
|
||||
does not exist already. The name may consist of
|
||||
alphanumeric ASCII characters plus underscore, dash, dot
|
||||
and colon.
|
||||
|
||||
.. _command_unsubscribe:
|
||||
|
||||
:command:`unsubscribe {NAME}`
|
||||
Unsubscribe from a channel.
|
||||
|
||||
@@ -1381,10 +1624,14 @@ client-to-client messages are local to the current partition.
|
||||
Obtain a list of all channels. The response is a list
|
||||
of "channel:" lines.
|
||||
|
||||
.. _command_readmessages:
|
||||
|
||||
:command:`readmessages`
|
||||
Reads messages for this client. The response is a list
|
||||
of "channel:" and "message:" lines.
|
||||
|
||||
.. _command_sendmessage:
|
||||
|
||||
:command:`sendmessage {CHANNEL} {TEXT}`
|
||||
Send a message to the specified channel.
|
||||
|
||||
@@ -1393,6 +1640,7 @@ client-to-client messages are local to the current partition.
|
||||
.. [#since_0_14] Since :program:`MPD` 0.14
|
||||
.. [#since_0_15] Since :program:`MPD` 0.15
|
||||
.. [#since_0_16] Since :program:`MPD` 0.16
|
||||
.. [#since_0_19] Since :program:`MPD` 0.20
|
||||
.. [#since_0_19] Since :program:`MPD` 0.19
|
||||
.. [#since_0_20] Since :program:`MPD` 0.20
|
||||
.. [#since_0_21] Since :program:`MPD` 0.21
|
||||
.. [#since_0_22_4] Since :program:`MPD` 0.22.4
|
||||
|
156
doc/user.rst
156
doc/user.rst
@@ -55,8 +55,8 @@ and unpack it (or `clone the git repository
|
||||
|
||||
In any case, you need:
|
||||
|
||||
* a C++17 compiler (e.g. GCC 8 or clang 5)
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
* a C++17 compiler (e.g. GCC 8 or clang 7)
|
||||
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* Boost 1.58
|
||||
* pkg-config
|
||||
@@ -69,6 +69,7 @@ For example, the following installs a fairly complete list of build dependencies
|
||||
.. code-block:: none
|
||||
|
||||
apt install meson g++ \
|
||||
libfmt-dev \
|
||||
libpcre3-dev \
|
||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
||||
@@ -86,7 +87,7 @@ 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 \
|
||||
@@ -110,6 +111,19 @@ The following command shows a list of compile-time options:
|
||||
|
||||
meson configure output/release
|
||||
|
||||
NB: Check the sysconfdir setting to determine where mpd will look for mpd.conf; if you expect mpd to look for /etc/mpd.conf the sysconfdir must be '/etc' (i.e., not 'etc' which will result in mpd looking for /usr/local/etc/mpd.conf):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
meson configure output/release |grep sysconfdir
|
||||
|
||||
If this is not /etc (or another path you wish to specify):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ meson configure output/release -Dsysconfdir='/etc' ; meson configure output/release |grep syscon
|
||||
sysconfdir /etc Sysconf data directory
|
||||
|
||||
When everything is ready and configured, compile:
|
||||
|
||||
.. code-block:: none
|
||||
@@ -141,6 +155,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.56.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:
|
||||
@@ -167,7 +190,12 @@ Compiling for Android
|
||||
You need:
|
||||
|
||||
* Android SDK
|
||||
* Android NDK
|
||||
* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_
|
||||
* `Meson 0.56.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
|
||||
@@ -413,7 +441,7 @@ The following table lists the audio_output options valid for all plugins:
|
||||
* - **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.
|
||||
* - **enabed yes|no**
|
||||
* - **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.
|
||||
@@ -500,6 +528,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.
|
||||
@@ -606,13 +639,20 @@ By default, all clients are unauthenticated and have a full set of permissions.
|
||||
- Allows reading of the database, displaying the current playlist, and current status of :program:`MPD`.
|
||||
* - **add**
|
||||
- Allows adding songs and loading playlists.
|
||||
* - **player**
|
||||
- Allows any player and queue manipulation (start/pause/stop
|
||||
playback etc.).
|
||||
* - **control**
|
||||
- Allows all other player and playlist manipulations.
|
||||
* - **admin**
|
||||
- Allows database updates and allows shutting down :program:`MPD`.
|
||||
- Allows manipulating outputs, stickers and partitions,
|
||||
mounting/unmounting storage and shutting down :program:`MPD`.
|
||||
|
||||
:code:`local_permissions` may be used to assign other permissions to clients connecting on a local socket.
|
||||
|
||||
:code:`host_permissions` may be used to assign permissions to clients
|
||||
with a certain IP address.
|
||||
|
||||
:code:`password` allows the client to send a password to gain other permissions. This option may be specified multiple times with different passwords.
|
||||
|
||||
Note that the :code:`password` option is not secure: passwords are sent in clear-text over the connection, and the client cannot verify the server's identity.
|
||||
@@ -622,6 +662,8 @@ Example:
|
||||
.. code-block:: none
|
||||
|
||||
default_permissions "read"
|
||||
host_permissions "192.168.0.100 read,add,control,admin"
|
||||
host_permissions "2003:1234:4567::1 read,add,control,admin"
|
||||
password "the_password@read,add,control"
|
||||
password "the_admin_password@read,add,control,admin"
|
||||
|
||||
@@ -669,6 +711,8 @@ 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
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -770,6 +814,8 @@ 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
|
||||
--------------------
|
||||
|
||||
@@ -1025,12 +1071,20 @@ Check list for bit-perfect playback:
|
||||
: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.
|
||||
|
||||
@@ -1091,39 +1145,71 @@ 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.
|
||||
|
||||
@@ -1141,6 +1227,34 @@ Your bug report should contain:
|
||||
* relevant portions of the log file (:option:`--verbose`)
|
||||
* be clear about what you expect MPD to do, and what is actually happening
|
||||
|
||||
.. _profiler:
|
||||
|
||||
Too Much CPU Usage
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you believe MPD consumes too much CPU, `write a bug report
|
||||
<https://github.com/MusicPlayerDaemon/MPD/issues>`_ with a profiling
|
||||
information.
|
||||
|
||||
On Linux, this can be obtained with :program:`perf` (on Debian,
|
||||
installed the package :file:`linux-perf`), for example::
|
||||
|
||||
perf record -p `pidof mpd`
|
||||
|
||||
Run this command while MPD consumes much CPU, let it run for a minute
|
||||
or so, and stop it by pressing ``Ctrl-C``. Then type::
|
||||
|
||||
perf report >mpd_perf.txt
|
||||
|
||||
Upload the output file to the bug report.
|
||||
|
||||
.. note::
|
||||
|
||||
This requires having debug symbols for MPD and all relevant
|
||||
libraries. See :ref:`crash` for details.
|
||||
|
||||
.. _crash:
|
||||
|
||||
MPD crashes
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
187
meson.build
187
meson.build
@@ -1,18 +1,30 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.22',
|
||||
meson_version: '>= 0.49.0',
|
||||
version: '0.23.2',
|
||||
meson_version: '>= 0.56.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'build.c_std=c99',
|
||||
'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',
|
||||
# If we build those libraries as Meson subproject, they shall be
|
||||
# linked statically into the MPD executable.
|
||||
'expat:default_library=static',
|
||||
'fmt:default_library=static',
|
||||
'gtest:default_library=static',
|
||||
'sqlite3:default_library=static',
|
||||
'vorbis:default_library=static',
|
||||
|
||||
# Not interested in compiler warnings from subprojects.
|
||||
'expat:werror=false',
|
||||
'expat:warning_level=0',
|
||||
'fmt:warning_level=0',
|
||||
'gtest:warning_level=0',
|
||||
'sqlite3:warning_level=0',
|
||||
'vorbis:warning_level=0',
|
||||
],
|
||||
license: 'GPLv2+',
|
||||
)
|
||||
@@ -24,15 +36,15 @@ 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('<5')
|
||||
warning('Your clang version is too old. You need at least version 5.')
|
||||
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.0')
|
||||
version_conf.set_quoted('PROTOCOL_VERSION', '0.23.1')
|
||||
configure_file(output: 'Version.h', configuration: version_conf)
|
||||
|
||||
conf = configuration_data()
|
||||
@@ -42,68 +54,79 @@ common_cppflags = [
|
||||
'-D_GNU_SOURCE',
|
||||
]
|
||||
|
||||
common_cflags = [
|
||||
]
|
||||
|
||||
common_cxxflags = [
|
||||
test_global_common_flags = [
|
||||
'-fvisibility=hidden',
|
||||
]
|
||||
|
||||
test_common_flags = [
|
||||
'-Wvla',
|
||||
'-Wdouble-promotion',
|
||||
|
||||
'-fvisibility=hidden',
|
||||
|
||||
'-ffast-math',
|
||||
'-ftree-vectorize',
|
||||
|
||||
'-Wcast-qual',
|
||||
'-Wdouble-promotion',
|
||||
'-Wmissing-declarations',
|
||||
'-Wshadow',
|
||||
'-Wunused',
|
||||
'-Wvla',
|
||||
'-Wwrite-strings',
|
||||
|
||||
# clang specific warning options:
|
||||
'-Wunreachable-code-aggressive',
|
||||
'-Wused-but-marked-unused',
|
||||
]
|
||||
|
||||
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',
|
||||
'-Wwrite-strings',
|
||||
'-Wsign-compare',
|
||||
'-Wcomma',
|
||||
'-Wcomma-subscript',
|
||||
'-Wextra-semi',
|
||||
'-Wmismatched-tags',
|
||||
'-Woverloaded-virtual',
|
||||
'-Wsign-promo',
|
||||
'-Wvolatile',
|
||||
'-Wvirtual-inheritance',
|
||||
|
||||
# a vtable without a dtor is just fine
|
||||
'-Wno-non-virtual-dtor',
|
||||
|
||||
# clang specific warning options:
|
||||
'-Wcomma',
|
||||
'-Wheader-hygiene',
|
||||
'-Winconsistent-missing-destructor-override',
|
||||
'-Wunreachable-code-break',
|
||||
'-Wunused',
|
||||
'-Wused-but-marked-unused',
|
||||
|
||||
'-Wno-non-virtual-dtor',
|
||||
]
|
||||
|
||||
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 + [
|
||||
'-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_cxxflags += [
|
||||
test_global_cxxflags += [
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
]
|
||||
test_cflags += [
|
||||
test_global_cflags += [
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
]
|
||||
@@ -112,9 +135,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') != ''
|
||||
@@ -128,10 +163,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')
|
||||
@@ -147,8 +201,6 @@ 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_CLOCALE', compiler.has_header('clocale'))
|
||||
|
||||
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'))
|
||||
@@ -164,17 +216,6 @@ conf.set('HAVE_STRCASESTR', compiler.has_function('strcasestr'))
|
||||
|
||||
conf.set('HAVE_PRCTL', is_linux)
|
||||
|
||||
conf.set('USE_EVENTFD', is_linux and get_option('eventfd'))
|
||||
conf.set('USE_SIGNALFD', is_linux and get_option('signalfd'))
|
||||
|
||||
if is_windows
|
||||
conf.set('USE_WINSELECT', true)
|
||||
elif is_linux and get_option('epoll')
|
||||
conf.set('USE_EPOLL', true)
|
||||
else
|
||||
conf.set('USE_POLL', true)
|
||||
endif
|
||||
|
||||
if not get_option('syslog').disabled()
|
||||
if compiler.has_function('syslog')
|
||||
conf.set('HAVE_SYSLOG', true)
|
||||
@@ -205,24 +246,27 @@ 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
|
||||
|
||||
fmt_dep = dependency('fmt', fallback: ['fmt', 'fmt_dep'])
|
||||
|
||||
log = static_library(
|
||||
'log',
|
||||
'src/Log.cxx',
|
||||
'src/LogBackend.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: fmt_dep,
|
||||
)
|
||||
|
||||
log_dep = declare_dependency(
|
||||
link_with: log,
|
||||
dependencies: fmt_dep,
|
||||
)
|
||||
|
||||
sources = [
|
||||
version_cxx,
|
||||
'src/Main.cxx',
|
||||
'src/protocol/Ack.cxx',
|
||||
'src/protocol/ArgParser.cxx',
|
||||
'src/protocol/Result.cxx',
|
||||
'src/command/CommandError.cxx',
|
||||
'src/command/PositionArg.cxx',
|
||||
'src/command/AllCommands.cxx',
|
||||
'src/command/QueueCommands.cxx',
|
||||
'src/command/TagCommands.cxx',
|
||||
@@ -237,7 +281,6 @@ sources = [
|
||||
'src/command/CommandListBuilder.cxx',
|
||||
'src/Idle.cxx',
|
||||
'src/IdleFlags.cxx',
|
||||
'src/decoder/Domain.cxx',
|
||||
'src/decoder/Thread.cxx',
|
||||
'src/decoder/Control.cxx',
|
||||
'src/decoder/Bridge.cxx',
|
||||
@@ -263,7 +306,6 @@ sources = [
|
||||
'src/LogInit.cxx',
|
||||
'src/ls.cxx',
|
||||
'src/Instance.cxx',
|
||||
'src/win32/Win32Main.cxx',
|
||||
'src/MusicBuffer.cxx',
|
||||
'src/MusicPipe.cxx',
|
||||
'src/MusicChunk.cxx',
|
||||
@@ -311,6 +353,12 @@ sources = [
|
||||
'src/PlaylistFile.cxx',
|
||||
]
|
||||
|
||||
if is_windows
|
||||
sources += [
|
||||
'src/win32/Win32Main.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
if not is_android
|
||||
sources += [
|
||||
'src/CommandLine.cxx',
|
||||
@@ -345,6 +393,7 @@ subdir('src/system')
|
||||
subdir('src/thread')
|
||||
subdir('src/net')
|
||||
subdir('src/event')
|
||||
subdir('src/win32')
|
||||
|
||||
subdir('src/apple')
|
||||
|
||||
@@ -362,6 +411,7 @@ subdir('src/lib/gcrypt')
|
||||
subdir('src/lib/nfs')
|
||||
subdir('src/lib/oss')
|
||||
subdir('src/lib/pcre')
|
||||
subdir('src/lib/pipewire')
|
||||
subdir('src/lib/pulse')
|
||||
subdir('src/lib/sndio')
|
||||
subdir('src/lib/sqlite')
|
||||
@@ -371,6 +421,8 @@ subdir('src/lib/yajl')
|
||||
|
||||
subdir('src/lib/crypto')
|
||||
|
||||
subdir('src/zeroconf')
|
||||
|
||||
subdir('src/fs')
|
||||
subdir('src/config')
|
||||
subdir('src/tag')
|
||||
@@ -386,7 +438,6 @@ subdir('src/decoder')
|
||||
subdir('src/encoder')
|
||||
subdir('src/song')
|
||||
subdir('src/playlist')
|
||||
subdir('src/zeroconf')
|
||||
|
||||
if curl_dep.found()
|
||||
sources += 'src/RemoteTagCache.cxx'
|
||||
@@ -502,8 +553,10 @@ mpd = build_target(
|
||||
zeroconf_dep,
|
||||
more_deps,
|
||||
chromaprint_dep,
|
||||
fmt_dep,
|
||||
],
|
||||
link_args: link_args,
|
||||
build_by_default: not get_option('fuzzer'),
|
||||
install: not is_android and not is_haiku,
|
||||
)
|
||||
|
||||
@@ -544,3 +597,7 @@ subdir('doc')
|
||||
if get_option('test')
|
||||
subdir('test')
|
||||
endif
|
||||
|
||||
if get_option('fuzzer')
|
||||
subdir('test/fuzzer')
|
||||
endif
|
||||
|
@@ -2,8 +2,6 @@ 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('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
|
||||
|
||||
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')
|
||||
@@ -14,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
|
||||
#
|
||||
@@ -57,7 +62,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
|
||||
#
|
||||
|
||||
option('database', type: 'boolean', value: true, description: 'enable support for the music database')
|
||||
option('upnp', type: 'feature', description: 'UPnP client support')
|
||||
option('upnp', type: 'combo',
|
||||
choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
|
||||
value: 'auto',
|
||||
description: 'UPnP client support')
|
||||
option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
|
||||
|
||||
#
|
||||
@@ -87,7 +95,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
|
||||
@@ -95,7 +107,6 @@ option('smbclient', type: 'feature', description: 'SMB support using libsmbclien
|
||||
|
||||
option('qobuz', type: 'feature', description: 'Qobuz client')
|
||||
option('soundcloud', type: 'feature', description: 'SoundCloud client')
|
||||
option('tidal', type: 'feature', description: 'Tidal client')
|
||||
|
||||
#
|
||||
# Archive plugins
|
||||
@@ -126,6 +137,7 @@ option('gme', type: 'feature', description: 'Game Music Emulator decoder plugin'
|
||||
option('mad', type: 'feature', description: 'MP3 decoder using libmad')
|
||||
option('mikmod', type: 'feature', description: 'MikMod decoder plugin')
|
||||
option('modplug', type: 'feature', description: 'Modplug decoder plugin')
|
||||
option('openmpt', type: 'feature', description: 'OpenMPT decoder plugin')
|
||||
option('mpcdec', type: 'feature', description: 'Musepack decoder plugin')
|
||||
option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
|
||||
option('opus', type: 'feature', description: 'Opus decoder plugin')
|
||||
@@ -165,9 +177,11 @@ option('jack', type: 'feature', description: 'JACK output plugin')
|
||||
option('openal', type: 'feature', description: 'OpenAL output plugin')
|
||||
option('oss', type: 'feature', description: 'Open Sound System support')
|
||||
option('pipe', type: 'boolean', value: true, description: 'Pipe output plugin')
|
||||
option('pipewire', type: 'feature', description: 'PipeWire support')
|
||||
option('pulse', type: 'feature', description: 'PulseAudio support')
|
||||
option('recorder', type: 'boolean', value: true, description: 'Recorder output plugin')
|
||||
option('shout', type: 'feature', description: 'Shoutcast streaming support using libshout')
|
||||
option('snapcast', type: 'boolean', value: true, description: 'Snapcast output plugin')
|
||||
option('sndio', type: 'feature', description: 'sndio output plugin')
|
||||
option('solaris_output', type: 'feature', description: 'Solaris /dev/audio support')
|
||||
|
||||
|
@@ -55,10 +55,10 @@ class AutotoolsProject(MakeProject):
|
||||
subprocess.check_call(configure, cwd=build, env=toolchain.env)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
def _build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
if self.subdirs is not None:
|
||||
for subdir in self.subdirs:
|
||||
MakeProject.build(self, toolchain, os.path.join(build, subdir))
|
||||
self.build_make(toolchain, os.path.join(build, subdir))
|
||||
else:
|
||||
MakeProject.build(self, toolchain, build)
|
||||
self.build_make(toolchain, build)
|
||||
|
@@ -12,7 +12,7 @@ class BoostProject(Project):
|
||||
name='boost', version=version,
|
||||
**kwargs)
|
||||
|
||||
def build(self, toolchain):
|
||||
def _build(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
|
||||
# install the headers manually; don't build any library
|
||||
@@ -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))
|
||||
|
@@ -1,27 +1,63 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from build.project import Project
|
||||
|
||||
def __write_cmake_compiler(f, language, compiler):
|
||||
s = compiler.split(' ', 1)
|
||||
if len(s) == 2:
|
||||
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
|
||||
compiler = s[1]
|
||||
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
|
||||
|
||||
def __write_cmake_toolchain_file(f, toolchain):
|
||||
if '-darwin' in toolchain.actual_arch:
|
||||
cmake_system_name = 'Darwin'
|
||||
elif toolchain.is_windows:
|
||||
cmake_system_name = 'Windows'
|
||||
else:
|
||||
cmake_system_name = 'Linux'
|
||||
|
||||
f.write(f"""
|
||||
set(CMAKE_SYSTEM_NAME {cmake_system_name})
|
||||
set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
|
||||
|
||||
set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
|
||||
set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
|
||||
|
||||
set(CMAKE_C_FLAGS "{toolchain.cflags} {toolchain.cppflags}")
|
||||
set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
|
||||
""")
|
||||
__write_cmake_compiler(f, 'C', toolchain.cc)
|
||||
__write_cmake_compiler(f, 'CXX', toolchain.cxx)
|
||||
|
||||
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)
|
||||
|
||||
# Several targets need a sysroot to prevent pkg-config from
|
||||
# looking for libraries on the build host (TODO: fix this
|
||||
# properly); but we must not do that on Android because the NDK
|
||||
# has a sysroot already
|
||||
if '-android' not in toolchain.actual_arch and '-darwin' not in toolchain.actual_arch:
|
||||
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
|
||||
|
||||
os.makedirs(build, exist_ok=True)
|
||||
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
|
||||
with open(cmake_toolchain_file, 'w') as f:
|
||||
__write_cmake_toolchain_file(f, toolchain)
|
||||
|
||||
configure = [
|
||||
'cmake',
|
||||
src,
|
||||
|
||||
'-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file,
|
||||
|
||||
'-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
|
||||
|
||||
@@ -29,17 +65,22 @@ def configure(toolchain, src, build, args=()):
|
||||
|
||||
class CmakeProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
windows_configure_args=[],
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.windows_configure_args = windows_configure_args
|
||||
|
||||
def configure(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
configure(toolchain, src, build, self.configure_args)
|
||||
configure_args = self.configure_args
|
||||
if toolchain.is_windows:
|
||||
configure_args = configure_args + self.windows_configure_args
|
||||
configure(toolchain, src, build, configure_args)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
def _build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
||||
|
@@ -10,12 +10,7 @@ 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):
|
||||
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)
|
@@ -7,7 +7,9 @@ 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.19.tar.xz',
|
||||
@@ -15,29 +17,17 @@ libmpdclient = MesonProject(
|
||||
'lib/libmpdclient.a',
|
||||
)
|
||||
|
||||
libogg = AutotoolsProject(
|
||||
libogg = CmakeProject(
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
|
||||
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
|
||||
'lib/libogg.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DINSTALL_DOCS=OFF',
|
||||
'-DINSTALL_CMAKE_PACKAGE_MODULE=OFF',
|
||||
],
|
||||
)
|
||||
|
||||
libvorbis = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz',
|
||||
'b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b',
|
||||
'lib/libvorbis.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
|
||||
edits={
|
||||
# this option is not understood by clang
|
||||
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
|
||||
}
|
||||
)
|
||||
|
||||
opus = AutotoolsProject(
|
||||
'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
|
||||
'65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
|
||||
@@ -121,18 +111,27 @@ libmodplug = AutotoolsProject(
|
||||
],
|
||||
)
|
||||
|
||||
libopenmpt = AutotoolsProject(
|
||||
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.8+release.autotools.tar.gz',
|
||||
'61de7cc0c011b10472ca16adcc123689',
|
||||
'lib/libopenmpt.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static'
|
||||
],
|
||||
)
|
||||
|
||||
wildmidi = CmakeProject(
|
||||
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
|
||||
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
|
||||
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.4',
|
||||
'6f267c8d331e9859906837e2c197093fddec31829d2ebf7b958cf6b7ae935430',
|
||||
'lib/libWildMidi.a',
|
||||
[
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DWANT_PLAYER=OFF',
|
||||
'-DWANT_STATIC=ON',
|
||||
],
|
||||
base='wildmidi-wildmidi-0.4.3',
|
||||
base='wildmidi-wildmidi-0.4.4',
|
||||
name='wildmidi',
|
||||
version='0.4.3',
|
||||
version='0.4.4',
|
||||
)
|
||||
|
||||
gme = CmakeProject(
|
||||
@@ -148,8 +147,8 @@ gme = CmakeProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.3.1.tar.xz',
|
||||
'ad009240d46e307b4e03a213a0f49c11b650e445b1f8be0dda2a9212b34d2ffb',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.4.tar.xz',
|
||||
'06b10a183ce5371f915c6bb15b7b1fffbe046e8275099c96affc29e17645d909',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -376,43 +375,45 @@ ffmpeg = FfmpegProject(
|
||||
],
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.72.0.tar.xz',
|
||||
'0ded0808c4d85f2ee0db86980ae610cc9d165e9ca9da466196cc73c346513713',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-debug',
|
||||
'--enable-http',
|
||||
'--enable-ipv6',
|
||||
'--disable-ftp', '--disable-file',
|
||||
'--disable-ldap', '--disable-ldaps',
|
||||
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
|
||||
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
|
||||
'--disable-smb',
|
||||
'--disable-gopher',
|
||||
'--disable-manual',
|
||||
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
|
||||
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
|
||||
'--disable-doh',
|
||||
'--disable-mime',
|
||||
'--disable-netrc',
|
||||
'--disable-progress-meter',
|
||||
'--disable-alt-svc',
|
||||
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
|
||||
],
|
||||
|
||||
patches='src/lib/curl/patches',
|
||||
openssl = OpenSSLProject(
|
||||
'https://www.openssl.org/source/openssl-3.0.0.tar.gz',
|
||||
'59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536',
|
||||
'include/openssl/ossl_typ.h',
|
||||
)
|
||||
|
||||
libexpat = AutotoolsProject(
|
||||
'https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.bz2',
|
||||
'f1063084dc4302a427dabcca499c8312b3a32a29b7d2506653ecc8f950a9a237',
|
||||
'lib/libexpat.a',
|
||||
curl = CmakeProject(
|
||||
'https://curl.se/download/curl-7.79.1.tar.xz',
|
||||
'0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--without-docbook',
|
||||
'-DBUILD_CURL_EXE=OFF',
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DCURL_DISABLE_VERBOSE_STRINGS=ON',
|
||||
'-DCURL_DISABLE_LDAP=ON',
|
||||
'-DCURL_DISABLE_TELNET=ON',
|
||||
'-DCURL_DISABLE_DICT=ON',
|
||||
'-DCURL_DISABLE_FILE=ON',
|
||||
'-DCURL_DISABLE_FTP=ON',
|
||||
'-DCURL_DISABLE_TFTP=ON',
|
||||
'-DCURL_DISABLE_LDAPS=ON',
|
||||
'-DCURL_DISABLE_RTSP=ON',
|
||||
'-DCURL_DISABLE_PROXY=ON',
|
||||
'-DCURL_DISABLE_POP3=ON',
|
||||
'-DCURL_DISABLE_IMAP=ON',
|
||||
'-DCURL_DISABLE_SMTP=ON',
|
||||
'-DCURL_DISABLE_GOPHER=ON',
|
||||
'-DCURL_DISABLE_COOKIES=ON',
|
||||
'-DCURL_DISABLE_CRYPTO_AUTH=ON',
|
||||
'-DCURL_DISABLE_ALTSVC=ON',
|
||||
'-DCMAKE_USE_LIBSSH2=OFF',
|
||||
'-DCURL_WINDOWS_SSPI=OFF',
|
||||
'-DCURL_DISABLE_NTLM=ON',
|
||||
'-DBUILD_TESTING=OFF',
|
||||
],
|
||||
windows_configure_args=[
|
||||
'-DCMAKE_USE_SCHANNEL=ON',
|
||||
],
|
||||
patches='src/lib/curl/patches',
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
@@ -429,11 +430,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(
|
||||
'https://dl.bintray.com/boostorg/release/1.74.0/source/boost_1_74_0.tar.bz2',
|
||||
'83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1',
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2',
|
||||
'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
@@ -22,7 +22,7 @@ class MakeProject(Project):
|
||||
subprocess.check_call(['/usr/bin/make'] + args,
|
||||
cwd=wd, env=toolchain.env)
|
||||
|
||||
def build(self, toolchain, wd, install=True):
|
||||
def build_make(self, toolchain, wd, install=True):
|
||||
self.make(toolchain, wd, self.get_make_args(toolchain))
|
||||
if install:
|
||||
self.make(toolchain, wd, self.get_make_install_args(toolchain))
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import os.path, subprocess, sys
|
||||
import platform
|
||||
|
||||
from build.project import Project
|
||||
|
||||
@@ -34,41 +35,46 @@ def make_cross_file(toolchain):
|
||||
path = os.path.join(toolchain.build_path, 'meson.cross')
|
||||
os.makedirs(toolchain.build_path, exist_ok=True)
|
||||
with open(path, 'w') as f:
|
||||
f.write("""
|
||||
f.write(f"""
|
||||
[binaries]
|
||||
c = '%s'
|
||||
cpp = '%s'
|
||||
ar = '%s'
|
||||
strip = '%s'
|
||||
pkgconfig = '%s'
|
||||
%s
|
||||
c = '{toolchain.cc}'
|
||||
cpp = '{toolchain.cxx}'
|
||||
ar = '{toolchain.ar}'
|
||||
strip = '{toolchain.strip}'
|
||||
pkgconfig = '{toolchain.pkg_config}'
|
||||
""")
|
||||
|
||||
if toolchain.is_windows and platform.system() != 'Windows':
|
||||
f.write(f"windres = '{toolchain.windres}'\n")
|
||||
|
||||
# Run unit tests with WINE when cross-building for Windows
|
||||
print("exe_wrapper = 'wine'", file=f)
|
||||
|
||||
f.write(f"""
|
||||
[properties]
|
||||
root = '%s'
|
||||
root = '{toolchain.install_prefix}'
|
||||
|
||||
c_args = %s
|
||||
c_link_args = %s
|
||||
[built-in options]
|
||||
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
|
||||
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
||||
|
||||
cpp_args = %s
|
||||
cpp_link_args = %s
|
||||
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
|
||||
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
||||
""")
|
||||
|
||||
if 'android' in toolchain.arch:
|
||||
f.write("""
|
||||
# Keep Meson from executing Android-x86 test binariees
|
||||
needs_exe_wrapper = true
|
||||
""")
|
||||
|
||||
f.write(f"""
|
||||
[host_machine]
|
||||
system = '%s'
|
||||
cpu_family = '%s'
|
||||
cpu = '%s'
|
||||
endian = '%s'
|
||||
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
|
||||
toolchain.pkg_config,
|
||||
windres,
|
||||
toolchain.install_prefix,
|
||||
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
|
||||
repr(toolchain.ldflags.split() + toolchain.libs.split()),
|
||||
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
|
||||
repr(toolchain.ldflags.split() + toolchain.libs.split()),
|
||||
system, cpu_family, cpu, endian))
|
||||
system = '{system}'
|
||||
cpu_family = '{cpu_family}'
|
||||
cpu = '{cpu}'
|
||||
endian = '{endian}'
|
||||
""")
|
||||
return path
|
||||
|
||||
def configure(toolchain, src, build, args=()):
|
||||
@@ -79,11 +85,6 @@ def configure(toolchain, src, build, args=()):
|
||||
|
||||
'--prefix', toolchain.install_prefix,
|
||||
|
||||
# this is necessary because Meson uses Debian's build machine
|
||||
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
|
||||
# builds, which is obviously wrong
|
||||
'--libdir', 'lib',
|
||||
|
||||
'--buildtype', 'plain',
|
||||
|
||||
'--default-library=static',
|
||||
@@ -110,7 +111,7 @@ class MesonProject(Project):
|
||||
configure(toolchain, src, build, self.configure_args)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
def _build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
||||
|
61
python/build/openssl.py
Normal file
61
python/build/openssl.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import subprocess
|
||||
|
||||
from build.makeproject import MakeProject
|
||||
|
||||
class OpenSSLProject(MakeProject):
|
||||
def __init__(self, url, md5, installed,
|
||||
**kwargs):
|
||||
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
|
||||
|
||||
def get_make_args(self, toolchain):
|
||||
return MakeProject.get_make_args(self, toolchain) + [
|
||||
'CC=' + toolchain.cc,
|
||||
'CFLAGS=' + toolchain.cflags,
|
||||
'CPPFLAGS=' + toolchain.cppflags,
|
||||
'AR=' + toolchain.ar,
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
'build_libs',
|
||||
]
|
||||
|
||||
def get_make_install_args(self, toolchain):
|
||||
# OpenSSL's Makefile runs "ranlib" during installation
|
||||
return MakeProject.get_make_install_args(self, toolchain) + [
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
]
|
||||
|
||||
def _build(self, toolchain):
|
||||
src = self.unpack(toolchain, out_of_tree=False)
|
||||
|
||||
# OpenSSL has a weird target architecture scheme with lots of
|
||||
# hard-coded architectures; this table translates between our
|
||||
# "toolchain_arch" (HOST_TRIPLET) and the OpenSSL target
|
||||
openssl_archs = {
|
||||
# not using "android-*" because those OpenSSL targets want
|
||||
# to know where the SDK is, but our own build scripts
|
||||
# prepared everything already to look like a regular Linux
|
||||
# build
|
||||
'arm-linux-androideabi': 'linux-generic32',
|
||||
'aarch64-linux-android': 'linux-aarch64',
|
||||
'i686-linux-android': 'linux-x86-clang',
|
||||
'x86_64-linux-android': 'linux-x86_64-clang',
|
||||
|
||||
# Kobo
|
||||
'arm-linux-gnueabihf': 'linux-generic32',
|
||||
|
||||
# Windows
|
||||
'i686-w64-mingw32': 'mingw',
|
||||
'x86_64-w64-mingw32': 'mingw64',
|
||||
}
|
||||
|
||||
openssl_arch = openssl_archs[toolchain.arch]
|
||||
|
||||
subprocess.check_call(['./Configure',
|
||||
'no-shared',
|
||||
'no-module', 'no-engine', 'no-static-engine',
|
||||
'no-async',
|
||||
'no-tests',
|
||||
'no-asm', # "asm" causes build failures on Windows
|
||||
openssl_arch,
|
||||
'--prefix=' + toolchain.install_prefix],
|
||||
cwd=src, env=toolchain.env)
|
||||
self.build_make(toolchain, src)
|
@@ -20,7 +20,7 @@ class Project:
|
||||
self.base = base
|
||||
|
||||
if name is None or version is None:
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)(\+.*)?$', self.base)
|
||||
if name is None: name = m.group(1)
|
||||
if version is None: version = m.group(2)
|
||||
|
||||
@@ -79,3 +79,6 @@ class Project:
|
||||
pass
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return path
|
||||
|
||||
def build(self, toolchain):
|
||||
self._build(toolchain)
|
||||
|
@@ -31,6 +31,8 @@ def guess_digest_algorithm(digest):
|
||||
return hashlib.sha1
|
||||
elif l == 64:
|
||||
return hashlib.sha256
|
||||
elif l == 128:
|
||||
return hashlib.sha512
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@@ -7,7 +7,7 @@ class ZlibProject(Project):
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
|
||||
def build(self, toolchain):
|
||||
def _build(self, toolchain):
|
||||
src = self.unpack(toolchain, out_of_tree=False)
|
||||
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet',
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "event/Features.h"
|
||||
#include "io/uring/Features.h"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/OptionDef.hxx"
|
||||
@@ -113,7 +114,7 @@ static void version()
|
||||
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",
|
||||
GIT_VERSION);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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,7 +27,7 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
static const char *const idle_names[] = {
|
||||
static constexpr const char * idle_names[] = {
|
||||
"database",
|
||||
"stored_playlist",
|
||||
"playlist",
|
||||
@@ -42,7 +42,7 @@ static const char *const idle_names[] = {
|
||||
"neighbor",
|
||||
"mount",
|
||||
"partition",
|
||||
nullptr
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const char*const*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,8 +25,6 @@
|
||||
#ifndef MPD_IDLE_FLAGS_HXX
|
||||
#define MPD_IDLE_FLAGS_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
|
||||
/** song database has been updated*/
|
||||
static constexpr unsigned IDLE_DATABASE = 0x1;
|
||||
|
||||
@@ -73,7 +71,7 @@ static constexpr unsigned IDLE_PARTITION = 0x2000;
|
||||
/**
|
||||
* Get idle names
|
||||
*/
|
||||
gcc_const
|
||||
[[gnu::const]]
|
||||
const char*const*
|
||||
idle_get_names() noexcept;
|
||||
|
||||
@@ -81,7 +79,7 @@ idle_get_names() noexcept;
|
||||
* Parse an idle name and return its mask. Returns 0 if the given
|
||||
* name is unknown.
|
||||
*/
|
||||
gcc_nonnull_all gcc_pure
|
||||
[[gnu::nonnull]] [[gnu::pure]]
|
||||
unsigned
|
||||
idle_parse_name(const char *name) noexcept;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
@@ -37,6 +37,10 @@
|
||||
#include "db/update/Service.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
|
||||
#ifdef ENABLE_INOTIFY
|
||||
#include "db/update/InotifyUpdate.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
#include "neighbor/Glue.hxx"
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "event/Loop.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
#include "event/MaskMonitor.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
#include "lib/systemd/Watchdog.hxx"
|
||||
@@ -44,6 +43,9 @@ class NeighborGlue;
|
||||
#include "db/Ptr.hxx"
|
||||
class Storage;
|
||||
class UpdateService;
|
||||
#ifdef ENABLE_INOTIFY
|
||||
class InotifyUpdate;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
@@ -121,6 +123,10 @@ struct Instance final
|
||||
Storage *storage = nullptr;
|
||||
|
||||
UpdateService *update = nullptr;
|
||||
|
||||
#ifdef ENABLE_INOTIFY
|
||||
std::unique_ptr<InotifyUpdate> inotify_update;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
@@ -168,7 +174,7 @@ struct Instance final
|
||||
* Find a #Partition with the given name. Returns nullptr if
|
||||
* no such partition was found.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
Partition *FindPartition(const char *name) noexcept;
|
||||
|
||||
void DeletePartition(Partition &partition) noexcept;
|
||||
@@ -194,7 +200,7 @@ struct Instance final
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
bool HasStickerDatabase() noexcept {
|
||||
bool HasStickerDatabase() const noexcept {
|
||||
return sticker_database != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,13 +25,16 @@
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Option.hxx"
|
||||
#include "config/Net.hxx"
|
||||
#include "lib/fmt/ExceptionFormatter.hxx"
|
||||
#include "lib/fmt/PathFormatter.hxx"
|
||||
#include "net/AllocatedSocketAddress.hxx"
|
||||
#include "net/UniqueSocketDescriptor.hxx"
|
||||
#include "net/SocketUtil.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/XDG.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -41,6 +44,10 @@
|
||||
|
||||
#define DEFAULT_PORT 6600
|
||||
|
||||
#if defined(USE_XDG) && defined(HAVE_UN)
|
||||
static constexpr Domain listen_domain("listen");
|
||||
#endif
|
||||
|
||||
int listen_port;
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
@@ -98,9 +105,9 @@ ListenXdgRuntimeDir(ClientListener &listener) noexcept
|
||||
listener.AddFD(std::move(fd), std::move(address));
|
||||
return true;
|
||||
} catch (...) {
|
||||
FormatError(std::current_exception(),
|
||||
"Failed to listen on '%s' (not fatal)",
|
||||
socket_path.c_str());
|
||||
FmtError(listen_domain,
|
||||
"Failed to listen on '{}' (not fatal): {}",
|
||||
socket_path, std::current_exception());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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,6 +22,7 @@
|
||||
#include "client/Client.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "storage/Registry.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
|
||||
@@ -67,9 +68,13 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
{
|
||||
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");
|
||||
throw std::invalid_argument("Unsupported URI scheme");
|
||||
break;
|
||||
|
||||
case UriPluginKind::STORAGE:
|
||||
/* plugin support will be checked after the
|
||||
Storage::MapToRelativeUTF8() call */
|
||||
break;
|
||||
|
||||
case UriPluginKind::PLAYLIST:
|
||||
@@ -88,6 +93,10 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
||||
suffix.data());
|
||||
}
|
||||
|
||||
if (kind == UriPluginKind::STORAGE &&
|
||||
GetStoragePluginByUri(uri) == nullptr)
|
||||
throw std::invalid_argument("Unsupported URI scheme");
|
||||
#endif
|
||||
|
||||
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
|
||||
@@ -105,7 +114,7 @@ LocateUri(UriPluginKind kind,
|
||||
const char *path_utf8 = StringAfterPrefixCaseASCII(uri, "file://");
|
||||
if (path_utf8 != nullptr) {
|
||||
if (!PathTraitsUTF8::IsAbsolute(path_utf8))
|
||||
throw std::runtime_error("Malformed file:// URI");
|
||||
throw std::invalid_argument("Malformed file:// URI");
|
||||
|
||||
return LocateFileUri(path_utf8, client
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
|
161
src/Log.cxx
161
src/Log.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,166 +17,35 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "LogV.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "lib/fmt/ExceptionFormatter.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/Exception.hxx"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
static constexpr Domain exception_domain("exception");
|
||||
|
||||
void
|
||||
LogFormatV(LogLevel level, const Domain &domain,
|
||||
const char *fmt, std::va_list ap) noexcept
|
||||
LogVFmt(LogLevel level, const Domain &domain,
|
||||
fmt::string_view format_str, fmt::format_args args) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
Log(level, domain, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(level, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(LogLevel::DEBUG, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(LogLevel::INFO, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(LogLevel::NOTICE, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(LogLevel::WARNING, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatError(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
LogFormatV(LogLevel::ERROR, domain, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e) noexcept
|
||||
{
|
||||
Log(level, exception_domain, GetFullMessage(e).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e, const char *msg) noexcept
|
||||
{
|
||||
LogFormat(level, exception_domain, "%s: %s", msg, GetFullMessage(e).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
LogFormat(LogLevel level, const std::exception &e, const char *fmt, ...) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
Log(level, e, msg);
|
||||
fmt::memory_buffer buffer;
|
||||
#if FMT_VERSION >= 80000
|
||||
fmt::vformat_to(std::back_inserter(buffer), format_str, args);
|
||||
#else
|
||||
fmt::vformat_to(buffer, format_str, args);
|
||||
#endif
|
||||
Log(level, domain, {buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception_ptr &ep) noexcept
|
||||
{
|
||||
Log(level, exception_domain, GetFullMessage(ep).c_str());
|
||||
Log(level, exception_domain, GetFullMessage(ep));
|
||||
}
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept
|
||||
{
|
||||
LogFormat(level, exception_domain, "%s: %s", msg,
|
||||
GetFullMessage(ep).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
LogFormat(LogLevel level, const std::exception_ptr &ep, const char *fmt, ...) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
Log(level, ep, msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogErrno(const Domain &domain, int e, const char *msg) noexcept
|
||||
{
|
||||
LogFormat(LogLevel::ERROR, domain, "%s: %s", msg, strerror(e));
|
||||
}
|
||||
|
||||
void
|
||||
LogErrno(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
LogErrno(domain, errno, msg);
|
||||
}
|
||||
|
||||
static void
|
||||
FormatErrnoV(const Domain &domain, int e, const char *fmt, std::va_list ap) noexcept
|
||||
{
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
|
||||
LogErrno(domain, e, msg);
|
||||
}
|
||||
|
||||
void
|
||||
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
FormatErrnoV(domain, e, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept
|
||||
{
|
||||
const int e = errno;
|
||||
|
||||
std::va_list ap;
|
||||
va_start(ap, fmt);
|
||||
FormatErrnoV(domain, e, fmt, ap);
|
||||
va_end(ap);
|
||||
LogFmt(level, exception_domain, "{}: {}", msg, ep);
|
||||
}
|
||||
|
141
src/Log.hxx
141
src/Log.hxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,29 +21,80 @@
|
||||
#define MPD_LOG_HXX
|
||||
|
||||
#include "LogLevel.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#if FMT_VERSION < 70000 || FMT_VERSION >= 80000
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
class Domain;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const Domain &domain, const char *msg) noexcept;
|
||||
|
||||
gcc_printf(3,4)
|
||||
void
|
||||
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept;
|
||||
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept;
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e) noexcept;
|
||||
LogVFmt(LogLevel level, const Domain &domain,
|
||||
fmt::string_view format_str, fmt::format_args args) noexcept;
|
||||
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
Log(LogLevel level, const std::exception &e, const char *msg) noexcept;
|
||||
LogFmt(LogLevel level, const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
#if FMT_VERSION >= 70000
|
||||
return LogVFmt(level, domain, fmt::to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str,
|
||||
args...));
|
||||
#else
|
||||
/* expensive fallback for older libfmt versions */
|
||||
const auto result = fmt::format(format_str, args...);
|
||||
return Log(level, domain, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
gcc_printf(3,4)
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
LogFormat(LogLevel level, const std::exception &e,
|
||||
const char *fmt, ...) noexcept;
|
||||
FmtDebug(const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
LogFmt(LogLevel::DEBUG, domain, format_str, args...);
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
FmtInfo(const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
LogFmt(LogLevel::INFO, domain, format_str, args...);
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
FmtNotice(const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
LogFmt(LogLevel::NOTICE, domain, format_str, args...);
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
FmtWarning(const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
LogFmt(LogLevel::WARNING, domain, format_str, args...);
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
void
|
||||
FmtError(const Domain &domain,
|
||||
const S &format_str, Args&&... args) noexcept
|
||||
{
|
||||
LogFmt(LogLevel::ERROR, domain, format_str, args...);
|
||||
}
|
||||
|
||||
void
|
||||
Log(LogLevel level, const std::exception_ptr &ep) noexcept;
|
||||
@@ -51,76 +102,36 @@ 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(LogLevel::DEBUG, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogInfo(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::INFO, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogNotice(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::NOTICE, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogWarning(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::WARNING, domain, msg);
|
||||
}
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
static inline void
|
||||
LogError(const Domain &domain, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, domain, msg);
|
||||
}
|
||||
|
||||
inline void
|
||||
LogError(const std::exception &e) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, e);
|
||||
}
|
||||
|
||||
inline void
|
||||
LogError(const std::exception &e, const char *msg) noexcept
|
||||
{
|
||||
Log(LogLevel::ERROR, e, msg);
|
||||
}
|
||||
|
||||
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)...);
|
||||
}
|
||||
|
||||
inline void
|
||||
LogError(const std::exception_ptr &ep) noexcept
|
||||
{
|
||||
@@ -133,30 +144,4 @@ LogError(const std::exception_ptr &ep, const char *msg) noexcept
|
||||
Log(LogLevel::ERROR, ep, msg);
|
||||
}
|
||||
|
||||
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
|
||||
FormatError(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
void
|
||||
LogErrno(const Domain &domain, int e, const char *msg) noexcept;
|
||||
|
||||
void
|
||||
LogErrno(const Domain &domain, const char *msg) noexcept;
|
||||
|
||||
gcc_printf(3,4)
|
||||
void
|
||||
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept;
|
||||
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept;
|
||||
|
||||
#endif /* LOG_H */
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
@@ -103,10 +103,9 @@ log_date() noexcept
|
||||
* characters.
|
||||
*/
|
||||
static int
|
||||
chomp_length(const char *p) noexcept
|
||||
chomp_length(std::string_view p) noexcept
|
||||
{
|
||||
size_t length = strlen(p);
|
||||
return StripRight(p, length);
|
||||
return StripRight(p.data(), p.size());
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
@@ -137,11 +136,11 @@ ToSysLogLevel(LogLevel log_level) noexcept
|
||||
}
|
||||
|
||||
static void
|
||||
SysLog(const Domain &domain, LogLevel log_level, const char *message) noexcept
|
||||
SysLog(const Domain &domain, LogLevel log_level, std::string_view message) noexcept
|
||||
{
|
||||
syslog(ToSysLogLevel(log_level), "%s: %.*s",
|
||||
domain.GetName(),
|
||||
chomp_length(message), message);
|
||||
chomp_length(message), message.data());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -161,12 +160,12 @@ LogFinishSysLog() noexcept
|
||||
#endif
|
||||
|
||||
static void
|
||||
FileLog(const Domain &domain, const char *message) noexcept
|
||||
FileLog(const Domain &domain, std::string_view message) noexcept
|
||||
{
|
||||
fprintf(stderr, "%s%s: %.*s\n",
|
||||
enable_timestamp ? log_date() : "",
|
||||
domain.GetName(),
|
||||
chomp_length(message), message);
|
||||
chomp_length(message), message.data());
|
||||
|
||||
#ifdef _WIN32
|
||||
/* force-flush the log file, because setvbuf() does not seem
|
||||
@@ -178,14 +177,20 @@ FileLog(const Domain &domain, const char *message) noexcept
|
||||
#endif /* !ANDROID */
|
||||
|
||||
void
|
||||
Log(LogLevel level, const Domain &domain, const char *msg) noexcept
|
||||
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ToAndroidLogLevel(level), "MPD",
|
||||
"%s: %s", domain.GetName(), msg);
|
||||
if (logListener != nullptr)
|
||||
"%s: %.*s", domain.GetName(),
|
||||
(int)msg.size(), msg.data());
|
||||
if (logListener != nullptr) {
|
||||
char buffer[1024];
|
||||
snprintf(buffer, sizeof(buffer), "%s: %.*s",
|
||||
domain.GetName(), (int)msg.size(), msg.data());
|
||||
|
||||
logListener->OnLog(Java::GetEnv(), ToAndroidLogLevel(level),
|
||||
"%s: %s", domain.GetName(), msg);
|
||||
buffer);
|
||||
}
|
||||
#else
|
||||
|
||||
if (level < log_threshold)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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
|
||||
@@ -234,22 +234,22 @@ cycle_log_files() noexcept
|
||||
if (out_path.IsNull())
|
||||
return 0;
|
||||
|
||||
FormatDebug(log_domain, "Cycling log files");
|
||||
LogDebug(log_domain, "Cycling log files");
|
||||
close_log_files();
|
||||
|
||||
fd = open_log_file();
|
||||
if (fd < 0) {
|
||||
const std::string out_path_utf8 = out_path.ToUTF8();
|
||||
FormatError(log_domain,
|
||||
"error re-opening log file: %s",
|
||||
out_path_utf8.c_str());
|
||||
FmtError(log_domain,
|
||||
"error re-opening log file: {}",
|
||||
out_path_utf8);
|
||||
return -1;
|
||||
}
|
||||
|
||||
redirect_logs(fd);
|
||||
close(fd);
|
||||
|
||||
FormatDebug(log_domain, "Done cycling log files");
|
||||
LogDebug(log_domain, "Done cycling log files");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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
|
||||
|
111
src/Main.cxx
111
src/Main.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
@@ -40,10 +40,11 @@
|
||||
#include "input/cache/Config.hxx"
|
||||
#include "input/cache/Manager.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "event/Call.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Config.hxx"
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "zeroconf/ZeroconfGlue.hxx"
|
||||
#include "zeroconf/Glue.hxx"
|
||||
#include "decoder/DecoderList.hxx"
|
||||
#include "pcm/AudioParser.hxx"
|
||||
#include "pcm/Convert.hxx"
|
||||
@@ -111,7 +112,7 @@
|
||||
|
||||
#include <climits>
|
||||
|
||||
#ifdef HAVE_CLOCALE
|
||||
#ifndef ANDROID
|
||||
#include <clocale>
|
||||
#endif
|
||||
|
||||
@@ -157,7 +158,17 @@ glue_daemonize_init(const struct options *options,
|
||||
static void
|
||||
glue_mapper_init(const ConfigData &config)
|
||||
{
|
||||
mapper_init(config.GetPath(ConfigOption::PLAYLIST_DIR));
|
||||
auto playlist_directory = config.GetPath(ConfigOption::PLAYLIST_DIR);
|
||||
|
||||
#ifdef ANDROID
|
||||
/* if there is no explicit configuration, store playlists in
|
||||
"/sdcard/Android/data/org.musicpd/files/playlists" */
|
||||
if (playlist_directory.IsNull())
|
||||
playlist_directory = context->GetExternalFilesDir(Java::GetEnv(),
|
||||
"playlists");
|
||||
#endif
|
||||
|
||||
mapper_init(std::move(playlist_directory));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
@@ -286,9 +297,8 @@ initialize_decoder_and_player(Instance &instance,
|
||||
"positive integer", s);
|
||||
|
||||
if (result < MIN_BUFFER_SIZE) {
|
||||
FormatWarning(config_domain, "buffer size %lu is too small, using %lu bytes instead",
|
||||
(unsigned long)result,
|
||||
(unsigned long)MIN_BUFFER_SIZE);
|
||||
FmtWarning(config_domain, "buffer size {} is too small, using {} bytes instead",
|
||||
result, MIN_BUFFER_SIZE);
|
||||
result = MIN_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
@@ -334,7 +344,7 @@ Instance::BeginShutdownUpdate() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
#ifdef ENABLE_INOTIFY
|
||||
mpd_inotify_finish();
|
||||
inotify_update.reset();
|
||||
#endif
|
||||
|
||||
if (update != nullptr)
|
||||
@@ -358,11 +368,9 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
#endif
|
||||
|
||||
#ifndef ANDROID
|
||||
#ifdef HAVE_CLOCALE
|
||||
/* initialize locale */
|
||||
std::setlocale(LC_CTYPE,"");
|
||||
std::setlocale(LC_COLLATE, "");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const ScopeIcuInit icu_init;
|
||||
@@ -443,7 +451,8 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
command_init();
|
||||
|
||||
for (auto &partition : instance.partitions) {
|
||||
partition.outputs.Configure(instance.rtio_thread.GetEventLoop(),
|
||||
partition.outputs.Configure(instance.io_thread.GetEventLoop(),
|
||||
instance.rtio_thread.GetEventLoop(),
|
||||
raw_config,
|
||||
config.replay_gain);
|
||||
partition.UpdateEffectiveReplayGainMode();
|
||||
@@ -478,7 +487,27 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
};
|
||||
#endif
|
||||
|
||||
ZeroconfInit(raw_config, instance.event_loop);
|
||||
#ifdef HAVE_ZEROCONF
|
||||
std::unique_ptr<ZeroconfHelper> zeroconf;
|
||||
try {
|
||||
auto &event_loop = instance.io_thread.GetEventLoop();
|
||||
BlockingCall(event_loop, [&](){
|
||||
zeroconf = ZeroconfInit(raw_config, event_loop);
|
||||
});
|
||||
} catch (...) {
|
||||
LogError(std::current_exception(),
|
||||
"Zeroconf initialization failed");
|
||||
}
|
||||
|
||||
AtScopeExit(&zeroconf, &instance) {
|
||||
if (zeroconf) {
|
||||
auto &event_loop = instance.io_thread.GetEventLoop();
|
||||
BlockingCall(event_loop, [&](){
|
||||
zeroconf.reset();
|
||||
});
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (create_db) {
|
||||
@@ -494,15 +523,21 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
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,
|
||||
raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
|
||||
INT_MAX));
|
||||
instance.update != nullptr) {
|
||||
try {
|
||||
instance.inotify_update =
|
||||
mpd_inotify_init(instance.event_loop,
|
||||
*instance.storage,
|
||||
*instance.update,
|
||||
raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
|
||||
INT_MAX));
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
}
|
||||
}
|
||||
#else
|
||||
FormatWarning(config_domain,
|
||||
"inotify: auto_update was disabled. enable during compilation phase");
|
||||
LogWarning(config_domain,
|
||||
"inotify: auto_update was disabled. enable during compilation phase");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -535,10 +570,10 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
|
||||
|
||||
/* cleanup */
|
||||
|
||||
if (instance.state_file)
|
||||
instance.state_file->Write();
|
||||
|
||||
instance.BeginShutdownUpdate();
|
||||
|
||||
ZeroconfDeinit();
|
||||
|
||||
instance.BeginShutdownPartitions();
|
||||
}
|
||||
|
||||
@@ -593,6 +628,15 @@ Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
|
||||
global_instance->Break();
|
||||
}
|
||||
|
||||
gcc_visibility_default
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_musicpd_Bridge_pause(JNIEnv *, jclass)
|
||||
{
|
||||
if (global_instance != nullptr)
|
||||
for (auto &partition : global_instance->partitions)
|
||||
partition.pc.LockSetPause(true);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
@@ -606,27 +650,26 @@ MainOrThrow(int argc, char *argv[])
|
||||
MainConfigured(options, raw_config);
|
||||
}
|
||||
|
||||
int mpd_main(int argc, char *argv[]) noexcept
|
||||
int
|
||||
mpd_main(int argc, char *argv[])
|
||||
{
|
||||
AtScopeExit() { log_deinit(); };
|
||||
|
||||
try {
|
||||
MainOrThrow(argc, argv);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
MainOrThrow(argc, argv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) noexcept
|
||||
{
|
||||
try {
|
||||
AtScopeExit() { log_deinit(); };
|
||||
|
||||
#ifdef _WIN32
|
||||
return win32_main(argc, argv);
|
||||
#else
|
||||
return mpd_main(argc, argv);
|
||||
#endif
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
@@ -40,7 +40,7 @@ extern Instance *global_instance;
|
||||
* after doing some initialization.
|
||||
*/
|
||||
int
|
||||
mpd_main(int argc, char *argv[]) noexcept;
|
||||
mpd_main(int argc, char *argv[]);
|
||||
|
||||
#endif
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,7 +24,6 @@
|
||||
#ifndef MPD_MAPPER_HXX
|
||||
#define MPD_MAPPER_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
@@ -44,7 +43,7 @@ mapper_init(AllocatedPath &&playlist_dir);
|
||||
* is basically done by converting the URI to the file system charset
|
||||
* and prepending the music directory.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
AllocatedPath
|
||||
map_uri_fs(const char *uri) noexcept;
|
||||
|
||||
@@ -56,7 +55,7 @@ map_uri_fs(const char *uri) noexcept;
|
||||
* @return the relative path in UTF-8, or an empty string if mapping
|
||||
* failed
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
std::string
|
||||
map_fs_to_utf8(Path path_fs) noexcept;
|
||||
|
||||
@@ -65,7 +64,7 @@ map_fs_to_utf8(Path path_fs) noexcept;
|
||||
/**
|
||||
* Returns the playlist directory.
|
||||
*/
|
||||
gcc_const
|
||||
[[gnu::const]]
|
||||
const AllocatedPath &
|
||||
map_spl_path() noexcept;
|
||||
|
||||
@@ -75,7 +74,7 @@ map_spl_path() noexcept;
|
||||
*
|
||||
* @return the path in file system encoding, or nullptr if mapping failed
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
AllocatedPath
|
||||
map_spl_utf8_to_fs(const char *name) noexcept;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,6 @@
|
||||
#ifndef MPD_MIX_RAMP_INFO_HXX
|
||||
#define MPD_MIX_RAMP_INFO_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class MixRampInfo {
|
||||
@@ -35,17 +33,17 @@ public:
|
||||
end.clear();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool IsDefined() const noexcept {
|
||||
return !start.empty() || !end.empty();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
const char *GetStart() const noexcept {
|
||||
return start.empty() ? nullptr : start.c_str();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
const char *GetEnd() const noexcept {
|
||||
return end.empty() ? nullptr : end.c_str();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
* is the same value which was passed to the constructor
|
||||
* music_buffer_new().
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
unsigned GetSize() const noexcept {
|
||||
return buffer.GetCapacity();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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
|
||||
@@ -105,7 +105,7 @@ struct MusicChunkInfo {
|
||||
* Checks if the audio format if the chunk is equal to the
|
||||
* specified audio_format.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool CheckFormat(AudioFormat audio_format) const noexcept;
|
||||
#endif
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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-2020 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,7 +22,6 @@
|
||||
|
||||
#include "MusicChunkPtr.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
@@ -59,7 +58,7 @@ public:
|
||||
* Checks if the audio format if the chunk is equal to the specified
|
||||
* audio_format.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool CheckFormat(AudioFormat other) const noexcept {
|
||||
return !audio_format.IsDefined() ||
|
||||
audio_format == other;
|
||||
@@ -68,7 +67,7 @@ public:
|
||||
/**
|
||||
* Checks if the specified chunk is enqueued in the music pipe.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool Contains(const MusicChunk *chunk) const noexcept;
|
||||
#endif
|
||||
|
||||
@@ -76,7 +75,7 @@ public:
|
||||
* Returns the first #MusicChunk from the pipe. Returns
|
||||
* nullptr if the pipe is empty.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
const MusicChunk *Peek() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return head.get();
|
||||
@@ -100,13 +99,13 @@ public:
|
||||
/**
|
||||
* Returns the number of chunks currently in this pipe.
|
||||
*/
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
unsigned GetSize() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return size;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool IsEmpty() const noexcept {
|
||||
return GetSize() == 0;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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 @@
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "lib/fmt/ExceptionFormatter.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
@@ -67,13 +68,14 @@ PrefetchSong(InputCacheManager &cache, const char *uri) noexcept
|
||||
if (cache.Contains(uri))
|
||||
return;
|
||||
|
||||
FormatDebug(cache_domain, "Prefetch '%s'", uri);
|
||||
FmtDebug(cache_domain, "Prefetch '{}'", uri);
|
||||
|
||||
try {
|
||||
cache.Prefetch(uri);
|
||||
} catch (...) {
|
||||
FormatError(std::current_exception(),
|
||||
"Prefetch '%s' failed", uri);
|
||||
FmtError(cache_domain,
|
||||
"Prefetch '{}' failed: {}",
|
||||
uri, std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,6 +27,7 @@
|
||||
#include "mixer/Listener.hxx"
|
||||
#include "player/Control.hxx"
|
||||
#include "player/Listener.hxx"
|
||||
#include "protocol/RangeArg.hxx"
|
||||
#include "ReplayGainMode.hxx"
|
||||
#include "SingleMode.hxx"
|
||||
#include "Chrono.hxx"
|
||||
@@ -38,6 +39,7 @@
|
||||
#include <memory>
|
||||
|
||||
struct Instance;
|
||||
struct RangeArg;
|
||||
class MultipleOutputs;
|
||||
class SongLoader;
|
||||
class ClientListener;
|
||||
@@ -133,24 +135,20 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
* @param start the position of the first song to delete
|
||||
* @param end the position after the last song to delete
|
||||
*/
|
||||
void DeleteRange(unsigned start, unsigned end) {
|
||||
playlist.DeleteRange(pc, start, end);
|
||||
void DeleteRange(RangeArg range) {
|
||||
playlist.DeleteRange(pc, range);
|
||||
}
|
||||
|
||||
void StaleSong(const char *uri) noexcept {
|
||||
playlist.StaleSong(pc, uri);
|
||||
}
|
||||
|
||||
void Shuffle(unsigned start, unsigned end) noexcept {
|
||||
playlist.Shuffle(pc, start, end);
|
||||
void Shuffle(RangeArg range) {
|
||||
playlist.Shuffle(pc, range);
|
||||
}
|
||||
|
||||
void MoveRange(unsigned start, unsigned end, int to) {
|
||||
playlist.MoveRange(pc, start, end, to);
|
||||
}
|
||||
|
||||
void MoveId(unsigned id, int to) {
|
||||
playlist.MoveId(pc, id, to);
|
||||
void MoveRange(RangeArg range, unsigned to) {
|
||||
playlist.MoveRange(pc, range, to);
|
||||
}
|
||||
|
||||
void SwapPositions(unsigned song1, unsigned song2) {
|
||||
@@ -161,10 +159,8 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
playlist.SwapIds(pc, id1, id2);
|
||||
}
|
||||
|
||||
void SetPriorityRange(unsigned start_position, unsigned end_position,
|
||||
uint8_t priority) {
|
||||
playlist.SetPriorityRange(pc, start_position, end_position,
|
||||
priority);
|
||||
void SetPriorityRange(RangeArg position_range, uint8_t priority) {
|
||||
playlist.SetPriorityRange(pc, position_range, priority);
|
||||
}
|
||||
|
||||
void SetPriorityId(unsigned song_id, uint8_t priority) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,6 +22,9 @@
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Option.hxx"
|
||||
#include "net/AddressInfo.hxx"
|
||||
#include "net/Resolver.hxx"
|
||||
#include "net/ToString.hxx"
|
||||
#include "util/IterableSplitString.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
@@ -41,6 +44,7 @@ static constexpr struct {
|
||||
} permission_names[] = {
|
||||
{ "read", PERMISSION_READ },
|
||||
{ "add", PERMISSION_ADD },
|
||||
{ "player", PERMISSION_PLAYER },
|
||||
{ "control", PERMISSION_CONTROL },
|
||||
{ "admin", PERMISSION_ADMIN },
|
||||
{ nullptr, 0 },
|
||||
@@ -54,6 +58,10 @@ static unsigned permission_default;
|
||||
static unsigned local_permissions;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
static std::map<std::string, unsigned> host_passwords;
|
||||
#endif
|
||||
|
||||
static unsigned
|
||||
ParsePermission(StringView s)
|
||||
{
|
||||
@@ -65,16 +73,20 @@ ParsePermission(StringView s)
|
||||
int(s.size), s.data);
|
||||
}
|
||||
|
||||
static unsigned parsePermissions(const char *string)
|
||||
static unsigned
|
||||
parsePermissions(std::string_view string)
|
||||
{
|
||||
assert(string != nullptr);
|
||||
|
||||
unsigned permission = 0;
|
||||
|
||||
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
|
||||
if (!i.empty())
|
||||
permission |= ParsePermission(i);
|
||||
|
||||
/* for backwards compatiblity with MPD 0.22 and older,
|
||||
"control" implies "play" */
|
||||
if (permission & PERMISSION_CONTROL)
|
||||
permission |= PERMISSION_PLAYER;
|
||||
|
||||
return permission;
|
||||
}
|
||||
|
||||
@@ -82,6 +94,7 @@ void
|
||||
initPermissions(const ConfigData &config)
|
||||
{
|
||||
permission_default = PERMISSION_READ | PERMISSION_ADD |
|
||||
PERMISSION_PLAYER |
|
||||
PERMISSION_CONTROL | PERMISSION_ADMIN;
|
||||
|
||||
for (const auto ¶m : config.GetParamList(ConfigOption::PASSWORD)) {
|
||||
@@ -98,8 +111,7 @@ initPermissions(const ConfigData &config)
|
||||
std::string password(value, separator);
|
||||
|
||||
unsigned permission = parsePermissions(separator + 1);
|
||||
permission_passwords.insert(std::make_pair(std::move(password),
|
||||
permission));
|
||||
permission_passwords.emplace(std::move(password), permission);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,17 +127,48 @@ initPermissions(const ConfigData &config)
|
||||
: permission_default;
|
||||
});
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
for (const auto ¶m : config.GetParamList(ConfigOption::HOST_PERMISSIONS)) {
|
||||
permission_default = 0;
|
||||
|
||||
param.With([](StringView value){
|
||||
auto [host_sv, permissions_s] = value.Split(' ');
|
||||
unsigned permissions = parsePermissions(permissions_s);
|
||||
|
||||
const std::string host_s{host_sv};
|
||||
|
||||
for (const auto &i : Resolve(host_s.c_str(), 0,
|
||||
AI_PASSIVE, SOCK_STREAM))
|
||||
host_passwords.emplace(HostToString(i),
|
||||
permissions);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
|
||||
int
|
||||
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
|
||||
GetPermissionsFromAddress(SocketAddress address) noexcept
|
||||
{
|
||||
if (auto i = host_passwords.find(HostToString(address));
|
||||
i != host_passwords.end())
|
||||
return i->second;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::optional<unsigned>
|
||||
GetPermissionFromPassword(const char *password) noexcept
|
||||
{
|
||||
auto i = permission_passwords.find(password);
|
||||
if (i == permission_passwords.end())
|
||||
return -1;
|
||||
return std::nullopt;
|
||||
|
||||
*permission = i->second;
|
||||
return 0;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
unsigned
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,25 +22,42 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
struct ConfigData;
|
||||
class SocketAddress;
|
||||
|
||||
static constexpr unsigned PERMISSION_NONE = 0;
|
||||
static constexpr unsigned PERMISSION_READ = 1;
|
||||
static constexpr unsigned PERMISSION_ADD = 2;
|
||||
static constexpr unsigned PERMISSION_CONTROL = 4;
|
||||
static constexpr unsigned PERMISSION_ADMIN = 8;
|
||||
static constexpr unsigned PERMISSION_PLAYER = 16;
|
||||
|
||||
int
|
||||
getPermissionFromPassword(const char *password, unsigned *permission) noexcept;
|
||||
/**
|
||||
* @return the permissions for the given password or std::nullopt if
|
||||
* the password is not accepted
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
std::optional<unsigned>
|
||||
GetPermissionFromPassword(const char *password) noexcept;
|
||||
|
||||
[[gnu::const]]
|
||||
unsigned
|
||||
getDefaultPermissions() noexcept;
|
||||
|
||||
#ifdef HAVE_UN
|
||||
[[gnu::const]]
|
||||
unsigned
|
||||
GetLocalPermissions() noexcept;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
[[gnu::pure]]
|
||||
int
|
||||
GetPermissionsFromAddress(SocketAddress address) noexcept;
|
||||
#endif
|
||||
|
||||
void
|
||||
initPermissions(const ConfigData &config);
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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,6 +22,7 @@
|
||||
#include "PlaylistError.hxx"
|
||||
#include "queue/Playlist.hxx"
|
||||
#include "queue/QueuePrint.hxx"
|
||||
#include "protocol/RangeArg.hxx"
|
||||
|
||||
#define SONG_FILE "file: "
|
||||
#define SONG_TIME "Time: "
|
||||
@@ -35,20 +36,17 @@ playlist_print_uris(Response &r, const playlist &playlist)
|
||||
}
|
||||
|
||||
void
|
||||
playlist_print_info(Response &r, const playlist &playlist,
|
||||
unsigned start, unsigned end)
|
||||
playlist_print_info(Response &r, const playlist &playlist, RangeArg range)
|
||||
{
|
||||
const Queue &queue = playlist.queue;
|
||||
|
||||
if (end > queue.GetLength())
|
||||
/* correct the "end" offset */
|
||||
end = queue.GetLength();
|
||||
|
||||
if (start > end)
|
||||
/* an invalid "start" offset is fatal */
|
||||
if (!range.CheckClip(queue.GetLength()))
|
||||
throw PlaylistError::BadRange();
|
||||
|
||||
queue_print_info(r, queue, start, end);
|
||||
if (range.IsEmpty())
|
||||
return;
|
||||
|
||||
queue_print_info(r, queue, range.start, range.end);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -62,7 +60,7 @@ playlist_print_id(Response &r, const playlist &playlist,
|
||||
/* no such song */
|
||||
throw PlaylistError::NoSuchSong();
|
||||
|
||||
playlist_print_info(r, playlist, position, position + 1);
|
||||
playlist_print_info(r, playlist, {unsigned(position), position + 1U});
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -87,18 +85,24 @@ playlist_print_find(Response &r, const playlist &playlist,
|
||||
void
|
||||
playlist_print_changes_info(Response &r, const playlist &playlist,
|
||||
uint32_t version,
|
||||
unsigned start, unsigned end)
|
||||
RangeArg range)
|
||||
{
|
||||
queue_print_changes_info(r, playlist.queue, version,
|
||||
start, end);
|
||||
const Queue &queue = playlist.queue;
|
||||
range.ClipRelaxed(queue.GetLength());
|
||||
|
||||
queue_print_changes_info(r, queue, version,
|
||||
range.start, range.end);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_print_changes_position(Response &r,
|
||||
const playlist &playlist,
|
||||
uint32_t version,
|
||||
unsigned start, unsigned end)
|
||||
RangeArg range)
|
||||
{
|
||||
const Queue &queue = playlist.queue;
|
||||
range.ClipRelaxed(queue.GetLength());
|
||||
|
||||
queue_print_changes_position(r, playlist.queue, version,
|
||||
start, end);
|
||||
range.start, range.end);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
struct playlist;
|
||||
struct RangeArg;
|
||||
class SongFilter;
|
||||
class Response;
|
||||
|
||||
@@ -41,8 +42,7 @@ playlist_print_uris(Response &r, const playlist &playlist);
|
||||
* Throws #PlaylistError if the range is invalid.
|
||||
*/
|
||||
void
|
||||
playlist_print_info(Response &r, const playlist &playlist,
|
||||
unsigned start, unsigned end);
|
||||
playlist_print_info(Response &r, const playlist &playlist, RangeArg range);
|
||||
|
||||
/**
|
||||
* Sends the song with the specified id to the client.
|
||||
@@ -73,7 +73,7 @@ playlist_print_find(Response &r, const playlist &playlist,
|
||||
void
|
||||
playlist_print_changes_info(Response &r, const playlist &playlist,
|
||||
uint32_t version,
|
||||
unsigned start, unsigned end);
|
||||
RangeArg range);
|
||||
|
||||
/**
|
||||
* Print changes since the specified playlist version, position only.
|
||||
@@ -82,6 +82,6 @@ void
|
||||
playlist_print_changes_position(Response &r,
|
||||
const playlist &playlist,
|
||||
uint32_t version,
|
||||
unsigned start, unsigned end);
|
||||
RangeArg range);
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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-2020 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,10 +19,14 @@
|
||||
|
||||
#include "RemoteTagCache.hxx"
|
||||
#include "RemoteTagCacheHandler.hxx"
|
||||
#include "lib/fmt/ExceptionFormatter.hxx"
|
||||
#include "input/ScanTags.hxx"
|
||||
#include "util/DeleteDisposer.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
static constexpr Domain remote_tag_cache_domain("remote_tag_cache");
|
||||
|
||||
RemoteTagCache::RemoteTagCache(EventLoop &event_loop,
|
||||
RemoteTagCacheHandler &_handler) noexcept
|
||||
:handler(_handler),
|
||||
@@ -42,9 +46,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();
|
||||
@@ -60,9 +64,9 @@ RemoteTagCache::Lookup(const std::string &uri) noexcept
|
||||
|
||||
item->scanner->Start();
|
||||
} catch (...) {
|
||||
FormatError(std::current_exception(),
|
||||
"Failed to scan tags of '%s'",
|
||||
uri.c_str());
|
||||
FmtError(remote_tag_cache_domain,
|
||||
"Failed to scan tags of '{}': {}",
|
||||
uri, std::current_exception());
|
||||
|
||||
item->scanner.reset();
|
||||
|
||||
@@ -70,15 +74,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();
|
||||
}
|
||||
@@ -130,7 +132,8 @@ RemoteTagCache::Item::OnRemoteTag(Tag &&_tag) noexcept
|
||||
void
|
||||
RemoteTagCache::Item::OnRemoteTagError(std::exception_ptr e) noexcept
|
||||
{
|
||||
FormatError(e, "Failed to scan tags of '%s'", uri.c_str());
|
||||
FmtError(remote_tag_cache_domain,
|
||||
"Failed to scan tags of '{}': {}", uri, e);
|
||||
|
||||
scanner.reset();
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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,7 +22,7 @@
|
||||
|
||||
#include "input/RemoteTagScanner.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "event/DeferEvent.hxx"
|
||||
#include "event/InjectEvent.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
@@ -40,7 +40,7 @@ class RemoteTagCache final {
|
||||
|
||||
RemoteTagCacheHandler &handler;
|
||||
|
||||
DeferEvent defer_invoke_handler;
|
||||
InjectEvent defer_invoke_handler;
|
||||
|
||||
Mutex mutex;
|
||||
|
||||
@@ -68,20 +68,20 @@ class RemoteTagCache final {
|
||||
struct Hash : std::hash<std::string> {
|
||||
using std::hash<std::string>::operator();
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
std::size_t operator()(const Item &item) const noexcept {
|
||||
return std::hash<std::string>::operator()(item.uri);
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool operator()(const Item &a,
|
||||
const Item &b) const noexcept {
|
||||
return a.uri == b.uri;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
[[gnu::pure]]
|
||||
bool operator()(const std::string &a,
|
||||
const Item &b) const noexcept {
|
||||
return a == b.uri;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2020 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-2020 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-2020 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-2020 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-2020 The Music Player Daemon Project
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user