Compare commits
893 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
76e3dec723 | ||
![]() |
ba6ef53ef9 | ||
![]() |
c93a28c641 | ||
![]() |
7088a679a2 | ||
![]() |
04c02a1eb8 | ||
![]() |
41487426f5 | ||
![]() |
0d24250aa7 | ||
![]() |
2050e2f886 | ||
![]() |
013e8479af | ||
![]() |
27535a7f78 | ||
![]() |
acaa725478 | ||
![]() |
f351550534 | ||
![]() |
66ecf39efe | ||
![]() |
5ad21d7e98 | ||
![]() |
ef5125f8f4 | ||
![]() |
bf2e07074b | ||
![]() |
20695ef369 | ||
![]() |
9374e0f445 | ||
![]() |
19ed233118 | ||
![]() |
faa4fff4dd | ||
![]() |
2276e7677b | ||
![]() |
93f9c2ab6b | ||
![]() |
4a993cd79e | ||
![]() |
02325d2ede | ||
![]() |
9c83464b95 | ||
![]() |
b1bbd70f0f | ||
![]() |
c31d11bfe0 | ||
![]() |
c8ec85d649 | ||
![]() |
e291f3d257 | ||
![]() |
dc22846d58 | ||
![]() |
c9aaabb5d4 | ||
![]() |
335d5d5d72 | ||
![]() |
51d793bec1 | ||
![]() |
249dcd967e | ||
![]() |
302972e9fc | ||
![]() |
31b380b266 | ||
![]() |
a869dfea85 | ||
![]() |
12838c6294 | ||
![]() |
49c7102547 | ||
![]() |
1ae8972859 | ||
![]() |
adcd2c8eac | ||
![]() |
45ff355835 | ||
![]() |
f8bf3afeae | ||
![]() |
f703da1516 | ||
![]() |
a582deee2c | ||
![]() |
12be9e818f | ||
![]() |
281cd7c057 | ||
![]() |
63e8766091 | ||
![]() |
18da582c87 | ||
![]() |
0562cf99ba | ||
![]() |
60ac702038 | ||
![]() |
1dedb96478 | ||
![]() |
7537722a44 | ||
![]() |
4ebd69193e | ||
![]() |
d4d92ac1a7 | ||
![]() |
5385d1fa80 | ||
![]() |
dbee2f1996 | ||
![]() |
6dd70926fa | ||
![]() |
5dfc0918c3 | ||
![]() |
2eb14658d3 | ||
![]() |
c1f196dbec | ||
![]() |
ce108acebb | ||
![]() |
0555e2c781 | ||
![]() |
6bfd1f1727 | ||
![]() |
baa77c8ae3 | ||
![]() |
7235dbadfd | ||
![]() |
ecec41025f | ||
![]() |
799843cc97 | ||
![]() |
16e91baa79 | ||
![]() |
f2536445f7 | ||
![]() |
ede70ee3a4 | ||
![]() |
66ed427a57 | ||
![]() |
c9e63e9fdb | ||
![]() |
ddf7f5c131 | ||
![]() |
660e40d07e | ||
![]() |
9f3db5a70b | ||
![]() |
1a59730782 | ||
![]() |
20029e7ce8 | ||
![]() |
fcb7950811 | ||
![]() |
4eb57e1e9a | ||
![]() |
1d52e2cc77 | ||
![]() |
209aceeb14 | ||
![]() |
ae70875f45 | ||
![]() |
eda7410f4c | ||
![]() |
e60141b4dd | ||
![]() |
5b21742095 | ||
![]() |
5665de5ee7 | ||
![]() |
d5b9be0393 | ||
![]() |
d2d9b45a81 | ||
![]() |
9ebbdb9b0b | ||
![]() |
48da345e79 | ||
![]() |
edbfa46cbc | ||
![]() |
90709a6de4 | ||
![]() |
992c2fa2d4 | ||
![]() |
ed915fed92 | ||
![]() |
8aa29d5a66 | ||
![]() |
9604e0aad2 | ||
![]() |
25d053cbf2 | ||
![]() |
055257a210 | ||
![]() |
50cfb997cc | ||
![]() |
d662c4c0cc | ||
![]() |
457d98c860 | ||
![]() |
6bb166aaaa | ||
![]() |
cca9bc5176 | ||
![]() |
8b6b25220d | ||
![]() |
3c3f1b7ff2 | ||
![]() |
57d89131e9 | ||
![]() |
7c6d1896a4 | ||
![]() |
ad83c7f704 | ||
![]() |
6b52d040b1 | ||
![]() |
404fa89937 | ||
![]() |
89377556cd | ||
![]() |
ec93114347 | ||
![]() |
df1c5ce314 | ||
![]() |
a0e4b6e266 | ||
![]() |
0de39b64cb | ||
![]() |
36a7b4e275 | ||
![]() |
d3cc8e0ac0 | ||
![]() |
db1ce4eeeb | ||
![]() |
c22cbbf828 | ||
![]() |
5acee73fc8 | ||
![]() |
466c337bcb | ||
![]() |
98a468a101 | ||
![]() |
47c58c01d1 | ||
![]() |
a9edf85a69 | ||
![]() |
e7a1862517 | ||
![]() |
f930f37d35 | ||
![]() |
4feb57e895 | ||
![]() |
712e3eb120 | ||
![]() |
d8e423df1a | ||
![]() |
09aa0dc676 | ||
![]() |
83174de420 | ||
![]() |
8ff0197a43 | ||
![]() |
a2b5db0003 | ||
![]() |
2803ec2e96 | ||
![]() |
ddd4f675a2 | ||
![]() |
d271dd2cce | ||
![]() |
ebfdd37451 | ||
![]() |
f6d6110aaa | ||
![]() |
a0730cf264 | ||
![]() |
ec61b3a5fc | ||
![]() |
521f2294cb | ||
![]() |
2f0674807c | ||
![]() |
de0f46b947 | ||
![]() |
1a57fa095f | ||
![]() |
167242fec0 | ||
![]() |
81208d78ac | ||
![]() |
da8b01771f | ||
![]() |
725fbe946b | ||
![]() |
b99ecb4dc9 | ||
![]() |
208a96b211 | ||
![]() |
43774455cc | ||
![]() |
5b61e077e0 | ||
![]() |
921cc3e5db | ||
![]() |
51dce3d161 | ||
![]() |
c10f013fc2 | ||
![]() |
2874d68bdb | ||
![]() |
11e83eb7e7 | ||
![]() |
9ec9a8705e | ||
![]() |
170635e3a6 | ||
![]() |
a5d1444ef4 | ||
![]() |
55708b39c3 | ||
![]() |
8c5ebdff36 | ||
![]() |
1c84f324a1 | ||
![]() |
3dba09f339 | ||
![]() |
7ebf8e66c9 | ||
![]() |
62218fe59d | ||
![]() |
95d9bb6dfc | ||
![]() |
33986075ef | ||
![]() |
766905ba9f | ||
![]() |
a29c64b6c7 | ||
![]() |
777e1cabc5 | ||
![]() |
396e97fc94 | ||
![]() |
9c6a4505c8 | ||
![]() |
238c3adad1 | ||
![]() |
c1d0a8b5ce | ||
![]() |
08ce24ec3f | ||
![]() |
79eb7623ef | ||
![]() |
b9e64d0472 | ||
![]() |
4f500149af | ||
![]() |
281b8714ef | ||
![]() |
d5be3cce9c | ||
![]() |
8dcefaf2e3 | ||
![]() |
48e3432a63 | ||
![]() |
69d3c611aa | ||
![]() |
36827e1134 | ||
![]() |
351ac4a2c0 | ||
![]() |
36fff59a38 | ||
![]() |
1e60a4386a | ||
![]() |
e9f1b53ae6 | ||
![]() |
5016839b90 | ||
![]() |
faf35e6082 | ||
![]() |
d07a6edd2f | ||
![]() |
553d4e9283 | ||
![]() |
7cef52478d | ||
![]() |
e7ce362d22 | ||
![]() |
c551c8b31b | ||
![]() |
9c36e71081 | ||
![]() |
c9c57af5f7 | ||
![]() |
2516496993 | ||
![]() |
3b565b5f97 | ||
![]() |
0742976138 | ||
![]() |
1a63663c85 | ||
![]() |
00a20fc8a5 | ||
![]() |
6ab4fb368c | ||
![]() |
fa39bb0a50 | ||
![]() |
645663cdfe | ||
![]() |
ee2bcbb41d | ||
![]() |
12b4ebf8d4 | ||
![]() |
8da4750ee2 | ||
![]() |
d22df2915c | ||
![]() |
e77d96cf89 | ||
![]() |
ae28ba84d2 | ||
![]() |
b3f3b01958 | ||
![]() |
52e9cab1c1 | ||
![]() |
1baaaa40cc | ||
![]() |
b8ed420058 | ||
![]() |
ed16ee3029 | ||
![]() |
7338b16c18 | ||
![]() |
f529441400 | ||
![]() |
03664d0426 | ||
![]() |
652cfb7caf | ||
![]() |
5540fbaec2 | ||
![]() |
0a0b473765 | ||
![]() |
df2d041483 | ||
![]() |
ffc6e19548 | ||
![]() |
d874d7661f | ||
![]() |
1d66e714e6 | ||
![]() |
d98a863b82 | ||
![]() |
3b6790c7dc | ||
![]() |
5ee3a9a9ca | ||
![]() |
dd26fa67f2 | ||
![]() |
89f5e60422 | ||
![]() |
c616165f81 | ||
![]() |
103832742d | ||
![]() |
3e7e0bcb18 | ||
![]() |
7d3d8f20ab | ||
![]() |
e1e3ce980a | ||
![]() |
7855a32579 | ||
![]() |
9c92afa5fe | ||
![]() |
edac498d03 | ||
![]() |
d7d9dbd2c2 | ||
![]() |
f8eece22c8 | ||
![]() |
df563db294 | ||
![]() |
01b0d9eb97 | ||
![]() |
a7f13d841f | ||
![]() |
686d8bbe69 | ||
![]() |
4b36af4a34 | ||
![]() |
4a23a4bfee | ||
![]() |
ac3ad452c0 | ||
![]() |
027c01511c | ||
![]() |
4fdcc0496f | ||
![]() |
0c4a2bea69 | ||
![]() |
2ba3401238 | ||
![]() |
1735284a2a | ||
![]() |
8a3192ffc1 | ||
![]() |
ffea273a28 | ||
![]() |
1783aac438 | ||
![]() |
29bf3d2c04 | ||
![]() |
ee16fc958c | ||
![]() |
5d73215a8d | ||
![]() |
b7356bc526 | ||
![]() |
8ec8282f38 | ||
![]() |
43b7b98949 | ||
![]() |
f791065a98 | ||
![]() |
d95db28695 | ||
![]() |
851fb16e7c | ||
![]() |
a4cbaafd10 | ||
![]() |
c5f2cdb822 | ||
![]() |
1892d29be0 | ||
![]() |
9e5a49b8cb | ||
![]() |
767ade02f4 | ||
![]() |
6e05071a47 | ||
![]() |
c8a990b9d3 | ||
![]() |
6868ceae9b | ||
![]() |
cace646346 | ||
![]() |
abcc225763 | ||
![]() |
b9673fc521 | ||
![]() |
b0ea3f4261 | ||
![]() |
0103219f00 | ||
![]() |
e15b4f40d6 | ||
![]() |
66235fddff | ||
![]() |
f9c5d026f4 | ||
![]() |
c46239af22 | ||
![]() |
48eb3ff8d9 | ||
![]() |
5646dcc791 | ||
![]() |
5d9876e338 | ||
![]() |
083340a937 | ||
![]() |
378fa5ee6a | ||
![]() |
4764daf3c2 | ||
![]() |
6357496d17 | ||
![]() |
001e2a604b | ||
![]() |
f370911c15 | ||
![]() |
ef5cf40fa6 | ||
![]() |
837bd79b20 | ||
![]() |
3edd4a24af | ||
![]() |
535a61b5a9 | ||
![]() |
e87b7daab5 | ||
![]() |
f20689519d | ||
![]() |
ee9c460f74 | ||
![]() |
84ba14fa29 | ||
![]() |
3c75963352 | ||
![]() |
5f9dd8287c | ||
![]() |
d99f074eb7 | ||
![]() |
a989140a07 | ||
![]() |
420a4c163d | ||
![]() |
1bab735580 | ||
![]() |
a0fcd0cf22 | ||
![]() |
5d5c753c74 | ||
![]() |
7e7e3eb8d5 | ||
![]() |
2e5f8aeba1 | ||
![]() |
c2fe961db1 | ||
![]() |
2a6acc04a1 | ||
![]() |
a467128093 | ||
![]() |
abf41eb85c | ||
![]() |
d83dea4463 | ||
![]() |
ec43721c3c | ||
![]() |
39d52762d1 | ||
![]() |
8d45d0d104 | ||
![]() |
26ec62714c | ||
![]() |
7873b5e78b | ||
![]() |
1b889c527f | ||
![]() |
9937ff8ac0 | ||
![]() |
abd1949825 | ||
![]() |
4e6bc77a70 | ||
![]() |
531948358b | ||
![]() |
0d3ec9c324 | ||
![]() |
21caca4aea | ||
![]() |
fbf3edf07d | ||
![]() |
76fcf25898 | ||
![]() |
56257f072b | ||
![]() |
44401158e8 | ||
![]() |
95b2df8261 | ||
![]() |
1ebadf8620 | ||
![]() |
9179d9592d | ||
![]() |
f208217412 | ||
![]() |
43c5058682 | ||
![]() |
97b4a6b51f | ||
![]() |
f405d27c56 | ||
![]() |
99949c8f6f | ||
![]() |
3a9697adf2 | ||
![]() |
78c4351e04 | ||
![]() |
0a427890fe | ||
![]() |
e735abe334 | ||
![]() |
c0070b2f13 | ||
![]() |
cfedc6e9b4 | ||
![]() |
b66d7f7e0b | ||
![]() |
e01df06cd7 | ||
![]() |
6584897b69 | ||
![]() |
aa4f45b9a5 | ||
![]() |
96ad5b8444 | ||
![]() |
097e5dfbdc | ||
![]() |
2ef7ee6ca7 | ||
![]() |
2685b53b30 | ||
![]() |
533e4fcdad | ||
![]() |
006b8fa3f0 | ||
![]() |
6a01153ce4 | ||
![]() |
34aab116ae | ||
![]() |
33232face9 | ||
![]() |
b88b2b3d79 | ||
![]() |
744d729dab | ||
![]() |
71b5e43153 | ||
![]() |
8459f27312 | ||
![]() |
7dfbdef505 | ||
![]() |
94386374ff | ||
![]() |
f5d3859238 | ||
![]() |
ef39da5973 | ||
![]() |
81e8c4bbff | ||
![]() |
8ca3642429 | ||
![]() |
1dc000c06a | ||
![]() |
4f093d5b97 | ||
![]() |
e1b032cbad | ||
![]() |
6f365c30eb | ||
![]() |
718e180423 | ||
![]() |
cead5e5bd7 | ||
![]() |
cf15629aea | ||
![]() |
a727d0bb0b | ||
![]() |
0a218ee56a | ||
![]() |
74beefcaf6 | ||
![]() |
399a3abefc | ||
![]() |
cee5036aca | ||
![]() |
790823abb4 | ||
![]() |
f546849352 | ||
![]() |
a85af593f1 | ||
![]() |
07067f8b95 | ||
![]() |
a1e824ada0 | ||
![]() |
f5f1bfbef1 | ||
![]() |
cd108ba3aa | ||
![]() |
2bb5bfa74e | ||
![]() |
624e7a447d | ||
![]() |
37420c342b | ||
![]() |
ef40e362c9 | ||
![]() |
ef369c2e2b | ||
![]() |
6452461c39 | ||
![]() |
1e89ca0994 | ||
![]() |
92bb10eed8 | ||
![]() |
8465c5fe0e | ||
![]() |
42e248a8da | ||
![]() |
e71a652985 | ||
![]() |
bfef0fbff3 | ||
![]() |
545685bc32 | ||
![]() |
13ad2b4dc2 | ||
![]() |
d019343017 | ||
![]() |
e977ec924c | ||
![]() |
b7d5652bf6 | ||
![]() |
725e48fce4 | ||
![]() |
43816c268b | ||
![]() |
72e80db823 | ||
![]() |
7b33c343f8 | ||
![]() |
6d0601b99f | ||
![]() |
b6e713711d | ||
![]() |
c53edeeb6c | ||
![]() |
dfb98417b3 | ||
![]() |
9716c3a30e | ||
![]() |
c4c44c4445 | ||
![]() |
0debe9bd6f | ||
![]() |
f1da118a6c | ||
![]() |
c30c46cd5f | ||
![]() |
d394017926 | ||
![]() |
04525c0259 | ||
![]() |
08a0bb756d | ||
![]() |
20c6159c04 | ||
![]() |
a47e9d1a4b | ||
![]() |
e93dd374a4 | ||
![]() |
0847ca4ec2 | ||
![]() |
572d8d0cc4 | ||
![]() |
c9a57d354d | ||
![]() |
52b77f6e31 | ||
![]() |
4c9782ee28 | ||
![]() |
26225307d3 | ||
![]() |
0129d1e158 | ||
![]() |
ece6037a1e | ||
![]() |
bea678a7cd | ||
![]() |
1fb3fbb4de | ||
![]() |
2c38d19af2 | ||
![]() |
311e627463 | ||
![]() |
8e1e36a0d5 | ||
![]() |
db0bb425e7 | ||
![]() |
e001dd2d45 | ||
![]() |
c38772c97f | ||
![]() |
3a31589fdd | ||
![]() |
460522bb67 | ||
![]() |
176debaf37 | ||
![]() |
072f83b240 | ||
![]() |
cbd6770f2f | ||
![]() |
0c3a7bcf12 | ||
![]() |
389fbaaca2 | ||
![]() |
9652efd107 | ||
![]() |
f3ea834322 | ||
![]() |
02a6a46e86 | ||
![]() |
75ad90abc7 | ||
![]() |
4104593180 | ||
![]() |
78e1704122 | ||
![]() |
b9e99575ce | ||
![]() |
fa5e06f95d | ||
![]() |
3041409334 | ||
![]() |
71536eb412 | ||
![]() |
fe77230d84 | ||
![]() |
accd262561 | ||
![]() |
3057d19cdf | ||
![]() |
8dd83a2cf3 | ||
![]() |
49b84f9229 | ||
![]() |
b43bf4dd74 | ||
![]() |
5ed0eb51d1 | ||
![]() |
72a1ca3b99 | ||
![]() |
14424281a0 | ||
![]() |
2b3fd0d4d3 | ||
![]() |
894b9cfdb9 | ||
![]() |
72ff9bd3e6 | ||
![]() |
7ecbb0454f | ||
![]() |
fa7fa2a55f | ||
![]() |
5c0576ca55 | ||
![]() |
039b354490 | ||
![]() |
b2f03e76ff | ||
![]() |
63b33b6ec5 | ||
![]() |
23670795db | ||
![]() |
8ea6c113b5 | ||
![]() |
37f026a0a6 | ||
![]() |
f67136df19 | ||
![]() |
e07073ff28 | ||
![]() |
64b0ba6da7 | ||
![]() |
99d4ae0c1a | ||
![]() |
f185b35088 | ||
![]() |
4e909f9411 | ||
![]() |
99f49e266c | ||
![]() |
0458211ebc | ||
![]() |
8a7b9d9f45 | ||
![]() |
3fcf463f9e | ||
![]() |
f77cd63286 | ||
![]() |
2378c2d754 | ||
![]() |
bbdb4a67e1 | ||
![]() |
21851c0673 | ||
![]() |
83f6498aac | ||
![]() |
7e219c362c | ||
![]() |
525a791987 | ||
![]() |
fb19210cfd | ||
![]() |
203f48d1fd | ||
![]() |
371c82b2bf | ||
![]() |
8176880173 | ||
![]() |
b0b2c5b3e0 | ||
![]() |
29742d23d3 | ||
![]() |
533a6b0240 | ||
![]() |
0c0400b6fc | ||
![]() |
2e83af7c21 | ||
![]() |
a3f7947ad2 | ||
![]() |
7d9d459ac2 | ||
![]() |
3ea1073809 | ||
![]() |
76a959a578 | ||
![]() |
f43873dc5f | ||
![]() |
921553d7bb | ||
![]() |
c476819cb1 | ||
![]() |
77a56c7c5a | ||
![]() |
46deb7ca82 | ||
![]() |
b03f9ece05 | ||
![]() |
27946a981f | ||
![]() |
74617389c8 | ||
![]() |
1d8840412f | ||
![]() |
df1152ee0f | ||
![]() |
b4a8b8c0d4 | ||
![]() |
ffa9f4b47f | ||
![]() |
667e22bbc7 | ||
![]() |
d4d838c8bb | ||
![]() |
c61a889c86 | ||
![]() |
567fe89a77 | ||
![]() |
950772ab8a | ||
![]() |
0bd0e2a3ec | ||
![]() |
47774ce882 | ||
![]() |
3c9bcdd347 | ||
![]() |
3934d2d30c | ||
![]() |
423ce9557a | ||
![]() |
947848ebf6 | ||
![]() |
5e22fe488e | ||
![]() |
c666cf1c44 | ||
![]() |
79435dbdec | ||
![]() |
27206368da | ||
![]() |
2be245b75e | ||
![]() |
17927f5c26 | ||
![]() |
754f26a97c | ||
![]() |
29241c4f83 | ||
![]() |
6f655eb9b9 | ||
![]() |
65dfd90141 | ||
![]() |
59abdbd2dd | ||
![]() |
a6d6873856 | ||
![]() |
e99df3a3be | ||
![]() |
8b657255cc | ||
![]() |
0e1dc79321 | ||
![]() |
0ef15e5fac | ||
![]() |
9ea8a840a9 | ||
![]() |
7b88089593 | ||
![]() |
b513943893 | ||
![]() |
edffc56600 | ||
![]() |
856b0e6886 | ||
![]() |
bf1eb46b87 | ||
![]() |
7e27d660e2 | ||
![]() |
53174ea45f | ||
![]() |
87b624f5d5 | ||
![]() |
443e96381a | ||
![]() |
1cbba4fc59 | ||
![]() |
344b6dd179 | ||
![]() |
d8c829fa0c | ||
![]() |
e1efc71ad3 | ||
![]() |
f804a739b1 | ||
![]() |
2ed870c854 | ||
![]() |
ce35ba9ac9 | ||
![]() |
724a59aaf7 | ||
![]() |
42d8c2981f | ||
![]() |
9aa91e0f17 | ||
![]() |
5aabee8996 | ||
![]() |
48a84ca23e | ||
![]() |
8751783a1b | ||
![]() |
3a2ec50d5f | ||
![]() |
ce9aeed4cb | ||
![]() |
f2be6432a9 | ||
![]() |
e96f8c0444 | ||
![]() |
2bf3bc3e10 | ||
![]() |
28143f86f9 | ||
![]() |
76ec3d3248 | ||
![]() |
d0c85a5a96 | ||
![]() |
46ed717af4 | ||
![]() |
ad37c88f80 | ||
![]() |
8c6e8a6eb8 | ||
![]() |
dd33317f45 | ||
![]() |
c344d63fb3 | ||
![]() |
62557f4d6b | ||
![]() |
9c46cc4ea0 | ||
![]() |
a236a439cc | ||
![]() |
c779e2674a | ||
![]() |
a94d4be466 | ||
![]() |
b7d2d4cfe8 | ||
![]() |
a6c797ee4b | ||
![]() |
3e40b2249f | ||
![]() |
2fe1b5034d | ||
![]() |
b4430839a3 | ||
![]() |
412cf974a4 | ||
![]() |
3fc6beeff0 | ||
![]() |
2ff35c8bfa | ||
![]() |
169db88c20 | ||
![]() |
ca419c84b8 | ||
![]() |
aede71b1dc | ||
![]() |
14d573cbf1 | ||
![]() |
7819aa6b20 | ||
![]() |
7cc6b63aac | ||
![]() |
4af6362751 | ||
![]() |
d54075197f | ||
![]() |
5cce56199b | ||
![]() |
3cc12817f6 | ||
![]() |
68875ba600 | ||
![]() |
c345c5ebae | ||
![]() |
2119a16e24 | ||
![]() |
9f5c938ff3 | ||
![]() |
b42a8d2364 | ||
![]() |
61fc01e79e | ||
![]() |
cceaec1d74 | ||
![]() |
8e5f9c8160 | ||
![]() |
c620fd42f4 | ||
![]() |
2a859f870a | ||
![]() |
b680753db8 | ||
![]() |
35af940166 | ||
![]() |
bc0fec0afe | ||
![]() |
e3eca82cc3 | ||
![]() |
e7651d0d0f | ||
![]() |
5cf4ce9318 | ||
![]() |
6a6f28668f | ||
![]() |
dadf1339b5 | ||
![]() |
c2c4228722 | ||
![]() |
5469941f2b | ||
![]() |
e888c9e827 | ||
![]() |
3d3bca5338 | ||
![]() |
6a3008d7ff | ||
![]() |
2556449b36 | ||
![]() |
446f9973cc | ||
![]() |
596f36bb78 | ||
![]() |
e7abdab58d | ||
![]() |
13cdc9a9f8 | ||
![]() |
a1b8806422 | ||
![]() |
e635d47912 | ||
![]() |
53ac72a878 | ||
![]() |
2be6184c8d | ||
![]() |
8b0b4ff086 | ||
![]() |
60f7ff3de5 | ||
![]() |
e76c752987 | ||
![]() |
042c1abc6e | ||
![]() |
1401621913 | ||
![]() |
3c034b0a0c | ||
![]() |
395191bd75 | ||
![]() |
ec7d8fb6bd | ||
![]() |
062948b110 | ||
![]() |
7f3dc5f040 | ||
![]() |
dd0798a317 | ||
![]() |
9209ccfa40 | ||
![]() |
2525d32e17 | ||
![]() |
f3ac8a7cd9 | ||
![]() |
f3d95f70e2 | ||
![]() |
57526067f5 | ||
![]() |
0545bab35d | ||
![]() |
293836494d | ||
![]() |
ae8bda190e | ||
![]() |
34d9d8abd4 | ||
![]() |
bd67e986f4 | ||
![]() |
e587518d85 | ||
![]() |
210b6c38bd | ||
![]() |
9592c0b466 | ||
![]() |
a33537b2b9 | ||
![]() |
81d5c9757c | ||
![]() |
f34124a50b | ||
![]() |
74a39c715b | ||
![]() |
195496333b | ||
![]() |
9ccaa90439 | ||
![]() |
4733c5fef0 | ||
![]() |
a97ddc8cb9 | ||
![]() |
59a5b000e0 | ||
![]() |
d49a2ccb08 | ||
![]() |
4a75acb46c | ||
![]() |
453368078b | ||
![]() |
6e3b643bdf | ||
![]() |
a769352a74 | ||
![]() |
8a63c27925 | ||
![]() |
310895f060 | ||
![]() |
4428894aba | ||
![]() |
3d12d7de62 | ||
![]() |
87593f95d4 | ||
![]() |
11626e48bf | ||
![]() |
4f021cbced | ||
![]() |
ba31d176c8 | ||
![]() |
68edbc3e4a | ||
![]() |
5068227a46 | ||
![]() |
f8f3bc89e7 | ||
![]() |
635f7026b0 | ||
![]() |
0f1e4f0326 | ||
![]() |
89355edb8a | ||
![]() |
37c8f5c1da | ||
![]() |
5bba2df526 | ||
![]() |
1935694440 | ||
![]() |
12dd6ea8bb | ||
![]() |
a1f922b040 | ||
![]() |
017cf088a1 | ||
![]() |
df627cc417 | ||
![]() |
1b20300b73 | ||
![]() |
3b9ffea36f | ||
![]() |
e242f3999c | ||
![]() |
523f89cc8c | ||
![]() |
0575a6d652 | ||
![]() |
08b88714e0 | ||
![]() |
bcaff4b844 | ||
![]() |
82f336a78f | ||
![]() |
350aa33022 | ||
![]() |
d6290a2f1a | ||
![]() |
71e9d08863 | ||
![]() |
9729dc7594 | ||
![]() |
92c1b8f31e | ||
![]() |
9ffa2604f8 | ||
![]() |
03b1fad4d4 | ||
![]() |
72eb4c534f | ||
![]() |
47d936e9cc | ||
![]() |
8e08407090 | ||
![]() |
c0a4558c62 | ||
![]() |
5ecb6fecc4 | ||
![]() |
b3df4dc2c9 | ||
![]() |
3db9ab82ea | ||
![]() |
2dc3acc5f0 | ||
![]() |
25686e5bce | ||
![]() |
8d70f808d9 | ||
![]() |
7c887af1ea | ||
![]() |
b7f435b50e | ||
![]() |
d3b15f8fda | ||
![]() |
eea726740b | ||
![]() |
0ea4c970d7 | ||
![]() |
57936a1374 | ||
![]() |
9242fde6b8 | ||
![]() |
d1f653be65 | ||
![]() |
0035dceb0a | ||
![]() |
838f7cd210 | ||
![]() |
13539961b2 | ||
![]() |
a26f2ef17d | ||
![]() |
d97c46bcdc | ||
![]() |
2b6542467c | ||
![]() |
8fa51faa38 | ||
![]() |
b2175629fd | ||
![]() |
2e28ed8f81 | ||
![]() |
4c4f8bf02a | ||
![]() |
e464be5f39 | ||
![]() |
d7d717f2ce | ||
![]() |
d1eeed6a5b | ||
![]() |
b159bc0c5f | ||
![]() |
a222c4879c | ||
![]() |
eb2f413cf0 | ||
![]() |
736fd0e293 | ||
![]() |
6592ca9f88 | ||
![]() |
762712c756 | ||
![]() |
73f9e17951 | ||
![]() |
7d6a605a85 | ||
![]() |
a6a8bdffc3 | ||
![]() |
296085ff23 | ||
![]() |
36aa8ce3c9 | ||
![]() |
c49c69d6ea | ||
![]() |
d5684f7444 | ||
![]() |
affb4bd923 | ||
![]() |
65772a74e0 | ||
![]() |
cca2c2f4ca | ||
![]() |
52e2fa91c4 | ||
![]() |
dca405a746 | ||
![]() |
e54748d355 | ||
![]() |
3680a6bbbb | ||
![]() |
6aa6a9c272 | ||
![]() |
8d1c7ca206 | ||
![]() |
52b8e0f9ec | ||
![]() |
08e2e2e791 | ||
![]() |
3c4f4793b5 | ||
![]() |
e2950a7e4d | ||
![]() |
531c0067ec | ||
![]() |
e228caaca6 | ||
![]() |
4b4aa64261 | ||
![]() |
9ac8f89b1d | ||
![]() |
c6a72a14ac | ||
![]() |
c74edd0e33 | ||
![]() |
98acf3f281 | ||
![]() |
26735390ff | ||
![]() |
9402b23dd5 | ||
![]() |
246db3d565 | ||
![]() |
eaf414cbc8 | ||
![]() |
327d41c00f | ||
![]() |
05d8ce3bcd | ||
![]() |
fe30db935b | ||
![]() |
6303d54cbb | ||
![]() |
9d91aa23e6 | ||
![]() |
c2ada39fd3 | ||
![]() |
def2fe8805 | ||
![]() |
77b6e27500 | ||
![]() |
99640171e2 | ||
![]() |
38b3ab791f | ||
![]() |
a86fc52eea | ||
![]() |
6d3ed3f16f | ||
![]() |
be798998bf | ||
![]() |
c3b425d570 | ||
![]() |
a25743875f | ||
![]() |
124d1a5942 | ||
![]() |
eec77b3ae7 | ||
![]() |
f680b0a431 | ||
![]() |
d4b00ff11c | ||
![]() |
532f94a187 | ||
![]() |
87ad2f8542 | ||
![]() |
640bac1aa4 | ||
![]() |
0b12fae84d | ||
![]() |
a8f891efcd | ||
![]() |
d9353c4fd3 | ||
![]() |
0c9fc2f809 | ||
![]() |
b5fc2419e8 | ||
![]() |
fe588a255b | ||
![]() |
1fc571088b | ||
![]() |
8d83914f05 | ||
![]() |
0fdcd381bc | ||
![]() |
4f293ecd6f | ||
![]() |
b6303313f0 | ||
![]() |
a28449a123 | ||
![]() |
6dcec36621 | ||
![]() |
1a954748a0 | ||
![]() |
84d0fd39a3 | ||
![]() |
4d4b7e3de0 | ||
![]() |
e2aea6bce5 | ||
![]() |
5779146a7f | ||
![]() |
a1d1c2beaa | ||
![]() |
ee9c60fad4 | ||
![]() |
2abad0f479 | ||
![]() |
1674a4ec82 | ||
![]() |
ce370bee60 | ||
![]() |
2fb40fe728 | ||
![]() |
b57330cf75 | ||
![]() |
8cd91fabf9 | ||
![]() |
e0580ee385 | ||
![]() |
e257484870 | ||
![]() |
2a1f4539f6 | ||
![]() |
906efdd320 | ||
![]() |
144d092637 | ||
![]() |
948b8f35e6 | ||
![]() |
e776c605ad | ||
![]() |
8b2f4fc823 | ||
![]() |
03018611f8 | ||
![]() |
2bde9afdb9 | ||
![]() |
f8b09c194f | ||
![]() |
0e69ad32c1 | ||
![]() |
9b4e14df71 | ||
![]() |
03f7803f26 | ||
![]() |
c6cbcc2c25 | ||
![]() |
27aa34d4ae | ||
![]() |
79e0db4ca0 | ||
![]() |
8f99c954ad | ||
![]() |
5735c9efc1 | ||
![]() |
9ae3acf2e7 | ||
![]() |
731ea9b489 | ||
![]() |
18b30b5019 | ||
![]() |
e6c3acaa6f | ||
![]() |
24d51b9d14 | ||
![]() |
a1edc199df | ||
![]() |
db4aa81528 | ||
![]() |
319ba94a52 | ||
![]() |
1025f0be91 | ||
![]() |
77d3b5e8f1 | ||
![]() |
a0ad96a787 | ||
![]() |
39c5af5dbc | ||
![]() |
acca6a799b | ||
![]() |
b77e62260a | ||
![]() |
b6995ca011 | ||
![]() |
715844fd08 | ||
![]() |
2b29ca796e | ||
![]() |
44b4b50949 | ||
![]() |
5184008812 | ||
![]() |
cf696ce443 | ||
![]() |
1a2ea4c06c | ||
![]() |
a800afcbf8 | ||
![]() |
ff4534613a | ||
![]() |
b8fe2c74bc | ||
![]() |
be3b5199b0 | ||
![]() |
d849a40af6 | ||
![]() |
4f48c10312 | ||
![]() |
5274fee8a7 | ||
![]() |
5462f34ed0 | ||
![]() |
0958ed5844 | ||
![]() |
e3cba18532 | ||
![]() |
9bf7fde49f | ||
![]() |
4783ebc918 | ||
![]() |
8bda52783b | ||
![]() |
07feec7ea6 | ||
![]() |
e7471f589a | ||
![]() |
24208be5cf | ||
![]() |
8db5f66498 | ||
![]() |
be670bfd1f | ||
![]() |
19acf090ed | ||
![]() |
2de7f6b457 | ||
![]() |
a2e265e9a2 | ||
![]() |
28d7b29d72 | ||
![]() |
9cb4aaf3c2 | ||
![]() |
80dc602193 | ||
![]() |
c7f5a87258 |
.gitignoreINSTALLMakefile.amNEWSautogen.shconfigure.ac
doc
m4
ax_append_compile_flags.m4ax_append_flag.m4ax_check_compile_flag.m4faad.m4mpd_auto.m4mpd_check_cflag.m4ucred.m4
mpd.service.inscripts
src
AudioCompress
ack.haiff.caiff.hape.cape.harchive
bz2_archive_plugin.cbz2_archive_plugin.hiso9660_archive_plugin.ciso9660_archive_plugin.hzzip_archive_plugin.czzip_archive_plugin.h
archive_api.carchive_api.harchive_internal.harchive_list.carchive_list.harchive_plugin.carchive_plugin.haudio_check.caudio_check.haudio_config.caudio_config.haudio_format.caudio_format.haudio_parser.caudio_parser.hbuffer.cbuffer.hcheck.hchunk.cchunk.hclient.cclient.hclient_event.cclient_expire.cclient_file.cclient_file.hclient_global.cclient_idle.cclient_idle.hclient_internal.hclient_list.cclient_message.cclient_message.hclient_new.cclient_process.cclient_read.cclient_subscribe.cclient_subscribe.hclient_write.cclock.cclock.hcmdline.ccmdline.hcommand.ccommand.hconf.cconf.hcrossfade.ccrossfade.hcue
daemon.cdaemon.hdatabase.cdatabase.hdb
dbUtils.cdbUtils.hdb_error.hdb_internal.hdb_lock.cdb_lock.hdb_plugin.hdb_print.cdb_print.hdb_save.cdb_save.hdb_selection.hdb_visitor.hdecoder
_flac_common.c_flac_common.h_ogg_common.c_ogg_common.haudiofile_decoder_plugin.cdsdiff_decoder_plugin.cdsdiff_decoder_plugin.hdsdlib.cdsdlib.hdsf_decoder_plugin.cdsf_decoder_plugin.hfaad_decoder_plugin.cffmpeg_decoder_plugin.cffmpeg_metadata.cffmpeg_metadata.hflac_compat.hflac_decoder_plugin.cflac_metadata.cflac_metadata.hflac_pcm.cflac_pcm.hfluidsynth_decoder_plugin.cgme_decoder_plugin.cmad_decoder_plugin.cmikmod_decoder_plugin.cmodplug_decoder_plugin.cmp4ff_decoder_plugin.cmpcdec_decoder_plugin.cmpg123_decoder_plugin.coggflac_decoder_plugin.cpcm_decoder_plugin.cpcm_decoder_plugin.hsidplay_decoder_plugin.cxxsndfile_decoder_plugin.cvorbis_comments.cvorbis_comments.hvorbis_decoder_plugin.cwavpack_decoder_plugin.cwildmidi_decoder_plugin.c
decoder_api.cdecoder_api.hdecoder_buffer.cdecoder_buffer.hdecoder_command.hdecoder_control.cdecoder_control.hdecoder_internal.cdecoder_internal.hdecoder_list.cdecoder_list.hdecoder_plugin.cdecoder_plugin.hdecoder_print.cdecoder_print.hdecoder_thread.cdecoder_thread.hdespotify_utils.cdespotify_utils.hdirectory.cdirectory.hdirectory_print.cdirectory_save.cdirectory_save.hdirvec.cdsd2pcm
dummy.cxxencoder
encoder_api.hencoder_list.cencoder_list.hencoder_plugin.hevent_pipe.cevent_pipe.hexclude.cexclude.hfd_util.cfd_util.hfifo_buffer.cfifo_buffer.hfilter
autoconvert_filter_plugin.cautoconvert_filter_plugin.hchain_filter_plugin.cchain_filter_plugin.hconvert_filter_plugin.cconvert_filter_plugin.hnormalize_filter_plugin.cnull_filter_plugin.creplay_gain_filter_plugin.creplay_gain_filter_plugin.hroute_filter_plugin.cvolume_filter_plugin.cvolume_filter_plugin.h
filter_config.cfilter_config.hfilter_internal.hfilter_plugin.cfilter_plugin.hfilter_registry.cfilter_registry.hgcc.hglib_compat.hglib_socket.hgrowing_fifo.cgrowing_fifo.hicy_metadata.cicy_metadata.hicy_server.cicy_server.hidle.cidle.hinotify_queue.cinotify_queue.hinotify_source.cinotify_source.hinotify_update.cinotify_update.hinput
archive_input_plugin.carchive_input_plugin.hcdio_paranoia_input_plugin.ccdio_paranoia_input_plugin.hcurl_input_plugin.ccurl_input_plugin.hdespotify_input_plugin.cdespotify_input_plugin.hffmpeg_input_plugin.cffmpeg_input_plugin.hfile_input_plugin.cfile_input_plugin.hmms_input_plugin.cmms_input_plugin.hrewind_input_plugin.crewind_input_plugin.hsoup_input_plugin.csoup_input_plugin.h
input_init.cinput_init.hinput_internal.cinput_internal.hinput_plugin.hinput_registry.cinput_registry.hinput_stream.cinput_stream.hio_thread.cio_thread.hlisten.clisten.hlocate.clocate.hlog.clog.hls.cls.hmain.cmain.hmain_win32.cmapper.cmapper.hmixer
alsa_mixer_plugin.coss_mixer_plugin.cpulse_mixer_plugin.cpulse_mixer_plugin.hroar_mixer_plugin.csoftware_mixer_plugin.csoftware_mixer_plugin.hwinmm_mixer_plugin.c
mixer_all.cmixer_all.hmixer_api.cmixer_api.hmixer_control.cmixer_control.hmixer_list.hmixer_plugin.hmixer_type.cmixer_type.hmpd_error.hnotify.cnotify.hopen.houtput
alsa_output_plugin.calsa_output_plugin.hao_output_plugin.cao_output_plugin.hffado_output_plugin.cffado_output_plugin.hfifo_output_plugin.cfifo_output_plugin.hhttpd_client.chttpd_client.hhttpd_internal.hhttpd_output_plugin.chttpd_output_plugin.hjack_output_plugin.cjack_output_plugin.hmvp_output_plugin.cmvp_output_plugin.hnull_output_plugin.cnull_output_plugin.hopenal_output_plugin.copenal_output_plugin.hoss_output_plugin.coss_output_plugin.hosx_output_plugin.cosx_output_plugin.hosx_plugin.cpipe_output_plugin.cpipe_output_plugin.hpulse_output_plugin.cpulse_output_plugin.hrecorder_output_plugin.crecorder_output_plugin.hroar_output_plugin.croar_output_plugin.hshout_output_plugin.cshout_output_plugin.hsolaris_output_plugin.csolaris_output_plugin.hwinmm_output_plugin.cwinmm_output_plugin.h
output_all.coutput_all.houtput_api.houtput_command.coutput_command.houtput_control.coutput_control.houtput_finish.coutput_init.coutput_internal.houtput_list.coutput_list.houtput_plugin.coutput_plugin.houtput_print.coutput_print.houtput_state.coutput_state.houtput_thread.coutput_thread.hpage.cpage.hpath.cpath.hpcm_buffer.cpcm_buffer.hpcm_byteswap.cpcm_byteswap.hpcm_channels.cpcm_channels.hpcm_convert.cpcm_convert.hpcm_dither.cpcm_dither.hpcm_dsd.cpcm_dsd.hpcm_dsd_usb.cpcm_dsd_usb.hpcm_export.cpcm_export.hpcm_format.cpcm_format.hpcm_mix.cpcm_mix.hpcm_pack.cpcm_pack.hpcm_prng.hpcm_resample.cpcm_resample.hpcm_resample_fallback.cpcm_resample_internal.hpcm_resample_libsamplerate.cpcm_utils.hpcm_volume.cpcm_volume.hpermission.cpermission.hpipe.cpipe.hplayer_control.cplayer_control.hplayer_thread.cplayer_thread.hplaylist.cplaylist.hplaylist
asx_playlist_plugin.casx_playlist_plugin.hcue_playlist_plugin.ccue_playlist_plugin.hdespotify_playlist_plugin.cdespotify_playlist_plugin.hembcue_playlist_plugin.cembcue_playlist_plugin.hextm3u_playlist_plugin.cextm3u_playlist_plugin.hflac_playlist_plugin.clastfm_playlist_plugin.clastfm_playlist_plugin.hm3u_playlist_plugin.cm3u_playlist_plugin.hpls_playlist_plugin.cpls_playlist_plugin.hrss_playlist_plugin.crss_playlist_plugin.hsoundcloud_playlist_plugin.csoundcloud_playlist_plugin.hxspf_playlist_plugin.cxspf_playlist_plugin.h
playlist_any.cplaylist_any.hplaylist_control.cplaylist_database.cplaylist_database.hplaylist_edit.cplaylist_error.hplaylist_global.cplaylist_internal.hplaylist_list.cplaylist_list.hplaylist_mapper.cplaylist_mapper.hplaylist_plugin.hplaylist_print.cplaylist_print.hplaylist_queue.cplaylist_queue.hplaylist_save.cplaylist_save.hplaylist_song.cplaylist_song.hplaylist_state.cplaylist_state.hplaylist_vector.cplaylist_vector.hpoison.hprotocol
queue.cqueue.hqueue_print.cqueue_print.hqueue_save.cqueue_save.hrefcount.hreplay_gain_ape.creplay_gain_ape.hreplay_gain_config.creplay_gain_config.hreplay_gain_info.creplay_gain_info.hresolver.cresolver.hriff.criff.hserver_socket.cserver_socket.hsig_handlers.csig_handlers.hsocket_util.csocket_util.hsong.csong.hsong_print.csong_print.hsong_save.csong_save.hsong_sort.csong_sort.hsong_sticker.csong_sticker.hsong_update.cstate_file.cstate_file.hstats.cstats.hsticker.csticker.hsticker_print.csticker_print.hstored_playlist.cstored_playlist.hstring_util.cstring_util.hstrset.cstrset.htag.ctag.htag_ape.ctag_ape.htag_file.ctag_file.htag_handler.ctag_handler.htag_id3.ctag_id3.htag_internal.htag_pool.ctag_pool.htag_print.ctag_print.htag_rva2.ctag_rva2.htag_save.ctag_save.htag_table.htext_file.ctext_file.htext_input_stream.ctext_input_stream.htimer.ctimer.htokenizer.ctokenizer.hupdate.cupdate.hupdate_archive.cupdate_archive.hupdate_container.cupdate_container.hupdate_db.cupdate_db.hupdate_internal.hupdate_io.cupdate_io.hupdate_queue.cupdate_queue.hupdate_remove.cupdate_remove.hupdate_song.cupdate_song.hupdate_walk.cupdate_walk.huri.curi.hutil
utils.cutils.hvolume.cvolume.hwin
zeroconf-avahi.czeroconf-bonjour.czeroconf-internal.hzeroconf.czeroconf.htest
dump_playlist.cdump_rva2.cdump_text_file.cread_conf.cread_mixer.cread_tags.crun_convert.crun_decoder.crun_encoder.crun_filter.crun_inotify.crun_input.crun_normalize.crun_output.crun_resolver.crun_tcp_connect.csignals.csignals.hsoftware_volume.cstdbin.htest_byte_reverse.ctest_pcm_all.htest_pcm_channels.ctest_pcm_dither.ctest_pcm_main.ctest_pcm_pack.ctest_pcm_volume.ctest_queue_priority.ctest_vorbis_encoder.c
valgrind.suppressions
8
.gitignore
vendored
8
.gitignore
vendored
@@ -34,11 +34,14 @@ missing
|
||||
mkinstalldirs
|
||||
mpd
|
||||
mpd.exe
|
||||
mpd.service
|
||||
stamp-h1
|
||||
tags
|
||||
*~
|
||||
.#*
|
||||
.stgit*
|
||||
src/dsd2pcm/dsd2pcm
|
||||
doc/doxygen.conf
|
||||
doc/protocol.html
|
||||
doc/protocol
|
||||
doc/user
|
||||
@@ -59,3 +62,8 @@ test/dump_playlist
|
||||
test/run_normalize
|
||||
test/tmp
|
||||
test/run_inotify
|
||||
test/test_queue_priority
|
||||
test/run_ntp_server
|
||||
test/run_resolver
|
||||
test/run_tcp_connect
|
||||
test/test_pcm
|
||||
|
21
INSTALL
21
INSTALL
@@ -13,7 +13,7 @@ Dependencies
|
||||
gcc - http://gcc.gnu.org/
|
||||
Any other C99 compliant compiler should also work.
|
||||
|
||||
GLib 2.12 - http://www.gtk.org/
|
||||
GLib 2.16 - http://www.gtk.org/
|
||||
General-purpose utility library.
|
||||
|
||||
|
||||
@@ -84,11 +84,6 @@ For Ogg Vorbis support. You will need libogg and libvorbis.
|
||||
FLAC - http://flac.sourceforge.net/
|
||||
For FLAC support. You will need version 1.1.0 or higher of libflac.
|
||||
|
||||
OggFLAC - http://www.xiph.org/ogg/vorbis/ and http://flac.sourceforge.net/
|
||||
For OggFLAC support. You will need liboggflac, which can be built from the
|
||||
FLAC sources if libogg is already installed. Versions of flac 1.1.3 and
|
||||
greater will automatically detect and use OggFLAC if it's available.
|
||||
|
||||
Audio File - http://www.68k.org/~michael/audiofile/
|
||||
For WAVE, AIFF, and AU support. You will need libaudiofile.
|
||||
|
||||
@@ -101,14 +96,14 @@ For Musepack support.
|
||||
MikMod - http://mikmod.raphnet.net/
|
||||
For MOD support. You will need libmikmod.
|
||||
|
||||
libavcodec, libavformat (ffmpeg) - http://ffmpeg.mplayerhq.hu/
|
||||
libavcodec, libavformat (ffmpeg or libav) - http://ffmpeg.mplayerhq.hu/ http://libav.org/
|
||||
Multi-codec library.
|
||||
|
||||
libsidplay2 - http://sidplay2.sourceforge.net/
|
||||
For C64 SID support.
|
||||
|
||||
libfluidsynth - http://fluidsynth.resonance.org/
|
||||
For MIDI support (DO NOT USE - use libwildmidi instead)
|
||||
For MIDI support.
|
||||
|
||||
libwildmidi - http://wildmidi.sourceforge.net/
|
||||
For MIDI support.
|
||||
@@ -119,6 +114,9 @@ WAVE, AIFF, and many others.
|
||||
libwavpack - http://www.wavpack.com/
|
||||
For WavPack playback.
|
||||
|
||||
despotify - https://github.com/SimonKagstrom/despotify
|
||||
For Spotify playback.
|
||||
|
||||
|
||||
Optional Miscellaneous Dependencies
|
||||
-----------------------------------
|
||||
@@ -138,8 +136,11 @@ For playing MMS streams.
|
||||
SQLite - http://www.sqlite.org/
|
||||
For the sticker database.
|
||||
|
||||
libcue - http://libcue.sourceforge.net/
|
||||
For CUE sheet support.
|
||||
libcdio - http://www.gnu.org/software/libcdio/
|
||||
For playing audio CDs.
|
||||
|
||||
libsystemd-daemon - http://freedesktop.org/wiki/Software/systemd/
|
||||
For systemd activation.
|
||||
|
||||
|
||||
pkg-config
|
||||
|
839
Makefile.am
839
Makefile.am
File diff suppressed because it is too large
Load Diff
213
NEWS
213
NEWS
@@ -1,4 +1,202 @@
|
||||
ver 0.16.1 (2010/01/09)
|
||||
ver 0.17.2 (2012/09/30)
|
||||
* protocol:
|
||||
- fix crash in local file check
|
||||
* decoder:
|
||||
- fluidsynth: remove throttle (requires libfluidsynth 1.1)
|
||||
- fluidsynth: stop playback at end of file
|
||||
- fluidsynth: check MIDI file format while scanning
|
||||
- fluidsynth: add sample rate setting
|
||||
- wavpack: support all APEv2 tags
|
||||
* output:
|
||||
- httpd: use monotonic clock, avoid hiccups after system clock adjustment
|
||||
- httpd: fix throttling bug after resuming playback
|
||||
* playlist:
|
||||
- cue: map "PERFORMER" to "artist" or "album artist"
|
||||
* mapper: fix non-UTF8 music directory name
|
||||
* mapper: fix potential crash in file permission check
|
||||
* playlist: fix use-after-free bug
|
||||
* playlist: fix memory leak
|
||||
* state_file: save song priorities
|
||||
* player: disable cross-fading in "single" mode
|
||||
* update: fix unsafe readlink() usage
|
||||
* configure.ac:
|
||||
- don't auto-detect the vorbis encoder when Tremor is enabled
|
||||
|
||||
ver 0.17.1 (2012/07/31)
|
||||
* protocol:
|
||||
- require appropriate permissions for searchadd{,pl}
|
||||
* tags:
|
||||
- aiff: support the AIFC format
|
||||
- ape: check for ID3 if no usable APE tag was found
|
||||
* playlist:
|
||||
- cue: support file types "MP3", "AIFF"
|
||||
* output:
|
||||
- fix noisy playback with conversion and software volume
|
||||
|
||||
ver 0.17 (2012/06/27)
|
||||
* protocol:
|
||||
- support client-to-client communication
|
||||
- "update" and "rescan" need only "CONTROL" permission
|
||||
- new command "seekcur" for simpler seeking within current song
|
||||
- new command "config" dumps location of music directory
|
||||
- add range parameter to command "load"
|
||||
- print extra "playlist" object for embedded CUE sheets
|
||||
- new commands "searchadd", "searchaddpl"
|
||||
* input:
|
||||
- cdio_paranoia: new input plugin to play audio CDs
|
||||
- curl: enable CURLOPT_NETRC
|
||||
- curl: non-blocking I/O
|
||||
- soup: new input plugin based on libsoup
|
||||
* tags:
|
||||
- RVA2: support separate album/track replay gain
|
||||
* decoder:
|
||||
- mpg123: implement seeking
|
||||
- ffmpeg: drop support for pre-0.5 ffmpeg
|
||||
- ffmpeg: support WebM
|
||||
- oggflac: delete this obsolete plugin
|
||||
- dsdiff: new decoder plugin
|
||||
* output:
|
||||
- alsa: support DSD-over-USB (dCS suggested standard)
|
||||
- httpd: support for streaming to a DLNA client
|
||||
- openal: improve buffer cancellation
|
||||
- osx: allow user to specify other audio devices
|
||||
- osx: implement 32 bit playback
|
||||
- shout: add possibility to set url
|
||||
- roar: new output plugin for RoarAudio
|
||||
- winmm: fail if wrong device specified instead of using default device
|
||||
* mixer:
|
||||
- alsa: listen for external volume changes
|
||||
* playlist:
|
||||
- allow references to songs outside the music directory
|
||||
- new CUE parser, without libcue
|
||||
- soundcloud: new plugin for accessing soundcloud.com
|
||||
* state_file: add option "restore_paused"
|
||||
* cue: show CUE track numbers
|
||||
* allow port specification in "bind_to_address" settings
|
||||
* support floating point samples
|
||||
* systemd socket activation
|
||||
* improve --version output
|
||||
* WIN32: fix renaming of stored playlists with non-ASCII names
|
||||
|
||||
|
||||
ver 0.16.8 (2012/04/04)
|
||||
* fix for libsamplerate assertion failure
|
||||
* decoder:
|
||||
- vorbis (and others): fix seeking at startup
|
||||
- ffmpeg: read the "year" tag
|
||||
* encoder:
|
||||
- vorbis: generate end-of-stream packet before tag
|
||||
- vorbis: generate end-of-stream packet when playback ends
|
||||
* output:
|
||||
- jack: check for connection failure before starting playback
|
||||
- jack: workaround for libjack1 crash bug
|
||||
- osx: fix stuttering due to buffering bug
|
||||
* fix endless loop in text file reader
|
||||
* update: skip symlinks in path that is to be updated
|
||||
|
||||
|
||||
ver 0.16.7 (2012/02/04)
|
||||
* input:
|
||||
- ffmpeg: support libavformat 0.7
|
||||
* decoder:
|
||||
- ffmpeg: support libavformat 0.8, libavcodec 0.9
|
||||
- ffmpeg: support all MPD tags
|
||||
* output:
|
||||
- httpd: fix excessive buffering
|
||||
- openal: force 16 bit playback, as 8 bit doesn't work
|
||||
- osx: remove sleep call from render callback
|
||||
- osx: clear render buffer when there's not enough data
|
||||
* fix moving after current song
|
||||
|
||||
|
||||
ver 0.16.6 (2011/12/01)
|
||||
* decoder:
|
||||
- fix assertion failure when resuming streams
|
||||
- ffmpeg: work around bogus channel count
|
||||
* encoder:
|
||||
- flac, null, wave: fix buffer corruption bug
|
||||
- wave: support packed 24 bit samples
|
||||
* mapper: fix the bogus "not a directory" error message
|
||||
* mapper: check "x" and "r" permissions on music directory
|
||||
* log: print reason for failure
|
||||
* event_pipe: fix WIN32 regression
|
||||
* define WINVER in ./configure
|
||||
* WIN32: autodetect filesystem encoding
|
||||
|
||||
|
||||
ver 0.16.5 (2011/10/09)
|
||||
* configure.ac
|
||||
- disable assertions in the non-debugging build
|
||||
- show solaris plugin result correctly
|
||||
- add option --enable-solaris-output
|
||||
* pcm_format: fix 32-to-24 bit conversion (the "silence" bug)
|
||||
* input:
|
||||
- rewind: reduce heap usage
|
||||
* decoder:
|
||||
- ffmpeg: higher precision timestamps
|
||||
- ffmpeg: don't require key frame for seeking
|
||||
- fix CUE track seeking
|
||||
* output:
|
||||
- openal: auto-fallback to mono if channel count is unsupported
|
||||
* player:
|
||||
- make seeking to CUE track more reliable
|
||||
- the "seek" command works when MPD is stopped
|
||||
- restore song position from state file (bug fix)
|
||||
- fix crash that sometimes occurred when audio device fails on startup
|
||||
- fix absolute path support in playlists
|
||||
* WIN32: close sockets properly
|
||||
* install systemd service file if systemd is available
|
||||
|
||||
|
||||
ver 0.16.4 (2011/09/01)
|
||||
* don't abort configure when avahi is not found
|
||||
* auto-detect libmad without pkg-config
|
||||
* fix memory leaks
|
||||
* don't resume playback when seeking to another song while paused
|
||||
* apply follow_inside_symlinks to absolute symlinks
|
||||
* fix playback discontinuation after seeking
|
||||
* input:
|
||||
- curl: limit the receive buffer size
|
||||
- curl: implement a hard-coded timeout of 10 seconds
|
||||
* decoder:
|
||||
- ffmpeg: workaround for semantic API change in recent ffmpeg versions
|
||||
- flac: validate the sample rate when scanning the tag
|
||||
- wavpack: obey all decoder commands, stop at CUE track border
|
||||
* encoder:
|
||||
- vorbis: don't send end-of-stream on flush
|
||||
* output:
|
||||
- alsa: fix SIGFPE when alsa announces a period size of 0
|
||||
- httpd: don't warn on client disconnect
|
||||
- osx: don't drain the buffer when closing
|
||||
- pulse: fix deadlock when resuming the stream
|
||||
- pulse: fix deadlock when the stream was suspended
|
||||
|
||||
|
||||
ver 0.16.3 (2011/06/04)
|
||||
* fix assertion failure in audio format mask parser
|
||||
* fix NULL pointer dereference in playlist parser
|
||||
* fix playlist files in base music directory
|
||||
* database: allow directories with just playlists
|
||||
* decoder:
|
||||
- ffmpeg: support libavcodec 0.7
|
||||
|
||||
|
||||
ver 0.16.2 (2011/03/18)
|
||||
* configure.ac:
|
||||
- fix bashism in tremor test
|
||||
* decoder:
|
||||
- tremor: fix configure test
|
||||
- gme: detect end of song
|
||||
* encoder:
|
||||
- vorbis: reset the Ogg stream after flush
|
||||
* output:
|
||||
- httpd: fix uninitialized variable
|
||||
- httpd: include sys/socket.h
|
||||
- oss: AFMT_S24_PACKED is little-endian
|
||||
- oss: disable 24 bit playback on FreeBSD
|
||||
|
||||
|
||||
ver 0.16.1 (2011/01/09)
|
||||
* audio_check: fix parameter in prototype
|
||||
* add void casts to suppress "result unused" warnings (clang)
|
||||
* input:
|
||||
@@ -128,9 +326,20 @@ ver 0.16 (2010/12/11)
|
||||
* make single mode 'sticky'
|
||||
|
||||
|
||||
ver 0.15.16 (2010/??/??)
|
||||
ver 0.15.17 (2011/??/??)
|
||||
* encoder:
|
||||
- vorbis: reset the Ogg stream after flush
|
||||
* decoders:
|
||||
- vorbis: fix tremor support
|
||||
|
||||
|
||||
ver 0.15.16 (2011/03/13)
|
||||
* output:
|
||||
- ao: initialize the ao_sample_format struct
|
||||
- jack: fix crash with mono playback
|
||||
* encoders:
|
||||
- lame: explicitly configure the output sample rate
|
||||
* update: log all file permission problems
|
||||
|
||||
|
||||
ver 0.15.15 (2010/11/08)
|
||||
|
@@ -16,7 +16,7 @@ if test -n "$AM_FORCE_VERSION"
|
||||
then
|
||||
AM_VERSIONS="$AM_FORCE_VERSION"
|
||||
else
|
||||
AM_VERSIONS='1.11 1.10'
|
||||
AM_VERSIONS='1.11'
|
||||
fi
|
||||
if test -n "$AC_FORCE_VERSION"
|
||||
then
|
||||
|
523
configure.ac
523
configure.ac
@@ -1,11 +1,19 @@
|
||||
AC_PREREQ(2.60)
|
||||
AC_INIT(mpd, 0.16.1, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
AC_INIT(mpd, 0.17.2, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=17
|
||||
VERSION_REVISION=0
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AM_INIT_AUTOMAKE([foreign 1.11 dist-bzip2 subdir-objects])
|
||||
AM_SILENT_RULES
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.16.0", [The MPD protocol version])
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.17.0", [The MPD protocol version])
|
||||
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -29,20 +37,39 @@ if test x$CXX = xg++; then
|
||||
HAVE_CXX=no
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_CXX, test x$HAVE_CXX = xyes)
|
||||
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_MAKE_SET
|
||||
PKG_PROG_PKG_CONFIG
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
|
||||
[], [with_systemdsystemunitdir=no])
|
||||
if test "x$with_systemdsystemunitdir" = xyes; then
|
||||
AC_MSG_CHECKING(for systemd)
|
||||
with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||
if test -z "$with_systemdsystemunitdir"; then
|
||||
AC_MSG_ERROR([Failed to detect systemd])
|
||||
fi
|
||||
AC_MSG_RESULT([$with_systemdsystemunitdir])
|
||||
fi
|
||||
if test "x$with_systemdsystemunitdir" != xno; then
|
||||
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Declare Variables
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_SUBST(AM_CPPFLAGS,"")
|
||||
AC_SUBST(AM_CFLAGS,"")
|
||||
AC_SUBST(AM_CXXFLAGS,"")
|
||||
|
||||
AC_SUBST(MPD_LIBS)
|
||||
AC_SUBST(MPD_CFLAGS)
|
||||
MPD_LIBS=""
|
||||
MPD_CFLAGS=""
|
||||
## Used for the windows resource file
|
||||
AC_SUBST(VERSION_MAJOR)
|
||||
AC_SUBST(VERSION_MINOR)
|
||||
AC_SUBST(VERSION_REVISION)
|
||||
AC_SUBST(VERSION_EXTRA)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl OS Specific Defaults
|
||||
@@ -51,9 +78,16 @@ AC_CANONICAL_HOST
|
||||
|
||||
case "$host_os" in
|
||||
mingw32* | windows*)
|
||||
MPD_LIBS="$MPD_LIBS -lws2_32"
|
||||
AC_CONFIG_FILES([
|
||||
src/win/mpd_win32_rc.rc
|
||||
])
|
||||
AC_CHECK_TOOL(WINDRES, windres)
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0501"
|
||||
LIBS="$LIBS -lws2_32"
|
||||
HAVE_WINDOWS=1
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL([HAVE_WINDOWS], [test x$HAVE_WINDOWS = x1])
|
||||
|
||||
if test -z "$prefix" || test "x$prefix" = xNONE; then
|
||||
local_lib=
|
||||
@@ -85,7 +119,7 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
|
||||
done
|
||||
for d in $local_include; do
|
||||
if test -d "$d"; then
|
||||
CFLAGS="$CFLAGS -I$d"
|
||||
CPPFLAGS="$CPPFLAGS -I$d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -94,22 +128,18 @@ fi
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Header/Library Checks
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_CHECK_FUNCS(daemon fork syslog)
|
||||
if test $ac_cv_func_syslog = no; then
|
||||
# syslog is not in the default libraries. See if it's in some other.
|
||||
for lib in bsd socket inet; do
|
||||
AC_CHECK_LIB($lib, syslog,
|
||||
[AC_DEFINE(HAVE_SYSLOG)
|
||||
LIBS="$LIBS -l$lib"; break])
|
||||
done
|
||||
fi
|
||||
AC_CHECK_FUNCS(daemon fork)
|
||||
|
||||
AC_CHECK_LIB(socket,socket,MPD_LIBS="$MPD_LIBS -lsocket",)
|
||||
AC_CHECK_LIB(nsl,gethostbyname,MPD_LIBS="$MPD_LIBS -lnsl",)
|
||||
AC_SEARCH_LIBS([syslog], [bsd socket inet],
|
||||
[AC_DEFINE(HAVE_SYSLOG, 1, [Define if syslog() is available])])
|
||||
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([gethostbyname], [nsl])
|
||||
|
||||
AC_CHECK_FUNCS(pipe2 accept4)
|
||||
|
||||
AC_CHECK_LIB(m,exp,MPD_LIBS="$MPD_LIBS -lm",)
|
||||
AC_SEARCH_LIBS([exp], [m],,
|
||||
[AC_MSG_ERROR([exp() not found])])
|
||||
|
||||
AC_CHECK_HEADERS(locale.h)
|
||||
AC_CHECK_HEADERS(valgrind/memcheck.h)
|
||||
@@ -121,6 +151,11 @@ AC_ARG_ENABLE(alsa,
|
||||
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
|
||||
[enable_alsa=auto])
|
||||
|
||||
AC_ARG_ENABLE(roar,
|
||||
AS_HELP_STRING([--enable-roar],
|
||||
[enable support for RoarAudio]),,
|
||||
[enable_roar=auto])
|
||||
|
||||
AC_ARG_ENABLE(ao,
|
||||
AS_HELP_STRING([--enable-ao],
|
||||
[enable support for libao]),,
|
||||
@@ -136,16 +171,21 @@ AC_ARG_ENABLE(bzip2,
|
||||
[enable bzip2 archive support (default: disabled)]),,
|
||||
enable_bzip2=no)
|
||||
|
||||
AC_ARG_ENABLE(cue,
|
||||
AS_HELP_STRING([--enable-cue],
|
||||
[enable support for libcue support]),,
|
||||
enable_cue=auto)
|
||||
AC_ARG_ENABLE(cdio-paranoia,
|
||||
AS_HELP_STRING([--enable-cdio-paranoia],
|
||||
[enable support for audio CD support]),,
|
||||
enable_cdio_paranoia=auto)
|
||||
|
||||
AC_ARG_ENABLE(curl,
|
||||
AS_HELP_STRING([--enable-curl],
|
||||
[enable support for libcurl HTTP streaming (default: auto)]),,
|
||||
[enable_curl=auto])
|
||||
|
||||
AC_ARG_ENABLE(soup,
|
||||
AS_HELP_STRING([--enable-soup],
|
||||
[enable support for libsoup HTTP streaming (default: auto)]),,
|
||||
[enable_soup=auto])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
AS_HELP_STRING([--enable-debug],
|
||||
[enable debugging (default: disabled)]),,
|
||||
@@ -171,34 +211,29 @@ AC_ARG_ENABLE(fifo,
|
||||
enable_fifo=yes)
|
||||
|
||||
AC_ARG_ENABLE(flac,
|
||||
AS_HELP_STRING([--disable-flac],
|
||||
[disable flac support (default: enable)]),,
|
||||
enable_flac=yes)
|
||||
AS_HELP_STRING([--enable-flac],
|
||||
[enable FLAC decoder]),,
|
||||
enable_flac=auto)
|
||||
|
||||
AC_ARG_ENABLE(fluidsynth,
|
||||
AS_HELP_STRING([--enable-fluidsynth],
|
||||
[enable MIDI support via fluidsynth (default: disable)]),,
|
||||
enable_fluidsynth=no)
|
||||
[enable MIDI support via fluidsynth (default: auto)]),,
|
||||
enable_fluidsynth=auto)
|
||||
|
||||
AC_ARG_ENABLE(gme,
|
||||
AS_HELP_STRING([--enable-gme],
|
||||
[enable Blargg's game music emulator plugin]),,
|
||||
enable_gme=auto)
|
||||
|
||||
AC_ARG_ENABLE(gprof,
|
||||
AS_HELP_STRING([--enable-gprof],
|
||||
[enable profiling via gprof (default: disabled)]),,
|
||||
enable_gprof=no)
|
||||
|
||||
AC_ARG_ENABLE(httpd-output,
|
||||
AS_HELP_STRING([--enable-httpd-output],
|
||||
[enables the HTTP server output]),,
|
||||
[enable_httpd_output=auto])
|
||||
|
||||
AC_ARG_ENABLE(id3,
|
||||
AS_HELP_STRING([--disable-id3],
|
||||
[disable id3 support (default: enable)]),,
|
||||
enable_id3=yes)
|
||||
AS_HELP_STRING([--enable-id3],
|
||||
[enable id3 support]),,
|
||||
enable_id3=auto)
|
||||
|
||||
AC_ARG_ENABLE(inotify,
|
||||
AS_HELP_STRING([--disable-inotify],
|
||||
@@ -227,6 +262,16 @@ AC_ARG_ENABLE(lastfm,
|
||||
[enable support for last.fm radio (default: disable)]),,
|
||||
[enable_lastfm=no])
|
||||
|
||||
AC_ARG_ENABLE(despotify,
|
||||
AS_HELP_STRING([--enable-despotify],
|
||||
[enable support for despotify (default: disable)]),,
|
||||
[enable_despotify=no])
|
||||
|
||||
AC_ARG_ENABLE(soundcloud,
|
||||
AS_HELP_STRING([--enable-soundcloud],
|
||||
[enable support for soundcloud.com]),,
|
||||
[enable_soundcloud=auto])
|
||||
|
||||
AC_ARG_ENABLE(lame-encoder,
|
||||
AS_HELP_STRING([--enable-lame-encoder],
|
||||
[enable the LAME mp3 encoder]),,
|
||||
@@ -276,11 +321,6 @@ AC_ARG_ENABLE(mvp,
|
||||
[enable support for Hauppauge Media MVP (default: disable)]),,
|
||||
enable_mvp=no)
|
||||
|
||||
AC_ARG_ENABLE(oggflac,
|
||||
AS_HELP_STRING([--disable-oggflac],
|
||||
[disable OggFLAC support (default: enable)]),,
|
||||
enable_oggflac=yes)
|
||||
|
||||
AC_ARG_ENABLE(openal,
|
||||
AS_HELP_STRING([--enable-openal],
|
||||
[enable OpenAL support (default: disable)]),,
|
||||
@@ -322,11 +362,21 @@ AC_ARG_ENABLE(sndfile,
|
||||
[enable sndfile support]),,
|
||||
enable_sndfile=auto)
|
||||
|
||||
AC_ARG_ENABLE(solaris_output,
|
||||
AS_HELP_STRING([--enable-solaris-output],
|
||||
[enables the Solaris /dev/audio output]),,
|
||||
[enable_solaris_output=auto])
|
||||
|
||||
AC_ARG_ENABLE(sqlite,
|
||||
AS_HELP_STRING([--enable-sqlite],
|
||||
[enable support for the SQLite database]),,
|
||||
[enable_sqlite=auto])
|
||||
|
||||
AC_ARG_ENABLE(systemd-daemon,
|
||||
AS_HELP_STRING([--enable-systemd-daemon],
|
||||
[use the systemd daemon library (default=auto)]),,
|
||||
[enable_systemd_daemon=auto])
|
||||
|
||||
AC_ARG_ENABLE(tcp,
|
||||
AS_HELP_STRING([--disable-tcp],
|
||||
[disable support for clients connecting via TCP (default: enable)]),,
|
||||
@@ -353,9 +403,9 @@ AC_ARG_ENABLE(un,
|
||||
[enable_un=yes])
|
||||
|
||||
AC_ARG_ENABLE(vorbis,
|
||||
AS_HELP_STRING([--disable-vorbis],
|
||||
[disable Ogg Vorbis support (default: enable)]),,
|
||||
enable_vorbis=yes)
|
||||
AS_HELP_STRING([--enable-vorbis],
|
||||
[enable Ogg Vorbis decoder]),,
|
||||
enable_vorbis=auto)
|
||||
|
||||
AC_ARG_ENABLE(vorbis-encoder,
|
||||
AS_HELP_STRING([--enable-vorbis-encoder],
|
||||
@@ -406,8 +456,13 @@ AC_ARG_WITH(tremor-includes,
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Mandatory Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12 gthread-2.0],,
|
||||
[AC_MSG_ERROR([GLib 2.12 is required])])
|
||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16 gthread-2.0],,
|
||||
[AC_MSG_ERROR([GLib 2.16 is required])])
|
||||
|
||||
if test x$GCC = xyes; then
|
||||
# suppress warnings in the GLib headers
|
||||
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Protocol Options
|
||||
@@ -423,7 +478,11 @@ if test x$enable_ipv6 = xyes; then
|
||||
AC_EGREP_CPP([AP_maGiC_VALUE],
|
||||
[
|
||||
#include <sys/types.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#ifdef PF_INET6
|
||||
#ifdef AF_INET6
|
||||
@@ -460,6 +519,13 @@ if
|
||||
AC_MSG_ERROR([No client interfaces configured!])
|
||||
fi
|
||||
|
||||
MPD_AUTO_PKG(systemd_daemon, SYSTEMD_DAEMON, libsystemd-daemon,
|
||||
[systemd activation], [libsystemd-daemon not found])
|
||||
AM_CONDITIONAL(ENABLE_SYSTEMD_DAEMON, test x$enable_systemd_daemon = xyes)
|
||||
if test x$enable_systemd_daemon = xyes; then
|
||||
AC_DEFINE([ENABLE_SYSTEMD_DAEMON], 1, [Define to use the systemd daemon library])
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl LIBC Features
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -499,24 +565,9 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl Metadata Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl ---------------------------------- libcue ---------------------------------
|
||||
MPD_AUTO_PKG(cue, CUE, [libcue],
|
||||
[libcue parsing library], [libcue not found])
|
||||
if test x$enable_cue = xyes; then
|
||||
AC_DEFINE([HAVE_CUE], 1,
|
||||
[Define to enable libcue support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes)
|
||||
|
||||
dnl -------------------------------- libid3tag --------------------------------
|
||||
if test x$enable_id3 = xyes; then
|
||||
PKG_CHECK_MODULES([ID3TAG], [id3tag],,
|
||||
AC_CHECK_LIB(id3tag, id3_file_open,
|
||||
[ID3TAG_LIBS="-lid3tag -lz" ID3TAG_CFLAGS=""],
|
||||
enable_id3=no))
|
||||
fi
|
||||
|
||||
MPD_AUTO_PKG_LIB(id3, ID3TAG, id3tag, id3tag, id3_file_open, [-lid3tag -lz], [],
|
||||
[id3tag], [libid3tag not found])
|
||||
if test x$enable_id3 = xyes; then
|
||||
AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag])
|
||||
fi
|
||||
@@ -530,36 +581,38 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl --------------------------------- zeroconf --------------------------------
|
||||
|
||||
case $with_zeroconf in
|
||||
no|avahi|bonjour)
|
||||
no|bonjour)
|
||||
enable_avahi=no
|
||||
;;
|
||||
|
||||
avahi)
|
||||
enable_avahi=yes
|
||||
;;
|
||||
|
||||
*)
|
||||
with_zeroconf=auto
|
||||
enable_avahi=auto
|
||||
;;
|
||||
esac
|
||||
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client avahi-glib],
|
||||
[avahi client library], [avahi client+glib not found])
|
||||
if test x$enable_avahi = xyes; then
|
||||
AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])
|
||||
with_zeroconf=avahi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_AVAHI, test x$enable_avahi = xyes)
|
||||
|
||||
enable_bounjour=no
|
||||
if test x$with_zeroconf != xno; then
|
||||
if test x$with_zeroconf = xavahi || test x$with_zeroconf = xauto; then
|
||||
PKG_CHECK_MODULES([AVAHI], [avahi-client avahi-glib],
|
||||
[found_avahi=1;AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])]
|
||||
MPD_LIBS="$MPD_LIBS $AVAHI_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AVAHI_CFLAGS",
|
||||
[found_avahi=0])
|
||||
fi
|
||||
|
||||
if test x$found_avahi = x1; then
|
||||
with_zeroconf=avahi
|
||||
elif test x$with_zeroconf = xavahi; then
|
||||
AC_MSG_ERROR([Avahi support requested but not found])
|
||||
fi
|
||||
|
||||
if test x$with_zeroconf = xbonjour || test x$with_zeroconf = xauto; then
|
||||
AC_CHECK_HEADER(dns_sd.h,
|
||||
[found_bonjour=1;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])],
|
||||
[found_bonjour=0])
|
||||
AC_CHECK_LIB(dns_sd, DNSServiceRegister,
|
||||
MPD_LIBS="$MPD_LIBS -ldns_sd")
|
||||
[enable_bonjour=yes;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])])
|
||||
AC_CHECK_LIB([dns_sd], [DNSServiceRegister])
|
||||
fi
|
||||
|
||||
if test x$found_bonjour = x1; then
|
||||
if test x$enable_bonjour = xyes; then
|
||||
with_zeroconf=bonjour
|
||||
elif test x$with_zeroconf = xbonjour; then
|
||||
AC_MSG_ERROR([Bonjour support requested but not found])
|
||||
@@ -574,7 +627,6 @@ if test x$with_zeroconf != xno; then
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno)
|
||||
AM_CONDITIONAL(HAVE_AVAHI, test x$with_zeroconf = xavahi)
|
||||
AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -624,6 +676,14 @@ if test x$enable_curl = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_CURL, test x$enable_curl = xyes)
|
||||
|
||||
dnl ----------------------------------- SOUP ----------------------------------
|
||||
MPD_AUTO_PKG(soup, SOUP, [libsoup-2.4],
|
||||
[libsoup HTTP streaming], [libsoup not found])
|
||||
if test x$enable_soup = xyes; then
|
||||
AC_DEFINE(ENABLE_SOUP, 1, [Define when libsoup is used for HTTP streaming])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SOUP, test x$enable_soup = xyes)
|
||||
|
||||
dnl --------------------------------- Last.FM ---------------------------------
|
||||
if test x$enable_lastfm = xyes; then
|
||||
if test x$enable_curl != xyes; then
|
||||
@@ -634,10 +694,38 @@ if test x$enable_lastfm = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
||||
|
||||
dnl ---------------------------------- libogg ---------------------------------
|
||||
if test x$with_tremor == xno || test -z $with_tremor; then
|
||||
PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no)
|
||||
dnl --------------------------------- Despotify ---------------------------------
|
||||
MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
|
||||
[Despotify support], [despotify not found])
|
||||
if test x$enable_despotify = xyes; then
|
||||
AC_DEFINE(ENABLE_DESPOTIFY, 1, [Define when despotify is enabled])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_DESPOTIFY, test x$enable_despotify = xyes)
|
||||
|
||||
dnl --------------------------------- Soundcloud ------------------------------
|
||||
if test x$enable_soundcloud != xno; then
|
||||
PKG_CHECK_MODULES([YAJL], [yajl >= 2.0],
|
||||
[found_soundcloud=yes],
|
||||
AC_CHECK_LIB([yajl], [yajl_alloc],
|
||||
[found_soundcloud=yes YAJL_CFLAGS=-DHAVE_YAJL1 YAJL_LIBS=-lyajl],
|
||||
[found_soundcloud=no]))
|
||||
fi
|
||||
MPD_AUTO_RESULT([soundcloud], [soundcloud.com support], [libyajl not found])
|
||||
if test x$enable_soundcloud = xyes; then
|
||||
AC_DEFINE(ENABLE_SOUNDCLOUD, 1, [Define when soundcloud is enabled])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SOUNDCLOUD, test x$enable_soundcloud = xyes)
|
||||
AC_SUBST(YAJL_LIBS)
|
||||
|
||||
dnl ---------------------------------- cdio ---------------------------------
|
||||
MPD_AUTO_PKG(cdio_paranoia, CDIO_PARANOIA, [libcdio_paranoia],
|
||||
[libcdio_paranoia audio CD library], [libcdio_paranoia not found])
|
||||
if test x$enable_cdio_paranoia = xyes; then
|
||||
AC_DEFINE([ENABLE_CDIO_PARANOIA], 1,
|
||||
[Define to enable libcdio_paranoia support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_CDIO_PARANOIA, test x$enable_cdio_paranoia = xyes)
|
||||
|
||||
dnl ---------------------------------- libmms ---------------------------------
|
||||
MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
|
||||
@@ -670,9 +758,10 @@ AM_CONDITIONAL(ENABLE_ISO9660_TEST, test x$MKISOFS != xno)
|
||||
dnl ---------------------------------- libbz2 ---------------------------------
|
||||
if test x$enable_bzip2 = xyes; then
|
||||
AC_CHECK_LIB(bz2, BZ2_bzDecompressInit,
|
||||
[MPD_LIBS="$MPD_LIBS -lbz2"],
|
||||
[BZ2_LIBS="-lbz2"],
|
||||
[AC_MSG_ERROR([libbz2 not found])])
|
||||
fi
|
||||
AC_SUBST(BZ2_LIBS)
|
||||
|
||||
AM_CONDITIONAL(HAVE_BZ2, test x$enable_bzip2 = xyes)
|
||||
if test x$enable_bzip2 = xyes; then
|
||||
@@ -732,20 +821,9 @@ AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes)
|
||||
AM_CONDITIONAL(HAVE_MP4, test x$enable_mp4 = xyes)
|
||||
|
||||
dnl ---------------------------------- ffmpeg ---------------------------------
|
||||
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52 libavcodec >= 51 libavutil >= 49],
|
||||
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52.31 libavcodec >= 52.20 libavutil >= 49.15],
|
||||
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
|
||||
|
||||
if test x$enable_ffmpeg = xyes; then
|
||||
# prior to ffmpeg svn12865, you had to specify include files
|
||||
# without path prefix
|
||||
old_CPPCFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $FFMPEG_CFLAGS"
|
||||
AC_CHECK_HEADER(libavcodec/avcodec.h,,
|
||||
AC_DEFINE(OLD_FFMPEG_INCLUDES, 1,
|
||||
[Define if avcodec.h instead of libavcodec/avcodec.h should be included]))
|
||||
CPPCFLAGS=$old_CPPFLAGS
|
||||
fi
|
||||
|
||||
if test x$enable_ffmpeg = xyes; then
|
||||
AC_DEFINE(HAVE_FFMPEG, 1, [Define for FFMPEG support])
|
||||
fi
|
||||
@@ -753,31 +831,12 @@ fi
|
||||
AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
|
||||
|
||||
dnl ----------------------------------- FLAC ----------------------------------
|
||||
|
||||
MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
|
||||
[FLAC decoder], [libFLAC not found])
|
||||
|
||||
if test x$enable_flac = xyes; then
|
||||
PKG_CHECK_MODULES(FLAC, [flac >= 1.1],
|
||||
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]),
|
||||
enable_flac=no)
|
||||
|
||||
oldcflags="$CFLAGS"
|
||||
oldlibs="$LIBS"
|
||||
CFLAGS="$CFLAGS $FLAC_CFLAGS"
|
||||
LIBS="$LIBS $FLAC_LIBS"
|
||||
if test x$enable_flac = xyes && test x$enable_oggflac = xyes; then
|
||||
AC_CHECK_DECL(FLAC_API_SUPPORTS_OGG_FLAC,
|
||||
[enable_oggflac=flac], [],
|
||||
[#include <FLAC/export.h>])
|
||||
fi
|
||||
CFLAGS="$oldcflags"
|
||||
LIBS="$oldlibs"
|
||||
|
||||
if test x$enable_oggflac = xflac; then
|
||||
if test x$enable_ogg = xyes; then
|
||||
FLAC_LIBS="${FLAC_LIBS} -logg"
|
||||
else
|
||||
enable_oggflac=yes
|
||||
AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")
|
||||
fi
|
||||
fi
|
||||
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes)
|
||||
@@ -786,7 +845,7 @@ enable_flac_encoder=$enable_flac
|
||||
|
||||
dnl -------------------------------- FluidSynth -------------------------------
|
||||
if test x$enable_fluidsynth = xyes; then
|
||||
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth],
|
||||
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth >= 1.1],
|
||||
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]),
|
||||
enable_fluidsynth=no)
|
||||
fi
|
||||
@@ -802,7 +861,8 @@ if test x$enable_gme = xyes; then
|
||||
fi
|
||||
|
||||
dnl ---------------------------------- libmad ---------------------------------
|
||||
MPD_AUTO_PKG(mad, MAD, [mad],
|
||||
MPD_AUTO_PKG_LIB(mad, MAD, [mad],
|
||||
mad, mad_stream_init, [-lmad], [],
|
||||
[libmad MP3 decoder plugin], [libmad not found])
|
||||
if test x$enable_mad = xyes; then
|
||||
AC_DEFINE(HAVE_MAD, 1, [Define to use libmad])
|
||||
@@ -862,52 +922,32 @@ AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes)
|
||||
|
||||
dnl --------------------------------- musepack --------------------------------
|
||||
if test x$enable_mpc = xyes; then
|
||||
if test "x$mpcdec_libraries" != "x" ; then
|
||||
MPCDEC_LIBS="-L$mpcdec_libraries"
|
||||
elif test "x$mpcdec_prefix" != "x" ; then
|
||||
MPCDEC_LIBS="-L$mpcdec_prefix/lib"
|
||||
fi
|
||||
|
||||
MPCDEC_LIBS="$MPCDEC_LIBS -lmpcdec"
|
||||
|
||||
if test "x$mpcdec_includes" != "x" ; then
|
||||
MPCDEC_CFLAGS="-I$mpcdec_includes"
|
||||
elif test "x$mpcdec_prefix" != "x" ; then
|
||||
MPCDEC_CFLAGS="-I$mpcdec_prefix/include"
|
||||
fi
|
||||
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $MPD_CFLAGS $MPCDEC_CFLAGS -I."
|
||||
LIBS="$LIBS $MPD_LIBS $MPCDEC_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
AC_CHECK_HEADER(mpc/mpcdec.h,
|
||||
old_mpcdec=no,
|
||||
[AC_CHECK_HEADER(mpcdec/mpcdec.h,
|
||||
old_mpcdec=yes,
|
||||
enable_mpc=no)])
|
||||
if test x$enable_mpc = xyes; then
|
||||
AC_CHECK_LIB(mpcdec,main,
|
||||
[MPD_LIBS="$MPD_LIBS $MPCDEC_LIBS";
|
||||
MPD_CFLAGS="$MPD_CFLAGS $MPCDEC_CFLAGS";],
|
||||
AC_CHECK_LIB(mpcdec,main,
|
||||
MPCDEC_LIBS="$MPCDEC_LIBS -lmpcdec",
|
||||
enable_mpc=no)
|
||||
fi
|
||||
if test x$enable_mpc = xyes; then
|
||||
AC_DEFINE(HAVE_MPCDEC,1,
|
||||
[Define to use libmpcdec for MPC decoding])
|
||||
if test x$old_mpcdec = xyes; then
|
||||
AC_DEFINE(MPC_IS_OLD_API, 1,
|
||||
[Define if an old pre-SV8 libmpcdec is used])
|
||||
fi
|
||||
else
|
||||
AC_MSG_WARN([mpcdec lib needed for MPC support -- disabling MPC support])
|
||||
fi
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
|
||||
if test x$enable_mpc = xyes; then
|
||||
AC_CHECK_HEADER([mpc/mpcdec.h],
|
||||
[AC_DEFINE(HAVE_MPCDEC,1,
|
||||
[Define to use libmpcdec for MPC decoding])],
|
||||
[AC_CHECK_HEADER(mpcdec/mpcdec.h,
|
||||
[AC_DEFINE(MPC_IS_OLD_API, 1,
|
||||
[Define if an old pre-SV8 libmpcdec is used])]
|
||||
)]
|
||||
)
|
||||
else
|
||||
AC_MSG_WARN([mpcdec lib needed for MPC support -- disabling MPC support])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(MPCDEC_LIBS)
|
||||
AC_SUBST(MPCDEC_CFLAGS)
|
||||
AM_CONDITIONAL(HAVE_MPCDEC, test x$enable_mpc = xyes)
|
||||
|
||||
dnl -------------------------------- Ogg Tremor -------------------------------
|
||||
@@ -934,53 +974,45 @@ if test x$enable_tremor = xyes; then
|
||||
ac_save_LIBS="$LIBS"
|
||||
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
||||
LIBS="$LIBS $TREMOR_LIBS"
|
||||
AC_CHECK_LIB(vorbisidec,ov_read,enable_vorbis=yes,enable_vorbis=no;
|
||||
AC_CHECK_LIB(vorbisidec,ov_read,,enable_tremor=no;
|
||||
AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
|
||||
if test x$enable_tremor = xyes; then
|
||||
AC_DEFINE(HAVE_TREMOR,1,
|
||||
[Define to use tremor (libvorbisidec) for ogg support])
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||
else
|
||||
TREMOR_CFLAGS=
|
||||
TREMOR_LIBS=
|
||||
fi
|
||||
|
||||
AC_SUBST(TREMOR_CFLAGS)
|
||||
AC_SUBST(TREMOR_LIBS)
|
||||
|
||||
dnl --------------------------------- OggFLAC ---------------------------------
|
||||
dnl OggFLAC must go after Ogg Tremor
|
||||
|
||||
if test x$enable_tremor = xyes && test x$enable_oggflac = xyes; then
|
||||
AC_MSG_WARN([disabling OggFLAC support because it is incompatible with tremor])
|
||||
enable_oggflac=no
|
||||
fi
|
||||
|
||||
if test x$enable_oggflac = xyes; then
|
||||
AC_CHECK_HEADER([OggFLAC/stream_decoder.h],, enable_oggflac=no)
|
||||
fi
|
||||
|
||||
if test x$enable_oggflac = xyes; then
|
||||
AC_DEFINE(HAVE_OGGFLAC,1,[Define for OggFLAC support])
|
||||
MPD_LIBS="$MPD_LIBS -lOggFLAC -lFLAC -lm"
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes)
|
||||
|
||||
dnl -------------------------------- Ogg Vorbis -------------------------------
|
||||
if test x$enable_vorbis = xyes; then
|
||||
if test x$enable_tremor = xyes; then
|
||||
|
||||
if test x$enable_tremor = xyes; then
|
||||
if test x$enable_vorbis = xyes; then
|
||||
AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."])
|
||||
enable_vorbis=no
|
||||
elif test x$enable_ogg = xyes; then
|
||||
PKG_CHECK_MODULES(VORBIS, [vorbis vorbisfile],
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]),
|
||||
enable_vorbis=no)
|
||||
else
|
||||
AC_MSG_WARN(["Ogg not detected, could not enable Vorbis."])
|
||||
enable_vorbis=no
|
||||
fi
|
||||
enable_vorbis=no
|
||||
|
||||
if test x$enable_vorbis_encoder = xauto; then
|
||||
AC_MSG_WARN([OggTremor detected, disabling the Vorbis encoder plugin.])
|
||||
enable_vorbis_encoder=no
|
||||
fi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes)
|
||||
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg],
|
||||
[Ogg Vorbis decoder], [libvorbis not found])
|
||||
if test x$enable_vorbis = xyes; then
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
|
||||
|
||||
dnl --------------------------------- sidplay ---------------------------------
|
||||
found_sidplay=$HAVE_CXX
|
||||
@@ -989,19 +1021,18 @@ MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], [No C++ compiler found])
|
||||
if test x$enable_sidplay != xno; then
|
||||
# we're not using pkg-config here
|
||||
# because libsidplay2's .pc file requires libtool
|
||||
AC_HAVE_LIBRARY(sidplay2, [found_sidplay=yes], [found_sidplay=no])
|
||||
AC_CHECK_LIB([sidplay2],[main],[found_sidplay=yes],[found_sidplay=no],[])
|
||||
|
||||
MPD_AUTO_PRE(sidplay, [sidplay decoder plugin],
|
||||
[libsidplay2 not found])
|
||||
fi
|
||||
|
||||
if test x$enable_sidplay != xno; then
|
||||
# can't use AC_HAVE_LIBRARY here, because the dash in the
|
||||
# library name triggers an autoconf bug
|
||||
AC_CHECK_LIB(resid-builder, main,
|
||||
AC_CHECK_LIB([resid-builder], [main],
|
||||
[found_sidplay=yes], [found_sidplay=no])
|
||||
|
||||
if test x$found_sidplay = xyes; then
|
||||
AC_HAVE_LIBRARY(sidutils,, [found_sidplay=no])
|
||||
AC_CHECK_LIB([sidutils],[main],[],[found_sidplay=no],[])
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT(sidplay, [sidplay decoder plugin],
|
||||
@@ -1064,7 +1095,6 @@ if
|
||||
test x$enable_mp4 = xno &&
|
||||
test x$enable_mpc = xno &&
|
||||
test x$enable_mpg123 = xno &&
|
||||
test x$enable_oggflac = xno &&
|
||||
test x$enable_sidplay = xno &&
|
||||
test x$enable_tremor = xno &&
|
||||
test x$enable_vorbis = xno &&
|
||||
@@ -1075,10 +1105,10 @@ if
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
||||
test x$enable_vorbis = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
|
||||
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
|
||||
|
||||
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
||||
test x$enable_flac = xyes || test x$enable_oggflac = xyes)
|
||||
test x$enable_flac = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Encoders for Streaming Audio Output Plugins
|
||||
@@ -1201,6 +1231,16 @@ fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ALSA, test x$enable_alsa = xyes)
|
||||
|
||||
dnl ----------------------------------- ROAR ----------------------------------
|
||||
MPD_AUTO_PKG(roar, ROAR, [libroar >= 0.4.0],
|
||||
[ROAR output plugin], [libroar not found])
|
||||
|
||||
if test x$enable_roar = xyes; then
|
||||
AC_DEFINE(HAVE_ROAR, 1, [Define to enable ROAR support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ROAR, test x$enable_roar = xyes)
|
||||
|
||||
dnl ----------------------------------- FFADO ---------------------------------
|
||||
|
||||
MPD_AUTO_PKG(ffado, FFADO, [libffado],
|
||||
@@ -1311,7 +1351,7 @@ enable_osx=no
|
||||
case "$host_os" in
|
||||
darwin*)
|
||||
AC_DEFINE(HAVE_OSX, 1, [Define for compiling OS X support])
|
||||
MPD_LIBS="$MPD_LIBS -framework AudioUnit -framework CoreServices"
|
||||
LIBS="$LIBS -framework AudioUnit -framework CoreAudio -framework CoreServices"
|
||||
enable_osx=yes ;;
|
||||
esac
|
||||
|
||||
@@ -1369,16 +1409,22 @@ fi
|
||||
AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
|
||||
|
||||
dnl --------------------------------- Solaris ---------------------------------
|
||||
case "$host_os" in
|
||||
|
||||
if test x$enable_solaris_output = xauto; then
|
||||
case "$host_os" in
|
||||
solaris*)
|
||||
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
|
||||
enable_solaris_output=yes
|
||||
;;
|
||||
|
||||
*)
|
||||
enable_solaris_output=no
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x$enable_solaris_output = xyes; then
|
||||
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes)
|
||||
|
||||
@@ -1388,7 +1434,7 @@ case "$host_os" in
|
||||
mingw32* | windows*)
|
||||
AC_DEFINE(ENABLE_WINMM_OUTPUT, 1, [Define to enable WinMM support])
|
||||
enable_winmm_output=yes
|
||||
MPD_LIBS="$MPD_LIBS -lwinmm"
|
||||
LIBS="$LIBS -lwinmm"
|
||||
;;
|
||||
|
||||
*)
|
||||
@@ -1401,6 +1447,7 @@ AM_CONDITIONAL(ENABLE_WINMM_OUTPUT, test x$enable_winmm_output = xyes)
|
||||
dnl --------------------- Post Audio Output Plugins Tests ---------------------
|
||||
if
|
||||
test x$enable_alsa = xno &&
|
||||
test x$enable_roar = xno &&
|
||||
test x$enable_ao = xno &&
|
||||
test x$enable_ffado = xno &&
|
||||
test x$enable_fifo = xno &&
|
||||
@@ -1450,35 +1497,28 @@ dnl CFLAGS
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl ---------------------------------- debug ----------------------------------
|
||||
#if test "x$enable_debug" = xno; then
|
||||
# don't set NDEBUG for now, until MPD is stable
|
||||
#AM_CFLAGS="$AM_CFLAGS -DNDEBUG"
|
||||
#fi
|
||||
if test "x$enable_debug" = xno; then
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
|
||||
fi
|
||||
|
||||
dnl ----------------------------------- GCC -----------------------------------
|
||||
if test x$GCC = xyes
|
||||
then
|
||||
MPD_CHECK_FLAG([-Wall])
|
||||
MPD_CHECK_FLAG([-Wextra])
|
||||
MPD_CHECK_FLAG([-Wno-deprecated-declarations])
|
||||
MPD_CHECK_FLAG([-Wmissing-prototypes])
|
||||
MPD_CHECK_FLAG([-Wshadow])
|
||||
MPD_CHECK_FLAG([-Wpointer-arith])
|
||||
MPD_CHECK_FLAG([-Wstrict-prototypes])
|
||||
MPD_CHECK_FLAG([-Wcast-qual])
|
||||
MPD_CHECK_FLAG([-Wwrite-strings])
|
||||
MPD_CHECK_FLAG([-pedantic])
|
||||
fi
|
||||
|
||||
dnl ------------------------------ gprof profiler -----------------------------
|
||||
if test "x$enable_gprof" = xyes; then
|
||||
MPD_CFLAGS="$MPD_CFLAGS -pg"
|
||||
MPD_LIBS="$MPD_LIBS -pg"
|
||||
AX_APPEND_COMPILE_FLAGS([-Wall])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wextra])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wmissing-prototypes])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wshadow])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wpointer-arith])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wstrict-prototypes])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
|
||||
AX_APPEND_COMPILE_FLAGS([-pedantic])
|
||||
fi
|
||||
|
||||
dnl ---------------------------- warnings as errors ---------------------------
|
||||
if test "x$enable_werror" = xyes; then
|
||||
AM_CFLAGS="$AM_CFLAGS -Werror -pedantic-errors"
|
||||
AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -1518,7 +1558,6 @@ results(mad, [MAD])
|
||||
results(mpg123, [MPG123])
|
||||
results(mp4, [MP4])
|
||||
results(mpc, [Musepack])
|
||||
results(oggflac, [OggFLAC], flac)
|
||||
printf '\n\t'
|
||||
results(tremor, [OggTremor])
|
||||
results(vorbis, [OggVorbis])
|
||||
@@ -1532,7 +1571,6 @@ results(inotify, [inotify])
|
||||
results(sqlite, [SQLite])
|
||||
|
||||
printf '\nMetadata support:\n\t'
|
||||
results(cue,[cue])
|
||||
results(id3,[ID3])
|
||||
|
||||
printf '\nPlayback support:\n\t'
|
||||
@@ -1542,17 +1580,18 @@ results(fifo,FIFO)
|
||||
results(recorder_output,[File Recorder])
|
||||
results(httpd_output,[HTTP Daemon])
|
||||
results(jack,[JACK])
|
||||
results(ao,[libao])
|
||||
results(oss,[OSS])
|
||||
printf '\n\t'
|
||||
results(ao,[libao])
|
||||
results(mvp, [Media MVP])
|
||||
results(oss,[OSS])
|
||||
results(openal,[OpenAL])
|
||||
results(osx, [OS X])
|
||||
results(pipe_output, [Pipeline])
|
||||
results(pulse, [PulseAudio])
|
||||
results(mvp, [Media MVP])
|
||||
results(shout, [SHOUTcast])
|
||||
printf '\n\t'
|
||||
results(solaris, [Solaris])
|
||||
results(pulse, [PulseAudio])
|
||||
results(roar,[ROAR])
|
||||
results(shout, [SHOUTcast])
|
||||
results(solaris_output, [Solaris])
|
||||
results(winmm_output, [WinMM])
|
||||
|
||||
if
|
||||
@@ -1568,9 +1607,14 @@ if
|
||||
fi
|
||||
|
||||
printf '\nStreaming support:\n\t'
|
||||
results(cdio_paranoia, [CDIO_PARANOIA])
|
||||
results(curl,[CURL])
|
||||
results(despotify,[Despotify])
|
||||
results(lastfm,[Last.FM])
|
||||
results(soundcloud,[Soundcloud])
|
||||
printf '\n\t'
|
||||
results(mms,[MMS])
|
||||
results(soup, [SOUP])
|
||||
|
||||
printf '\n\n##########################################\n\n'
|
||||
|
||||
@@ -1579,6 +1623,9 @@ echo 'Generating files needed for compilation'
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Generate files
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_OUTPUT(Makefile)
|
||||
AC_CONFIG_FILES(Makefile)
|
||||
AC_CONFIG_FILES(doc/doxygen.conf)
|
||||
AC_CONFIG_FILES(mpd.service)
|
||||
AC_OUTPUT
|
||||
|
||||
echo 'MPD is ready for compilation, type "make" to begin.'
|
||||
|
@@ -57,7 +57,7 @@
|
||||
Some example code:
|
||||
</para>
|
||||
|
||||
<programlisting lang="C">static inline bool
|
||||
<programlisting lang="C">static inline int
|
||||
foo(const char *abc, int xyz)
|
||||
{
|
||||
if (abc == NULL) {
|
||||
|
@@ -31,7 +31,7 @@ PROJECT_NAME = MPD
|
||||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = @VERSION@
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
@@ -481,7 +481,7 @@ FILE_VERSION_FILTER =
|
||||
# The QUIET tag can be used to turn on/off the messages that are generated
|
||||
# by doxygen. Possible values are YES and NO. If left blank NO is used.
|
||||
|
||||
QUIET = NO
|
||||
QUIET = YES
|
||||
|
||||
# The WARNINGS tag can be used to turn on/off the warning messages that are
|
||||
# generated by doxygen. Possible values are YES and NO. If left blank
|
||||
@@ -534,7 +534,7 @@ WARN_LOGFILE =
|
||||
# directories like "/usr/src/myproject". Separate the files or directories
|
||||
# with spaces.
|
||||
|
||||
INPUT = src/
|
||||
INPUT = @abs_top_srcdir@/src/
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
@@ -69,6 +69,9 @@ mpd will be saved to this file when mpd is terminated by a TERM signal or by
|
||||
the "kill" command. When mpd is restarted, it will read the state file and
|
||||
restore the state of mpd (including the playlist).
|
||||
.TP
|
||||
.B restore_paused <yes or no>
|
||||
Put MPD into pause mode instead of starting playback after startup.
|
||||
.TP
|
||||
.B user <username>
|
||||
This specifies the user that MPD will run as, if set. MPD should
|
||||
never run as root, and you may use this option to make MPD change its
|
||||
@@ -80,6 +83,10 @@ This specifies which address mpd binds to and listens on. Multiple
|
||||
bind_to_address parameters may be specified. The default is "any", which binds
|
||||
to all available addresses.
|
||||
|
||||
You can set a port that is different from the global port setting,
|
||||
e.g. "localhost:6602". IPv6 addresses must be enclosed in square
|
||||
brackets if you want to configure a port, e.g. "[::1]:6602".
|
||||
|
||||
To bind to a Unix domain socket, specify an absolute path. For a
|
||||
system-wide MPD, we suggest the path "\fB/var/run/mpd/socket\fP".
|
||||
.TP
|
||||
@@ -209,7 +216,7 @@ default is 5.
|
||||
.TP
|
||||
.B max_playlist_length <number>
|
||||
This specifies the maximum number of songs that can be in the playlist. The
|
||||
default is 4096.
|
||||
default is 16384.
|
||||
.TP
|
||||
.B max_command_list_size <size in KiB>
|
||||
This specifies the maximum size a command list can be. The default is 2048.
|
||||
@@ -245,11 +252,12 @@ when saving playlists. The default is "no".
|
||||
This specifies the tag types that will be scanned for and made available to
|
||||
clients. Note that you must recreate (not update) your database for changes to
|
||||
this parameter to take effect. Possible values are artist, album, title,
|
||||
track, name, genre, date, composer, performer, comment, and disc. Multiple
|
||||
tags may be specified as a comma separated list. An example value is
|
||||
"artist,album,title,track". The special value "none" may be used alone to
|
||||
disable all metadata. The default is to use all known tag types except for
|
||||
comments.
|
||||
track, name, genre, date, composer, performer, comment, disc,
|
||||
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
|
||||
An example value is "artist,album,title,track". The special value "none" may
|
||||
be used alone to disable all metadata. The default is to use all known tag
|
||||
types except for comments and those starting with "musicbrainz".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the wheter to support automatic update of music database when
|
||||
@@ -259,6 +267,17 @@ of database.
|
||||
.B auto_update_depth <N>
|
||||
Limit the depth of the directories being watched, 0 means only watch
|
||||
the music directory itself. There is no limit by default.
|
||||
.TP
|
||||
.B despotify_user <name>
|
||||
This specifies the user to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_password <name>
|
||||
This specifies the password to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_high_bitrate <yes or no>
|
||||
This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
|
||||
better but requires more processing and higher bandwidth. Default is yes.
|
||||
.TP
|
||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B type <type>
|
||||
@@ -464,8 +483,26 @@ connect to the icecast server. The default is 2 seconds.
|
||||
.B description <description>
|
||||
This specifies a description of the stream.
|
||||
.TP
|
||||
.B url <url>
|
||||
This specifies a URL associated with the stream.
|
||||
.TP
|
||||
.B genre <genre>
|
||||
This specifies the genre(s) of the stream.
|
||||
.SH OPTIONAL TCP KEEPALIVE PARAMETERS
|
||||
.TP
|
||||
.B tcp_keep_alive <yes or no>
|
||||
Enable tcp keepalive on new client connections. (default is "no")
|
||||
.TP
|
||||
.B tcp_keep_alive_idle <seconds>
|
||||
Time in seconds since the last communication on the connection and before
|
||||
the keepalive probing is started. (default is 7200 seconds)
|
||||
.TP
|
||||
.B tcp_keep_alive_interval <seconds>
|
||||
Interval in seconds between keepalive probes, once a probe started. (default is 75 seconds)
|
||||
.TP
|
||||
.B tcp_keep_alive_count <number of times>
|
||||
Number of failed probes before the connection is pronounced dead and
|
||||
the connection is closed. (default is 9 times)
|
||||
.SH FILES
|
||||
.TP
|
||||
.BI ~/.mpdconf
|
||||
|
@@ -103,15 +103,19 @@
|
||||
#
|
||||
#gapless_mp3_playback "yes"
|
||||
#
|
||||
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
|
||||
# of starting playback after startup.
|
||||
#
|
||||
#restore_paused "no"
|
||||
#
|
||||
# This setting enables MPD to create playlists in a format usable by other
|
||||
# music players.
|
||||
#
|
||||
#save_absolute_paths_in_playlists "no"
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. Optionally, 'comment' can be added to this
|
||||
# list.
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the mpd.conf man page.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
@@ -235,6 +239,7 @@ input {
|
||||
## protocol "icecast2" # optional
|
||||
## user "source" # optional
|
||||
## description "My Stream Description" # optional
|
||||
## url "http://example.com" # optional
|
||||
## genre "jazz" # optional
|
||||
## public "no" # optional
|
||||
## timeout "2" # optional
|
||||
@@ -366,6 +371,35 @@ input {
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Client TCP keep alive #######################################################
|
||||
#
|
||||
# For clients connected by TCP on supported platforms.
|
||||
# Allows detection of dangling connections due to clients disappearing from
|
||||
# the network without closing their connections.
|
||||
#
|
||||
# This is not usually necessary but can be useful in cases such as wifi connectected
|
||||
# clients that go in and out of network range or turn off wifi without closing their
|
||||
# connections. Combined with low max_connections this can soon cause clients to not
|
||||
# be able to connect.
|
||||
#
|
||||
#
|
||||
# Enable tcp keepalive on new client connections (default is "no")
|
||||
#
|
||||
#tcp_keep_alive "no"
|
||||
#
|
||||
# Time in seconds since the last communication on the connection and before
|
||||
# the keepalive probing is started. (default is 7200 seconds)
|
||||
#tcp_keep_alive_idle "7200"
|
||||
#
|
||||
# Interval in seconds between keepalive probes, once a probe started.
|
||||
# (default is 75 seconds)
|
||||
#tcp_keep_alive_interval "75"
|
||||
#
|
||||
# Number of failed probes before the connection is pronounced dead and
|
||||
# the connection is closed. (default is 9 times)
|
||||
#tcp_keep_alive_count "9"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
|
458
doc/protocol.xml
458
doc/protocol.xml
@@ -8,22 +8,58 @@
|
||||
<title>General protocol syntax</title>
|
||||
|
||||
<section>
|
||||
<title>Requests</title>
|
||||
<title>Protocol overview</title>
|
||||
|
||||
<para>
|
||||
If arguments contain spaces, they should be surrounded by double quotation
|
||||
marks.
|
||||
The MPD command protocol exchanges line-based text records
|
||||
between client and server over TCP. Once the client is
|
||||
connected to the server, they conduct a conversation until the
|
||||
client closes the connection. The conversation flow is always
|
||||
initiated by the client.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The client transmits a command sequence, terminated by the
|
||||
newline character <constant>\n</constant>. The server will
|
||||
respond with one or more lines, the last of which will be a
|
||||
completion code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When the client connects to the server, the server will answer
|
||||
with the following line:
|
||||
|
||||
<synopsis>OK MPD version</synopsis>
|
||||
|
||||
where <varname>version</varname> is a version identifier such as
|
||||
0.12.2. This version identifier is the version of the protocol
|
||||
spoken, not the real version of the daemon. (There is no way to
|
||||
retrieve this real version identifier from the connection.)
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Requests</title>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>COMMAND</command>
|
||||
<arg rep="repeat"><replaceable>ARG</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<para>
|
||||
If arguments contain spaces, they should be surrounded by double
|
||||
quotation marks.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Argument strings are separated from the command and any other
|
||||
arguments by linear white-space (' ' or '\t').
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All data between the client and the server is encoded in
|
||||
UTF-8. (Note: In UTF-8 all standard ansi characters, 0-127 are
|
||||
the same as a standard ansi encoding. Also, no ansi character
|
||||
the same as in standard ansi encoding. Also, no ansi character
|
||||
appears in any multi-byte characters. So, you can use
|
||||
standard C functions like <function>strlen</function>, and
|
||||
<function>strcpy</function> just fine with UTF-8 encoded
|
||||
@@ -38,13 +74,97 @@
|
||||
<title>Responses</title>
|
||||
|
||||
<para>
|
||||
A command returns <returnvalue>OK</returnvalue> on completion
|
||||
or <returnvalue>ACK some error</returnvalue> on failure.
|
||||
These denote the end of command execution.
|
||||
A command returns <returnvalue>OK</returnvalue> on completion or
|
||||
<returnvalue>ACK some error</returnvalue> on failure. These
|
||||
denote the end of command execution.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Failure responses</title>
|
||||
|
||||
<para>
|
||||
The nature of the error can be gleaned from the information
|
||||
that follows the <returnvalue>ACK</returnvalue>.
|
||||
<returnvalue>ACK</returnvalue> lines are of the form:
|
||||
|
||||
<synopsis>ACK [error@command_listNum] {current_command} message_text\n</synopsis>
|
||||
|
||||
These responses are generated by a call to
|
||||
<function>commandError</function>. They contain four separate
|
||||
terms. Let's look at each of them:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>error</returnvalue>: numeric value of one
|
||||
of the <constant>ACK_ERROR</constant> constants defined
|
||||
in <filename>ack.h</filename>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>command_listNum</returnvalue>:
|
||||
offset of the command that caused the error in a <link
|
||||
linkend="command_lists">Command List</link>.
|
||||
An error will always cause a command list to terminate
|
||||
at the command that causes the error.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>current_command</returnvalue>:
|
||||
name of the command, in a <link
|
||||
linkend="command_lists">Command List</link>,
|
||||
that was executing when the error occurred.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>message_text</returnvalue>:
|
||||
some (hopefully) informative text that describes the
|
||||
nature of the error.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>foo</title>
|
||||
<para>
|
||||
An example might help. Consider the following sequence
|
||||
sent from the client to the server.
|
||||
|
||||
<synopsis>
|
||||
command_list_begin
|
||||
volume 86
|
||||
play 10240
|
||||
status
|
||||
command_list_end
|
||||
</synopsis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The server responds with:
|
||||
|
||||
<returnvalue>
|
||||
ACK [50@1] {play} song doesn't exist: "10240"
|
||||
</returnvalue>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This tells us that the play command, which was the
|
||||
second in the list (the first or only command is
|
||||
numbered 0), failed with error 50. The number 50
|
||||
translates to <constant>ACK_ERROR_NO_EXIST</constant>--the
|
||||
song doesn't exist. This is reiterated by the message text
|
||||
which also tells us which song doesn't exist.
|
||||
</para>
|
||||
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="command_lists">
|
||||
<title>Command lists</title>
|
||||
|
||||
<para>
|
||||
@@ -83,6 +203,47 @@
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Recipes</title>
|
||||
|
||||
<section>
|
||||
<title>Queuing</title>
|
||||
|
||||
<para>
|
||||
Often, users run MPD with "<link
|
||||
linkend="command_random">random</link>" enabled, but want to
|
||||
be able to insert songs "before" the rest of the playlist.
|
||||
That is commonly called "queuing".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
MPD implements this by allowing the client to specify a
|
||||
"priority" for each song in the playlist (commands <link
|
||||
linkend="command_prio"><command>prio</command></link> and
|
||||
<link
|
||||
linkend="command_prioid"><command>prioid</command></link>). A
|
||||
higher priority means that the song is going to be played
|
||||
before the other songs.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In "random" mode, MPD maintains an internal randomized
|
||||
sequence of songs. In this sequence, songs with a higher
|
||||
priority come first, and all songs with the same priority are
|
||||
shuffled (by default, all songs are shuffled, because all have
|
||||
the same priority "0"). When you increase the priority of a
|
||||
song, it is moved to the front of the sequence according to
|
||||
its new priority, but always after the current one. A song
|
||||
that has been played already (it's "before" the current song
|
||||
in that sequence) will only be scheduled for repeated playback
|
||||
if its priority has become bigger than the priority of the
|
||||
current song. Decreasing the priority of a song will moved it
|
||||
farther to the end of the sequence. Changing the priority of
|
||||
the current song has no effect on the sequence.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Command reference</title>
|
||||
|
||||
@@ -198,6 +359,25 @@
|
||||
<option>crossfade</option>, replay gain
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>sticker</returnvalue>: the sticker database
|
||||
has been modified.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>subscription</returnvalue>: a client
|
||||
has subscribed or unsubscribed to a channel
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>message</returnvalue>: a message was
|
||||
received on a channel this client is subscribed to;
|
||||
this event is only emitted when the queue is empty
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
While a client is waiting for <command>idle</command>
|
||||
@@ -364,7 +544,7 @@
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>updatings_db</varname>:
|
||||
<varname>updating_db</varname>:
|
||||
<returnvalue>job id</returnvalue>
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -557,7 +737,11 @@
|
||||
Sets the replay gain mode. One of
|
||||
<parameter>off</parameter>,
|
||||
<parameter>track</parameter>,
|
||||
<parameter>album</parameter>.
|
||||
<parameter>album</parameter>,
|
||||
<parameter>auto</parameter><footnote
|
||||
id="replay_gain_auto_since_0_16">
|
||||
<simpara>added in MPD 0.16</simpara>
|
||||
</footnote>.
|
||||
</para>
|
||||
<para>
|
||||
Changing the mode during playback may take several
|
||||
@@ -694,6 +878,23 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_seekcur">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>seekcur</command>
|
||||
<arg choice="req"><replaceable>TIME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Seeks to the position <varname>TIME</varname> within the
|
||||
current song. If prefixed by '+' or '-', then the time
|
||||
is relative to the current playing position.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_stop">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -953,6 +1154,46 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_prio">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>prio</command>
|
||||
<arg choice="req"><replaceable>PRIORITY</replaceable></arg>
|
||||
<arg choice="req" rep="repeat"><replaceable>START:END</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Set the priority of the specified songs. A higher
|
||||
priority means that it will be played first when
|
||||
"random" mode is enabled.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A priority is an integer between 0 and 255. The default
|
||||
priority of new songs is 0.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_prioid">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>prioid</command>
|
||||
<arg choice="req"><replaceable>PRIORITY</replaceable></arg>
|
||||
<arg choice="req" rep="repeat"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Same as <link
|
||||
linkend="command_prio"><command>prio</command></link>,
|
||||
but address the songs with their id.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_shuffle">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1071,12 +1312,14 @@ OK
|
||||
<cmdsynopsis>
|
||||
<command>load</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>START:END</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Loads the playlist into the current queue. Playlist
|
||||
plugins are supported.
|
||||
plugins are supported. A range may be specified to load
|
||||
only a part of the playlist.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -1313,6 +1556,11 @@ OK
|
||||
the list of stored playlists. This behavior is
|
||||
deprecated; use "listplaylists" instead.
|
||||
</para>
|
||||
<para>
|
||||
Clients that are connected via UNIX domain socket may
|
||||
use this command to read the tags of an arbitrary local
|
||||
file (URI beginning with "file:///").
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_search">
|
||||
@@ -1333,6 +1581,51 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_searchadd">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>searchadd</command>
|
||||
<arg choice="req"><replaceable>TYPE</replaceable></arg>
|
||||
<arg choice="req"><replaceable>WHAT</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Searches for any song that contains <varname>WHAT</varname>
|
||||
in tag <varname>TYPE</varname> and adds them to current playlist.
|
||||
</para>
|
||||
<para>
|
||||
Parameters have the same meaning as for <command>find</command>,
|
||||
except that search is not case sensitive.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_searchaddpl">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>searchaddpl</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
<arg choice="req"><replaceable>TYPE</replaceable></arg>
|
||||
<arg choice="req"><replaceable>WHAT</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Searches for any song that contains <varname>WHAT</varname>
|
||||
in tag <varname>TYPE</varname> and adds them to the playlist
|
||||
named <varname>NAME</varname>.
|
||||
</para>
|
||||
<para>
|
||||
If a playlist by that name doesn't exist it is created.
|
||||
</para>
|
||||
<para>
|
||||
Parameters have the same meaning as for <command>find</command>,
|
||||
except that search is not case sensitive.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_update">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1561,6 +1854,7 @@ OK
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>disableoutput</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
@@ -1573,6 +1867,7 @@ OK
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>enableoutput</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
@@ -1600,6 +1895,47 @@ OK
|
||||
<title>Reflection</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="command_config">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>config</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Dumps configuration values that may be interesting for
|
||||
the client. This command is only permitted to "local"
|
||||
clients (connected via UNIX domain socket).
|
||||
</para>
|
||||
<para>
|
||||
The following response attributes are available:
|
||||
</para>
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>
|
||||
Name
|
||||
</entry>
|
||||
<entry>
|
||||
Description
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>music_directory</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The absolute path of the music directory.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_commands">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1670,5 +2006,105 @@ suffix: mpc</programlisting>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Client to client</title>
|
||||
|
||||
<para>
|
||||
Clients can communicate with each others over "channels". A
|
||||
channel is created by a client subscribing to it. More than
|
||||
one client can be subscribed to a channel at a time; all of
|
||||
them will receive the messages which get sent to it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Each time a client subscribes or unsubscribes, the global idle
|
||||
event <varname>subscription</varname> is generated. In
|
||||
conjunction with the <command>channels</command> command, this
|
||||
may be used to auto-detect clients providing additional
|
||||
services.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
New messages are indicated by the <varname>message</varname>
|
||||
idle event.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="command_subscribe">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>subscribe</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_unsubscribe">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>unsubscribe</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Unsubscribe from a channel.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_channels">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>channels</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Obtain a list of all channels. The response is a list
|
||||
of "channel:" lines.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_readmessages">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>readmessages</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Reads messages for this client. The response is a list
|
||||
of "channel:" and "message:" lines.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_sendmessage">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>sendmessage</command>
|
||||
<arg choice="req"><replaceable>CHANNEL</replaceable></arg>
|
||||
<arg choice="req"><replaceable>TEXT</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Send a message to the specified channel.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
</chapter>
|
||||
</book>
|
||||
|
496
doc/user.xml
496
doc/user.xml
@@ -99,6 +99,47 @@ cd mpd-version</programlisting>
|
||||
|
||||
<programlisting>make install</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><filename>systemd</filename> socket activation</title>
|
||||
|
||||
<para>
|
||||
Using <filename>systemd</filename>, you can launch
|
||||
<filename>mpd</filename> on demand when the first client
|
||||
attempts to connect. Create two files in
|
||||
<filename>/etc/systemd/system/</filename>; first
|
||||
<filename>mpd.socket</filename>:
|
||||
</para>
|
||||
|
||||
<programlisting>[Socket]
|
||||
ListenStream=/run/mpd.socket
|
||||
ListenStream=6600
|
||||
[Install]
|
||||
WantedBy=sockets.target</programlisting>
|
||||
|
||||
<para>
|
||||
Now create <filename>mpd.service</filename>:
|
||||
</para>
|
||||
|
||||
<programlisting>[Unit]
|
||||
Description=Music Player Daemon
|
||||
After=sound.target
|
||||
[Service]
|
||||
ExecStart=/usr/bin/mpd --stdout --no-daemon</programlisting>
|
||||
|
||||
<para>
|
||||
Start the socket:
|
||||
</para>
|
||||
|
||||
<programlisting>systemctl enable mpd.socket
|
||||
systemctl start mpd.socket</programlisting>
|
||||
|
||||
<para>
|
||||
In this configuration, <filename>mpd</filename> will ignore
|
||||
the <varname>bind_to_address</varname> and
|
||||
<varname>port</varname> settings.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
@@ -235,6 +276,16 @@ cd mpd-version</programlisting>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Configuring encoder plugins</title>
|
||||
|
||||
<para>
|
||||
Encoders are used by some of the output plugins (such as
|
||||
<varname>shout</varname>). The encoder settings are included
|
||||
in the <varname>audio_output</varname> section.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Configuring audio outputs</title>
|
||||
|
||||
@@ -322,7 +373,8 @@ cd mpd-version</programlisting>
|
||||
<varname>24_3</varname> (signed 24 bit integer
|
||||
samples, no padding, 3 bytes per sample),
|
||||
<varname>32</varname> (signed 32 bit integer
|
||||
samples).
|
||||
samples), <varname>f</varname> (32 bit floating
|
||||
point, -1.0 to 1.0).
|
||||
</para>
|
||||
</entry>
|
||||
</row>
|
||||
@@ -346,7 +398,7 @@ cd mpd-version</programlisting>
|
||||
If set to "yes", then MPD attempts to keep this audio
|
||||
output always open. This may be useful for streaming
|
||||
servers, when you don't want to disconnect all
|
||||
listeners even when playback is accidently stopped.
|
||||
listeners even when playback is accidentally stopped.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -446,7 +498,7 @@ cd mpd-version</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To configure a filter, add a
|
||||
To configure a playlist plugin, add a
|
||||
<varname>playlist_plugin</varname> block to
|
||||
<filename>mpd.conf</filename>:
|
||||
</para>
|
||||
@@ -621,11 +673,191 @@ cd mpd-version</programlisting>
|
||||
Plays streams with the MMS protocol.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>cdio_paranoia</varname></title>
|
||||
|
||||
<para>
|
||||
Plays audio CDs. The URI has the form:
|
||||
"<filename>cdda://[DEVICE][/TRACK]</filename>". The
|
||||
simplest form <filename>cdda://</filename> plays the whole
|
||||
disc in the default drive.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>despotify</varname></title>
|
||||
|
||||
<para>
|
||||
Plays <ulink url="http://www.spotify.com">Spotify</ulink> tracks using the despotify
|
||||
library. The despotify plugin uses a <filename>spt://</filename> URI and a Spotify
|
||||
URL. So for example, you can add a song with:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<filename>mpc add spt://spotify:track:5qENVY0YEdZ7fiuOax70x1</filename>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You need a Spotify premium account to use this plugin, and you need
|
||||
to setup username and password in the configuration file. The
|
||||
configuration settings are global since the despotify playlist plugin
|
||||
use the same settings.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>despotify_user</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets up the Spotify username (required)
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>despotify_password</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets up the Spotify password (required)
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>despotify_high_bitrate</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Set up if high bitrate should be used for Spotify tunes.
|
||||
High bitrate sounds better but slow systems can have problems
|
||||
with playback (default yes).
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>soup</varname></title>
|
||||
|
||||
<para>
|
||||
Opens remote files or streams over HTTP.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>proxy</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the address of the HTTP proxy server.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Decoder plugins</title>
|
||||
|
||||
<section>
|
||||
<title><varname>dsdiff</varname></title>
|
||||
|
||||
<para>
|
||||
Decodes DFF files containing DSDIFF data (e.g. SACD rips).
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>lsbitfirst</varname>
|
||||
<parameter>yes|no</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
Decode the least significant bit first. Default is
|
||||
"no".
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>dsf</varname></title>
|
||||
|
||||
<para>
|
||||
Decodes DSF files containing DSDIFF data (e.g. SACD rips).
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>fluidsynth</varname></title>
|
||||
|
||||
<para>
|
||||
MIDI decoder based on libfluidsynth.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>sample_rate</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The sample rate that shall be synthesized by the
|
||||
plugin. Defaults to 48000.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>soundfont</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The absolute path of the soundfont file. Defaults
|
||||
to
|
||||
<filename>/usr/share/sounds/sf2/FluidR3_GM.sf2</filename>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>mikmod</varname></title>
|
||||
|
||||
@@ -655,6 +887,209 @@ cd mpd-version</programlisting>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>wildmidi</varname></title>
|
||||
|
||||
<para>
|
||||
MIDI decoder based on libwildmidi.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>config_file</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The absolute path of the timidity config file. Defaults
|
||||
to
|
||||
<filename>/etc/timidity/timidity.cfg</filename>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Encoder plugins</title>
|
||||
|
||||
<section>
|
||||
<title><varname>flac</varname></title>
|
||||
|
||||
<para>
|
||||
Encodes into FLAC (lossless).
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>compression</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the <filename>libFLAC</filename> compression
|
||||
level. The levels range from 0 (fastest, least
|
||||
compression) to 8 (slowest, most compression).
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>lame</varname></title>
|
||||
|
||||
<para>
|
||||
Encodes into MP3 using the LAME library.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>quality</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the quality for VBR. 0 is the highest quality,
|
||||
9 is the lowest quality. Cannot be used with
|
||||
<varname>bitrate</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>bitrate</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the bit rate in kilobit per second. Cannot be
|
||||
used with <varname>quality</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>null</varname></title>
|
||||
|
||||
<para>
|
||||
Does not encode anything, passes the input PCM data as-is.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>twolame</varname></title>
|
||||
|
||||
<para>
|
||||
Encodes into MP2 using the <filename>twolame</filename>
|
||||
library.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>quality</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the quality for VBR. 0 is the highest quality,
|
||||
9 is the lowest quality. Cannot be used with
|
||||
<varname>bitrate</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>bitrate</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the bit rate in kilobit per second. Cannot be
|
||||
used with <varname>quality</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>vorbis</varname></title>
|
||||
|
||||
<para>
|
||||
Encodes into Ogg Vorbis.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>quality</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the quality for VBR. -1 is the lowest quality,
|
||||
10 is the highest quality. Cannot be used with
|
||||
<varname>bitrate</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>bitrate</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the bit rate in kilobit per second. Cannot be
|
||||
used with <varname>quality</varname>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>wave</varname></title>
|
||||
|
||||
<para>
|
||||
Encodes into WAV (lossless).
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -764,6 +1199,23 @@ cd mpd-version</programlisting>
|
||||
bit, floating point, ...).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>dsd_usb</varname>
|
||||
<parameter>yes|no</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
If set to <parameter>yes</parameter>, then DSD over
|
||||
USB according to the <ulink
|
||||
url="http://www.sonore.us/DoP_openStandard_1v1.pdf">pro
|
||||
posed standard by dCS and others</ulink> 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
|
||||
<parameter>no</parameter> and you can enable the
|
||||
option at your own risk.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
@@ -1364,6 +1816,15 @@ cd mpd-version</programlisting>
|
||||
Sets a short description of the stream (optional).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>url</varname>
|
||||
<parameter>URL</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets a URL associated with the stream (optional).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>public</varname>
|
||||
@@ -1466,6 +1927,14 @@ cd mpd-version</programlisting>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>embcue</varname></title>
|
||||
|
||||
<para>
|
||||
Reads CUE sheets from the "CUESHEET" tag of song files.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>m3u</varname></title>
|
||||
|
||||
@@ -1498,6 +1967,27 @@ cd mpd-version</programlisting>
|
||||
playlist files.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>despotify</varname></title>
|
||||
|
||||
<para>
|
||||
Adds <ulink url="http://www.spotify.com/">Spotify</ulink>
|
||||
playlists. Spotify playlists use the <filename>spt://</filename> URI,
|
||||
and a Spotify playlist URL. So for example, you can load a playlist
|
||||
with
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<filename>mpc load spt://spotify:user:simon.kagstrom:playlist:3SUwkOe5VbVHysZcidEZtH</filename>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
See the despotify input plugin for configuration options (username
|
||||
and password needs to be setup)
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
</book>
|
||||
|
63
m4/ax_append_compile_flags.m4
Normal file
63
m4/ax_append_compile_flags.m4
Normal file
@@ -0,0 +1,63 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. During the check the flag is always added to the
|
||||
# current language's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and
|
||||
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
|
||||
# AX_APPEND_LINK_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
|
||||
[for flag in $1; do
|
||||
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3])
|
||||
done
|
||||
])dnl AX_APPEND_COMPILE_FLAGS
|
69
m4/ax_append_flag.m4
Normal file
69
m4/ax_append_flag.m4
Normal file
@@ -0,0 +1,69 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
|
||||
# added in between.
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
|
||||
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
|
||||
# FLAG.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_APPEND_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])dnl
|
||||
AS_VAR_SET_IF(FLAGS,
|
||||
[case " AS_VAR_GET(FLAGS) " in
|
||||
*" $1 "*)
|
||||
AC_RUN_LOG([: FLAGS already contains $1])
|
||||
;;
|
||||
*)
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS $1"])
|
||||
AS_VAR_SET(FLAGS, ["AS_VAR_GET(FLAGS) $1"])
|
||||
;;
|
||||
esac],
|
||||
[AS_VAR_SET(FLAGS,["$1"])])
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])dnl AX_APPEND_FLAG
|
72
m4/ax_check_compile_flag.m4
Normal file
72
m4/ax_check_compile_flag.m4
Normal file
@@ -0,0 +1,72 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
18
m4/faad.m4
18
m4/faad.m4
@@ -39,8 +39,8 @@ if test x$enable_aac = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -I."
|
||||
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS -I."
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
AC_CHECK_HEADER(faad.h,,enable_aac=no)
|
||||
if test x$enable_aac = xyes; then
|
||||
@@ -50,10 +50,10 @@ if test x$enable_aac = xyes; then
|
||||
AC_CHECK_DECL(faacDecInit2,,enable_aac=no,[#include <faad.h>])
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_CHECK_LIB(faad,faacDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
|
||||
AC_CHECK_LIB(faad,faacDecInit2,,enable_aac=no)
|
||||
if test x$enable_aac = xno; then
|
||||
enable_aac=yes
|
||||
AC_CHECK_LIB(faad,NeAACDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
|
||||
AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no)
|
||||
fi
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
@@ -131,8 +131,8 @@ if test x$enable_aac = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -Werror"
|
||||
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror"
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
|
||||
AC_MSG_CHECKING(for broken libfaad headers)
|
||||
@@ -188,5 +188,11 @@ if test x$enable_aac = xyes; then
|
||||
CPPFLAGS=$oldcppflags
|
||||
else
|
||||
enable_mp4=no
|
||||
FAAD_CFLAGS=""
|
||||
FAAD_LIBS=""
|
||||
fi
|
||||
|
||||
AC_SUBST(FAAD_CFLAGS)
|
||||
AC_SUBST(FAAD_LIBS)
|
||||
|
||||
])
|
||||
|
@@ -63,3 +63,18 @@ AC_DEFUN([MPD_AUTO_PKG], [
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$4], [$5])
|
||||
])
|
||||
|
||||
dnl Check with pkg-config first, fall back to AC_CHECK_LIB.
|
||||
dnl
|
||||
dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg
|
||||
AC_DEFUN([MPD_AUTO_PKG_LIB], [
|
||||
if eval "test x`echo '$'enable_$1` != xno"; then
|
||||
PKG_CHECK_MODULES([$2], [$3],
|
||||
[eval "found_$1=yes"],
|
||||
AC_CHECK_LIB($4, $5,
|
||||
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
|
||||
[eval "found_$1=no"]))
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$8], [$9])
|
||||
])
|
||||
|
@@ -1,18 +0,0 @@
|
||||
AC_DEFUN([MPD_CHECK_FLAG],[
|
||||
var=`echo "$1" | tr "=-" "__"`
|
||||
AC_CACHE_CHECK([whether the C compiler accepts $1],
|
||||
[mpd_check_cflag_$var],[
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
int main(void) { return 0; }
|
||||
])], [ eval "mpd_check_cflag_$var=yes"
|
||||
], [ eval "mpd_check_cflag_$var=no" ])
|
||||
CFLAGS="$save_CFLAGS"
|
||||
])
|
||||
if eval "test x`echo '$mpd_check_cflag_'$var` = xyes"
|
||||
then
|
||||
AM_CFLAGS="$AM_CFLAGS $1"
|
||||
fi
|
||||
])
|
||||
])
|
@@ -20,7 +20,7 @@ AC_DEFUN([STRUCT_UCRED],[
|
||||
mpd_cv_have_struct_ucred=no)
|
||||
if test x$mpd_cv_have_struct_ucred = xyes; then
|
||||
# :(
|
||||
MPD_CFLAGS="$MPD_CFLAGS -D_GNU_SOURCE"
|
||||
CFLAGS="$CFLAGS -D_GNU_SOURCE"
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
9
mpd.service.in
Normal file
9
mpd.service.in
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Music Player Daemon
|
||||
After=sound.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@prefix@/bin/mpd --no-daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@@ -3,7 +3,7 @@ PWD=`pwd`
|
||||
|
||||
## If we're not in the scripts directory
|
||||
## assume the base directory.
|
||||
if test "`basename $PWD`" == "scripts"; then
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd ../
|
||||
else
|
||||
MYOLDPWD=`pwd`
|
||||
@@ -18,7 +18,7 @@ fi
|
||||
make
|
||||
make dist
|
||||
|
||||
if test "`basename $PWD`" == "scripts"; then
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd contrib/
|
||||
else
|
||||
cd $MYOLDPWD
|
||||
|
@@ -16,16 +16,16 @@
|
||||
struct Compressor {
|
||||
//! The compressor's preferences
|
||||
struct CompressorConfig prefs;
|
||||
|
||||
|
||||
//! History of the peak values
|
||||
int *peaks;
|
||||
|
||||
|
||||
//! History of the gain values
|
||||
int *gain;
|
||||
|
||||
|
||||
//! History of clip amounts
|
||||
int *clipped;
|
||||
|
||||
|
||||
unsigned int pos;
|
||||
unsigned int bufsz;
|
||||
};
|
||||
@@ -33,6 +33,9 @@ struct Compressor {
|
||||
struct Compressor *Compressor_new(unsigned int history)
|
||||
{
|
||||
struct Compressor *obj = malloc(sizeof(struct Compressor));
|
||||
if (obj == NULL)
|
||||
/* out of memory, not much we can do */
|
||||
abort();
|
||||
|
||||
obj->prefs.target = TARGET;
|
||||
obj->prefs.maxgain = GAINMAX;
|
||||
@@ -41,9 +44,9 @@ struct Compressor *Compressor_new(unsigned int history)
|
||||
obj->peaks = obj->gain = obj->clipped = NULL;
|
||||
obj->bufsz = 0;
|
||||
obj->pos = 0;
|
||||
|
||||
|
||||
Compressor_setHistory(obj, history);
|
||||
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -61,6 +64,10 @@ void Compressor_delete(struct Compressor *obj)
|
||||
static int *resizeArray(int *data, int newsz, int oldsz)
|
||||
{
|
||||
data = realloc(data, newsz*sizeof(int));
|
||||
if (data == NULL)
|
||||
/* out of memory, not much we can do */
|
||||
abort();
|
||||
|
||||
if (newsz > oldsz)
|
||||
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
|
||||
return data;
|
||||
@@ -70,7 +77,7 @@ void Compressor_setHistory(struct Compressor *obj, unsigned int history)
|
||||
{
|
||||
if (!history)
|
||||
history = BUCKETS;
|
||||
|
||||
|
||||
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
|
||||
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
|
||||
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
|
||||
@@ -82,7 +89,7 @@ struct CompressorConfig *Compressor_getConfig(struct Compressor *obj)
|
||||
return &obj->prefs;
|
||||
}
|
||||
|
||||
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||
unsigned int count)
|
||||
{
|
||||
struct CompressorConfig *prefs = Compressor_getConfig(obj);
|
||||
@@ -97,7 +104,7 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||
int *clipped = obj->clipped + slot;
|
||||
unsigned int ramp = count;
|
||||
int delta;
|
||||
|
||||
|
||||
ap = audio;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
@@ -124,15 +131,15 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||
|
||||
//! Determine target gain
|
||||
newGain = (1 << 10)*prefs->target/peakVal;
|
||||
|
||||
|
||||
//! Adjust the gain with inertia from the previous gain value
|
||||
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
|
||||
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
|
||||
>> prefs->smooth;
|
||||
|
||||
|
||||
//! Make sure it's no more than the maximum gain value
|
||||
if (newGain > (prefs->maxgain << 10))
|
||||
newGain = prefs->maxgain << 10;
|
||||
|
||||
|
||||
//! Make sure it's no less than 1:1
|
||||
if (newGain < (1 << 10))
|
||||
newGain = 1 << 10;
|
||||
@@ -144,7 +151,7 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||
//! Truncate the ramp time
|
||||
ramp = peakPos;
|
||||
}
|
||||
|
||||
|
||||
//! Record the new gain
|
||||
obj->gain[slot] = newGain;
|
||||
|
||||
|
14
src/ack.h
14
src/ack.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef MPD_ACK_H
|
||||
#define MPD_ACK_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
enum ack {
|
||||
ACK_ERROR_NOT_LIST = 1,
|
||||
ACK_ERROR_ARG = 2,
|
||||
@@ -36,4 +38,14 @@ enum ack {
|
||||
ACK_ERROR_EXIST = 56,
|
||||
};
|
||||
|
||||
/**
|
||||
* Quark for GError.domain; the code is an enum #ack.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
ack_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("ack");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -57,7 +57,7 @@ aiff_seek_id3(FILE *file)
|
||||
ret = fstat(fileno(file), &st);
|
||||
if (ret < 0) {
|
||||
g_warning("Failed to stat file descriptor: %s",
|
||||
strerror(errno));
|
||||
g_strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@ aiff_seek_id3(FILE *file)
|
||||
if (size != 1 ||
|
||||
memcmp(header.id, "FORM", 4) != 0 ||
|
||||
GUINT32_FROM_BE(header.size) > (uint32_t)st.st_size ||
|
||||
memcmp(header.format, "AIFF", 4) != 0)
|
||||
(memcmp(header.format, "AIFF", 4) != 0 &&
|
||||
memcmp(header.format, "AIFC", 4) != 0))
|
||||
/* not a AIFF file */
|
||||
return 0;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -60,8 +60,10 @@ ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
|
||||
assert(remaining > 10);
|
||||
|
||||
char *buffer = g_malloc(remaining);
|
||||
if (fread(buffer, 1, remaining, fp) != remaining)
|
||||
if (fread(buffer, 1, remaining, fp) != remaining) {
|
||||
g_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read tags */
|
||||
unsigned n = GUINT32_FROM_LE(footer.count);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "config.h"
|
||||
#include "archive/bz2_archive_plugin.h"
|
||||
#include "archive_api.h"
|
||||
#include "input_internal.h"
|
||||
#include "input_plugin.h"
|
||||
#include "refcount.h"
|
||||
|
||||
@@ -102,6 +103,11 @@ bz2_destroy(struct bz2_input_stream *data)
|
||||
|
||||
/* archive open && listing routine */
|
||||
|
||||
#if GCC_CHECK_VERSION(4, 2)
|
||||
/* workaround for a warning caused by G_STATIC_MUTEX_INIT */
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
static struct archive_file *
|
||||
bz2_open(const char *pathname, GError **error_r)
|
||||
{
|
||||
@@ -113,7 +119,11 @@ bz2_open(const char *pathname, GError **error_r)
|
||||
refcount_init(&context->ref);
|
||||
|
||||
//open archive
|
||||
context->istream = input_stream_open(pathname, error_r);
|
||||
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
|
||||
context->istream = input_stream_open(pathname,
|
||||
g_static_mutex_get_mutex(&mutex),
|
||||
NULL,
|
||||
error_r);
|
||||
if (context->istream == NULL) {
|
||||
g_free(context);
|
||||
return NULL;
|
||||
@@ -168,12 +178,15 @@ bz2_close(struct archive_file *file)
|
||||
/* single archive handling */
|
||||
|
||||
static struct input_stream *
|
||||
bz2_open_stream(struct archive_file *file, const char *path, GError **error_r)
|
||||
bz2_open_stream(struct archive_file *file, const char *path,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r)
|
||||
{
|
||||
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
|
||||
struct bz2_input_stream *bis = g_new(struct bz2_input_stream, 1);
|
||||
|
||||
input_stream_init(&bis->base, &bz2_inputplugin, path);
|
||||
input_stream_init(&bis->base, &bz2_inputplugin, path,
|
||||
mutex, cond);
|
||||
|
||||
bis->archive = context;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "config.h"
|
||||
#include "archive/iso9660_archive_plugin.h"
|
||||
#include "archive_api.h"
|
||||
#include "input_internal.h"
|
||||
#include "input_plugin.h"
|
||||
#include "refcount.h"
|
||||
|
||||
@@ -172,15 +173,17 @@ struct iso9660_input_stream {
|
||||
};
|
||||
|
||||
static struct input_stream *
|
||||
iso9660_archive_open_stream(struct archive_file *file,
|
||||
const char *pathname, GError **error_r)
|
||||
iso9660_archive_open_stream(struct archive_file *file, const char *pathname,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r)
|
||||
{
|
||||
struct iso9660_archive_file *context =
|
||||
(struct iso9660_archive_file *)file;
|
||||
struct iso9660_input_stream *iis;
|
||||
|
||||
iis = g_new(struct iso9660_input_stream, 1);
|
||||
input_stream_init(&iis->base, &iso9660_input_plugin, pathname);
|
||||
input_stream_init(&iis->base, &iso9660_input_plugin, pathname,
|
||||
mutex, cond);
|
||||
|
||||
iis->archive = context;
|
||||
iis->statbuf = iso9660_ifs_stat_translate(context->iso, pathname);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "archive/zzip_archive_plugin.h"
|
||||
#include "archive_api.h"
|
||||
#include "archive_api.h"
|
||||
#include "input_internal.h"
|
||||
#include "input_plugin.h"
|
||||
#include "refcount.h"
|
||||
|
||||
@@ -134,14 +135,17 @@ struct zzip_input_stream {
|
||||
|
||||
static struct input_stream *
|
||||
zzip_archive_open_stream(struct archive_file *file,
|
||||
const char *pathname, GError **error_r)
|
||||
const char *pathname,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r)
|
||||
{
|
||||
struct zzip_archive *context = (struct zzip_archive *) file;
|
||||
struct zzip_input_stream *zis;
|
||||
ZZIP_STAT z_stat;
|
||||
|
||||
zis = g_new(struct zzip_input_stream, 1);
|
||||
input_stream_init(&zis->base, &zzip_input_plugin, pathname);
|
||||
input_stream_init(&zis->base, &zzip_input_plugin, pathname,
|
||||
mutex, cond);
|
||||
|
||||
zis->archive = context;
|
||||
zis->file = zzip_file_open(context->dir, pathname, 0);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "archive_list.h"
|
||||
#include "archive_plugin.h"
|
||||
#include "utils.h"
|
||||
#include "string_util.h"
|
||||
#include "archive/bz2_archive_plugin.h"
|
||||
#include "archive/iso9660_archive_plugin.h"
|
||||
#include "archive/zzip_archive_plugin.h"
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
static const struct archive_plugin *const archive_plugins[] = {
|
||||
const struct archive_plugin *const archive_plugins[] = {
|
||||
#ifdef HAVE_BZ2
|
||||
&bz2_archive_plugin,
|
||||
#endif
|
||||
@@ -44,55 +44,34 @@ static const struct archive_plugin *const archive_plugins[] = {
|
||||
/** which plugins have been initialized successfully? */
|
||||
static bool archive_plugins_enabled[G_N_ELEMENTS(archive_plugins) - 1];
|
||||
|
||||
#define archive_plugins_for_each_enabled(plugin) \
|
||||
archive_plugins_for_each(plugin) \
|
||||
if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins])
|
||||
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_suffix(const char *suffix)
|
||||
{
|
||||
if (suffix == NULL)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (archive_plugins_enabled[i] &&
|
||||
plugin->suffixes != NULL &&
|
||||
string_array_contains(plugin->suffixes, suffix)) {
|
||||
++i;
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->suffixes != NULL &&
|
||||
string_array_contains(plugin->suffixes, suffix))
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_name(const char *name)
|
||||
{
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (archive_plugins_enabled[i] &&
|
||||
strcmp(plugin->name, name) == 0)
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (strcmp(plugin->name, name) == 0)
|
||||
return plugin;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void archive_plugin_print_all_suffixes(FILE * fp)
|
||||
{
|
||||
const char *const*suffixes;
|
||||
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (!archive_plugins_enabled[i])
|
||||
continue;
|
||||
|
||||
suffixes = plugin->suffixes;
|
||||
while (suffixes && *suffixes) {
|
||||
fprintf(fp, "%s ", *suffixes);
|
||||
suffixes++;
|
||||
}
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
fflush(fp);
|
||||
}
|
||||
|
||||
void archive_plugin_init_all(void)
|
||||
{
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
@@ -104,10 +83,8 @@ void archive_plugin_init_all(void)
|
||||
|
||||
void archive_plugin_deinit_all(void)
|
||||
{
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (archive_plugins_enabled[i] && plugin->finish != NULL)
|
||||
archive_plugins[i]->finish();
|
||||
}
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->finish != NULL)
|
||||
plugin->finish();
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,10 +20,16 @@
|
||||
#ifndef MPD_ARCHIVE_LIST_H
|
||||
#define MPD_ARCHIVE_LIST_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct archive_plugin;
|
||||
|
||||
extern const struct archive_plugin *const archive_plugins[];
|
||||
|
||||
#define archive_plugins_for_each(plugin) \
|
||||
for (const struct archive_plugin *plugin, \
|
||||
*const*archive_plugin_iterator = &archive_plugins[0]; \
|
||||
(plugin = *archive_plugin_iterator) != NULL; \
|
||||
++archive_plugin_iterator)
|
||||
|
||||
/* interface for using plugins */
|
||||
|
||||
const struct archive_plugin *
|
||||
@@ -32,8 +38,6 @@ archive_plugin_from_suffix(const char *suffix);
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_name(const char *name);
|
||||
|
||||
void archive_plugin_print_all_suffixes(FILE * fp);
|
||||
|
||||
/* this is where we "load" all the "plugins" ;-) */
|
||||
void archive_plugin_init_all(void);
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -81,12 +81,14 @@ archive_file_scan_next(struct archive_file *file)
|
||||
}
|
||||
|
||||
struct input_stream *
|
||||
archive_file_open_stream(struct archive_file *file,
|
||||
const char *path, GError **error_r)
|
||||
archive_file_open_stream(struct archive_file *file, const char *path,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r)
|
||||
{
|
||||
assert(file != NULL);
|
||||
assert(file->plugin != NULL);
|
||||
assert(file->plugin->open_stream != NULL);
|
||||
|
||||
return file->plugin->open_stream(file, path, error_r);
|
||||
return file->plugin->open_stream(file, path, mutex, cond,
|
||||
error_r);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -68,11 +68,12 @@ struct archive_plugin {
|
||||
* Opens an input_stream of a file within the archive.
|
||||
*
|
||||
* @param path the path within the archive
|
||||
* @param error_r location to store the error occuring, or
|
||||
* @param error_r location to store the error occurring, or
|
||||
* NULL to ignore errors
|
||||
*/
|
||||
struct input_stream *(*open_stream)(struct archive_file *af,
|
||||
const char *path,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r);
|
||||
|
||||
/**
|
||||
@@ -101,7 +102,8 @@ char *
|
||||
archive_file_scan_next(struct archive_file *file);
|
||||
|
||||
struct input_stream *
|
||||
archive_file_open_stream(struct archive_file *file,
|
||||
const char *path, GError **error_r);
|
||||
archive_file_open_stream(struct archive_file *file, const char *path,
|
||||
GMutex *mutex, GCond *cond,
|
||||
GError **error_r);
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -28,6 +28,7 @@
|
||||
/**
|
||||
* The GLib quark used for errors reported by this library.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
audio_format_quark(void)
|
||||
{
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
#include "audio_config.h"
|
||||
#include "audio_format.h"
|
||||
#include "audio_parser.h"
|
||||
#include "output_internal.h"
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,8 +17,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_AUDIO_H
|
||||
#define MPD_AUDIO_H
|
||||
#ifndef MPD_AUDIO_CONFIG_H
|
||||
#define MPD_AUDIO_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,11 +22,24 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#define REVERSE_ENDIAN_SUFFIX "_le"
|
||||
#else
|
||||
#define REVERSE_ENDIAN_SUFFIX "_be"
|
||||
#endif
|
||||
void
|
||||
audio_format_mask_apply(struct audio_format *af,
|
||||
const struct audio_format *mask)
|
||||
{
|
||||
assert(audio_format_valid(af));
|
||||
assert(audio_format_mask_valid(mask));
|
||||
|
||||
if (mask->sample_rate != 0)
|
||||
af->sample_rate = mask->sample_rate;
|
||||
|
||||
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
|
||||
af->format = mask->format;
|
||||
|
||||
if (mask->channels != 0)
|
||||
af->channels = mask->channels;
|
||||
|
||||
assert(audio_format_valid(af));
|
||||
}
|
||||
|
||||
const char *
|
||||
sample_format_to_string(enum sample_format format)
|
||||
@@ -41,14 +54,17 @@ sample_format_to_string(enum sample_format format)
|
||||
case SAMPLE_FORMAT_S16:
|
||||
return "16";
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
return "24_3";
|
||||
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
return "24";
|
||||
|
||||
case SAMPLE_FORMAT_S32:
|
||||
return "32";
|
||||
|
||||
case SAMPLE_FORMAT_FLOAT:
|
||||
return "f";
|
||||
|
||||
case SAMPLE_FORMAT_DSD:
|
||||
return "dsd";
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
@@ -63,9 +79,8 @@ audio_format_to_string(const struct audio_format *af,
|
||||
assert(af != NULL);
|
||||
assert(s != NULL);
|
||||
|
||||
snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
|
||||
snprintf(s->buffer, sizeof(s->buffer), "%u:%s:%u",
|
||||
af->sample_rate, sample_format_to_string(af->format),
|
||||
af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
|
||||
af->channels);
|
||||
|
||||
return s->buffer;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,10 @@
|
||||
#ifndef MPD_AUDIO_FORMAT_H
|
||||
#define MPD_AUDIO_FORMAT_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
enum sample_format {
|
||||
SAMPLE_FORMAT_UNDEFINED = 0,
|
||||
@@ -29,11 +31,6 @@ enum sample_format {
|
||||
SAMPLE_FORMAT_S8,
|
||||
SAMPLE_FORMAT_S16,
|
||||
|
||||
/**
|
||||
* Signed 24 bit integer samples, without padding.
|
||||
*/
|
||||
SAMPLE_FORMAT_S24,
|
||||
|
||||
/**
|
||||
* Signed 24 bit integer samples, packed in 32 bit integers
|
||||
* (the most significant byte is filled with the sign bit).
|
||||
@@ -41,8 +38,22 @@ enum sample_format {
|
||||
SAMPLE_FORMAT_S24_P32,
|
||||
|
||||
SAMPLE_FORMAT_S32,
|
||||
|
||||
/**
|
||||
* 32 bit floating point samples in the host's format. The
|
||||
* range is -1.0f to +1.0f.
|
||||
*/
|
||||
SAMPLE_FORMAT_FLOAT,
|
||||
|
||||
/**
|
||||
* Direct Stream Digital. 1-bit samples; each frame has one
|
||||
* byte (8 samples) per channel.
|
||||
*/
|
||||
SAMPLE_FORMAT_DSD,
|
||||
};
|
||||
|
||||
static const unsigned MAX_CHANNELS = 8;
|
||||
|
||||
/**
|
||||
* This structure describes the format of a raw PCM stream.
|
||||
*/
|
||||
@@ -65,13 +76,6 @@ struct audio_format {
|
||||
* fully supported currently.
|
||||
*/
|
||||
uint8_t channels;
|
||||
|
||||
/**
|
||||
* If zero, then samples are stored in host byte order. If
|
||||
* nonzero, then samples are stored in the reverse host byte
|
||||
* order.
|
||||
*/
|
||||
uint8_t reverse_endian;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -90,7 +94,6 @@ static inline void audio_format_clear(struct audio_format *af)
|
||||
af->sample_rate = 0;
|
||||
af->format = SAMPLE_FORMAT_UNDEFINED;
|
||||
af->channels = 0;
|
||||
af->reverse_endian = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +107,6 @@ static inline void audio_format_init(struct audio_format *af,
|
||||
af->sample_rate = sample_rate;
|
||||
af->format = (uint8_t)format;
|
||||
af->channels = channels;
|
||||
af->reverse_endian = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,9 +163,10 @@ audio_valid_sample_format(enum sample_format format)
|
||||
switch (format) {
|
||||
case SAMPLE_FORMAT_S8:
|
||||
case SAMPLE_FORMAT_S16:
|
||||
case SAMPLE_FORMAT_S24:
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
case SAMPLE_FORMAT_S32:
|
||||
case SAMPLE_FORMAT_FLOAT:
|
||||
case SAMPLE_FORMAT_DSD:
|
||||
return true;
|
||||
|
||||
case SAMPLE_FORMAT_UNDEFINED:
|
||||
@@ -179,13 +182,14 @@ audio_valid_sample_format(enum sample_format format)
|
||||
static inline bool
|
||||
audio_valid_channel_count(unsigned channels)
|
||||
{
|
||||
return channels >= 1 && channels <= 8;
|
||||
return channels >= 1 && channels <= MAX_CHANNELS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the format is not valid for playback with MPD.
|
||||
* This function performs some basic validity checks.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline bool audio_format_valid(const struct audio_format *af)
|
||||
{
|
||||
return audio_valid_sample_rate(af->sample_rate) &&
|
||||
@@ -197,6 +201,7 @@ static inline bool audio_format_valid(const struct audio_format *af)
|
||||
* Returns false if the format mask is not valid for playback with
|
||||
* MPD. This function performs some basic validity checks.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline bool audio_format_mask_valid(const struct audio_format *af)
|
||||
{
|
||||
return (af->sample_rate == 0 ||
|
||||
@@ -211,53 +216,54 @@ static inline bool audio_format_equals(const struct audio_format *a,
|
||||
{
|
||||
return a->sample_rate == b->sample_rate &&
|
||||
a->format == b->format &&
|
||||
a->channels == b->channels &&
|
||||
a->reverse_endian == b->reverse_endian;
|
||||
a->channels == b->channels;
|
||||
}
|
||||
|
||||
static inline void
|
||||
void
|
||||
audio_format_mask_apply(struct audio_format *af,
|
||||
const struct audio_format *mask)
|
||||
const struct audio_format *mask);
|
||||
|
||||
G_GNUC_CONST
|
||||
static inline unsigned
|
||||
sample_format_size(enum sample_format format)
|
||||
{
|
||||
if (mask->sample_rate != 0)
|
||||
af->sample_rate = mask->sample_rate;
|
||||
|
||||
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
|
||||
af->format = mask->format;
|
||||
|
||||
if (mask->channels != 0)
|
||||
af->channels = mask->channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of each (mono) sample in bytes.
|
||||
*/
|
||||
static inline unsigned audio_format_sample_size(const struct audio_format *af)
|
||||
{
|
||||
switch (af->format) {
|
||||
switch (format) {
|
||||
case SAMPLE_FORMAT_S8:
|
||||
return 1;
|
||||
|
||||
case SAMPLE_FORMAT_S16:
|
||||
return 2;
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
return 3;
|
||||
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
case SAMPLE_FORMAT_S32:
|
||||
case SAMPLE_FORMAT_FLOAT:
|
||||
return 4;
|
||||
|
||||
case SAMPLE_FORMAT_DSD:
|
||||
/* each frame has 8 samples per channel */
|
||||
return 1;
|
||||
|
||||
case SAMPLE_FORMAT_UNDEFINED:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of each (mono) sample in bytes.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline unsigned audio_format_sample_size(const struct audio_format *af)
|
||||
{
|
||||
return sample_format_size((enum sample_format)af->format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of each full frame in bytes.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline unsigned
|
||||
audio_format_frame_size(const struct audio_format *af)
|
||||
{
|
||||
@@ -268,6 +274,7 @@ audio_format_frame_size(const struct audio_format *af)
|
||||
* Returns the floating point factor which converts a time span to a
|
||||
* storage size in bytes.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline double audio_format_time_to_size(const struct audio_format *af)
|
||||
{
|
||||
return af->sample_rate * audio_format_frame_size(af);
|
||||
@@ -280,6 +287,7 @@ static inline double audio_format_time_to_size(const struct audio_format *af)
|
||||
* @param format a #sample_format enum value
|
||||
* @return the string
|
||||
*/
|
||||
G_GNUC_PURE G_GNUC_MALLOC
|
||||
const char *
|
||||
sample_format_to_string(enum sample_format format);
|
||||
|
||||
@@ -291,6 +299,7 @@ sample_format_to_string(enum sample_format format);
|
||||
* @param s a buffer to print into
|
||||
* @return the string, or NULL if the #audio_format object is invalid
|
||||
*/
|
||||
G_GNUC_PURE G_GNUC_MALLOC
|
||||
const char *
|
||||
audio_format_to_string(const struct audio_format *af,
|
||||
struct audio_format_string *s);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "audio_parser.h"
|
||||
#include "audio_format.h"
|
||||
#include "audio_check.h"
|
||||
#include "gcc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@@ -81,6 +82,18 @@ parse_sample_format(const char *src, bool mask,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*src == 'f') {
|
||||
*sample_format_r = SAMPLE_FORMAT_FLOAT;
|
||||
*endptr_r = src + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (memcmp(src, "dsd", 3) == 0) {
|
||||
*sample_format_r = SAMPLE_FORMAT_DSD;
|
||||
*endptr_r = src + 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = strtoul(src, &endptr, 10);
|
||||
if (endptr == src) {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
@@ -98,11 +111,11 @@ parse_sample_format(const char *src, bool mask,
|
||||
break;
|
||||
|
||||
case 24:
|
||||
if (memcmp(endptr, "_3", 2) == 0) {
|
||||
sample_format = SAMPLE_FORMAT_S24;
|
||||
if (memcmp(endptr, "_3", 2) == 0)
|
||||
/* for backwards compatibility */
|
||||
endptr += 2;
|
||||
} else
|
||||
sample_format = SAMPLE_FORMAT_S24_P32;
|
||||
|
||||
sample_format = SAMPLE_FORMAT_S24_P32;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
@@ -160,6 +173,11 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
||||
|
||||
/* parse sample rate */
|
||||
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
rate = 0;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
|
||||
return false;
|
||||
|
||||
@@ -171,6 +189,11 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
||||
|
||||
/* parse sample format */
|
||||
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
sample_format = SAMPLE_FORMAT_UNDEFINED;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
|
||||
return false;
|
||||
|
||||
@@ -192,6 +215,8 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
||||
}
|
||||
|
||||
audio_format_init(dest, rate, sample_format, channels);
|
||||
assert(mask ? audio_format_mask_valid(dest)
|
||||
: audio_format_valid(dest));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -38,7 +38,7 @@ struct audio_format;
|
||||
* @param dest the destination #audio_format struct
|
||||
* @param src the input string
|
||||
* @param mask if true, then "*" is allowed for any number of items
|
||||
* @param error_r location to store the error occuring, or NULL to
|
||||
* @param error_r location to store the error occurring, or NULL to
|
||||
* ignore errors
|
||||
* @return true on success
|
||||
*/
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
33
src/client.h
33
src/client.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,20 +27,36 @@
|
||||
|
||||
struct client;
|
||||
struct sockaddr;
|
||||
struct player_control;
|
||||
|
||||
void client_manager_init(void);
|
||||
void client_manager_deinit(void);
|
||||
|
||||
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid);
|
||||
void client_new(struct player_control *player_control,
|
||||
int fd, const struct sockaddr *sa, size_t sa_length, int uid);
|
||||
|
||||
G_GNUC_PURE
|
||||
bool client_is_expired(const struct client *client);
|
||||
|
||||
/**
|
||||
* returns the uid of the client process, or a negative value if the
|
||||
* uid is unknown
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
int client_get_uid(const struct client *client);
|
||||
|
||||
/**
|
||||
* Is this client running on the same machine, connected with a local
|
||||
* (UNIX domain) socket?
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline bool
|
||||
client_is_local(const struct client *client)
|
||||
{
|
||||
return client_get_uid(client) > 0;
|
||||
}
|
||||
|
||||
G_GNUC_PURE
|
||||
unsigned client_get_permission(const struct client *client);
|
||||
|
||||
void client_set_permission(struct client *client, unsigned permission);
|
||||
@@ -60,17 +76,4 @@ void client_vprintf(struct client *client, const char *fmt, va_list args);
|
||||
*/
|
||||
G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Adds the specified idle flags to all clients and immediately sends
|
||||
* notifications to all waiting clients.
|
||||
*/
|
||||
void client_manager_idle_add(unsigned flags);
|
||||
|
||||
/**
|
||||
* Checks whether the client has pending idle flags. If yes, they are
|
||||
* sent immediately and "true" is returned". If no, it puts the
|
||||
* client into waiting mode and returns false.
|
||||
*/
|
||||
bool client_idle_wait(struct client *client, unsigned flags);
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
70
src/client_file.c
Normal file
70
src/client_file.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "client_file.h"
|
||||
#include "client.h"
|
||||
#include "ack.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool
|
||||
client_allow_file(const struct client *client, const char *path_fs,
|
||||
GError **error_r)
|
||||
{
|
||||
#ifdef WIN32
|
||||
(void)client;
|
||||
(void)path_fs;
|
||||
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
return false;
|
||||
#else
|
||||
const int uid = client_get_uid(client);
|
||||
if (uid >= 0 && (uid_t)uid == geteuid())
|
||||
/* always allow access if user runs his own MPD
|
||||
instance */
|
||||
return true;
|
||||
|
||||
if (uid <= 0) {
|
||||
/* unauthenticated client */
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path_fs, &st) < 0) {
|
||||
g_set_error(error_r, g_file_error_quark(), errno,
|
||||
"%s", g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) {
|
||||
/* client is not owner */
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
42
src/client_file.h
Normal file
42
src/client_file.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_FILE_H
|
||||
#define MPD_CLIENT_FILE_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct client;
|
||||
|
||||
/**
|
||||
* Is this client allowed to use the specified local file?
|
||||
*
|
||||
* Note that this function is vulnerable to timing/symlink attacks.
|
||||
* We cannot fix this as long as there are plugins that open a file by
|
||||
* its name, and not by file descriptor / callbacks.
|
||||
*
|
||||
* @param path_fs the absolute path name in filesystem encoding
|
||||
* @return true if access is allowed
|
||||
*/
|
||||
bool
|
||||
client_allow_file(const struct client *client, const char *path_fs,
|
||||
GError **error_r);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "client_idle.h"
|
||||
#include "client_internal.h"
|
||||
#include "idle.h"
|
||||
|
||||
@@ -50,12 +51,9 @@ client_idle_notify(struct client *client)
|
||||
g_timer_start(client->last_activity);
|
||||
}
|
||||
|
||||
static void
|
||||
client_idle_callback(gpointer data, gpointer user_data)
|
||||
void
|
||||
client_idle_add(struct client *client, unsigned flags)
|
||||
{
|
||||
struct client *client = data;
|
||||
unsigned flags = GPOINTER_TO_UINT(user_data);
|
||||
|
||||
if (client_is_expired(client))
|
||||
return;
|
||||
|
||||
@@ -67,6 +65,15 @@ client_idle_callback(gpointer data, gpointer user_data)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_idle_callback(gpointer data, gpointer user_data)
|
||||
{
|
||||
struct client *client = data;
|
||||
unsigned flags = GPOINTER_TO_UINT(user_data);
|
||||
|
||||
client_idle_add(client, flags);
|
||||
}
|
||||
|
||||
void client_manager_idle_add(unsigned flags)
|
||||
{
|
||||
assert(flags != 0);
|
||||
|
45
src/client_idle.h
Normal file
45
src/client_idle.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_IDLE_H
|
||||
#define MPD_CLIENT_IDLE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct client;
|
||||
|
||||
void
|
||||
client_idle_add(struct client *client, unsigned flags);
|
||||
|
||||
/**
|
||||
* Adds the specified idle flags to all clients and immediately sends
|
||||
* notifications to all waiting clients.
|
||||
*/
|
||||
void
|
||||
client_manager_idle_add(unsigned flags);
|
||||
|
||||
/**
|
||||
* Checks whether the client has pending idle flags. If yes, they are
|
||||
* sent immediately and "true" is returned". If no, it puts the
|
||||
* client into waiting mode and returns false.
|
||||
*/
|
||||
bool
|
||||
client_idle_wait(struct client *client, unsigned flags);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,17 +21,25 @@
|
||||
#define MPD_CLIENT_INTERNAL_H
|
||||
|
||||
#include "client.h"
|
||||
#include "client_message.h"
|
||||
#include "command.h"
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "client"
|
||||
|
||||
enum {
|
||||
CLIENT_MAX_SUBSCRIPTIONS = 16,
|
||||
CLIENT_MAX_MESSAGES = 64,
|
||||
};
|
||||
|
||||
struct deferred_buffer {
|
||||
size_t size;
|
||||
char data[sizeof(long)];
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct player_control *player_control;
|
||||
|
||||
GIOChannel *channel;
|
||||
guint source_id;
|
||||
|
||||
@@ -67,6 +75,28 @@ struct client {
|
||||
|
||||
/** idle flags that the client wants to receive */
|
||||
unsigned idle_subscriptions;
|
||||
|
||||
/**
|
||||
* A list of channel names this client is subscribed to.
|
||||
*/
|
||||
GSList *subscriptions;
|
||||
|
||||
/**
|
||||
* The number of subscriptions in #subscriptions. Used to
|
||||
* limit the number of subscriptions.
|
||||
*/
|
||||
unsigned num_subscriptions;
|
||||
|
||||
/**
|
||||
* A list of messages this client has received in reverse
|
||||
* order (latest first).
|
||||
*/
|
||||
GSList *messages;
|
||||
|
||||
/**
|
||||
* The number of messages in #messages.
|
||||
*/
|
||||
unsigned num_messages;
|
||||
};
|
||||
|
||||
extern unsigned int client_max_connections;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
96
src/client_message.c
Normal file
96
src/client_message.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "client_message.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
|
||||
G_GNUC_PURE
|
||||
static bool
|
||||
valid_channel_char(const char ch)
|
||||
{
|
||||
return g_ascii_isalnum(ch) ||
|
||||
ch == '_' || ch == '-' || ch == '.' || ch == ':';
|
||||
}
|
||||
|
||||
bool
|
||||
client_message_valid_channel_name(const char *name)
|
||||
{
|
||||
do {
|
||||
if (!valid_channel_char(*name))
|
||||
return false;
|
||||
} while (*++name != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
client_message_init_null(struct client_message *msg)
|
||||
{
|
||||
assert(msg != NULL);
|
||||
|
||||
msg->channel = NULL;
|
||||
msg->message = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
client_message_init(struct client_message *msg,
|
||||
const char *channel, const char *message)
|
||||
{
|
||||
assert(msg != NULL);
|
||||
|
||||
msg->channel = g_strdup(channel);
|
||||
msg->message = g_strdup(message);
|
||||
}
|
||||
|
||||
void
|
||||
client_message_copy(struct client_message *dest,
|
||||
const struct client_message *src)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(client_message_defined(src));
|
||||
|
||||
client_message_init(dest, src->channel, src->message);
|
||||
}
|
||||
|
||||
struct client_message *
|
||||
client_message_dup(const struct client_message *src)
|
||||
{
|
||||
struct client_message *dest = g_slice_new(struct client_message);
|
||||
client_message_copy(dest, src);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void
|
||||
client_message_deinit(struct client_message *msg)
|
||||
{
|
||||
assert(msg != NULL);
|
||||
|
||||
g_free(msg->channel);
|
||||
g_free(msg->message);
|
||||
}
|
||||
|
||||
void
|
||||
client_message_free(struct client_message *msg)
|
||||
{
|
||||
client_message_deinit(msg);
|
||||
g_slice_free(struct client_message, msg);
|
||||
}
|
72
src/client_message.h
Normal file
72
src/client_message.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_MESSAGE_H
|
||||
#define MPD_CLIENT_MESSAGE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* A client-to-client message.
|
||||
*/
|
||||
struct client_message {
|
||||
char *channel;
|
||||
|
||||
char *message;
|
||||
};
|
||||
|
||||
G_GNUC_PURE
|
||||
bool
|
||||
client_message_valid_channel_name(const char *name);
|
||||
|
||||
G_GNUC_PURE
|
||||
static inline bool
|
||||
client_message_defined(const struct client_message *msg)
|
||||
{
|
||||
assert(msg != NULL);
|
||||
assert((msg->channel == NULL) == (msg->message == NULL));
|
||||
|
||||
return msg->channel != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
client_message_init_null(struct client_message *msg);
|
||||
|
||||
void
|
||||
client_message_init(struct client_message *msg,
|
||||
const char *channel, const char *message);
|
||||
|
||||
void
|
||||
client_message_copy(struct client_message *dest,
|
||||
const struct client_message *src);
|
||||
|
||||
G_GNUC_MALLOC
|
||||
struct client_message *
|
||||
client_message_dup(const struct client_message *src);
|
||||
|
||||
void
|
||||
client_message_deinit(struct client_message *msg);
|
||||
|
||||
void
|
||||
client_message_free(struct client_message *msg);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,9 +19,11 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "client_internal.h"
|
||||
#include "fd_util.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include "socket_util.h"
|
||||
#include "resolver.h"
|
||||
#include "permission.h"
|
||||
#include "glib_socket.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
@@ -41,12 +43,15 @@
|
||||
|
||||
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
|
||||
|
||||
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
void
|
||||
client_new(struct player_control *player_control,
|
||||
int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
{
|
||||
static unsigned int next_client_num;
|
||||
struct client *client;
|
||||
char *remote;
|
||||
|
||||
assert(player_control != NULL);
|
||||
assert(fd >= 0);
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
@@ -66,7 +71,7 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
progname, hostaddr);
|
||||
|
||||
g_free(hostaddr);
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,17 +81,14 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
|
||||
if (client_list_is_full()) {
|
||||
g_warning("Max Connections Reached!");
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
client = g_new0(struct client, 1);
|
||||
client->player_control = player_control;
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
client->channel = g_io_channel_unix_new(fd);
|
||||
#else
|
||||
client->channel = g_io_channel_win32_new_socket(fd);
|
||||
#endif
|
||||
client->channel = g_io_channel_new_socket(fd);
|
||||
/* GLib is responsible for closing the file descriptor */
|
||||
g_io_channel_set_close_on_unref(client->channel, true);
|
||||
/* NULL encoding means the stream is binary safe; the MPD
|
||||
@@ -117,6 +119,10 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
|
||||
client->send_buf_used = 0;
|
||||
|
||||
client->subscriptions = NULL;
|
||||
client->messages = NULL;
|
||||
client->num_messages = 0;
|
||||
|
||||
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
|
||||
|
||||
client_list_add(client);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 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 (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
123
src/client_subscribe.c
Normal file
123
src/client_subscribe.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "client_subscribe.h"
|
||||
#include "client_internal.h"
|
||||
#include "client_idle.h"
|
||||
#include "idle.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
G_GNUC_PURE
|
||||
static GSList *
|
||||
client_find_subscription(const struct client *client, const char *channel)
|
||||
{
|
||||
for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
|
||||
if (strcmp((const char *)i->data, channel) == 0)
|
||||
return i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum client_subscribe_result
|
||||
client_subscribe(struct client *client, const char *channel)
|
||||
{
|
||||
assert(client != NULL);
|
||||
assert(channel != NULL);
|
||||
|
||||
if (!client_message_valid_channel_name(channel))
|
||||
return CLIENT_SUBSCRIBE_INVALID;
|
||||
|
||||
if (client_find_subscription(client, channel) != NULL)
|
||||
return CLIENT_SUBSCRIBE_ALREADY;
|
||||
|
||||
if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
|
||||
return CLIENT_SUBSCRIBE_FULL;
|
||||
|
||||
client->subscriptions = g_slist_prepend(client->subscriptions,
|
||||
g_strdup(channel));
|
||||
++client->num_subscriptions;
|
||||
|
||||
idle_add(IDLE_SUBSCRIPTION);
|
||||
|
||||
return CLIENT_SUBSCRIBE_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
client_unsubscribe(struct client *client, const char *channel)
|
||||
{
|
||||
GSList *i = client_find_subscription(client, channel);
|
||||
if (i == NULL)
|
||||
return false;
|
||||
|
||||
assert(client->num_subscriptions > 0);
|
||||
|
||||
client->subscriptions = g_slist_remove(client->subscriptions, i->data);
|
||||
--client->num_subscriptions;
|
||||
|
||||
idle_add(IDLE_SUBSCRIPTION);
|
||||
|
||||
assert((client->num_subscriptions == 0) ==
|
||||
(client->subscriptions == NULL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
client_unsubscribe_all(struct client *client)
|
||||
{
|
||||
for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
|
||||
g_free(i->data);
|
||||
|
||||
g_slist_free(client->subscriptions);
|
||||
client->subscriptions = NULL;
|
||||
client->num_subscriptions = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
client_push_message(struct client *client, const struct client_message *msg)
|
||||
{
|
||||
assert(client != NULL);
|
||||
assert(msg != NULL);
|
||||
assert(client_message_defined(msg));
|
||||
|
||||
if (client->num_messages >= CLIENT_MAX_MESSAGES ||
|
||||
client_find_subscription(client, msg->channel) == NULL)
|
||||
return false;
|
||||
|
||||
if (client->messages == NULL)
|
||||
client_idle_add(client, IDLE_MESSAGE);
|
||||
|
||||
client->messages = g_slist_prepend(client->messages,
|
||||
client_message_dup(msg));
|
||||
++client->num_messages;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GSList *
|
||||
client_read_messages(struct client *client)
|
||||
{
|
||||
GSList *messages = g_slist_reverse(client->messages);
|
||||
|
||||
client->messages = NULL;
|
||||
client->num_messages = 0;
|
||||
|
||||
return messages;
|
||||
}
|
59
src/client_subscribe.h
Normal file
59
src/client_subscribe.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_SUBSCRIBE_H
|
||||
#define MPD_CLIENT_SUBSCRIBE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <glib.h>
|
||||
|
||||
struct client;
|
||||
struct client_message;
|
||||
|
||||
enum client_subscribe_result {
|
||||
/** success */
|
||||
CLIENT_SUBSCRIBE_OK,
|
||||
|
||||
/** invalid channel name */
|
||||
CLIENT_SUBSCRIBE_INVALID,
|
||||
|
||||
/** already subscribed to this channel */
|
||||
CLIENT_SUBSCRIBE_ALREADY,
|
||||
|
||||
/** too many subscriptions */
|
||||
CLIENT_SUBSCRIBE_FULL,
|
||||
};
|
||||
|
||||
enum client_subscribe_result
|
||||
client_subscribe(struct client *client, const char *channel);
|
||||
|
||||
bool
|
||||
client_unsubscribe(struct client *client, const char *channel);
|
||||
|
||||
void
|
||||
client_unsubscribe_all(struct client *client);
|
||||
|
||||
bool
|
||||
client_push_message(struct client *client, const struct client_message *msg);
|
||||
|
||||
G_GNUC_MALLOC
|
||||
GSList *
|
||||
client_read_messages(struct client *client);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
95
src/clock.c
Normal file
95
src/clock.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
unsigned
|
||||
monotonic_clock_ms(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetTickCount();
|
||||
#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */
|
||||
static mach_timebase_info_data_t base;
|
||||
if (base.denom == 0)
|
||||
(void)mach_timebase_info(&base);
|
||||
|
||||
return (unsigned)((mach_absolute_time() * base.numer)
|
||||
/ (1000000 * base.denom));
|
||||
#elif defined(CLOCK_MONOTONIC)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
#else
|
||||
/* we have no monotonic clock, fall back to gettimeofday() */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t
|
||||
monotonic_clock_us(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
LARGE_INTEGER l_value, l_frequency;
|
||||
|
||||
if (!QueryPerformanceCounter(&l_value) ||
|
||||
!QueryPerformanceFrequency(&l_frequency))
|
||||
return 0;
|
||||
|
||||
uint64_t value = l_value.QuadPart;
|
||||
uint64_t frequency = l_frequency.QuadPart;
|
||||
|
||||
if (frequency > 1000000) {
|
||||
value *= 10000;
|
||||
value /= frequency / 100;
|
||||
} else if (frequency < 1000000) {
|
||||
value *= 10000;
|
||||
value /= frequency;
|
||||
value *= 100;
|
||||
}
|
||||
|
||||
return value;
|
||||
#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */
|
||||
static mach_timebase_info_data_t base;
|
||||
if (base.denom == 0)
|
||||
(void)mach_timebase_info(&base);
|
||||
|
||||
return ((uint64_t)mach_absolute_time() * (uint64_t)base.numer)
|
||||
/ (1000 * (uint64_t)base.denom);
|
||||
#elif defined(CLOCK_MONOTONIC)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000000 + (uint64_t)(ts.tv_nsec / 1000);
|
||||
#else
|
||||
/* we have no monotonic clock, fall back to gettimeofday() */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
return (uint64_t)tv.tv_sec * 1000 + (uint64_t)(tv.tv_usec) / 1000(;
|
||||
#endif
|
||||
}
|
||||
|
41
src/clock.h
Normal file
41
src/clock.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLOCK_H
|
||||
#define MPD_CLOCK_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Returns the value of a monotonic clock in milliseconds.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
unsigned
|
||||
monotonic_clock_ms(void);
|
||||
|
||||
/**
|
||||
* Returns the value of a monotonic clock in microseconds.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
uint64_t
|
||||
monotonic_clock_us(void);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,15 +25,23 @@
|
||||
#include "decoder_list.h"
|
||||
#include "decoder_plugin.h"
|
||||
#include "output_list.h"
|
||||
#include "output_plugin.h"
|
||||
#include "input_registry.h"
|
||||
#include "input_plugin.h"
|
||||
#include "playlist_list.h"
|
||||
#include "playlist_plugin.h"
|
||||
#include "ls.h"
|
||||
#include "mpd_error.h"
|
||||
#include "glib_compat.h"
|
||||
|
||||
#ifdef ENABLE_ENCODER
|
||||
#include "encoder_list.h"
|
||||
#include "encoder_plugin.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "archive_list.h"
|
||||
#include "archive_plugin.h"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
@@ -54,59 +62,70 @@ cmdline_quark(void)
|
||||
return g_quark_from_static_string("cmdline");
|
||||
}
|
||||
|
||||
static void
|
||||
print_all_decoders(FILE *fp)
|
||||
{
|
||||
for (unsigned i = 0; decoder_plugins[i] != NULL; ++i) {
|
||||
const struct decoder_plugin *plugin = decoder_plugins[i];
|
||||
const char *const*suffixes;
|
||||
|
||||
fprintf(fp, "[%s]", plugin->name);
|
||||
|
||||
for (suffixes = plugin->suffixes;
|
||||
suffixes != NULL && *suffixes != NULL;
|
||||
++suffixes) {
|
||||
fprintf(fp, " %s", *suffixes);
|
||||
}
|
||||
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
G_GNUC_NORETURN
|
||||
static void version(void)
|
||||
{
|
||||
puts(PACKAGE " (MPD: Music Player Daemon) " VERSION " \n"
|
||||
"\n"
|
||||
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright (C) 2008-2010 Max Kellermann <max@duempel.org>\n"
|
||||
"Copyright (C) 2008-2012 Max Kellermann <max@duempel.org>\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"
|
||||
"\n"
|
||||
"Supported decoders:\n");
|
||||
"Decoders plugins:");
|
||||
|
||||
print_all_decoders(stdout);
|
||||
decoder_plugins_for_each(plugin) {
|
||||
printf(" [%s]", plugin->name);
|
||||
|
||||
const char *const*suffixes = plugin->suffixes;
|
||||
if (suffixes != NULL)
|
||||
for (; *suffixes != NULL; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
}
|
||||
|
||||
puts("\n"
|
||||
"Supported outputs:\n");
|
||||
audio_output_plugin_print_all_types(stdout);
|
||||
"Output plugins:");
|
||||
audio_output_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
puts("");
|
||||
|
||||
#ifdef ENABLE_ENCODER
|
||||
puts("\n"
|
||||
"Supported encoders:\n");
|
||||
encoder_plugin_print_all_types(stdout);
|
||||
"Encoder plugins:");
|
||||
encoder_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
puts("\n"
|
||||
"Supported archives:\n");
|
||||
archive_plugin_init_all();
|
||||
archive_plugin_print_all_suffixes(stdout);
|
||||
"Archive plugins:");
|
||||
archive_plugins_for_each(plugin) {
|
||||
printf(" [%s]", plugin->name);
|
||||
|
||||
const char *const*suffixes = plugin->suffixes;
|
||||
if (suffixes != NULL)
|
||||
for (; *suffixes != NULL; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
}
|
||||
#endif
|
||||
|
||||
puts("\n"
|
||||
"Supported protocols:\n");
|
||||
"Input plugins:");
|
||||
input_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
|
||||
puts("\n\n"
|
||||
"Playlist plugins:");
|
||||
playlist_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
|
||||
puts("\n\n"
|
||||
"Protocols:");
|
||||
print_supported_uri_schemes_to_fp(stdout);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
@@ -194,8 +213,6 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
||||
if(g_file_test(system_path,
|
||||
G_FILE_TEST_IS_REGULAR)) {
|
||||
ret = config_read_file(system_path,error_r);
|
||||
g_free(system_path);
|
||||
g_free(&system_config_dirs);
|
||||
break;
|
||||
}
|
||||
++i;;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
1098
src/command.c
1098
src/command.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
167
src/conf.c
167
src/conf.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "config.h"
|
||||
#include "conf.h"
|
||||
#include "utils.h"
|
||||
#include "string_util.h"
|
||||
#include "tokenizer.h"
|
||||
#include "path.h"
|
||||
#include "glib_compat.h"
|
||||
#include "mpd_error.h"
|
||||
|
||||
#include <glib.h>
|
||||
@@ -58,6 +58,7 @@ static struct config_entry config_entries[] = {
|
||||
{ .name = CONF_LOG_FILE, false, false },
|
||||
{ .name = CONF_PID_FILE, false, false },
|
||||
{ .name = CONF_STATE_FILE, false, false },
|
||||
{ .name = "restore_paused", false, false },
|
||||
{ .name = CONF_USER, false, false },
|
||||
{ .name = CONF_GROUP, false, false },
|
||||
{ .name = CONF_BIND_TO_ADDRESS, true, false },
|
||||
@@ -97,6 +98,9 @@ static struct config_entry config_entries[] = {
|
||||
{ .name = CONF_PLAYLIST_PLUGIN, true, true },
|
||||
{ .name = CONF_AUTO_UPDATE, false, false },
|
||||
{ .name = CONF_AUTO_UPDATE_DEPTH, false, false },
|
||||
{ .name = CONF_DESPOTIFY_USER, false, false },
|
||||
{ .name = CONF_DESPOTIFY_PASSWORD, false, false},
|
||||
{ .name = CONF_DESPOTIFY_HIGH_BITRATE, false, false },
|
||||
{ .name = "filter", true, true },
|
||||
};
|
||||
|
||||
@@ -138,7 +142,7 @@ config_new_param(const char *value, int line)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
config_param_free(struct config_param *param)
|
||||
{
|
||||
g_free(param->value);
|
||||
@@ -218,20 +222,13 @@ void config_global_check(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
config_add_block_param(struct config_param * param, const char *name,
|
||||
const char *value, int line, GError **error_r)
|
||||
const char *value, int line)
|
||||
{
|
||||
struct block_param *bp;
|
||||
|
||||
bp = config_get_block_param(param, name);
|
||||
if (bp != NULL) {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"\"%s\" first defined on line %i, and "
|
||||
"redefined on line %i\n", name,
|
||||
bp->line, line);
|
||||
return false;
|
||||
}
|
||||
assert(config_get_block_param(param, name) == NULL);
|
||||
|
||||
param->num_block_params++;
|
||||
|
||||
@@ -245,7 +242,46 @@ config_add_block_param(struct config_param * param, const char *name,
|
||||
bp->value = g_strdup(value);
|
||||
bp->line = line;
|
||||
bp->used = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
config_read_name_value(struct config_param *param, char *input, unsigned line,
|
||||
GError **error_r)
|
||||
{
|
||||
const char *name = tokenizer_next_word(&input, error_r);
|
||||
if (name == NULL) {
|
||||
assert(*input != 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *value = tokenizer_next_string(&input, error_r);
|
||||
if (value == NULL) {
|
||||
if (*input == 0) {
|
||||
assert(error_r == NULL || *error_r == NULL);
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"Value missing");
|
||||
} else {
|
||||
assert(error_r == NULL || *error_r != NULL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*input != 0 && *input != CONF_COMMENT) {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"Unknown tokens after value");
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct block_param *bp = config_get_block_param(param, name);
|
||||
if (bp != NULL) {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"\"%s\" is duplicate, first defined on line %i",
|
||||
name, bp->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
config_add_block_param(param, name, value, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,11 +290,9 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
|
||||
{
|
||||
struct config_param *ret = config_new_param(NULL, *count);
|
||||
GError *error = NULL;
|
||||
bool success;
|
||||
|
||||
while (true) {
|
||||
char *line;
|
||||
const char *name, *value;
|
||||
|
||||
line = fgets(string, MAX_STRING_SIZE, fp);
|
||||
if (line == NULL) {
|
||||
@@ -269,7 +303,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
line = g_strchug(line);
|
||||
line = strchug_fast(line);
|
||||
if (*line == 0 || *line == CONF_COMMENT)
|
||||
continue;
|
||||
|
||||
@@ -277,7 +311,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
|
||||
/* end of this block; return from the function
|
||||
(and from this "while" loop) */
|
||||
|
||||
line = g_strchug(line + 1);
|
||||
line = strchug_fast(line + 1);
|
||||
if (*line != 0 && *line != CONF_COMMENT) {
|
||||
config_param_free(ret);
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
@@ -291,42 +325,13 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
|
||||
|
||||
/* parse name and value */
|
||||
|
||||
name = tokenizer_next_word(&line, &error);
|
||||
if (name == NULL) {
|
||||
if (!config_read_name_value(ret, line, *count, &error)) {
|
||||
assert(*line != 0);
|
||||
config_param_free(ret);
|
||||
g_propagate_prefixed_error(error_r, error,
|
||||
"line %i: ", *count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
value = tokenizer_next_string(&line, &error);
|
||||
if (value == NULL) {
|
||||
config_param_free(ret);
|
||||
if (*line == 0)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Value missing", *count);
|
||||
else
|
||||
g_propagate_prefixed_error(error_r, error,
|
||||
"line %i: ",
|
||||
*count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*line != 0 && *line != CONF_COMMENT) {
|
||||
config_param_free(ret);
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Unknown tokens after value",
|
||||
*count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
success = config_add_block_param(ret, name, value, *count,
|
||||
error_r);
|
||||
if (!success) {
|
||||
config_param_free(ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +349,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
if (!(fp = fopen(file, "r"))) {
|
||||
g_set_error(error_r, config_quark(), errno,
|
||||
"Failed to open %s: %s",
|
||||
file, strerror(errno));
|
||||
file, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -355,7 +360,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
|
||||
count++;
|
||||
|
||||
line = g_strchug(string);
|
||||
line = strchug_fast(string);
|
||||
if (*line == 0 || *line == CONF_COMMENT)
|
||||
continue;
|
||||
|
||||
@@ -367,6 +372,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
assert(*line != 0);
|
||||
g_propagate_prefixed_error(error_r, error,
|
||||
"line %i: ", count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -378,6 +384,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"unrecognized parameter in config file at "
|
||||
"line %i: %s\n", count, name);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -387,6 +394,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
"config parameter \"%s\" is first defined "
|
||||
"on line %i and redefined on line %i\n",
|
||||
name, param->line, count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -398,20 +406,24 @@ config_read_file(const char *file, GError **error_r)
|
||||
if (*line != '{') {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: '{' expected", count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
line = g_strchug(line + 1);
|
||||
line = strchug_fast(line + 1);
|
||||
if (*line != 0 && *line != CONF_COMMENT) {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Unknown tokens after '{'",
|
||||
count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = config_read_block(fp, &count, string, error_r);
|
||||
if (param == NULL)
|
||||
if (param == NULL) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* a string value */
|
||||
|
||||
@@ -428,6 +440,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -435,6 +448,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Unknown tokens after value",
|
||||
count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -448,7 +462,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct config_param *
|
||||
const struct config_param *
|
||||
config_get_next_param(const char *name, const struct config_param * last)
|
||||
{
|
||||
struct config_entry *entry;
|
||||
@@ -488,22 +502,23 @@ config_get_string(const char *name, const char *default_value)
|
||||
return param->value;
|
||||
}
|
||||
|
||||
const char *
|
||||
config_get_path(const char *name)
|
||||
char *
|
||||
config_dup_path(const char *name, GError **error_r)
|
||||
{
|
||||
struct config_param *param = config_get_param(name);
|
||||
char *path;
|
||||
assert(error_r != NULL);
|
||||
assert(*error_r == NULL);
|
||||
|
||||
const struct config_param *param = config_get_param(name);
|
||||
if (param == NULL)
|
||||
return NULL;
|
||||
|
||||
path = parsePath(param->value);
|
||||
if (path == NULL)
|
||||
MPD_ERROR("error parsing \"%s\" at line %i\n",
|
||||
name, param->line);
|
||||
char *path = parsePath(param->value, error_r);
|
||||
if (G_UNLIKELY(path == NULL))
|
||||
g_prefix_error(error_r,
|
||||
"Invalid path in \"%s\" at line %i: ",
|
||||
name, param->line);
|
||||
|
||||
g_free(param->value);
|
||||
return param->value = path;
|
||||
return path;
|
||||
}
|
||||
|
||||
unsigned
|
||||
@@ -544,7 +559,7 @@ config_get_positive(const char *name, unsigned default_value)
|
||||
return (unsigned)value;
|
||||
}
|
||||
|
||||
struct block_param *
|
||||
const struct block_param *
|
||||
config_get_block_param(const struct config_param * param, const char *name)
|
||||
{
|
||||
if (param == NULL)
|
||||
@@ -582,7 +597,7 @@ const char *
|
||||
config_get_block_string(const struct config_param *param, const char *name,
|
||||
const char *default_value)
|
||||
{
|
||||
struct block_param *bp = config_get_block_param(param, name);
|
||||
const struct block_param *bp = config_get_block_param(param, name);
|
||||
|
||||
if (bp == NULL)
|
||||
return default_value;
|
||||
@@ -590,11 +605,31 @@ config_get_block_string(const struct config_param *param, const char *name,
|
||||
return bp->value;
|
||||
}
|
||||
|
||||
char *
|
||||
config_dup_block_path(const struct config_param *param, const char *name,
|
||||
GError **error_r)
|
||||
{
|
||||
assert(error_r != NULL);
|
||||
assert(*error_r == NULL);
|
||||
|
||||
const struct block_param *bp = config_get_block_param(param, name);
|
||||
if (bp == NULL)
|
||||
return NULL;
|
||||
|
||||
char *path = parsePath(bp->value, error_r);
|
||||
if (G_UNLIKELY(path == NULL))
|
||||
g_prefix_error(error_r,
|
||||
"Invalid path in \"%s\" at line %i: ",
|
||||
name, bp->line);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
unsigned
|
||||
config_get_block_unsigned(const struct config_param *param, const char *name,
|
||||
unsigned default_value)
|
||||
{
|
||||
struct block_param *bp = config_get_block_param(param, name);
|
||||
const struct block_param *bp = config_get_block_param(param, name);
|
||||
long value;
|
||||
char *endptr;
|
||||
|
||||
@@ -615,7 +650,7 @@ bool
|
||||
config_get_block_bool(const struct config_param *param, const char *name,
|
||||
bool default_value)
|
||||
{
|
||||
struct block_param *bp = config_get_block_param(param, name);
|
||||
const struct block_param *bp = config_get_block_param(param, name);
|
||||
bool success, value;
|
||||
|
||||
if (bp == NULL)
|
||||
|
48
src/conf.h
48
src/conf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -72,6 +72,9 @@
|
||||
#define CONF_PLAYLIST_PLUGIN "playlist_plugin"
|
||||
#define CONF_AUTO_UPDATE "auto_update"
|
||||
#define CONF_AUTO_UPDATE_DEPTH "auto_update_depth"
|
||||
#define CONF_DESPOTIFY_USER "despotify_user"
|
||||
#define CONF_DESPOTIFY_PASSWORD "despotify_password"
|
||||
#define CONF_DESPOTIFY_HIGH_BITRATE "despotify_high_bitrate"
|
||||
|
||||
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
|
||||
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
|
||||
@@ -108,6 +111,7 @@ struct config_param {
|
||||
* A GQuark for GError instances, resulting from malformed
|
||||
* configuration.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
config_quark(void)
|
||||
{
|
||||
@@ -129,11 +133,11 @@ config_read_file(const char *file, GError **error_r);
|
||||
/* don't free the returned value
|
||||
set _last_ to NULL to get first entry */
|
||||
G_GNUC_PURE
|
||||
struct config_param *
|
||||
const struct config_param *
|
||||
config_get_next_param(const char *name, const struct config_param *last);
|
||||
|
||||
G_GNUC_PURE
|
||||
static inline struct config_param *
|
||||
static inline const struct config_param *
|
||||
config_get_param(const char *name)
|
||||
{
|
||||
return config_get_next_param(name, NULL);
|
||||
@@ -152,17 +156,15 @@ config_get_string(const char *name, const char *default_value);
|
||||
|
||||
/**
|
||||
* Returns an optional configuration variable which contains an
|
||||
* absolute path. If there is a tilde prefix, it is expanded. Aborts
|
||||
* MPD if the path is not a valid absolute path.
|
||||
* absolute path. If there is a tilde prefix, it is expanded.
|
||||
* Returns NULL if the value is not present. If the path could not be
|
||||
* parsed, returns NULL and sets the error.
|
||||
*
|
||||
* The return value must be freed with g_free().
|
||||
*/
|
||||
/* We lie here really. This function is not pure as it has side
|
||||
effects -- it parse the value and creates new string freeing
|
||||
previous one. However, because this works the very same way each
|
||||
time (ie. from the outside it appears as if function had no side
|
||||
effects) we should be in the clear declaring it pure. */
|
||||
G_GNUC_PURE
|
||||
const char *
|
||||
config_get_path(const char *name);
|
||||
G_GNUC_MALLOC
|
||||
char *
|
||||
config_dup_path(const char *name, GError **error_r);
|
||||
|
||||
G_GNUC_PURE
|
||||
unsigned
|
||||
@@ -173,7 +175,7 @@ unsigned
|
||||
config_get_positive(const char *name, unsigned default_value);
|
||||
|
||||
G_GNUC_PURE
|
||||
struct block_param *
|
||||
const struct block_param *
|
||||
config_get_block_param(const struct config_param *param, const char *name);
|
||||
|
||||
G_GNUC_PURE
|
||||
@@ -184,6 +186,7 @@ const char *
|
||||
config_get_block_string(const struct config_param *param, const char *name,
|
||||
const char *default_value);
|
||||
|
||||
G_GNUC_MALLOC
|
||||
static inline char *
|
||||
config_dup_block_string(const struct config_param *param, const char *name,
|
||||
const char *default_value)
|
||||
@@ -191,6 +194,15 @@ config_dup_block_string(const struct config_param *param, const char *name,
|
||||
return g_strdup(config_get_block_string(param, name, default_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as config_dup_path(), but looks up the setting in the
|
||||
* specified block.
|
||||
*/
|
||||
G_GNUC_MALLOC
|
||||
char *
|
||||
config_dup_block_path(const struct config_param *param, const char *name,
|
||||
GError **error_r);
|
||||
|
||||
G_GNUC_PURE
|
||||
unsigned
|
||||
config_get_block_unsigned(const struct config_param *param, const char *name,
|
||||
@@ -201,11 +213,15 @@ bool
|
||||
config_get_block_bool(const struct config_param *param, const char *name,
|
||||
bool default_value);
|
||||
|
||||
G_GNUC_MALLOC
|
||||
struct config_param *
|
||||
config_new_param(const char *value, int line);
|
||||
|
||||
bool
|
||||
void
|
||||
config_param_free(struct config_param *param);
|
||||
|
||||
void
|
||||
config_add_block_param(struct config_param * param, const char *name,
|
||||
const char *value, int line, GError **error_r);
|
||||
const char *value, int line);
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "crossfade.h"
|
||||
#include "pcm_mix.h"
|
||||
#include "chunk.h"
|
||||
#include "audio_format.h"
|
||||
#include "tag.h"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
339
src/cue/cue_parser.c
Normal file
339
src/cue/cue_parser.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "cue_parser.h"
|
||||
#include "string_util.h"
|
||||
#include "song.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct cue_parser {
|
||||
enum {
|
||||
/**
|
||||
* Parsing the CUE header.
|
||||
*/
|
||||
HEADER,
|
||||
|
||||
/**
|
||||
* Parsing a "FILE ... WAVE".
|
||||
*/
|
||||
WAVE,
|
||||
|
||||
/**
|
||||
* Ignore everything until the next "FILE".
|
||||
*/
|
||||
IGNORE_FILE,
|
||||
|
||||
/**
|
||||
* Parsing a "TRACK ... AUDIO".
|
||||
*/
|
||||
TRACK,
|
||||
|
||||
/**
|
||||
* Ignore everything until the next "TRACK".
|
||||
*/
|
||||
IGNORE_TRACK,
|
||||
} state;
|
||||
|
||||
struct tag *tag;
|
||||
|
||||
char *filename;
|
||||
|
||||
struct song *current, *previous, *finished;
|
||||
|
||||
bool last_updated;
|
||||
};
|
||||
|
||||
struct cue_parser *
|
||||
cue_parser_new(void)
|
||||
{
|
||||
struct cue_parser *parser = g_new(struct cue_parser, 1);
|
||||
parser->state = HEADER;
|
||||
parser->tag = tag_new();
|
||||
parser->filename = NULL;
|
||||
parser->current = NULL;
|
||||
parser->previous = NULL;
|
||||
parser->finished = NULL;
|
||||
return parser;
|
||||
}
|
||||
|
||||
void
|
||||
cue_parser_free(struct cue_parser *parser)
|
||||
{
|
||||
tag_free(parser->tag);
|
||||
g_free(parser->filename);
|
||||
|
||||
if (parser->current != NULL)
|
||||
song_free(parser->current);
|
||||
|
||||
if (parser->finished != NULL)
|
||||
song_free(parser->finished);
|
||||
|
||||
g_free(parser);
|
||||
}
|
||||
|
||||
static const char *
|
||||
cue_next_word(char *p, char **pp)
|
||||
{
|
||||
assert(p >= *pp);
|
||||
assert(!g_ascii_isspace(*p));
|
||||
|
||||
const char *word = p;
|
||||
while (*p != 0 && !g_ascii_isspace(*p))
|
||||
++p;
|
||||
|
||||
*p = 0;
|
||||
*pp = p + 1;
|
||||
return word;
|
||||
}
|
||||
|
||||
static const char *
|
||||
cue_next_quoted(char *p, char **pp)
|
||||
{
|
||||
assert(p >= *pp);
|
||||
assert(p[-1] == '"');
|
||||
|
||||
char *end = strchr(p, '"');
|
||||
if (end == NULL) {
|
||||
/* syntax error - ignore it silently */
|
||||
*pp = p + strlen(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
*end = 0;
|
||||
*pp = end + 1;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *
|
||||
cue_next_token(char **pp)
|
||||
{
|
||||
char *p = strchug_fast(*pp);
|
||||
if (*p == 0)
|
||||
return NULL;
|
||||
|
||||
return cue_next_word(p, pp);
|
||||
}
|
||||
|
||||
static const char *
|
||||
cue_next_value(char **pp)
|
||||
{
|
||||
char *p = strchug_fast(*pp);
|
||||
if (*p == 0)
|
||||
return NULL;
|
||||
|
||||
if (*p == '"')
|
||||
return cue_next_quoted(p + 1, pp);
|
||||
else
|
||||
return cue_next_word(p, pp);
|
||||
}
|
||||
|
||||
static void
|
||||
cue_add_tag(struct tag *tag, enum tag_type type, char *p)
|
||||
{
|
||||
const char *value = cue_next_value(&p);
|
||||
if (value != NULL)
|
||||
tag_add_item(tag, type, value);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
cue_parse_rem(char *p, struct tag *tag)
|
||||
{
|
||||
const char *type = cue_next_token(&p);
|
||||
if (type == NULL)
|
||||
return;
|
||||
|
||||
enum tag_type type2 = tag_name_parse_i(type);
|
||||
if (type2 != TAG_NUM_OF_ITEM_TYPES)
|
||||
cue_add_tag(tag, type2, p);
|
||||
}
|
||||
|
||||
static struct tag *
|
||||
cue_current_tag(struct cue_parser *parser)
|
||||
{
|
||||
if (parser->state == HEADER)
|
||||
return parser->tag;
|
||||
else if (parser->state == TRACK)
|
||||
return parser->current->tag;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
cue_parse_position(const char *p)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long minutes = strtoul(p, &endptr, 10);
|
||||
if (endptr == p || *endptr != ':')
|
||||
return -1;
|
||||
|
||||
p = endptr + 1;
|
||||
unsigned long seconds = strtoul(p, &endptr, 10);
|
||||
if (endptr == p || *endptr != ':')
|
||||
return -1;
|
||||
|
||||
p = endptr + 1;
|
||||
unsigned long frames = strtoul(p, &endptr, 10);
|
||||
if (endptr == p || *endptr != 0)
|
||||
return -1;
|
||||
|
||||
return minutes * 60000 + seconds * 1000 + frames * 1000 / 75;
|
||||
}
|
||||
|
||||
static void
|
||||
cue_parser_feed2(struct cue_parser *parser, char *p)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
const char *command = cue_next_token(&p);
|
||||
if (command == NULL)
|
||||
return;
|
||||
|
||||
if (strcmp(command, "REM") == 0) {
|
||||
struct tag *tag = cue_current_tag(parser);
|
||||
if (tag != NULL)
|
||||
cue_parse_rem(p, tag);
|
||||
} else if (strcmp(command, "PERFORMER") == 0) {
|
||||
/* MPD knows a "performer" tag, but it is not a good
|
||||
match for this CUE tag; from the Hydrogenaudio
|
||||
Knowledgebase: "At top-level this will specify the
|
||||
CD artist, while at track-level it specifies the
|
||||
track artist." */
|
||||
|
||||
enum tag_type type = parser->state == TRACK
|
||||
? TAG_ARTIST
|
||||
: TAG_ALBUM_ARTIST;
|
||||
|
||||
struct tag *tag = cue_current_tag(parser);
|
||||
if (tag != NULL)
|
||||
cue_add_tag(tag, type, p);
|
||||
} else if (strcmp(command, "TITLE") == 0) {
|
||||
if (parser->state == HEADER)
|
||||
cue_add_tag(parser->tag, TAG_ALBUM, p);
|
||||
else if (parser->state == TRACK)
|
||||
cue_add_tag(parser->current->tag, TAG_TITLE, p);
|
||||
} else if (strcmp(command, "FILE") == 0) {
|
||||
cue_parser_finish(parser);
|
||||
|
||||
const char *filename = cue_next_value(&p);
|
||||
if (filename == NULL)
|
||||
return;
|
||||
|
||||
const char *type = cue_next_token(&p);
|
||||
if (type == NULL)
|
||||
return;
|
||||
|
||||
if (strcmp(type, "WAVE") != 0 &&
|
||||
strcmp(type, "MP3") != 0 &&
|
||||
strcmp(type, "AIFF") != 0) {
|
||||
parser->state = IGNORE_FILE;
|
||||
return;
|
||||
}
|
||||
|
||||
parser->state = WAVE;
|
||||
g_free(parser->filename);
|
||||
parser->filename = g_strdup(filename);
|
||||
} else if (parser->state == IGNORE_FILE) {
|
||||
return;
|
||||
} else if (strcmp(command, "TRACK") == 0) {
|
||||
cue_parser_finish(parser);
|
||||
|
||||
const char *nr = cue_next_token(&p);
|
||||
if (nr == NULL)
|
||||
return;
|
||||
|
||||
const char *type = cue_next_token(&p);
|
||||
if (type == NULL)
|
||||
return;
|
||||
|
||||
if (strcmp(type, "AUDIO") != 0) {
|
||||
parser->state = IGNORE_TRACK;
|
||||
return;
|
||||
}
|
||||
|
||||
parser->state = TRACK;
|
||||
parser->current = song_remote_new(parser->filename);
|
||||
assert(parser->current->tag == NULL);
|
||||
parser->current->tag = tag_dup(parser->tag);
|
||||
tag_add_item(parser->current->tag, TAG_TRACK, nr);
|
||||
parser->last_updated = false;
|
||||
} else if (parser->state == IGNORE_TRACK) {
|
||||
return;
|
||||
} else if (parser->state == TRACK && strcmp(command, "INDEX") == 0) {
|
||||
const char *nr = cue_next_token(&p);
|
||||
if (nr == NULL)
|
||||
return;
|
||||
|
||||
const char *position = cue_next_token(&p);
|
||||
if (position == NULL)
|
||||
return;
|
||||
|
||||
int position_ms = cue_parse_position(position);
|
||||
if (position_ms < 0)
|
||||
return;
|
||||
|
||||
if (!parser->last_updated && parser->previous != NULL &&
|
||||
parser->previous->start_ms < (unsigned)position_ms) {
|
||||
parser->last_updated = true;
|
||||
parser->previous->end_ms = position_ms;
|
||||
parser->previous->tag->time =
|
||||
(parser->previous->end_ms - parser->previous->start_ms + 500) / 1000;
|
||||
}
|
||||
|
||||
parser->current->start_ms = position_ms;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cue_parser_feed(struct cue_parser *parser, const char *line)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
assert(line != NULL);
|
||||
|
||||
char *allocated = g_strdup(line);
|
||||
cue_parser_feed2(parser, allocated);
|
||||
g_free(allocated);
|
||||
}
|
||||
|
||||
void
|
||||
cue_parser_finish(struct cue_parser *parser)
|
||||
{
|
||||
if (parser->finished != NULL)
|
||||
song_free(parser->finished);
|
||||
|
||||
parser->finished = parser->previous;
|
||||
parser->previous = parser->current;
|
||||
parser->current = NULL;
|
||||
}
|
||||
|
||||
struct song *
|
||||
cue_parser_get(struct cue_parser *parser)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
|
||||
struct song *song = parser->finished;
|
||||
parser->finished = NULL;
|
||||
return song;
|
||||
}
|
58
src/cue/cue_parser.h
Normal file
58
src/cue/cue_parser.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CUE_PARSER_H
|
||||
#define MPD_CUE_PARSER_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct cue_parser *
|
||||
cue_parser_new(void);
|
||||
|
||||
void
|
||||
cue_parser_free(struct cue_parser *parser);
|
||||
|
||||
/**
|
||||
* Feed a text line from the CUE file into the parser. Call
|
||||
* cue_parser_get() after this to see if a song has been finished.
|
||||
*/
|
||||
void
|
||||
cue_parser_feed(struct cue_parser *parser, const char *line);
|
||||
|
||||
/**
|
||||
* Tell the parser that the end of the file has been reached. Call
|
||||
* cue_parser_get() after this to see if a song has been finished.
|
||||
* This procedure must be done twice!
|
||||
*/
|
||||
void
|
||||
cue_parser_finish(struct cue_parser *parser);
|
||||
|
||||
/**
|
||||
* Check if a song was finished by the last cue_parser_feed() or
|
||||
* cue_parser_finish() call.
|
||||
*
|
||||
* @return a song object that must be freed by the caller, or NULL if
|
||||
* no song was finished at this time
|
||||
*/
|
||||
struct song *
|
||||
cue_parser_get(struct cue_parser *parser);
|
||||
|
||||
#endif
|
@@ -1,235 +0,0 @@
|
||||
#include "config.h"
|
||||
#include "cue_tag.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include <libcue/libcue.h>
|
||||
#include <assert.h>
|
||||
|
||||
static struct tag *
|
||||
cue_tag_cd(struct Cdtext *cdtext, struct Rem *rem)
|
||||
{
|
||||
struct tag *tag;
|
||||
char *tmp;
|
||||
|
||||
assert(cdtext != NULL);
|
||||
|
||||
tag = tag_new();
|
||||
|
||||
tag_begin_add(tag);
|
||||
|
||||
/* TAG_ALBUM_ARTIST */
|
||||
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
|
||||
|
||||
/* TAG_ARTIST */
|
||||
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
/* TAG_PERFORMER */
|
||||
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_PERFORMER, tmp);
|
||||
|
||||
/* TAG_COMPOSER */
|
||||
if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_COMPOSER, tmp);
|
||||
|
||||
/* TAG_ALBUM */
|
||||
if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ALBUM, tmp);
|
||||
|
||||
/* TAG_GENRE */
|
||||
if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_GENRE, tmp);
|
||||
|
||||
/* TAG_DATE */
|
||||
if ((tmp = rem_get(REM_DATE, rem)) != NULL)
|
||||
tag_add_item(tag, TAG_DATE, tmp);
|
||||
|
||||
/* TAG_COMMENT */
|
||||
if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_COMMENT, tmp);
|
||||
|
||||
/* TAG_DISC */
|
||||
if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_DISC, tmp);
|
||||
|
||||
/* stream name, usually empty
|
||||
* tag_add_item(tag, TAG_NAME,);
|
||||
*/
|
||||
|
||||
/* REM MUSICBRAINZ entry?
|
||||
tag_add_item(tag, TAG_MUSICBRAINZ_ARTISTID,);
|
||||
tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMID,);
|
||||
tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMARTISTID,);
|
||||
tag_add_item(tag, TAG_MUSICBRAINZ_TRACKID,);
|
||||
*/
|
||||
|
||||
tag_end_add(tag);
|
||||
|
||||
if (tag_is_empty(tag)) {
|
||||
tag_free(tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static struct tag *
|
||||
cue_tag_track(struct Cdtext *cdtext, struct Rem *rem)
|
||||
{
|
||||
struct tag *tag;
|
||||
char *tmp;
|
||||
|
||||
assert(cdtext != NULL);
|
||||
|
||||
tag = tag_new();
|
||||
|
||||
tag_begin_add(tag);
|
||||
|
||||
/* TAG_ARTIST */
|
||||
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_ARTIST, tmp);
|
||||
|
||||
/* TAG_TITLE */
|
||||
if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_TITLE, tmp);
|
||||
|
||||
/* TAG_GENRE */
|
||||
if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_GENRE, tmp);
|
||||
|
||||
/* TAG_DATE */
|
||||
if ((tmp = rem_get(REM_DATE, rem)) != NULL)
|
||||
tag_add_item(tag, TAG_DATE, tmp);
|
||||
|
||||
/* TAG_COMPOSER */
|
||||
if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_COMPOSER, tmp);
|
||||
|
||||
/* TAG_PERFORMER */
|
||||
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_PERFORMER, tmp);
|
||||
|
||||
/* TAG_COMMENT */
|
||||
if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_COMMENT, tmp);
|
||||
|
||||
/* TAG_DISC */
|
||||
if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
|
||||
tag_add_item(tag, TAG_DISC, tmp);
|
||||
|
||||
tag_end_add(tag);
|
||||
|
||||
if (tag_is_empty(tag)) {
|
||||
tag_free(tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
cue_tag(struct Cd *cd, unsigned tnum)
|
||||
{
|
||||
struct tag *cd_tag, *track_tag, *tag;
|
||||
struct Track *track;
|
||||
|
||||
assert(cd != NULL);
|
||||
|
||||
track = cd_get_track(cd, tnum);
|
||||
if (track == NULL)
|
||||
return NULL;
|
||||
|
||||
/* tag from CDtext info */
|
||||
cd_tag = cue_tag_cd(cd_get_cdtext(cd), cd_get_rem(cd));
|
||||
|
||||
/* tag from TRACKtext info */
|
||||
track_tag = cue_tag_track(track_get_cdtext(track),
|
||||
track_get_rem(track));
|
||||
|
||||
tag = tag_merge_replace(cd_tag, track_tag);
|
||||
if (tag == NULL)
|
||||
return NULL;
|
||||
|
||||
tag->time = track_get_length(track)
|
||||
- track_get_index(track, 1)
|
||||
+ track_get_zero_pre(track);
|
||||
track = cd_get_track(cd, tnum + 1);
|
||||
if (track != NULL)
|
||||
tag->time += track_get_index(track, 1)
|
||||
- track_get_zero_pre(track);
|
||||
/* libcue returns the track duration in frames, and there are
|
||||
75 frames per second; this formula rounds down */
|
||||
tag->time = tag->time / 75;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
cue_tag_file(FILE *fp, unsigned tnum)
|
||||
{
|
||||
struct Cd *cd;
|
||||
struct tag *tag;
|
||||
|
||||
assert(fp != NULL);
|
||||
|
||||
if (tnum > 256)
|
||||
return NULL;
|
||||
|
||||
cd = cue_parse_file(fp);
|
||||
if (cd == NULL)
|
||||
return NULL;
|
||||
|
||||
tag = cue_tag(cd, tnum);
|
||||
cd_delete(cd);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
cue_tag_string(const char *str, unsigned tnum)
|
||||
{
|
||||
struct Cd *cd;
|
||||
struct tag *tag;
|
||||
|
||||
assert(str != NULL);
|
||||
|
||||
if (tnum > 256)
|
||||
return NULL;
|
||||
|
||||
cd = cue_parse_string(str);
|
||||
if (cd == NULL)
|
||||
return NULL;
|
||||
|
||||
tag = cue_tag(cd, tnum);
|
||||
cd_delete(cd);
|
||||
|
||||
return tag;
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#ifndef MPD_CUE_TAG_H
|
||||
#define MPD_CUE_TAG_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#ifdef HAVE_CUE /* libcue */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct tag;
|
||||
struct Cd;
|
||||
|
||||
struct tag *
|
||||
cue_tag(struct Cd *cd, unsigned tnum);
|
||||
|
||||
struct tag *
|
||||
cue_tag_file(FILE *file, unsigned tnum);
|
||||
|
||||
struct tag *
|
||||
cue_tag_string(const char *str, unsigned tnum);
|
||||
|
||||
#endif /* libcue */
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -80,7 +80,7 @@ daemonize_kill(void)
|
||||
|
||||
ret = kill(pid, SIGTERM);
|
||||
if (ret < 0)
|
||||
MPD_ERROR("unable to kill proccess %i: %s",
|
||||
MPD_ERROR("unable to kill process %i: %s",
|
||||
pid, g_strerror(errno));
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
365
src/database.c
365
src/database.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,14 +19,16 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "database.h"
|
||||
#include "db_error.h"
|
||||
#include "db_save.h"
|
||||
#include "db_selection.h"
|
||||
#include "db_visitor.h"
|
||||
#include "db_plugin.h"
|
||||
#include "db/simple_db_plugin.h"
|
||||
#include "directory.h"
|
||||
#include "directory_save.h"
|
||||
#include "song.h"
|
||||
#include "path.h"
|
||||
#include "stats.h"
|
||||
#include "text_file.h"
|
||||
#include "tag.h"
|
||||
#include "tag_internal.h"
|
||||
#include "conf.h"
|
||||
#include "glib_compat.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@@ -35,85 +37,64 @@
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "database"
|
||||
|
||||
#define DIRECTORY_INFO_BEGIN "info_begin"
|
||||
#define DIRECTORY_INFO_END "info_end"
|
||||
#define DB_FORMAT_PREFIX "format: "
|
||||
#define DIRECTORY_MPD_VERSION "mpd_version: "
|
||||
#define DIRECTORY_FS_CHARSET "fs_charset: "
|
||||
#define DB_TAG_PREFIX "tag: "
|
||||
static struct db *db;
|
||||
static bool db_is_open;
|
||||
|
||||
enum {
|
||||
DB_FORMAT = 1,
|
||||
};
|
||||
|
||||
static char *database_path;
|
||||
|
||||
static struct directory *music_root;
|
||||
|
||||
static time_t database_mtime;
|
||||
|
||||
/**
|
||||
* The quark used for GError.domain.
|
||||
*/
|
||||
static inline GQuark
|
||||
db_quark(void)
|
||||
bool
|
||||
db_init(const struct config_param *path, GError **error_r)
|
||||
{
|
||||
return g_quark_from_static_string("database");
|
||||
}
|
||||
assert(db == NULL);
|
||||
assert(!db_is_open);
|
||||
|
||||
void
|
||||
db_init(const char *path)
|
||||
{
|
||||
database_path = g_strdup(path);
|
||||
if (path == NULL)
|
||||
return true;
|
||||
|
||||
if (path != NULL)
|
||||
music_root = directory_new("", NULL);
|
||||
struct config_param *param = config_new_param("database", path->line);
|
||||
config_add_block_param(param, "path", path->value, path->line);
|
||||
|
||||
db = db_plugin_new(&simple_db_plugin, param, error_r);
|
||||
|
||||
config_param_free(param);
|
||||
|
||||
return db != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
db_finish(void)
|
||||
{
|
||||
assert((database_path == NULL) == (music_root == NULL));
|
||||
if (db_is_open)
|
||||
db_plugin_close(db);
|
||||
|
||||
if (music_root != NULL)
|
||||
directory_free(music_root);
|
||||
|
||||
g_free(database_path);
|
||||
}
|
||||
|
||||
void
|
||||
db_clear(void)
|
||||
{
|
||||
assert(music_root != NULL);
|
||||
|
||||
directory_free(music_root);
|
||||
music_root = directory_new("", NULL);
|
||||
if (db != NULL)
|
||||
db_plugin_free(db);
|
||||
}
|
||||
|
||||
struct directory *
|
||||
db_get_root(void)
|
||||
{
|
||||
assert(music_root != NULL);
|
||||
assert(db != NULL);
|
||||
|
||||
return music_root;
|
||||
return simple_db_get_root(db);
|
||||
}
|
||||
|
||||
struct directory *
|
||||
db_get_directory(const char *name)
|
||||
{
|
||||
if (music_root == NULL)
|
||||
if (db == NULL)
|
||||
return NULL;
|
||||
|
||||
struct directory *music_root = db_get_root();
|
||||
if (name == NULL)
|
||||
return music_root;
|
||||
|
||||
return directory_lookup_directory(music_root, name);
|
||||
struct directory *directory =
|
||||
directory_lookup_directory(music_root, name);
|
||||
return directory;
|
||||
}
|
||||
|
||||
struct song *
|
||||
@@ -123,281 +104,67 @@ db_get_song(const char *file)
|
||||
|
||||
g_debug("get song: %s", file);
|
||||
|
||||
if (music_root == NULL)
|
||||
if (db == NULL)
|
||||
return NULL;
|
||||
|
||||
return directory_lookup_song(music_root, file);
|
||||
}
|
||||
|
||||
int
|
||||
db_walk(const char *name,
|
||||
int (*forEachSong)(struct song *, void *),
|
||||
int (*forEachDir)(struct directory *, void *), void *data)
|
||||
{
|
||||
struct directory *directory;
|
||||
|
||||
if (music_root == NULL)
|
||||
return -1;
|
||||
|
||||
if ((directory = db_get_directory(name)) == NULL) {
|
||||
struct song *song;
|
||||
if ((song = db_get_song(name)) && forEachSong) {
|
||||
return forEachSong(song, data);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return directory_walk(directory, forEachSong, forEachDir, data);
|
||||
return db_plugin_get_song(db, file, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
db_check(void)
|
||||
db_visit(const struct db_selection *selection,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
assert(database_path != NULL);
|
||||
|
||||
/* Check if the file exists */
|
||||
if (access(database_path, F_OK)) {
|
||||
/* If the file doesn't exist, we can't check if we can write
|
||||
* it, so we are going to try to get the directory path, and
|
||||
* see if we can write a file in that */
|
||||
char *dirPath = g_path_get_dirname(database_path);
|
||||
|
||||
/* Check that the parent part of the path is a directory */
|
||||
if (stat(dirPath, &st) < 0) {
|
||||
g_free(dirPath);
|
||||
g_warning("Couldn't stat parent directory of db file "
|
||||
"\"%s\": %s", database_path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
g_free(dirPath);
|
||||
g_warning("Couldn't create db file \"%s\" because the "
|
||||
"parent path is not a directory", database_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if we can write to the directory */
|
||||
if (access(dirPath, R_OK | W_OK)) {
|
||||
g_warning("Can't create db file in \"%s\": %s",
|
||||
dirPath, strerror(errno));
|
||||
g_free(dirPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_free(dirPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Path exists, now check if it's a regular file */
|
||||
if (stat(database_path, &st) < 0) {
|
||||
g_warning("Couldn't stat db file \"%s\": %s",
|
||||
database_path, strerror(errno));
|
||||
if (db == NULL) {
|
||||
g_set_error_literal(error_r, db_quark(), DB_DISABLED,
|
||||
"No database");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
g_warning("db file \"%s\" is not a regular file", database_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* And check that we can write to it */
|
||||
if (access(database_path, R_OK | W_OK)) {
|
||||
g_warning("Can't open db file \"%s\" for reading/writing: %s",
|
||||
database_path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return db_plugin_visit(db, selection, visitor, ctx, error_r);
|
||||
}
|
||||
|
||||
bool
|
||||
db_save(void)
|
||||
db_walk(const char *uri,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r)
|
||||
{
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
struct db_selection selection;
|
||||
db_selection_init(&selection, uri, true);
|
||||
|
||||
assert(database_path != NULL);
|
||||
assert(music_root != NULL);
|
||||
return db_visit(&selection, visitor, ctx, error_r);
|
||||
}
|
||||
|
||||
g_debug("removing empty directories from DB");
|
||||
directory_prune_empty(music_root);
|
||||
bool
|
||||
db_save(GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db_is_open);
|
||||
|
||||
g_debug("sorting DB");
|
||||
|
||||
directory_sort(music_root);
|
||||
|
||||
g_debug("writing DB");
|
||||
|
||||
fp = fopen(database_path, "w");
|
||||
if (!fp) {
|
||||
g_warning("unable to write to db file \"%s\": %s",
|
||||
database_path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
|
||||
fprintf(fp, DB_FORMAT_PREFIX "%u\n", DB_FORMAT);
|
||||
fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
|
||||
fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, path_get_fs_charset());
|
||||
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||
if (!ignore_tag_items[i])
|
||||
fprintf(fp, DB_TAG_PREFIX "%s\n", tag_item_names[i]);
|
||||
|
||||
fprintf(fp, "%s\n", DIRECTORY_INFO_END);
|
||||
|
||||
directory_save(fp, music_root);
|
||||
|
||||
if (ferror(fp)) {
|
||||
g_warning("Failed to write to database file: %s",
|
||||
strerror(errno));
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (stat(database_path, &st) == 0)
|
||||
database_mtime = st.st_mtime;
|
||||
|
||||
return true;
|
||||
return simple_db_save(db, error_r);
|
||||
}
|
||||
|
||||
bool
|
||||
db_load(GError **error)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
struct stat st;
|
||||
GString *buffer = g_string_sized_new(1024);
|
||||
char *line;
|
||||
int format = 0;
|
||||
bool found_charset = false, found_version = false;
|
||||
bool success;
|
||||
bool tags[TAG_NUM_OF_ITEM_TYPES];
|
||||
assert(db != NULL);
|
||||
assert(!db_is_open);
|
||||
|
||||
assert(database_path != NULL);
|
||||
assert(music_root != NULL);
|
||||
|
||||
fp = fopen(database_path, "r");
|
||||
if (fp == NULL) {
|
||||
g_set_error(error, db_quark(), errno,
|
||||
"Failed to open database file \"%s\": %s",
|
||||
database_path, strerror(errno));
|
||||
g_string_free(buffer, true);
|
||||
if (!db_plugin_open(db, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get initial info */
|
||||
line = read_text_line(fp, buffer);
|
||||
if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
|
||||
fclose(fp);
|
||||
g_set_error(error, db_quark(), 0, "Database corrupted");
|
||||
g_string_free(buffer, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(tags, false, sizeof(tags));
|
||||
|
||||
while ((line = read_text_line(fp, buffer)) != NULL &&
|
||||
strcmp(line, DIRECTORY_INFO_END) != 0) {
|
||||
if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
|
||||
format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
|
||||
} else if (g_str_has_prefix(line, DIRECTORY_MPD_VERSION)) {
|
||||
if (found_version) {
|
||||
fclose(fp);
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Duplicate version line");
|
||||
g_string_free(buffer, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
found_version = true;
|
||||
} else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) {
|
||||
const char *new_charset, *old_charset;
|
||||
|
||||
if (found_charset) {
|
||||
fclose(fp);
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Duplicate charset line");
|
||||
g_string_free(buffer, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
found_charset = true;
|
||||
|
||||
new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1;
|
||||
old_charset = path_get_fs_charset();
|
||||
if (old_charset != NULL
|
||||
&& strcmp(new_charset, old_charset)) {
|
||||
fclose(fp);
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Existing database has charset "
|
||||
"\"%s\" instead of \"%s\"; "
|
||||
"discarding database file",
|
||||
new_charset, old_charset);
|
||||
g_string_free(buffer, true);
|
||||
return false;
|
||||
}
|
||||
} else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
|
||||
const char *name = line + sizeof(DB_TAG_PREFIX) - 1;
|
||||
enum tag_type tag = tag_name_parse(name);
|
||||
if (tag == TAG_NUM_OF_ITEM_TYPES) {
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Unrecognized tag '%s', "
|
||||
"discarding database file",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
|
||||
tags[tag] = true;
|
||||
} else {
|
||||
fclose(fp);
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Malformed line: %s", line);
|
||||
g_string_free(buffer, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (format != DB_FORMAT) {
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Database format mismatch, "
|
||||
"discarding database file");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
|
||||
if (!ignore_tag_items[i] && !tags[i]) {
|
||||
g_set_error(error, db_quark(), 0,
|
||||
"Tag list mismatch, "
|
||||
"discarding database file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
g_debug("reading DB");
|
||||
|
||||
success = directory_load(fp, music_root, buffer, error);
|
||||
g_string_free(buffer, true);
|
||||
fclose(fp);
|
||||
|
||||
if (!success)
|
||||
return false;
|
||||
db_is_open = true;
|
||||
|
||||
stats_update();
|
||||
|
||||
if (stat(database_path, &st) == 0)
|
||||
database_mtime = st.st_mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t
|
||||
db_get_mtime(void)
|
||||
{
|
||||
return database_mtime;
|
||||
assert(db != NULL);
|
||||
assert(db_is_open);
|
||||
|
||||
return simple_db_get_mtime(db);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,62 +20,76 @@
|
||||
#ifndef MPD_DATABASE_H
|
||||
#define MPD_DATABASE_H
|
||||
|
||||
#include "gcc.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct config_param;
|
||||
struct directory;
|
||||
struct db_selection;
|
||||
struct db_visitor;
|
||||
|
||||
/**
|
||||
* Initialize the database library.
|
||||
*
|
||||
* @param path the absolute path of the database file
|
||||
*/
|
||||
void
|
||||
db_init(const char *path);
|
||||
bool
|
||||
db_init(const struct config_param *path, GError **error_r);
|
||||
|
||||
void
|
||||
db_finish(void);
|
||||
|
||||
/**
|
||||
* Clear the database.
|
||||
*/
|
||||
void
|
||||
db_clear(void);
|
||||
|
||||
/**
|
||||
* Returns the root directory object. Returns NULL if there is no
|
||||
* configured music directory.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
struct directory *
|
||||
db_get_root(void);
|
||||
|
||||
/**
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
gcc_nonnull(1)
|
||||
G_GNUC_PURE
|
||||
struct directory *
|
||||
db_get_directory(const char *name);
|
||||
|
||||
gcc_nonnull(1)
|
||||
G_GNUC_PURE
|
||||
struct song *
|
||||
db_get_song(const char *file);
|
||||
|
||||
int db_walk(const char *name,
|
||||
int (*forEachSong)(struct song *, void *),
|
||||
int (*forEachDir)(struct directory *, void *), void *data);
|
||||
gcc_nonnull(1,2)
|
||||
bool
|
||||
db_visit(const struct db_selection *selection,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r);
|
||||
|
||||
gcc_nonnull(1,2)
|
||||
bool
|
||||
db_walk(const char *uri,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r);
|
||||
|
||||
bool
|
||||
db_check(void);
|
||||
|
||||
bool
|
||||
db_save(void);
|
||||
db_save(GError **error_r);
|
||||
|
||||
bool
|
||||
db_load(GError **error);
|
||||
|
||||
G_GNUC_PURE
|
||||
time_t
|
||||
db_get_mtime(void);
|
||||
|
||||
/**
|
||||
* Returns true if there is a valid database file on the disk.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline bool
|
||||
db_exists(void)
|
||||
{
|
||||
|
357
src/db/simple_db_plugin.c
Normal file
357
src/db/simple_db_plugin.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "simple_db_plugin.h"
|
||||
#include "db_internal.h"
|
||||
#include "db_error.h"
|
||||
#include "db_selection.h"
|
||||
#include "db_visitor.h"
|
||||
#include "db_save.h"
|
||||
#include "db_lock.h"
|
||||
#include "conf.h"
|
||||
#include "directory.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct simple_db {
|
||||
struct db base;
|
||||
|
||||
char *path;
|
||||
|
||||
struct directory *root;
|
||||
|
||||
time_t mtime;
|
||||
};
|
||||
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
simple_db_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("simple_db");
|
||||
}
|
||||
|
||||
G_GNUC_PURE
|
||||
static const struct directory *
|
||||
simple_db_lookup_directory(const struct simple_db *db, const char *uri)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->root != NULL);
|
||||
assert(uri != NULL);
|
||||
|
||||
db_lock();
|
||||
struct directory *directory =
|
||||
directory_lookup_directory(db->root, uri);
|
||||
db_unlock();
|
||||
return directory;
|
||||
}
|
||||
|
||||
static struct db *
|
||||
simple_db_init(const struct config_param *param, GError **error_r)
|
||||
{
|
||||
struct simple_db *db = g_malloc(sizeof(*db));
|
||||
db_base_init(&db->base, &simple_db_plugin);
|
||||
|
||||
GError *error = NULL;
|
||||
db->path = config_dup_block_path(param, "path", &error);
|
||||
if (db->path == NULL) {
|
||||
g_free(db);
|
||||
if (error != NULL)
|
||||
g_propagate_error(error_r, error);
|
||||
else
|
||||
g_set_error(error_r, simple_db_quark(), 0,
|
||||
"No \"path\" parameter specified");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &db->base;
|
||||
}
|
||||
|
||||
static void
|
||||
simple_db_finish(struct db *_db)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
|
||||
g_free(db->path);
|
||||
g_free(db);
|
||||
}
|
||||
|
||||
static bool
|
||||
simple_db_check(struct simple_db *db, GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->path != NULL);
|
||||
|
||||
/* Check if the file exists */
|
||||
if (access(db->path, F_OK)) {
|
||||
/* If the file doesn't exist, we can't check if we can write
|
||||
* it, so we are going to try to get the directory path, and
|
||||
* see if we can write a file in that */
|
||||
char *dirPath = g_path_get_dirname(db->path);
|
||||
|
||||
/* Check that the parent part of the path is a directory */
|
||||
struct stat st;
|
||||
if (stat(dirPath, &st) < 0) {
|
||||
g_free(dirPath);
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Couldn't stat parent directory of db file "
|
||||
"\"%s\": %s",
|
||||
db->path, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
g_free(dirPath);
|
||||
g_set_error(error_r, simple_db_quark(), 0,
|
||||
"Couldn't create db file \"%s\" because the "
|
||||
"parent path is not a directory",
|
||||
db->path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if we can write to the directory */
|
||||
if (access(dirPath, X_OK | W_OK)) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Can't create db file in \"%s\": %s",
|
||||
dirPath, g_strerror(errno));
|
||||
g_free(dirPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_free(dirPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Path exists, now check if it's a regular file */
|
||||
struct stat st;
|
||||
if (stat(db->path, &st) < 0) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Couldn't stat db file \"%s\": %s",
|
||||
db->path, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
g_set_error(error_r, simple_db_quark(), 0,
|
||||
"db file \"%s\" is not a regular file",
|
||||
db->path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* And check that we can write to it */
|
||||
if (access(db->path, R_OK | W_OK)) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Can't open db file \"%s\" for reading/writing: %s",
|
||||
db->path, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
simple_db_load(struct simple_db *db, GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->path != NULL);
|
||||
assert(db->root != NULL);
|
||||
|
||||
FILE *fp = fopen(db->path, "r");
|
||||
if (fp == NULL) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Failed to open database file \"%s\": %s",
|
||||
db->path, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!db_load_internal(fp, db->root, error_r)) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
struct stat st;
|
||||
if (stat(db->path, &st) == 0)
|
||||
db->mtime = st.st_mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
|
||||
db->root = directory_new_root();
|
||||
db->mtime = 0;
|
||||
|
||||
GError *error = NULL;
|
||||
if (!simple_db_load(db, &error)) {
|
||||
directory_free(db->root);
|
||||
|
||||
g_warning("Failed to load database: %s", error->message);
|
||||
g_error_free(error);
|
||||
|
||||
if (!simple_db_check(db, error_r))
|
||||
return false;
|
||||
|
||||
db->root = directory_new_root();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
simple_db_close(struct db *_db)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
|
||||
assert(db->root != NULL);
|
||||
|
||||
directory_free(db->root);
|
||||
}
|
||||
|
||||
static struct song *
|
||||
simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
|
||||
assert(db->root != NULL);
|
||||
|
||||
db_lock();
|
||||
struct song *song = directory_lookup_song(db->root, uri);
|
||||
db_unlock();
|
||||
if (song == NULL)
|
||||
g_set_error(error_r, db_quark(), DB_NOT_FOUND,
|
||||
"No such song: %s", uri);
|
||||
|
||||
return song;
|
||||
}
|
||||
|
||||
static bool
|
||||
simple_db_visit(struct db *_db, const struct db_selection *selection,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r)
|
||||
{
|
||||
const struct simple_db *db = (const struct simple_db *)_db;
|
||||
const struct directory *directory =
|
||||
simple_db_lookup_directory(db, selection->uri);
|
||||
if (directory == NULL) {
|
||||
struct song *song;
|
||||
if (visitor->song != NULL &&
|
||||
(song = simple_db_get_song(_db, selection->uri, NULL)) != NULL)
|
||||
return visitor->song(song, ctx, error_r);
|
||||
|
||||
g_set_error(error_r, db_quark(), DB_NOT_FOUND,
|
||||
"No such directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selection->recursive && visitor->directory != NULL &&
|
||||
!visitor->directory(directory, ctx, error_r))
|
||||
return false;
|
||||
|
||||
db_lock();
|
||||
bool ret = directory_walk(directory, selection->recursive,
|
||||
visitor, ctx, error_r);
|
||||
db_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct db_plugin simple_db_plugin = {
|
||||
.name = "simple",
|
||||
.init = simple_db_init,
|
||||
.finish = simple_db_finish,
|
||||
.open = simple_db_open,
|
||||
.close = simple_db_close,
|
||||
.get_song = simple_db_get_song,
|
||||
.visit = simple_db_visit,
|
||||
};
|
||||
|
||||
struct directory *
|
||||
simple_db_get_root(struct db *_db)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
|
||||
assert(db != NULL);
|
||||
assert(db->root != NULL);
|
||||
|
||||
return db->root;
|
||||
}
|
||||
|
||||
bool
|
||||
simple_db_save(struct db *_db, GError **error_r)
|
||||
{
|
||||
struct simple_db *db = (struct simple_db *)_db;
|
||||
struct directory *music_root = db->root;
|
||||
|
||||
db_lock();
|
||||
|
||||
g_debug("removing empty directories from DB");
|
||||
directory_prune_empty(music_root);
|
||||
|
||||
g_debug("sorting DB");
|
||||
directory_sort(music_root);
|
||||
|
||||
db_unlock();
|
||||
|
||||
g_debug("writing DB");
|
||||
|
||||
FILE *fp = fopen(db->path, "w");
|
||||
if (!fp) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"unable to write to db file \"%s\": %s",
|
||||
db->path, g_strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
db_save_internal(fp, music_root);
|
||||
|
||||
if (ferror(fp)) {
|
||||
g_set_error(error_r, simple_db_quark(), errno,
|
||||
"Failed to write to database file: %s",
|
||||
g_strerror(errno));
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
struct stat st;
|
||||
if (stat(db->path, &st) == 0)
|
||||
db->mtime = st.st_mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t
|
||||
simple_db_get_mtime(const struct db *_db)
|
||||
{
|
||||
const struct simple_db *db = (const struct simple_db *)_db;
|
||||
|
||||
assert(db != NULL);
|
||||
assert(db->root != NULL);
|
||||
|
||||
return db->mtime;
|
||||
}
|
42
src/db/simple_db_plugin.h
Normal file
42
src/db/simple_db_plugin.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_SIMPLE_DB_PLUGIN_H
|
||||
#define MPD_SIMPLE_DB_PLUGIN_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
extern const struct db_plugin simple_db_plugin;
|
||||
|
||||
struct db;
|
||||
|
||||
G_GNUC_PURE
|
||||
struct directory *
|
||||
simple_db_get_root(struct db *db);
|
||||
|
||||
bool
|
||||
simple_db_save(struct db *db, GError **error_r);
|
||||
|
||||
G_GNUC_PURE
|
||||
time_t
|
||||
simple_db_get_mtime(const struct db *db);
|
||||
|
||||
#endif
|
439
src/dbUtils.c
439
src/dbUtils.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,311 +20,190 @@
|
||||
#include "config.h"
|
||||
#include "dbUtils.h"
|
||||
#include "locate.h"
|
||||
#include "directory.h"
|
||||
#include "database.h"
|
||||
#include "client.h"
|
||||
#include "db_visitor.h"
|
||||
#include "playlist.h"
|
||||
#include "song.h"
|
||||
#include "song_print.h"
|
||||
#include "tag.h"
|
||||
#include "strset.h"
|
||||
#include "stored_playlist.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct _ListCommandItem {
|
||||
int8_t tagType;
|
||||
const struct locate_item_list *criteria;
|
||||
} ListCommandItem;
|
||||
|
||||
typedef struct _SearchStats {
|
||||
const struct locate_item_list *criteria;
|
||||
int numberOfSongs;
|
||||
unsigned long playTime;
|
||||
} SearchStats;
|
||||
|
||||
static int
|
||||
printDirectoryInDirectory(struct directory *directory, void *data)
|
||||
static bool
|
||||
add_to_queue_song(struct song *song, void *ctx, GError **error_r)
|
||||
{
|
||||
struct client *client = data;
|
||||
struct player_control *pc = ctx;
|
||||
|
||||
if (!directory_is_root(directory))
|
||||
client_printf(client, "directory: %s\n", directory_get_path(directory));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
printSongInDirectory(struct song *song, G_GNUC_UNUSED void *data)
|
||||
{
|
||||
struct client *client = data;
|
||||
song_print_uri(client, song);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct search_data {
|
||||
struct client *client;
|
||||
const struct locate_item_list *criteria;
|
||||
};
|
||||
|
||||
static int
|
||||
searchInDirectory(struct song *song, void *_data)
|
||||
{
|
||||
struct search_data *data = _data;
|
||||
|
||||
if (locate_song_search(song, data->criteria))
|
||||
song_print_info(data->client, song);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
searchForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria)
|
||||
{
|
||||
int ret;
|
||||
struct locate_item_list *new_list
|
||||
= locate_item_list_casefold(criteria);
|
||||
struct search_data data;
|
||||
|
||||
data.client = client;
|
||||
data.criteria = new_list;
|
||||
|
||||
ret = db_walk(name, searchInDirectory, NULL, &data);
|
||||
|
||||
locate_item_list_free(new_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
findInDirectory(struct song *song, void *_data)
|
||||
{
|
||||
struct search_data *data = _data;
|
||||
|
||||
if (locate_song_match(song, data->criteria))
|
||||
song_print_info(data->client, song);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
findSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria)
|
||||
{
|
||||
struct search_data data;
|
||||
|
||||
data.client = client;
|
||||
data.criteria = criteria;
|
||||
|
||||
return db_walk(name, findInDirectory, NULL, &data);
|
||||
}
|
||||
|
||||
static void printSearchStats(struct client *client, SearchStats *stats)
|
||||
{
|
||||
client_printf(client, "songs: %i\n", stats->numberOfSongs);
|
||||
client_printf(client, "playtime: %li\n", stats->playTime);
|
||||
}
|
||||
|
||||
static int
|
||||
searchStatsInDirectory(struct song *song, void *data)
|
||||
{
|
||||
SearchStats *stats = data;
|
||||
|
||||
if (locate_song_match(song, stats->criteria)) {
|
||||
stats->numberOfSongs++;
|
||||
stats->playTime += song_get_duration(song);
|
||||
enum playlist_result result =
|
||||
playlist_append_song(&g_playlist, pc, song, NULL);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_set_error(error_r, playlist_quark(), result,
|
||||
"Playlist error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
searchStatsForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria)
|
||||
static const struct db_visitor add_to_queue_visitor = {
|
||||
.song = add_to_queue_song,
|
||||
};
|
||||
|
||||
bool
|
||||
addAllIn(struct player_control *pc, const char *uri, GError **error_r)
|
||||
{
|
||||
SearchStats stats;
|
||||
int ret;
|
||||
|
||||
stats.criteria = criteria;
|
||||
stats.numberOfSongs = 0;
|
||||
stats.playTime = 0;
|
||||
|
||||
ret = db_walk(name, searchStatsInDirectory, NULL, &stats);
|
||||
if (ret == 0)
|
||||
printSearchStats(client, &stats);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int printAllIn(struct client *client, const char *name)
|
||||
{
|
||||
return db_walk(name, printSongInDirectory,
|
||||
printDirectoryInDirectory, client);
|
||||
}
|
||||
|
||||
static int
|
||||
directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data)
|
||||
{
|
||||
return playlist_append_song(&g_playlist, song, NULL);
|
||||
return db_walk(uri, &add_to_queue_visitor, pc, error_r);
|
||||
}
|
||||
|
||||
struct add_data {
|
||||
const char *path;
|
||||
};
|
||||
|
||||
static int
|
||||
directoryAddSongToStoredPlaylist(struct song *song, void *_data)
|
||||
static bool
|
||||
add_to_spl_song(struct song *song, void *ctx, GError **error_r)
|
||||
{
|
||||
struct add_data *data = _data;
|
||||
struct add_data *data = ctx;
|
||||
|
||||
if (spl_append_song(data->path, song) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
if (!spl_append_song(data->path, song, error_r))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int addAllIn(const char *name)
|
||||
{
|
||||
return db_walk(name, directoryAddSongToPlaylist, NULL, NULL);
|
||||
}
|
||||
|
||||
int addAllInToStoredPlaylist(const char *name, const char *utf8file)
|
||||
{
|
||||
struct add_data data = {
|
||||
.path = utf8file,
|
||||
};
|
||||
|
||||
return db_walk(name, directoryAddSongToStoredPlaylist, NULL, &data);
|
||||
}
|
||||
|
||||
static int
|
||||
findAddInDirectory(struct song *song, void *_data)
|
||||
{
|
||||
struct search_data *data = _data;
|
||||
|
||||
if (locate_song_match(song, data->criteria))
|
||||
return directoryAddSongToPlaylist(song, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int findAddIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria)
|
||||
{
|
||||
struct search_data data;
|
||||
|
||||
data.client = client;
|
||||
data.criteria = criteria;
|
||||
|
||||
return db_walk(name, findAddInDirectory, NULL, &data);
|
||||
}
|
||||
|
||||
static int
|
||||
directoryPrintSongInfo(struct song *song, void *data)
|
||||
{
|
||||
struct client *client = data;
|
||||
song_print_info(client, song);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printInfoForAllIn(struct client *client, const char *name)
|
||||
{
|
||||
return db_walk(name, directoryPrintSongInfo,
|
||||
printDirectoryInDirectory, client);
|
||||
}
|
||||
|
||||
static ListCommandItem *
|
||||
newListCommandItem(int tagType, const struct locate_item_list *criteria)
|
||||
{
|
||||
ListCommandItem *item = g_new(ListCommandItem, 1);
|
||||
|
||||
item->tagType = tagType;
|
||||
item->criteria = criteria;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void freeListCommandItem(ListCommandItem * item)
|
||||
{
|
||||
g_free(item);
|
||||
}
|
||||
|
||||
static void
|
||||
visitTag(struct client *client, struct strset *set,
|
||||
struct song *song, enum tag_type tagType)
|
||||
{
|
||||
struct tag *tag = song->tag;
|
||||
bool found = false;
|
||||
|
||||
if (tagType == LOCATE_TAG_FILE_TYPE) {
|
||||
song_print_uri(client, song);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag)
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < tag->num_items; i++) {
|
||||
if (tag->items[i]->type == tagType) {
|
||||
strset_add(set, tag->items[i]->value);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
strset_add(set, "");
|
||||
}
|
||||
|
||||
struct list_tags_data {
|
||||
struct client *client;
|
||||
ListCommandItem *item;
|
||||
struct strset *set;
|
||||
static const struct db_visitor add_to_spl_visitor = {
|
||||
.song = add_to_spl_song,
|
||||
};
|
||||
|
||||
static int
|
||||
listUniqueTagsInDirectory(struct song *song, void *_data)
|
||||
bool
|
||||
addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
|
||||
GError **error_r)
|
||||
{
|
||||
struct list_tags_data *data = _data;
|
||||
ListCommandItem *item = data->item;
|
||||
|
||||
if (locate_song_match(song, item->criteria))
|
||||
visitTag(data->client, data->set, song, item->tagType);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int listAllUniqueTags(struct client *client, int type,
|
||||
const struct locate_item_list *criteria)
|
||||
{
|
||||
int ret;
|
||||
ListCommandItem *item = newListCommandItem(type, criteria);
|
||||
struct list_tags_data data = {
|
||||
.client = client,
|
||||
.item = item,
|
||||
struct add_data data = {
|
||||
.path = path_utf8,
|
||||
};
|
||||
|
||||
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
|
||||
data.set = strset_new();
|
||||
}
|
||||
|
||||
ret = db_walk(NULL, listUniqueTagsInDirectory, NULL, &data);
|
||||
|
||||
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
|
||||
const char *value;
|
||||
|
||||
strset_rewind(data.set);
|
||||
|
||||
while ((value = strset_next(data.set)) != NULL)
|
||||
client_printf(client, "%s: %s\n",
|
||||
tag_item_names[type],
|
||||
value);
|
||||
|
||||
strset_free(data.set);
|
||||
}
|
||||
|
||||
freeListCommandItem(item);
|
||||
|
||||
return ret;
|
||||
return db_walk(uri_utf8, &add_to_spl_visitor, &data, error_r);
|
||||
}
|
||||
|
||||
struct find_add_data {
|
||||
struct player_control *pc;
|
||||
const struct locate_item_list *criteria;
|
||||
};
|
||||
|
||||
static bool
|
||||
find_add_song(struct song *song, void *ctx, GError **error_r)
|
||||
{
|
||||
struct find_add_data *data = ctx;
|
||||
|
||||
if (!locate_song_match(song, data->criteria))
|
||||
return true;
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_append_song(&g_playlist, data->pc,
|
||||
song, NULL);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_set_error(error_r, playlist_quark(), result,
|
||||
"Playlist error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor find_add_visitor = {
|
||||
.song = find_add_song,
|
||||
};
|
||||
|
||||
bool
|
||||
findAddIn(struct player_control *pc, const char *name,
|
||||
const struct locate_item_list *criteria, GError **error_r)
|
||||
{
|
||||
struct find_add_data data;
|
||||
data.pc = pc;
|
||||
data.criteria = criteria;
|
||||
|
||||
return db_walk(name, &find_add_visitor, &data, error_r);
|
||||
}
|
||||
|
||||
static bool
|
||||
searchadd_visitor_song(struct song *song, void *_data, GError **error_r)
|
||||
{
|
||||
struct find_add_data *data = _data;
|
||||
|
||||
if (!locate_song_search(song, data->criteria))
|
||||
return true;
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_append_song(&g_playlist, data->pc, song, NULL);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_set_error(error_r, playlist_quark(), result,
|
||||
"Playlist error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor searchadd_visitor = {
|
||||
.song = searchadd_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
search_add_songs(struct player_control *pc, const char *uri,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
struct locate_item_list *new_list =
|
||||
locate_item_list_casefold(criteria);
|
||||
struct find_add_data data = {
|
||||
.pc = pc,
|
||||
.criteria = new_list,
|
||||
};
|
||||
|
||||
bool success = db_walk(uri, &searchadd_visitor, &data, error_r);
|
||||
|
||||
locate_item_list_free(new_list);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
struct search_add_playlist_data {
|
||||
const char *playlist;
|
||||
const struct locate_item_list *criteria;
|
||||
};
|
||||
|
||||
static bool
|
||||
searchaddpl_visitor_song(struct song *song, void *_data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct search_add_playlist_data *data = _data;
|
||||
|
||||
if (!locate_song_search(song, data->criteria))
|
||||
return true;
|
||||
|
||||
if (!spl_append_song(data->playlist, song, error_r))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor searchaddpl_visitor = {
|
||||
.song = searchaddpl_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
search_add_to_playlist(const char *uri, const char *path_utf8,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
struct locate_item_list *new_list
|
||||
= locate_item_list_casefold(criteria);
|
||||
struct search_add_playlist_data data = {
|
||||
.playlist = path_utf8,
|
||||
.criteria = new_list,
|
||||
};
|
||||
|
||||
bool success = db_walk(uri, &searchaddpl_visitor, &data, error_r);
|
||||
|
||||
locate_item_list_free(new_list);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,37 +20,37 @@
|
||||
#ifndef MPD_DB_UTILS_H
|
||||
#define MPD_DB_UTILS_H
|
||||
|
||||
struct client;
|
||||
#include "gcc.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct locate_item_list;
|
||||
struct player_control;
|
||||
|
||||
int printAllIn(struct client *client, const char *name);
|
||||
gcc_nonnull(1,2)
|
||||
bool
|
||||
addAllIn(struct player_control *pc, const char *uri, GError **error_r);
|
||||
|
||||
int addAllIn(const char *name);
|
||||
gcc_nonnull(1,2)
|
||||
bool
|
||||
addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
|
||||
GError **error_r);
|
||||
|
||||
int addAllInToStoredPlaylist(const char *name, const char *utf8file);
|
||||
gcc_nonnull(1,2,3)
|
||||
bool
|
||||
findAddIn(struct player_control *pc, const char *name,
|
||||
const struct locate_item_list *criteria, GError **error_r);
|
||||
|
||||
int printInfoForAllIn(struct client *client, const char *name);
|
||||
gcc_nonnull(1,2,3)
|
||||
bool
|
||||
search_add_songs(struct player_control *pc, const char *uri,
|
||||
const struct locate_item_list *criteria, GError **error_r);
|
||||
|
||||
int
|
||||
searchForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria);
|
||||
|
||||
int
|
||||
findSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria);
|
||||
|
||||
int
|
||||
findAddIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria);
|
||||
|
||||
int
|
||||
searchStatsForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria);
|
||||
|
||||
unsigned long sumSongTimesIn(const char *name);
|
||||
|
||||
int
|
||||
listAllUniqueTags(struct client *client, int type,
|
||||
const struct locate_item_list *criteria);
|
||||
gcc_nonnull(1,2,3)
|
||||
bool
|
||||
search_add_to_playlist(const char *uri, const char *path_utf8,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r);
|
||||
|
||||
#endif
|
||||
|
45
src/db_error.h
Normal file
45
src/db_error.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_ERROR_H
|
||||
#define MPD_DB_ERROR_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
enum db_error {
|
||||
/**
|
||||
* The database is disabled, i.e. none is configured in this
|
||||
* MPD instance.
|
||||
*/
|
||||
DB_DISABLED,
|
||||
|
||||
DB_NOT_FOUND,
|
||||
};
|
||||
|
||||
/**
|
||||
* Quark for GError.domain; the code is an enum #db_error.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
db_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("db");
|
||||
}
|
||||
|
||||
#endif
|
35
src/db_internal.h
Normal file
35
src/db_internal.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_INTERNAL_H
|
||||
#define MPD_DB_INTERNAL_H
|
||||
|
||||
#include "db_plugin.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static inline void
|
||||
db_base_init(struct db *db, const struct db_plugin *plugin)
|
||||
{
|
||||
assert(plugin != NULL);
|
||||
|
||||
db->plugin = plugin;
|
||||
}
|
||||
|
||||
#endif
|
33
src/db_lock.c
Normal file
33
src/db_lock.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "db_lock.h"
|
||||
#include "gcc.h"
|
||||
|
||||
#if GCC_CHECK_VERSION(4, 2)
|
||||
/* workaround for a warning caused by G_STATIC_MUTEX_INIT */
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
GStaticMutex db_mutex = G_STATIC_MUTEX_INIT;
|
||||
|
||||
#ifndef NDEBUG
|
||||
GThread *db_mutex_holder;
|
||||
#endif
|
84
src/db_lock.h
Normal file
84
src/db_lock.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* Support for locking data structures from the database, for safe
|
||||
* multi-threading.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_LOCK_H
|
||||
#define MPD_DB_LOCK_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern GStaticMutex db_mutex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
extern GThread *db_mutex_holder;
|
||||
|
||||
/**
|
||||
* Does the current thread hold the database lock?
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static inline bool
|
||||
holding_db_lock(void)
|
||||
{
|
||||
return db_mutex_holder == g_thread_self();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Obtain the global database lock. This is needed before
|
||||
* dereferencing a #song or #directory. It is not recursive.
|
||||
*/
|
||||
static inline void
|
||||
db_lock(void)
|
||||
{
|
||||
assert(!holding_db_lock());
|
||||
|
||||
g_static_mutex_lock(&db_mutex);
|
||||
|
||||
assert(db_mutex_holder == NULL);
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = g_thread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the global database lock.
|
||||
*/
|
||||
static inline void
|
||||
db_unlock(void)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = NULL;
|
||||
#endif
|
||||
|
||||
g_static_mutex_unlock(&db_mutex);
|
||||
}
|
||||
|
||||
#endif
|
156
src/db_plugin.h
Normal file
156
src/db_plugin.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* This header declares the db_plugin class. It describes a
|
||||
* plugin API for databases of song metadata.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_PLUGIN_H
|
||||
#define MPD_DB_PLUGIN_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct config_param;
|
||||
struct db_selection;
|
||||
struct db_visitor;
|
||||
|
||||
struct db {
|
||||
const struct db_plugin *plugin;
|
||||
};
|
||||
|
||||
struct db_plugin {
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* Allocates and configures a database.
|
||||
*/
|
||||
struct db *(*init)(const struct config_param *param, GError **error_r);
|
||||
|
||||
/**
|
||||
* Free instance data.
|
||||
*/
|
||||
void (*finish)(struct db *db);
|
||||
|
||||
/**
|
||||
* Open the database. Read it into memory if applicable.
|
||||
*/
|
||||
bool (*open)(struct db *db, GError **error_r);
|
||||
|
||||
/**
|
||||
* Close the database, free allocated memory.
|
||||
*/
|
||||
void (*close)(struct db *db);
|
||||
|
||||
/**
|
||||
* Look up a song (including tag data) in the database.
|
||||
*
|
||||
* @param the URI of the song within the music directory
|
||||
* (UTF-8)
|
||||
*/
|
||||
struct song *(*get_song)(struct db *db, const char *uri,
|
||||
GError **error_r);
|
||||
|
||||
/**
|
||||
* Visit the selected entities.
|
||||
*/
|
||||
bool (*visit)(struct db *db, const struct db_selection *selection,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r);
|
||||
};
|
||||
|
||||
G_GNUC_MALLOC
|
||||
static inline struct db *
|
||||
db_plugin_new(const struct db_plugin *plugin, const struct config_param *param,
|
||||
GError **error_r)
|
||||
{
|
||||
assert(plugin != NULL);
|
||||
assert(plugin->init != NULL);
|
||||
assert(plugin->finish != NULL);
|
||||
assert(plugin->get_song != NULL);
|
||||
assert(plugin->visit != NULL);
|
||||
assert(error_r == NULL || *error_r == NULL);
|
||||
|
||||
struct db *db = plugin->init(param, error_r);
|
||||
assert(db == NULL || db->plugin == plugin);
|
||||
assert(db != NULL || error_r == NULL || *error_r != NULL);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
static inline void
|
||||
db_plugin_free(struct db *db)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->plugin != NULL);
|
||||
assert(db->plugin->finish != NULL);
|
||||
|
||||
db->plugin->finish(db);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
db_plugin_open(struct db *db, GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->plugin != NULL);
|
||||
|
||||
return db->plugin->open != NULL
|
||||
? db->plugin->open(db, error_r)
|
||||
: true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
db_plugin_close(struct db *db)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->plugin != NULL);
|
||||
|
||||
if (db->plugin->close != NULL)
|
||||
db->plugin->close(db);
|
||||
}
|
||||
|
||||
static inline struct song *
|
||||
db_plugin_get_song(struct db *db, const char *uri, GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->plugin != NULL);
|
||||
assert(db->plugin->get_song != NULL);
|
||||
assert(uri != NULL);
|
||||
|
||||
return db->plugin->get_song(db, uri, error_r);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
db_plugin_visit(struct db *db, const struct db_selection *selection,
|
||||
const struct db_visitor *visitor, void *ctx,
|
||||
GError **error_r)
|
||||
{
|
||||
assert(db != NULL);
|
||||
assert(db->plugin != NULL);
|
||||
assert(selection != NULL);
|
||||
assert(visitor != NULL);
|
||||
assert(error_r == NULL || *error_r == NULL);
|
||||
|
||||
return db->plugin->visit(db, selection, visitor, ctx, error_r);
|
||||
}
|
||||
|
||||
#endif
|
393
src/db_print.c
Normal file
393
src/db_print.c
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "db_print.h"
|
||||
#include "db_selection.h"
|
||||
#include "db_visitor.h"
|
||||
#include "locate.h"
|
||||
#include "directory.h"
|
||||
#include "database.h"
|
||||
#include "client.h"
|
||||
#include "song.h"
|
||||
#include "song_print.h"
|
||||
#include "playlist_vector.h"
|
||||
#include "tag.h"
|
||||
#include "strset.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct _ListCommandItem {
|
||||
int8_t tagType;
|
||||
const struct locate_item_list *criteria;
|
||||
} ListCommandItem;
|
||||
|
||||
typedef struct _SearchStats {
|
||||
const struct locate_item_list *criteria;
|
||||
int numberOfSongs;
|
||||
unsigned long playTime;
|
||||
} SearchStats;
|
||||
|
||||
static bool
|
||||
print_visitor_directory(const struct directory *directory, void *data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct client *client = data;
|
||||
|
||||
if (!directory_is_root(directory))
|
||||
client_printf(client, "directory: %s\n", directory_get_path(directory));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
print_playlist_in_directory(struct client *client,
|
||||
const struct directory *directory,
|
||||
const char *name_utf8)
|
||||
{
|
||||
if (directory_is_root(directory))
|
||||
client_printf(client, "playlist: %s\n", name_utf8);
|
||||
else
|
||||
client_printf(client, "playlist: %s/%s\n",
|
||||
directory_get_path(directory), name_utf8);
|
||||
}
|
||||
|
||||
static bool
|
||||
print_visitor_song(struct song *song, void *data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
assert(song != NULL);
|
||||
assert(song->parent != NULL);
|
||||
|
||||
struct client *client = data;
|
||||
song_print_uri(client, song);
|
||||
|
||||
if (song->tag != NULL && song->tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, song->parent,
|
||||
song->uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
print_visitor_song_info(struct song *song, void *data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
assert(song != NULL);
|
||||
assert(song->parent != NULL);
|
||||
|
||||
struct client *client = data;
|
||||
song_print_info(client, song);
|
||||
|
||||
if (song->tag != NULL && song->tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, song->parent,
|
||||
song->uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
print_visitor_playlist(const struct playlist_metadata *playlist,
|
||||
const struct directory *directory, void *ctx,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct client *client = ctx;
|
||||
print_playlist_in_directory(client, directory, playlist->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
print_visitor_playlist_info(const struct playlist_metadata *playlist,
|
||||
const struct directory *directory,
|
||||
void *ctx, G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct client *client = ctx;
|
||||
print_playlist_in_directory(client, directory, playlist->name);
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
struct tm tm;
|
||||
#endif
|
||||
char timestamp[32];
|
||||
time_t t = playlist->mtime;
|
||||
strftime(timestamp, sizeof(timestamp),
|
||||
#ifdef G_OS_WIN32
|
||||
"%Y-%m-%dT%H:%M:%SZ",
|
||||
gmtime(&t)
|
||||
#else
|
||||
"%FT%TZ",
|
||||
gmtime_r(&t, &tm)
|
||||
#endif
|
||||
);
|
||||
client_printf(client, "Last-Modified: %s\n", timestamp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor print_visitor = {
|
||||
.directory = print_visitor_directory,
|
||||
.song = print_visitor_song,
|
||||
.playlist = print_visitor_playlist,
|
||||
};
|
||||
|
||||
static const struct db_visitor print_info_visitor = {
|
||||
.directory = print_visitor_directory,
|
||||
.song = print_visitor_song_info,
|
||||
.playlist = print_visitor_playlist_info,
|
||||
};
|
||||
|
||||
bool
|
||||
db_selection_print(struct client *client, const struct db_selection *selection,
|
||||
bool full, GError **error_r)
|
||||
{
|
||||
return db_visit(selection, full ? &print_info_visitor : &print_visitor,
|
||||
client, error_r);
|
||||
}
|
||||
|
||||
struct search_data {
|
||||
struct client *client;
|
||||
const struct locate_item_list *criteria;
|
||||
};
|
||||
|
||||
static bool
|
||||
search_visitor_song(struct song *song, void *_data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct search_data *data = _data;
|
||||
|
||||
if (locate_song_search(song, data->criteria))
|
||||
song_print_info(data->client, song);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor search_visitor = {
|
||||
.song = search_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
searchForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
struct locate_item_list *new_list
|
||||
= locate_item_list_casefold(criteria);
|
||||
struct search_data data;
|
||||
|
||||
data.client = client;
|
||||
data.criteria = new_list;
|
||||
|
||||
bool success = db_walk(name, &search_visitor, &data, error_r);
|
||||
|
||||
locate_item_list_free(new_list);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_visitor_song(struct song *song, void *_data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct search_data *data = _data;
|
||||
|
||||
if (locate_song_match(song, data->criteria))
|
||||
song_print_info(data->client, song);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor find_visitor = {
|
||||
.song = find_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
findSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
struct search_data data;
|
||||
|
||||
data.client = client;
|
||||
data.criteria = criteria;
|
||||
|
||||
return db_walk(name, &find_visitor, &data, error_r);
|
||||
}
|
||||
|
||||
static void printSearchStats(struct client *client, SearchStats *stats)
|
||||
{
|
||||
client_printf(client, "songs: %i\n", stats->numberOfSongs);
|
||||
client_printf(client, "playtime: %li\n", stats->playTime);
|
||||
}
|
||||
|
||||
static bool
|
||||
stats_visitor_song(struct song *song, void *data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
SearchStats *stats = data;
|
||||
|
||||
if (locate_song_match(song, stats->criteria)) {
|
||||
stats->numberOfSongs++;
|
||||
stats->playTime += song_get_duration(song);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor stats_visitor = {
|
||||
.song = stats_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
searchStatsForSongsIn(struct client *client, const char *name,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
SearchStats stats;
|
||||
|
||||
stats.criteria = criteria;
|
||||
stats.numberOfSongs = 0;
|
||||
stats.playTime = 0;
|
||||
|
||||
if (!db_walk(name, &stats_visitor, &stats, error_r))
|
||||
return false;
|
||||
|
||||
printSearchStats(client, &stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
printAllIn(struct client *client, const char *uri_utf8, GError **error_r)
|
||||
{
|
||||
struct db_selection selection;
|
||||
db_selection_init(&selection, uri_utf8, true);
|
||||
return db_selection_print(client, &selection, false, error_r);
|
||||
}
|
||||
|
||||
bool
|
||||
printInfoForAllIn(struct client *client, const char *uri_utf8,
|
||||
GError **error_r)
|
||||
{
|
||||
struct db_selection selection;
|
||||
db_selection_init(&selection, uri_utf8, true);
|
||||
return db_selection_print(client, &selection, true, error_r);
|
||||
}
|
||||
|
||||
static ListCommandItem *
|
||||
newListCommandItem(int tagType, const struct locate_item_list *criteria)
|
||||
{
|
||||
ListCommandItem *item = g_new(ListCommandItem, 1);
|
||||
|
||||
item->tagType = tagType;
|
||||
item->criteria = criteria;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void freeListCommandItem(ListCommandItem * item)
|
||||
{
|
||||
g_free(item);
|
||||
}
|
||||
|
||||
static void
|
||||
visitTag(struct client *client, struct strset *set,
|
||||
struct song *song, enum tag_type tagType)
|
||||
{
|
||||
struct tag *tag = song->tag;
|
||||
bool found = false;
|
||||
|
||||
if (tagType == LOCATE_TAG_FILE_TYPE) {
|
||||
song_print_uri(client, song);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag)
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < tag->num_items; i++) {
|
||||
if (tag->items[i]->type == tagType) {
|
||||
strset_add(set, tag->items[i]->value);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
strset_add(set, "");
|
||||
}
|
||||
|
||||
struct list_tags_data {
|
||||
struct client *client;
|
||||
ListCommandItem *item;
|
||||
struct strset *set;
|
||||
};
|
||||
|
||||
static bool
|
||||
unique_tags_visitor_song(struct song *song, void *_data,
|
||||
G_GNUC_UNUSED GError **error_r)
|
||||
{
|
||||
struct list_tags_data *data = _data;
|
||||
ListCommandItem *item = data->item;
|
||||
|
||||
if (locate_song_match(song, item->criteria))
|
||||
visitTag(data->client, data->set, song, item->tagType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct db_visitor unique_tags_visitor = {
|
||||
.song = unique_tags_visitor_song,
|
||||
};
|
||||
|
||||
bool
|
||||
listAllUniqueTags(struct client *client, int type,
|
||||
const struct locate_item_list *criteria,
|
||||
GError **error_r)
|
||||
{
|
||||
ListCommandItem *item = newListCommandItem(type, criteria);
|
||||
struct list_tags_data data = {
|
||||
.client = client,
|
||||
.item = item,
|
||||
};
|
||||
|
||||
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
|
||||
data.set = strset_new();
|
||||
}
|
||||
|
||||
if (!db_walk("", &unique_tags_visitor, &data, error_r)) {
|
||||
freeListCommandItem(item);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
|
||||
const char *value;
|
||||
|
||||
strset_rewind(data.set);
|
||||
|
||||
while ((value = strset_next(data.set)) != NULL)
|
||||
client_printf(client, "%s: %s\n",
|
||||
tag_item_names[type],
|
||||
value);
|
||||
|
||||
strset_free(data.set);
|
||||
}
|
||||
|
||||
freeListCommandItem(item);
|
||||
|
||||
return true;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user