Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c946b69e1 | ||
|
|
61ad94c0a0 | ||
|
|
56de4c63a7 | ||
|
|
1dfa31c275 | ||
|
|
e16087eb54 | ||
|
|
289293f23b | ||
|
|
59112c1a83 | ||
|
|
dd30ed460d | ||
|
|
bc54b5abaf | ||
|
|
05d316615a | ||
|
|
0eb8702a8e | ||
|
|
dfc6a6f93a | ||
|
|
1bb0b3c9bf | ||
|
|
89d5649b4f | ||
|
|
4b3b8d2795 | ||
|
|
eeff731858 | ||
|
|
63abd73942 | ||
|
|
c74bec2d29 | ||
|
|
bd74d21c83 | ||
|
|
41aee3d6bb | ||
|
|
396e75ed2d | ||
|
|
769e9c1730 | ||
|
|
33d38f33e5 | ||
|
|
876b6402c4 | ||
|
|
0e334a8f92 | ||
|
|
6218734783 | ||
|
|
da5ac9757a | ||
|
|
36107c11d5 | ||
|
|
5adf684dbe | ||
|
|
6bc3adbd33 | ||
|
|
c445a0732f | ||
|
|
20baed87ff | ||
|
|
ef9a2015eb | ||
|
|
583c71eca3 | ||
|
|
1e3c3c8f6c | ||
|
|
0a36166317 | ||
|
|
661f01319c | ||
|
|
6f8941d7be | ||
|
|
f7116edc9a | ||
|
|
ea69d56ab4 | ||
|
|
223e129477 | ||
|
|
4a5674a679 | ||
|
|
91c3a81cb1 | ||
|
|
e6921730c0 | ||
|
|
89ef0636a8 | ||
|
|
769ef001ec | ||
|
|
44b8ac79da | ||
|
|
5ada65b2a0 | ||
|
|
e433e7eda1 | ||
|
|
7de7dea1da | ||
|
|
86fb9bbd6c | ||
|
|
159474cf9b | ||
|
|
a2e7d4bdd1 | ||
|
|
b143a942ae | ||
|
|
c446dc263a | ||
|
|
60f718c2ce | ||
|
|
05df81be2f | ||
|
|
92cd62f0e9 | ||
|
|
602dfbabc2 | ||
|
|
c492a4122c | ||
|
|
fd55e84274 | ||
|
|
dba264cc5f | ||
|
|
7ff27a42ca | ||
|
|
8e1aa18b8f | ||
|
|
99cab7304d | ||
|
|
b5a9d52592 | ||
|
|
ca36938a83 | ||
|
|
de9d77a8ef | ||
|
|
0c3ebfbbb3 | ||
|
|
c575b861ed | ||
|
|
8323649c10 | ||
|
|
0dd84fad78 | ||
|
|
7dc25b3d33 | ||
|
|
5599e736ba | ||
|
|
e4e9a6b493 | ||
|
|
c50ee144df | ||
|
|
f5f32d902b | ||
|
|
f993d371a6 | ||
|
|
b5fb769bff | ||
|
|
28cbf6d803 | ||
|
|
89ddd937b0 | ||
|
|
5da950f61b | ||
|
|
7bff1b6c3c | ||
|
|
2baadd19cf | ||
|
|
2852aadc71 | ||
|
|
32e1854e13 | ||
|
|
b31daaa657 | ||
|
|
2401fc46ba | ||
|
|
0957d2d957 | ||
|
|
f8df9c502b | ||
|
|
52f071d8b0 | ||
|
|
a175ec5b5c | ||
|
|
f32ee0a9be | ||
|
|
f93702b82c | ||
|
|
0d4b006537 | ||
|
|
3f8477a442 | ||
|
|
bc9143b2a2 | ||
|
|
16c553196c | ||
|
|
b063ba412f | ||
|
|
aa406f191a | ||
|
|
f63b0cab81 | ||
|
|
beacc6ed1e | ||
|
|
028cfaa44c | ||
|
|
ac2949cf88 | ||
|
|
a098fae0f8 | ||
|
|
960e1f473f | ||
|
|
ffd72ed6f7 | ||
|
|
5eeab858d1 | ||
|
|
f64045168d | ||
|
|
f16d977e9b | ||
|
|
8274a69236 | ||
|
|
ce9f48e3d5 | ||
|
|
69735bdb22 | ||
|
|
f3e2143b4b | ||
|
|
2110eb9855 | ||
|
|
3c015990c5 | ||
|
|
0d3026b97d | ||
|
|
9cb8379c2d | ||
|
|
33c8ea7e2b | ||
|
|
561bed9fed | ||
|
|
7bdfa6a59d | ||
|
|
b887a941bf | ||
|
|
b76f5250a8 | ||
|
|
e372369b9e | ||
|
|
aaa0ea23e2 | ||
|
|
f8fe2cd532 | ||
|
|
bb362325ad | ||
|
|
56555ad550 | ||
|
|
456b441210 | ||
|
|
982594a4a5 | ||
|
|
eaded8dfd9 | ||
|
|
c583b95ee2 | ||
|
|
cda57dd733 | ||
|
|
60fbfce540 | ||
|
|
d8af2eaf4c | ||
|
|
9a571988ae | ||
|
|
b5b81a8bba | ||
|
|
e76551b4a8 | ||
|
|
74e071b1d7 | ||
|
|
327585327f | ||
|
|
4b445ca4df | ||
|
|
ef4a844233 | ||
|
|
80515658b0 | ||
|
|
e7d7dc6b6b | ||
|
|
aae0476967 | ||
|
|
aa6cf97324 | ||
|
|
8506671c5c | ||
|
|
022976d2f6 | ||
|
|
18d53167d0 | ||
|
|
9519bfddd4 | ||
|
|
0c556e32fa | ||
|
|
f9a02d9b3b | ||
|
|
3b6c986704 | ||
|
|
c78e8764a0 | ||
|
|
7f4d10c85d | ||
|
|
6b30ed0ba6 | ||
|
|
912d5c4d78 | ||
|
|
faa4bd82ae | ||
|
|
409f6b8dd5 | ||
|
|
4e79698b4e | ||
|
|
07b28605b5 | ||
|
|
c4403cc4de | ||
|
|
7e0742f29b | ||
|
|
b21aef221e | ||
|
|
20934f3ac8 | ||
|
|
715e497789 | ||
|
|
c0d389c283 | ||
|
|
6720575bd9 | ||
|
|
b1ec750d1d | ||
|
|
d9c78da997 | ||
|
|
1fa26e763d | ||
|
|
bb71b3d3d9 | ||
|
|
65e8e56e4a | ||
|
|
ee415d0e50 | ||
|
|
9db88a6c4f | ||
|
|
e5151a0a19 | ||
|
|
570234717a | ||
|
|
3f60d1f8de | ||
|
|
65d100c814 | ||
|
|
61bdab8621 | ||
|
|
8c94c9ecbf | ||
|
|
7fd895ecb5 | ||
|
|
213bc07646 | ||
|
|
1f081c181c | ||
|
|
db1d31900e | ||
|
|
2219c79ac3 | ||
|
|
d2782179aa | ||
|
|
d90a31afc7 | ||
|
|
1c236fe0f3 | ||
|
|
24165fb5f3 | ||
|
|
71da960386 | ||
|
|
a16c2d0a26 | ||
|
|
53de44a225 | ||
|
|
e32cff60e5 | ||
|
|
0ebba8d17c | ||
|
|
464db79c04 | ||
|
|
65019d8323 | ||
|
|
f131d316ca | ||
|
|
e6663c96b5 | ||
|
|
5f06d11fe9 | ||
|
|
6db2836662 | ||
|
|
cb209f8d98 | ||
|
|
5634e17a2e | ||
|
|
7c990edeaf | ||
|
|
9149f62760 | ||
|
|
872ec6b11c | ||
|
|
064a21a07c | ||
|
|
57c3c7b2e0 | ||
|
|
6dc75d7a44 | ||
|
|
5f3b7e05c5 | ||
|
|
61d3d8ad00 | ||
|
|
e36440f834 | ||
|
|
4029543d51 | ||
|
|
8aee9ac91f | ||
|
|
6e5e15f06f | ||
|
|
a72bd4cb7c | ||
|
|
d062ab553c | ||
|
|
a41edde12a | ||
|
|
7608927b4f | ||
|
|
24d7647c08 | ||
|
|
a14369cb7d | ||
|
|
3e81c4f3b6 | ||
|
|
84a6c19677 | ||
|
|
624a1d5509 | ||
|
|
8b6d35cfe2 | ||
|
|
867e502448 | ||
|
|
74275b969e | ||
|
|
275dde0630 | ||
|
|
46b470f30b | ||
|
|
fbfec95a8a | ||
|
|
1e75a18f55 | ||
|
|
a98849bf30 | ||
|
|
1b998e9d01 | ||
|
|
ebd6d3f3f9 | ||
|
|
1204afadbb | ||
|
|
cd08b72df7 | ||
|
|
c5ad7c98e2 | ||
|
|
4b8222d792 | ||
|
|
a3524ef8aa | ||
|
|
49ab8aa67d | ||
|
|
fa85639efb | ||
|
|
84bca81a9a | ||
|
|
206ea72a27 | ||
|
|
cc2aceb3d8 | ||
|
|
00b95aa6c3 | ||
|
|
a7632cc1e5 | ||
|
|
39f07025c6 | ||
|
|
658cb0403f | ||
|
|
db1519bcea | ||
|
|
885d1cc77c | ||
|
|
4005becd0a | ||
|
|
4c16b53c2a | ||
|
|
93dbf13d88 | ||
|
|
c5dd1a9002 | ||
|
|
18af7d6392 | ||
|
|
dbeb31d4fe | ||
|
|
0fb9234ebc | ||
|
|
6e4cec829a | ||
|
|
5a433c42e7 | ||
|
|
f808dd131b | ||
|
|
1bbf9a2318 | ||
|
|
ccff70d9c4 | ||
|
|
0347ca1bcc | ||
|
|
23564c14f9 | ||
|
|
93fca13510 | ||
|
|
1a39066cfc | ||
|
|
70b1574aa6 | ||
|
|
898f814dce | ||
|
|
9d425d33ab | ||
|
|
c6b0f58074 | ||
|
|
78b3802f8f | ||
|
|
833b90ccff | ||
|
|
c27a381286 | ||
|
|
ad29b43836 | ||
|
|
5cd9c51410 | ||
|
|
6476d42371 | ||
|
|
a48d9006ad | ||
|
|
c5c2057859 | ||
|
|
1889ab4825 | ||
|
|
0c81560f2c | ||
|
|
26005d6584 | ||
|
|
b0b0bcbdb7 | ||
|
|
546099fa2d | ||
|
|
c53fde6dca | ||
|
|
c9aeefc67a | ||
|
|
37594d3384 | ||
|
|
00dc5d8c38 | ||
|
|
ff8f6b1d00 | ||
|
|
aa60ba1fb1 | ||
|
|
3b8341f249 | ||
|
|
a68bcb6ee1 | ||
|
|
24a52a294c | ||
|
|
03ea67b20b | ||
|
|
4f20003f9a | ||
|
|
a92d13652d | ||
|
|
cf25fbc75d | ||
|
|
c1fb54ff1c | ||
|
|
bc484864ad | ||
|
|
465ffaa5a7 | ||
|
|
357c6bef0b | ||
|
|
d5de7f9b55 | ||
|
|
809beedcf8 | ||
|
|
40071ffaf9 | ||
|
|
501e3c1217 | ||
|
|
ebeb8a13a4 | ||
|
|
8be598c533 | ||
|
|
cf6a6a876f | ||
|
|
ea842a95ca | ||
|
|
f6f1328721 | ||
|
|
1e2901810f | ||
|
|
c5f67d070d | ||
|
|
527d2087c1 | ||
|
|
f6b5c0190d | ||
|
|
a5225dfc2e | ||
|
|
7920f9ef2a | ||
|
|
37d9cf11c3 | ||
|
|
fdc5078a85 | ||
|
|
e4332642f4 | ||
|
|
2d74797e86 | ||
|
|
97ccb60326 | ||
|
|
f9d331041d | ||
|
|
4b76e156a9 | ||
|
|
65660e57ca | ||
|
|
48e2722d9e | ||
|
|
91ec945a37 | ||
|
|
ba62e297b5 | ||
|
|
7f3d89aaa7 | ||
|
|
b7321e3121 | ||
|
|
2ecf0bcb41 | ||
|
|
fec92ea03e | ||
|
|
6a8ec5869b | ||
|
|
e7f585690d | ||
|
|
a76064e46c | ||
|
|
db2407dd75 | ||
|
|
d48a1bddde | ||
|
|
f83fc7fe4e | ||
|
|
3bd6f53788 | ||
|
|
37638369c1 | ||
|
|
39c5242f30 | ||
|
|
5d69984ef9 | ||
|
|
1f734d662e | ||
|
|
ff3855a436 | ||
|
|
30be69f08b | ||
|
|
e1bd9eabe7 | ||
|
|
997c78a08b | ||
|
|
e35d2277c3 | ||
|
|
f29352b864 | ||
|
|
2ca60b09d4 | ||
|
|
8ddf6ceb1f | ||
|
|
aabcc86d9c | ||
|
|
ecae9d0996 | ||
|
|
8eb0b02adb | ||
|
|
33417d4b7d | ||
|
|
ce296e691b | ||
|
|
eb49d303ad | ||
|
|
cf17287794 | ||
|
|
8be00cbee8 | ||
|
|
592f4a8bac | ||
|
|
42cb1f784a | ||
|
|
38529e6809 | ||
|
|
c1fe7df02d | ||
|
|
5c31f511f6 | ||
|
|
fc1c1ae31a | ||
|
|
ac0c61de98 | ||
|
|
fdb02cedea | ||
|
|
68fa9beb22 | ||
|
|
3075929824 | ||
|
|
754481ab13 | ||
|
|
8bf2d81998 | ||
|
|
80f795d32c | ||
|
|
9e6f2a4952 | ||
|
|
0146153d0a | ||
|
|
28079165c4 | ||
|
|
40884a7588 | ||
|
|
9b990c03a6 | ||
|
|
a72bbf29f6 | ||
|
|
7f740b1fc3 | ||
|
|
8ae0e6d4d3 | ||
|
|
f15ad447ca | ||
|
|
67c29560db | ||
|
|
8e787dca86 | ||
|
|
f06dd86a99 | ||
|
|
73f3cc64a5 | ||
|
|
2601de5c30 | ||
|
|
ee3ccfa598 | ||
|
|
396cbb7b64 | ||
|
|
c64f08b2b5 | ||
|
|
3cf82f4dac | ||
|
|
a43256e802 | ||
|
|
6f91694d51 | ||
|
|
d25ce692f9 | ||
|
|
f495fc01bc | ||
|
|
4aacbd2731 | ||
|
|
bb7b1f64ce | ||
|
|
548e0a9821 | ||
|
|
af23e322a1 | ||
|
|
b467e2db41 | ||
|
|
13f34a17b1 | ||
|
|
d0baeec53d | ||
|
|
6cb283cdf2 | ||
|
|
43f5fe421b | ||
|
|
09c041056e | ||
|
|
fda4658e92 | ||
|
|
9968bb2bb8 | ||
|
|
e6397b28a5 | ||
|
|
61fa747813 | ||
|
|
a2ae89e5f9 | ||
|
|
57f7bd1bd6 | ||
|
|
47ed1e6a71 | ||
|
|
9d527e2fb2 | ||
|
|
8e820cdad6 | ||
|
|
0069bba318 | ||
|
|
a67d0683ca | ||
|
|
2b8c063c32 | ||
|
|
4ae3655025 | ||
|
|
29622ba221 | ||
|
|
45a7756828 | ||
|
|
516c5fc84a | ||
|
|
2ea34d4b97 | ||
|
|
6952505368 | ||
|
|
60af816a66 | ||
|
|
364deea051 | ||
|
|
a300b62184 | ||
|
|
4e1716d58b | ||
|
|
7c98cbeb65 | ||
|
|
72852e04da | ||
|
|
7726044c61 | ||
|
|
76827abd78 | ||
|
|
561b6303bc | ||
|
|
4d85878921 | ||
|
|
b7abdf1973 | ||
|
|
015d269667 | ||
|
|
5ede29b401 | ||
|
|
4895d562b3 | ||
|
|
5215e55848 | ||
|
|
f8750645c2 | ||
|
|
9b6d75f370 | ||
|
|
c9f72d984e | ||
|
|
48022d92fe | ||
|
|
1732205174 | ||
|
|
32e57ade15 | ||
|
|
b8cd8bd31b | ||
|
|
b0c7c8b682 | ||
|
|
4af96b2bb6 | ||
|
|
8d7fd835a8 | ||
|
|
56beb897cf | ||
|
|
3bcea4b692 | ||
|
|
7369311501 | ||
|
|
9eef7e40f5 | ||
|
|
5a4bff5be8 | ||
|
|
a040f38a3f | ||
|
|
3ff09aeb46 | ||
|
|
045f1cd766 | ||
|
|
c691b7c39d | ||
|
|
ed21bd4b3e | ||
|
|
be7716326e | ||
|
|
8c21f781e1 | ||
|
|
5c1223adfb | ||
|
|
23055da02d | ||
|
|
20981a6c61 | ||
|
|
b082fbc0a5 | ||
|
|
2af45fd3fe | ||
|
|
7d621cbc28 | ||
|
|
48ef8a93a9 | ||
|
|
80d0c6d430 | ||
|
|
ada8603e9f | ||
|
|
ce73883ba2 | ||
|
|
bfbc2e3488 | ||
|
|
ef493f65a0 | ||
|
|
d76fce8cbc | ||
|
|
87b444684a | ||
|
|
f9dfdb2c98 | ||
|
|
fd7bb4eb7e | ||
|
|
2ddb96e134 | ||
|
|
5ebe6bcd00 | ||
|
|
6b4afd8877 | ||
|
|
8a8821530a | ||
|
|
bea7022b4c | ||
|
|
553cc536c0 | ||
|
|
8fbef76dc0 | ||
|
|
2686b4c190 | ||
|
|
43109f8172 | ||
|
|
cf181e1d26 | ||
|
|
14c6f68fb7 | ||
|
|
47f93d97d5 | ||
|
|
0ffbd7824d | ||
|
|
5ea6ec4328 | ||
|
|
6b4fbe002d | ||
|
|
7a078dc880 | ||
|
|
63e106a12d | ||
|
|
63a32e473f | ||
|
|
e07fafda6e | ||
|
|
d887d06a4f | ||
|
|
8d839ef3cf | ||
|
|
353be0dbf5 | ||
|
|
05ce5f7853 | ||
|
|
f520ceaf25 | ||
|
|
371401fa36 | ||
|
|
574a76c231 | ||
|
|
f6bb76acb0 | ||
|
|
3f5044c82d | ||
|
|
394876992b | ||
|
|
365d4fa1bf | ||
|
|
66309d806a | ||
|
|
e7a97ed277 | ||
|
|
c09c290ac8 | ||
|
|
efdd51c361 | ||
|
|
f6346f3c41 | ||
|
|
2c72fb8e8e | ||
|
|
0c9de181f8 | ||
|
|
757ebe9085 | ||
|
|
61dc4fa95e | ||
|
|
c05fa78fa0 | ||
|
|
6a16fc2526 | ||
|
|
e790443829 | ||
|
|
9c1a0bc151 | ||
|
|
1379ff7845 | ||
|
|
3a13612d15 | ||
|
|
5305b6a3a8 | ||
|
|
e9d9a6c441 | ||
|
|
8838ee481e | ||
|
|
0fd4c9c998 | ||
|
|
b857108db4 | ||
|
|
16ec910ba5 | ||
|
|
5e98d96f65 | ||
|
|
7e2b9a30a2 | ||
|
|
4d683922c1 | ||
|
|
9d594988d9 | ||
|
|
c441e888c2 | ||
|
|
298daf9895 | ||
|
|
d04dff9130 | ||
|
|
5eef5a594c | ||
|
|
cce98709a7 | ||
|
|
3fef151fca | ||
|
|
a6fe517f87 | ||
|
|
964c5008ca | ||
|
|
f250ae58a4 | ||
|
|
7af194822c | ||
|
|
c4b1f3f4fe | ||
|
|
f4b13533ce | ||
|
|
638baf564d | ||
|
|
21b3d99249 | ||
|
|
d8dda5b24f | ||
|
|
25fb4361dc | ||
|
|
8ec1808313 | ||
|
|
43efca9d04 | ||
|
|
34b9480a1b | ||
|
|
51dfeb8190 | ||
|
|
3a03d940a1 | ||
|
|
69268b7d21 | ||
|
|
e69cf3942e | ||
|
|
2e6c8e6537 | ||
|
|
ca084f1216 | ||
|
|
a1bc3617a4 | ||
|
|
a1e2042315 | ||
|
|
3bb173c979 | ||
|
|
70135a5bf4 | ||
|
|
efa9ac77b8 | ||
|
|
e8b330f3ba | ||
|
|
56adf4a1ee | ||
|
|
9f92b3b774 | ||
|
|
9d774b4486 | ||
|
|
163e1e3f2e | ||
|
|
adaaf51e9a | ||
|
|
2ba5978f84 | ||
|
|
46240fb7d1 | ||
|
|
a4fa8ca79d | ||
|
|
701f709c23 | ||
|
|
3d22a66701 | ||
|
|
a401ff4fb4 | ||
|
|
72419227e9 | ||
|
|
22bff59789 | ||
|
|
b3746dc58e | ||
|
|
b950bcbd0b | ||
|
|
aa6206a235 | ||
|
|
8cf23b79bf | ||
|
|
bb884eeab7 | ||
|
|
72d2201dd6 | ||
|
|
1b65df6c45 | ||
|
|
a7d1f128ad | ||
|
|
17ef29887e | ||
|
|
06822e2514 | ||
|
|
fc1c26f58f | ||
|
|
ef3cd00ef1 | ||
|
|
08f483d77d | ||
|
|
405ebe5ee2 | ||
|
|
59b4cefd53 | ||
|
|
d037147c23 | ||
|
|
34097e2d81 | ||
|
|
e0f51f756c | ||
|
|
8c9b834bc4 | ||
|
|
9178740c26 | ||
|
|
922cca9652 | ||
|
|
e2f0354deb | ||
|
|
6891252cf6 | ||
|
|
c9debdcce5 | ||
|
|
6911dfde02 | ||
|
|
e7ddb2d7b2 | ||
|
|
51fde7a52b | ||
|
|
b36178d6d4 | ||
|
|
c19dffcb46 | ||
|
|
e04245cff5 | ||
|
|
d5b08a8f51 | ||
|
|
49179170fa | ||
|
|
e224ad1a6a | ||
|
|
ba552bd1f8 | ||
|
|
ad1de823bd | ||
|
|
f2f8ad68a3 | ||
|
|
23e3dd91b3 | ||
|
|
b4df21776e | ||
|
|
741538fa3c | ||
|
|
270d32b45e | ||
|
|
cedf54865d | ||
|
|
c0199cf356 | ||
|
|
c95bb9d29c | ||
|
|
29ae400b80 | ||
|
|
0cd4d540aa | ||
|
|
6ce710dbb1 | ||
|
|
90744c3b80 | ||
|
|
d777cf74a5 | ||
|
|
9b1d6eb6e0 | ||
|
|
676fa7199b | ||
|
|
fd30b3501e | ||
|
|
2a601d2731 | ||
|
|
296c9ca767 | ||
|
|
245f1fe366 | ||
|
|
ce3b8b7894 | ||
|
|
0c61eb11f5 | ||
|
|
9ec4866cea | ||
|
|
d48dbc6bbb | ||
|
|
5cb9d78e35 | ||
|
|
78c866ce59 | ||
|
|
ab6fcc92e5 | ||
|
|
ba640e2659 | ||
|
|
9849a8334f | ||
|
|
ad645d960d | ||
|
|
98604a2ab8 | ||
|
|
bd8da45c1a | ||
|
|
b46787ae93 | ||
|
|
48b2b9235d | ||
|
|
efd2e44ded | ||
|
|
93e4e47cd6 | ||
|
|
9ec65c5f9a | ||
|
|
1597d028f0 | ||
|
|
2178397c2f | ||
|
|
568f8327ef | ||
|
|
7e2b7b7376 | ||
|
|
253e578a98 | ||
|
|
d590d037ad | ||
|
|
64298127c5 | ||
|
|
9122136594 | ||
|
|
1ee76f7488 | ||
|
|
91bfdcd4bd | ||
|
|
2b02ae4196 | ||
|
|
326f866d7e | ||
|
|
86b1d6f443 | ||
|
|
62e35e9a42 | ||
|
|
dc02fb4a06 | ||
|
|
c639d774cf | ||
|
|
58bc146d11 | ||
|
|
429e2fcc40 | ||
|
|
7d8140316c | ||
|
|
3dfb8e0b0b | ||
|
|
9aeea8bcad | ||
|
|
850ce9ac3c | ||
|
|
61b901a481 | ||
|
|
08a2c2d148 | ||
|
|
d87bb1f11c | ||
|
|
bf4d642125 | ||
|
|
7163886d31 | ||
|
|
a3930e1c05 | ||
|
|
32a037b1c6 | ||
|
|
c170b2a06d | ||
|
|
0807c32a1c | ||
|
|
c447212562 | ||
|
|
54bbde5c1d | ||
|
|
7009984357 | ||
|
|
7229b73862 | ||
|
|
66b9da36dd | ||
|
|
9d9c5f53e7 | ||
|
|
9a7300cda1 | ||
|
|
e618103f38 | ||
|
|
114b6b8a41 | ||
|
|
5064beffe9 | ||
|
|
19c4e7d7d3 | ||
|
|
8b799c7904 | ||
|
|
a6c5894272 | ||
|
|
0fdc62c914 | ||
|
|
88af84579c | ||
|
|
917dc7695f | ||
|
|
65e3e4faa2 | ||
|
|
9e7ec3a65c | ||
|
|
7ac7b127c6 | ||
|
|
3945753c5c | ||
|
|
599585d57a | ||
|
|
10cf36401f | ||
|
|
fe2834f4a6 | ||
|
|
674617c0f8 | ||
|
|
f92f8928b0 | ||
|
|
45868bc523 | ||
|
|
9b2594355f | ||
|
|
e30cb26200 | ||
|
|
63d3ab1cc1 | ||
|
|
188d907e10 | ||
|
|
e972a86233 | ||
|
|
52a76e87ad | ||
|
|
8ab19f8121 | ||
|
|
6f3c8c68d4 | ||
|
|
98385ff4c8 | ||
|
|
62a6be26af | ||
|
|
5c1169ba9d | ||
|
|
0b47a5ca3a | ||
|
|
9da75a777b | ||
|
|
2fc2190b59 | ||
|
|
e108935430 | ||
|
|
bbde74db8d | ||
|
|
9fce4edf00 | ||
|
|
ea3c6c3278 | ||
|
|
6a0bd397cd | ||
|
|
7bbb137fd9 | ||
|
|
e67d32445f | ||
|
|
83280f3f99 | ||
|
|
9c81fbaa2b | ||
|
|
1d7415b91d | ||
|
|
bc5a042dcc | ||
|
|
e5af0bdd52 | ||
|
|
dc2939a4f9 | ||
|
|
f52ec56a0f | ||
|
|
75b38df3d6 | ||
|
|
0f5aeea345 | ||
|
|
d80fc36b29 | ||
|
|
4ca42c9c11 | ||
|
|
6c760902d6 | ||
|
|
2a0fcc7566 | ||
|
|
b763ccdd08 | ||
|
|
e72a0d1141 | ||
|
|
295e5a1ccf | ||
|
|
497fafb555 | ||
|
|
585f556006 | ||
|
|
7b115126c9 | ||
|
|
1a9165a084 | ||
|
|
4a74d54cdc | ||
|
|
a0755d4b1c | ||
|
|
084181a9a5 | ||
|
|
5c381b9410 | ||
|
|
0be9500518 | ||
|
|
9c2da72dcc | ||
|
|
3f6145cd07 | ||
|
|
e155f19d0d | ||
|
|
0c7ea8a5a0 | ||
|
|
1f3474a154 | ||
|
|
c0c1f9cc61 | ||
|
|
e4382c23a3 | ||
|
|
29b5305ab4 | ||
|
|
758154e87e | ||
|
|
ea8b1815f6 | ||
|
|
ebc0b7205f | ||
|
|
06ee770184 | ||
|
|
900325186b | ||
|
|
3b9816f3b3 | ||
|
|
3e5779e5ce | ||
|
|
66d0cb4a1b | ||
|
|
54c85c7951 | ||
|
|
941da4be1c | ||
|
|
69c94baf18 | ||
|
|
90016f6eda | ||
|
|
06b647e30d | ||
|
|
84ebcb84f9 | ||
|
|
b11610face | ||
|
|
6e90419014 | ||
|
|
0457c3c30e | ||
|
|
945cfbf21b | ||
|
|
5769cb0dee | ||
|
|
efd043e309 | ||
|
|
fd19563160 | ||
|
|
1a2ed24566 | ||
|
|
d2fecc47ec | ||
|
|
f9b0a5efb9 | ||
|
|
e304e34ac7 | ||
|
|
ff3826c0c8 | ||
|
|
c96b043a19 | ||
|
|
a1d0f14eb8 | ||
|
|
0234f4dab9 | ||
|
|
e00411f7ad | ||
|
|
3b2350cd8f | ||
|
|
d490091a9a | ||
|
|
5be36b517f | ||
|
|
dab323e8ff | ||
|
|
7e02f86af8 | ||
|
|
26ed43fca8 | ||
|
|
dab4e3d5f8 | ||
|
|
07e8b6b095 | ||
|
|
52523a5d3c | ||
|
|
ded4b7ef6f | ||
|
|
aa9d669a2b | ||
|
|
5378884bf8 | ||
|
|
631d947a9a | ||
|
|
7280807588 | ||
|
|
ca010ac522 | ||
|
|
e2e1ef9bc6 | ||
|
|
a96b793c98 | ||
|
|
f7ba0ff634 | ||
|
|
a0339d9814 | ||
|
|
eb7a1d7117 | ||
|
|
65fa0e21fa | ||
|
|
60ea86d499 | ||
|
|
0411bb6b44 | ||
|
|
d0198b15ee | ||
|
|
c4e8678d8f | ||
|
|
d6cd9cd7e4 |
@@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
[Tt]est[Rr]esult*
|
||||||
|
/cache
|
||||||
|
/logs
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|
||||||
|
# coverage generated:
|
||||||
|
/cover-html/
|
||||||
|
.coverage
|
||||||
|
.coveralls.yml
|
||||||
|
|
||||||
# Compiled source #
|
# Compiled source #
|
||||||
###################
|
###################
|
||||||
@@ -6,12 +16,13 @@
|
|||||||
*.pyproj
|
*.pyproj
|
||||||
*.sln
|
*.sln
|
||||||
|
|
||||||
# Logs and databases #
|
# Headphones files #
|
||||||
######################
|
######################
|
||||||
*.log
|
*.log
|
||||||
*.db*
|
*.db*
|
||||||
*.db-journal
|
*.db-journal
|
||||||
*.ini
|
*.ini
|
||||||
|
version.lock
|
||||||
logs/*
|
logs/*
|
||||||
cache/*
|
cache/*
|
||||||
|
|
||||||
@@ -19,6 +30,7 @@ cache/*
|
|||||||
##################
|
##################
|
||||||
*.crt
|
*.crt
|
||||||
*.key
|
*.key
|
||||||
|
*.csr
|
||||||
|
|
||||||
# OS generated files #
|
# OS generated files #
|
||||||
######################
|
######################
|
||||||
@@ -28,8 +40,12 @@ ehthumbs.db
|
|||||||
Icon?
|
Icon?
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
#ignore thumbnails created by windows
|
#Ignore files generated by PyCharm
|
||||||
Thumbs.db
|
.idea/*
|
||||||
|
|
||||||
|
#Ignore files generated by vi
|
||||||
|
*.swp
|
||||||
|
|
||||||
#Ignore files build by Visual Studio
|
#Ignore files build by Visual Studio
|
||||||
*.obj
|
*.obj
|
||||||
*.exe
|
*.exe
|
||||||
@@ -55,8 +71,4 @@ Thumbs.db
|
|||||||
obj/
|
obj/
|
||||||
[Rr]elease*/
|
[Rr]elease*/
|
||||||
_ReSharper*/
|
_ReSharper*/
|
||||||
[Tt]est[Rr]esult*
|
.vscode
|
||||||
/cache
|
|
||||||
/logs
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[pep8]
|
||||||
|
# E121 continuation line under-indented for hanging indent
|
||||||
|
# E122 continuation line missing indentation or outdented
|
||||||
|
# E124 closing bracket does not match visual indentation
|
||||||
|
# E125 continuation line with same indent as next logical line
|
||||||
|
# E126 continuation line over-indented for hanging indent
|
||||||
|
# E127 continuation line over-indented for visual indent
|
||||||
|
# E128 continuation line under-indented for visual indent
|
||||||
|
# E261 at least two spaces before inline comment
|
||||||
|
# E262 inline comment should start with '# '
|
||||||
|
# E265 block comment should start with '# '
|
||||||
|
# E501 line too long (312 > 160 characters)
|
||||||
|
# E502 the backslash is redundant between brackets
|
||||||
|
ignore = E121,E122,E124,E125,E126,E127,E128,E261,E262,E265,E501,E502
|
||||||
|
max-line-length = 160
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Travis CI configuration file
|
||||||
|
# http://about.travis-ci.org/docs/
|
||||||
|
|
||||||
|
language: python
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
pip: true
|
||||||
|
directories:
|
||||||
|
- lib
|
||||||
|
|
||||||
|
# Available Python versions:
|
||||||
|
# http://about.travis-ci.org/docs/user/ci-environment/#Python-VM-images
|
||||||
|
python:
|
||||||
|
- "2.6"
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- python: "2.7"
|
||||||
|
env: SENDCOVERAGE=1
|
||||||
|
|
||||||
|
# pylint 1.4 does not run under python 2.6
|
||||||
|
install:
|
||||||
|
- pip install pyOpenSSL
|
||||||
|
- pip install pylint==1.3.1
|
||||||
|
- pip install pyflakes
|
||||||
|
- pip install pep8
|
||||||
|
# coverage stuff:
|
||||||
|
- pip install coveralls
|
||||||
|
- pip install coverage
|
||||||
|
script:
|
||||||
|
- pep8 headphones
|
||||||
|
- pyflakes headphones
|
||||||
|
- nosetests
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
# coverage stuff:
|
||||||
|
- if [ $SENDCOVERAGE ]; then coveralls; fi
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# API Reference
|
||||||
|
The API is still pretty new and needs some serious cleaning up on the backend but should be reasonably functional. There are no error codes yet.
|
||||||
|
|
||||||
|
## General structure
|
||||||
|
The API endpoint is `http://ip:port + HTTP_ROOT + /api?apikey=$apikey&cmd=$command`
|
||||||
|
|
||||||
|
Data response in JSON formatted. If executing a command like "delArtist" or "addArtist" you'll get back an "OK", else, you'll get the data you requested.
|
||||||
|
|
||||||
|
## API methods
|
||||||
|
|
||||||
|
### getIndex
|
||||||
|
Fetch data from index page. Returns: ArtistName, ArtistSortName, ArtistID, Status, DateAdded, [LatestAlbum, ReleaseDate, AlbumID], HaveTracks, TotalTracks, IncludeExtras, LastUpdated, [ArtworkURL, ThumbURL]: a remote url to the artwork/thumbnail.
|
||||||
|
|
||||||
|
To get the cached image path, see getArtistArt command. ThumbURL is added/updated when an artist is added/updated. If your using the database method to get the artwork, it's more reliable to use the ThumbURL than the ArtworkURL)
|
||||||
|
|
||||||
|
### getArtist&id=$artistid
|
||||||
|
Fetch artist data. returns the artist object (see above) and album info: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type, ArtworkURL: hosted image path. For cached image, see getAlbumArt command)
|
||||||
|
|
||||||
|
### getAlbum&id=$albumid
|
||||||
|
Fetch data from album page. Returns the album object, a description object and a tracks object. Tracks contain: AlbumASIN, AlbumTitle, TrackID, Format, TrackDuration (ms), ArtistName, TrackTitle, AlbumID, ArtistID, Location, TrackNumber, CleanName (stripped of punctuation /styling), BitRate)
|
||||||
|
|
||||||
|
### getUpcoming
|
||||||
|
Returns: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type
|
||||||
|
|
||||||
|
### getWanted
|
||||||
|
Returns: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type
|
||||||
|
|
||||||
|
### getSimilar
|
||||||
|
Returns similar artists - with a higher "Count" being more likely to be similar. Returns: Count, ArtistName, ArtistID
|
||||||
|
|
||||||
|
### getHistory
|
||||||
|
Returns: Status, DateAdded, Title, URL (nzb), FolderName, AlbumID, Size (bytes)
|
||||||
|
|
||||||
|
### getLogs
|
||||||
|
Not working yet
|
||||||
|
|
||||||
|
### findArtist&name=$artistname[&limit=$limit]
|
||||||
|
Perform artist query on musicbrainz. Returns: url, score, name, uniquename (contains disambiguation info), id)
|
||||||
|
|
||||||
|
### findAlbum&name=$albumname[&limit=$limit]
|
||||||
|
Perform album query on musicbrainz. Returns: title, url (artist), id (artist), albumurl, albumid, score, uniquename (artist - with disambiguation)
|
||||||
|
|
||||||
|
### addArtist&id=$artistid
|
||||||
|
Add an artist to the db by artistid)
|
||||||
|
|
||||||
|
### addAlbum&id=$releaseid
|
||||||
|
Add an album to the db by album release id
|
||||||
|
|
||||||
|
### delArtist&id=$artistid
|
||||||
|
Delete artist from db by artistid)
|
||||||
|
|
||||||
|
### pauseArtist&id=$artistid
|
||||||
|
Pause an artist in db)
|
||||||
|
|
||||||
|
### resumeArtist&id=$artistid
|
||||||
|
Resume an artist in db)
|
||||||
|
|
||||||
|
### refreshArtist&id=$artistid
|
||||||
|
Refresh info for artist in db from musicbrainz
|
||||||
|
|
||||||
|
### queueAlbum&id=$albumid[&new=True&lossless=True]
|
||||||
|
Mark an album as wanted and start the searcher. Optional paramters: 'new' looks for new versions, 'lossless' looks only for lossless versions
|
||||||
|
|
||||||
|
### unqueueAlbum&id=$albumid
|
||||||
|
Unmark album as wanted / i.e. mark as skipped
|
||||||
|
|
||||||
|
### forceSearch
|
||||||
|
force search for wanted albums - not launched in a separate thread so it may take a bit to complete
|
||||||
|
### forceProcess[&dir=/path/to/folder]
|
||||||
|
Force post process albums in download directory - also not launched in a separate thread
|
||||||
|
### forceActiveArtistsUpdate
|
||||||
|
force Active Artist Update - also not launched in a separate thread
|
||||||
|
|
||||||
|
### getVersion
|
||||||
|
Returns some version information: git_path, install_type, current_version, installed_version, commits_behind
|
||||||
|
|
||||||
|
### checkGithub
|
||||||
|
Updates the version information above and returns getVersion data
|
||||||
|
|
||||||
|
### shutdown
|
||||||
|
Shut down headphones
|
||||||
|
|
||||||
|
### restart
|
||||||
|
Restart headphones
|
||||||
|
|
||||||
|
### update
|
||||||
|
Update headphones - you may want to check the install type in get version and not allow this if type==exe
|
||||||
|
|
||||||
|
### getArtistArt&id=$artistid
|
||||||
|
Returns either a relative path to the cached image, or a remote url if the image can't be saved to the cache dir
|
||||||
|
|
||||||
|
getAlbumArt&id=$albumid
|
||||||
|
see above
|
||||||
|
|
||||||
|
### getArtistInfo&id=$artistid
|
||||||
|
Returns Summary and Content, both formatted in html.
|
||||||
|
|
||||||
|
### getAlbumInfo&id=$albumid
|
||||||
|
See above, returns Summary and Content.
|
||||||
|
|
||||||
|
### getArtistThumb&id=$artistid
|
||||||
|
Returns either a relative path to the cached thumbnail artist image, or an http:// address if the cache dir can't be written to.
|
||||||
|
|
||||||
|
### getAlbumThumb&id=$albumid
|
||||||
|
See above.
|
||||||
|
|
||||||
|
### choose_specific_download&id=$albumid
|
||||||
|
Gives you a list of results from searcher.searchforalbum(). Basically runs a normal search, but rather than sorting them and downloading the best result, it dumps the data, which you can then pass on to download_specific_release(). Returns a list of dictionaries with params: title, size, url, provider & kind - all of these values must be passed back to download_specific_release
|
||||||
|
|
||||||
|
### download_specific_release&id=albumid&title=$title&size=$size&url=$url&provider=$provider&kind=$kind
|
||||||
|
Allows you to manually pass a choose_specific_download release back to searcher.send_to_downloader()
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
The API is still pretty new and needs some serious cleaning up on the backend but should be
|
|
||||||
reasonably functional. There are no error codes yet,
|
|
||||||
|
|
||||||
|
|
||||||
General structure:
|
|
||||||
http://localhost:8181 + HTTP_ROOT + /api?apikey=$apikey&cmd=$command
|
|
||||||
|
|
||||||
Data returned in json format. If executing a command like "delArtist" or "addArtist" you'll get back an "OK", else, you'll get the data you requested
|
|
||||||
|
|
||||||
$commands¶meters[&optionalparameters]:
|
|
||||||
|
|
||||||
|
|
||||||
getIndex (fetch data from index page. Returns: ArtistName, ArtistSortName, ArtistID, Status, DateAdded,
|
|
||||||
[LatestAlbum, ReleaseDate, AlbumID], HaveTracks, TotalTracks,
|
|
||||||
IncludeExtras, LastUpdated, [ArtworkURL, ThumbURL]: a remote url to the artwork/thumbnail. To get the cached image path, see getArtistArt command.
|
|
||||||
ThumbURL is added/updated when an artist is added/updated. If your using the database method to get the artwork,
|
|
||||||
it's more reliable to use the ThumbURL than the ArtworkURL)
|
|
||||||
|
|
||||||
getArtist&id=$artistid (fetch artist data. returns the artist object (see above) and album info: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type, ArtworkURL: hosted image path. For cached image, see getAlbumArt command)
|
|
||||||
|
|
||||||
getAlbum&id=$albumid (fetch data from album page. Returns the album object, a description object and a tracks object. Tracks contain: AlbumASIN, AlbumTitle, TrackID, Format, TrackDuration (ms), ArtistName, TrackTitle, AlbumID, ArtistID, Location, TrackNumber, CleanName (stripped of punctuation /styling), BitRate)
|
|
||||||
|
|
||||||
getUpcoming (Returns: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type)
|
|
||||||
|
|
||||||
getWanted (Returns: Status, AlbumASIN, DateAdded, AlbumTitle, ArtistName, ReleaseDate, AlbumID, ArtistID, Type)
|
|
||||||
|
|
||||||
getSimilar (Returns similar artists - with a higher "Count" being more likely to be similar. Returns: Count, ArtistName, ArtistID)
|
|
||||||
|
|
||||||
getHistory (Returns: Status, DateAdded, Title, URL (nzb), FolderName, AlbumID, Size (bytes))
|
|
||||||
|
|
||||||
getLogs (not working yet)
|
|
||||||
|
|
||||||
findArtist&name=$artistname[&limit=$limit] (perform artist query on musicbrainz. Returns: url, score, name, uniquename (contains disambiguation info), id)
|
|
||||||
|
|
||||||
findAlbum&name=$albumname[&limit=$limit] (perform album query on musicbrainz. Returns: title, url (artist), id (artist), albumurl, albumid, score, uniquename (artist - with disambiguation)
|
|
||||||
|
|
||||||
addArtist&id=$artistid (add an artist to the db by artistid)
|
|
||||||
addAlbum&id=$releaseid (add an album to the db by album release id)
|
|
||||||
|
|
||||||
delArtist&id=$artistid (delete artist from db by artistid)
|
|
||||||
|
|
||||||
pauseArtist&id=$artistid (pause an artist in db)
|
|
||||||
resumeArtist&id=$artistid (resume an artist in db)
|
|
||||||
|
|
||||||
refreshArtist&id=$artistid (refresh info for artist in db from musicbrainz)
|
|
||||||
|
|
||||||
queueAlbum&id=$albumid[&new=True&lossless=True] (Mark an album as wanted and start the searcher. Optional paramters: 'new' looks for new versions, 'lossless' looks only for lossless versions)
|
|
||||||
unqueueAlbum&id=$albumid (Unmark album as wanted / i.e. mark as skipped)
|
|
||||||
|
|
||||||
forceSearch (force search for wanted albums - not launched in a separate thread so it may take a bit to complete)
|
|
||||||
forceProcess (force post process albums in download directory - also not launched in a separate thread)
|
|
||||||
|
|
||||||
getVersion (Returns some version information: git_path, install_type, current_version, installed_version, commits_behind
|
|
||||||
checkGithub (updates the version information above and returns getVersion data)
|
|
||||||
|
|
||||||
shutdown (shut down headphones)
|
|
||||||
restart (restart headphones)
|
|
||||||
update (update headphones - you may want to check the install type in get version and not allow this if type==exe)
|
|
||||||
|
|
||||||
getArtistArt&id=$artistid (Returns either a relative path to the cached image, or a remote url if the image can't be saved to the cache dir)
|
|
||||||
getAlbumArt&id=$albumid (see above)
|
|
||||||
|
|
||||||
getArtistInfo&id=$artistid (Returns Summary and Content, both formatted in html)
|
|
||||||
getAlbumInfo&id=$albumid (See above, returns Summary and Content)
|
|
||||||
|
|
||||||
getArtistThumb&id=$artistid (Returns either a relative path to the cached thumbnail artist image, or an http:// address if the cache dir can't be written to)
|
|
||||||
getAlbumThumb&id=$albumid (see above)
|
|
||||||
|
|
||||||
choose_specific_download&id=$albumid (Gives you a list of results from searcher.searchforalbum(). Basically runs a normal search, but rather than sorting them and downloading the best result, it dumps the data, which you can then pass on to download_specific_release(). Returns a list of dictionaries with params: title, size, url, provider & kind - all of these values must be passed back to download_specific_release)
|
|
||||||
|
|
||||||
download_specific_release&id=albumid&title=$title&size=$size&url=$url&provider=$provider&kind=$kind (Allows you to manually pass a choose_specific_download release back to searcher.send_to_downloader())
|
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v0.5.12
|
||||||
|
Released 25 February 2016
|
||||||
|
|
||||||
|
This is mostly a hotfix update
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: Experimental Deluge Support
|
||||||
|
* Fixed: Some pep8 stuff
|
||||||
|
* Improved: Use curly braces for pathrender optional variables
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.11...v0.5.12).
|
||||||
|
|
||||||
|
## v0.5.11
|
||||||
|
Released 20 February 2016
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: Soft chroot option
|
||||||
|
* Fixed: Post processing temporary directory fix (#2504)
|
||||||
|
* Fixed: Ubuntu init script (#2509)
|
||||||
|
* Fixed: Image cache uncaught exception (#2485)
|
||||||
|
* Improved: $Date/$date variable in folder renaming
|
||||||
|
* Improved: Reuse transmission session id
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.10...v0.5.11).
|
||||||
|
|
||||||
|
## v0.5.10
|
||||||
|
Released 29 January 2016
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: API option to post-process single folders
|
||||||
|
* Added: Ability to specify extension when re-encoding
|
||||||
|
* Added: Option to stop renaming folders
|
||||||
|
* Fixed: Utorrent torrents not being removed (#2385)
|
||||||
|
* Fixed: Torznab to transmission
|
||||||
|
* Fixed: Magnet folder names in history
|
||||||
|
* Fixed: Multiple torcache fixes
|
||||||
|
* Fixed: Updated requests & urllib3 to latest versions to fix errors with pyOpenSSL
|
||||||
|
* Improved: Use a temporary folder during post-processing
|
||||||
|
* Improved: Added verify_ssl_cert option
|
||||||
|
* Improved: Fixed track matching progress
|
||||||
|
* Improved: pylint, pep8 & pylint fixes
|
||||||
|
* Improved: Stop JS links from scrolling to the top of the page
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.9...v0.5.10).
|
||||||
|
|
||||||
|
## v0.5.9
|
||||||
|
Released 05 September 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: Providers Strike, Jackett, custom Torznabs
|
||||||
|
* Added: Option to stop post-processing if no good match found (#2343)
|
||||||
|
* Fixed: Blackhole -> Magnet, limit to torcache
|
||||||
|
* Fixed: Kat 403 flac error
|
||||||
|
* Fixed: Last.fm errors
|
||||||
|
* Fixed: Pushover notifications
|
||||||
|
* Improved: Rutracker logging, switched to requests lib
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.8...v0.5.9).
|
||||||
|
|
||||||
|
## v0.5.8
|
||||||
|
Released 13 July 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: Option to only include official extras
|
||||||
|
* Added: Option to wait until album release date before searching
|
||||||
|
* Fixed: NotifyMyAndroid notifications
|
||||||
|
* Fixed: Plex Notifications
|
||||||
|
* Fixed: Metacritic parsing
|
||||||
|
* Fixed: Pushbullet notifications
|
||||||
|
* Fixed: What.cd not honoring custom search term (#2279)
|
||||||
|
* Improved: XSS Search bug
|
||||||
|
* Improved: Config page layout
|
||||||
|
* Improved: Set localhost as default
|
||||||
|
* Improved: Better single artist scanning
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.7...v0.5.8).
|
||||||
|
|
||||||
|
## v0.5.7
|
||||||
|
Released 01 July 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Improved: Moved pushover to use requests lib
|
||||||
|
* Improved: Plex tokens with Plex Home
|
||||||
|
* Improved: Added getLogs & clearLogs to api
|
||||||
|
* Improved: Cache MetaCritic scores. Added user-agent header to fix 403 errors
|
||||||
|
* Improved: Specify whether to delete folders when force post-processing
|
||||||
|
* Improved: Convert target bitrate to vbr preset for what.cd searching
|
||||||
|
* Improved: Switched Pushover to requests lib
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.6...v0.5.7).
|
||||||
|
|
||||||
|
## v0.5.6
|
||||||
|
Released 08 June 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: Metacritic scores
|
||||||
|
* Added: Series support (e.g. Cafe Del Mar, Now That's What I Call Music, etc)
|
||||||
|
* Added: Filter out clean/edited/censored releases (#2198)
|
||||||
|
* Added: Button on the log page to toggle verbose/debug logging
|
||||||
|
* Fixed: Connecting to SABnzbd over https with python >= 2.7.9
|
||||||
|
* Fixed: Email Notifications with SSL
|
||||||
|
* Fixed: Don't limit musicbrainz results to first 100
|
||||||
|
* Fixed: nzbget url fix
|
||||||
|
* Fixed: OSX Notifications
|
||||||
|
* Improved: Cuesplit, allow wav, ape to be split
|
||||||
|
* Improved: Moved the 'freeze db' option to the advanced->misc. tab
|
||||||
|
* Improved: Moved kickass searching to json api, so it doesn't throw 404 errors anymore when there are no results
|
||||||
|
* Improved: SSL for headphones indexer
|
||||||
|
* Improved: Disable update dialog box if check_github is diabled
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.5...v0.5.6).
|
||||||
|
|
||||||
|
## v0.5.5
|
||||||
|
Released 04 May 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: force ID3v2.3 during post processing (#2121)
|
||||||
|
* Added: MusicBrainz authentication (#2125)
|
||||||
|
* Added: Email notifications (addresses #1045)
|
||||||
|
* Fixed: Kickass url updated to kickass.to (#2119)
|
||||||
|
* Fixed: Piratebay returning 0 bytes for all files
|
||||||
|
* Fixed: Albums stopped automatically refreshing when adding an artist
|
||||||
|
* Fixed: Min/max sizes for target bitrate
|
||||||
|
* Fixed: Don't filter any results if looking for a specific download
|
||||||
|
* Fixed: Sort by size in the specific download table
|
||||||
|
* Fixed: Deal with beets recommendation.none correctly
|
||||||
|
* Improved: Close dialog window automatically when choosing a specific download
|
||||||
|
* Improved: Move some repetitive log messages to debug level
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.4...v0.5.5).
|
||||||
|
|
||||||
|
## v0.5.4
|
||||||
|
Released 05 February 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: backported 'Scan Artist' feature from `sarakha63/headphones`
|
||||||
|
* Fixed: change file permissions of cache files according to settings (#2102)
|
||||||
|
* Fixed: hide Songkick if no regional events are available
|
||||||
|
* Fixed: only reschedule jobs if timeout changed (#2099)
|
||||||
|
* Fixed: limit dialog height (#2096)
|
||||||
|
* Improved: upgraded requests to 2.5.1
|
||||||
|
* Improved: upgraded unidecode to 0.04.17
|
||||||
|
* Improved: upgraded tzlocal to 1.1.2
|
||||||
|
* Improved: upgraded PyTZ to 2014.10
|
||||||
|
* Improved: upgraded mako to 1.0.1
|
||||||
|
* Improved: upgraded mutagen to 1.27
|
||||||
|
* Improved: upgraded beets to 1.3.10
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.3...v0.5.4).
|
||||||
|
|
||||||
|
## v0.5.3
|
||||||
|
Released 15 January 2015
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: update active artists via API (#2075)
|
||||||
|
* Fixed: queue instance lacks `add` method (#2055)
|
||||||
|
* Improved: better SSL error messages (#2058)
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.2...v0.5.3).
|
||||||
|
|
||||||
|
## v0.5.2
|
||||||
|
Released 28 December 2014
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: advanced option to ignore certain folders by patterns. (#2037)
|
||||||
|
* Added: advanced option to ignore certain files by patterns (library only)
|
||||||
|
* Added: specify optional paths to CUE splitting tools (#1938)
|
||||||
|
* Added: experimental support for OPB (provide details yourself!)
|
||||||
|
* Fixed: magnet to torrent conversion (#1926)
|
||||||
|
* Fixed: new KAT URL (#2043)
|
||||||
|
* Fixed: LMS notifications (#1564)
|
||||||
|
* Improved: notify user of SSL-related warnings, instead of silently failing.
|
||||||
|
* Improved: show all search results for 'Choose Specific Release'
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.1...v0.5.2).
|
||||||
|
|
||||||
|
## v0.5.1
|
||||||
|
Released 13 November 2014
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: allow one to disable interval tasks (#2002)
|
||||||
|
* Added: script to downgrade Headphones to last version that started (Linux only)
|
||||||
|
* Fixed: SSL issues in CherryPy. Self-generated certificates will be 2048 now (#1995)
|
||||||
|
* Fixed: Transmission URL detection (#1998)
|
||||||
|
* Fixed: missing dependencies for APScheduler and CherryPy (#2001)
|
||||||
|
* Improved: symlink infinite recursion detection
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5...v0.5.1).
|
||||||
|
|
||||||
|
## v0.5
|
||||||
|
Released 10 November 2014
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: CUE splitter
|
||||||
|
* Added: filter search result by MusicBrainz Release Group ID
|
||||||
|
* Added: follow symlinks while scanning library (#1953)
|
||||||
|
* Fixed: crash during post processing (#1897)
|
||||||
|
* Fixed: embedding lyrics (#1896)
|
||||||
|
* Fixed: HTTP errors with older versions of Python 2.6
|
||||||
|
* Fixed: jump back to top of page (#1948)
|
||||||
|
* Improved: parse MusicBrainz RGID first when post processing (#1952)
|
||||||
|
* Improved: Growl unicode characters (#1695)
|
||||||
|
* Improved: search handling for PB and KAT
|
||||||
|
* Improved: Last.FM API support (#1877)
|
||||||
|
* Improved: upgraded CherryPy to version 3.6.0
|
||||||
|
* Improved: upgraded Requests to version 2.4.1
|
||||||
|
* Improved: upgraded APScheduler to version 3.0.1
|
||||||
|
* Improved: lot of code refactoring
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.4...v0.5).
|
||||||
|
|
||||||
|
## v0.4
|
||||||
|
Released 20 September 2014
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
* Added: support for libav-tools (which replaces FFmpeg under Ubuntu)
|
||||||
|
* Added: option to freeze library when post processing
|
||||||
|
* Added: Songkick per area
|
||||||
|
* Added: rename original NFO file (#1797)
|
||||||
|
* Removed: removed dead search providers
|
||||||
|
* Fixed: removed left-overs of old packages
|
||||||
|
* Improved: rename by original folder (#1811)
|
||||||
|
* Improved: uTorrent fixess
|
||||||
|
* Improved: delayed loading of album art
|
||||||
|
* Improved: search result parser
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.3.4...v0.4).
|
||||||
|
|
||||||
|
## v0.3.4
|
||||||
|
Released 15 May 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.3.3...v0.3.4).
|
||||||
|
|
||||||
|
## v0.3.3
|
||||||
|
Released 05 May 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.3.2...v0.3.3).
|
||||||
|
|
||||||
|
## v0.3.2
|
||||||
|
Released 17 April 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.3.1...v0.3.2).
|
||||||
|
|
||||||
|
## v0.3.1
|
||||||
|
Released 11 April 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.3...v0.3.1).
|
||||||
|
|
||||||
|
## v0.3
|
||||||
|
Released 09 April 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.2.3...v0.3).
|
||||||
|
|
||||||
|
## v0.2.3
|
||||||
|
Released 28 March 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.2.2...v0.2.3).
|
||||||
|
|
||||||
|
## v0.2.2
|
||||||
|
Released 15 January 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.2.1...v0.2.2).
|
||||||
|
|
||||||
|
## v0.2.1
|
||||||
|
Released 13 January 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.2...v0.2.1).
|
||||||
|
|
||||||
|
## v0.2
|
||||||
|
Released 13 January 2014
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.1.2...v0.2).
|
||||||
|
|
||||||
|
## v0.1.2
|
||||||
|
Released 11 October 2013
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.1.1...v0.1.2).
|
||||||
|
|
||||||
|
## v0.1.1
|
||||||
|
Released 26 August 2013
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.1...v0.1.1).
|
||||||
|
|
||||||
|
## v0.1
|
||||||
|
Released 05 August 2013
|
||||||
|
|
||||||
|
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/2156e1341405d07c5bcfbe994f6b354b32d94cda...v0.1).
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Contributing to Headphones
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
In case you read this because you are posting an issue, please take a minute and conside the things below. The issue tracker is not a support forum. It is primarily intended to submit bugs, improvements or feature requests. However, we are glad to help you, and make sure the problem is not caused by Headphones, but don't expect step-by-step answers.
|
||||||
|
|
||||||
|
* Use the search function. Chances are that your problem is already discussed. Do not append to (closed) issues if your problem does not fit the discussion.
|
||||||
|
* Visit the [Troubleshooting](../../wiki/TroubleShooting) wiki first.
|
||||||
|
* Use [proper formatting](https://help.github.com/articles/github-flavored-markdown/). Paste your logs in code blocks.
|
||||||
|
* Close your issue if you resolved it.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
If you think you can contribute code to the Headphones repository, do not hesitate to submit a pull request.
|
||||||
|
|
||||||
|
### Branches
|
||||||
|
All pull requests should be based on the `develop` branch, to minimize cross merges. When you want to develop a new feature, clone the repository with `git clone origin/develop -b FEATURE_NAME`. Use meaningful commit messages.
|
||||||
|
|
||||||
|
### Python Code
|
||||||
|
|
||||||
|
#### Compatibility
|
||||||
|
The code should work with Python 2.6 and 2.7. Note that Headphones runs on different platforms, including Network Attached Storage devices such as Synology.
|
||||||
|
|
||||||
|
Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `headphones.logger.*` for this. Web requests are invoked via `headphones.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling.
|
||||||
|
|
||||||
|
#### Code conventions
|
||||||
|
Although Headphones did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
|
||||||
|
|
||||||
|
* 4 space indentation
|
||||||
|
* 80 characters per line
|
||||||
|
* `package_name`
|
||||||
|
* `ClassName`
|
||||||
|
* `method_name`
|
||||||
|
* `field_name`
|
||||||
|
* `_private_something`
|
||||||
|
* `self.__really_private_field`
|
||||||
|
* `_global`
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
Document your code. Use docstrings See [PEP-257](https://www.python.org/dev/peps/pep-0257/) for more information.
|
||||||
|
|
||||||
|
#### Continuous Integration
|
||||||
|
Headphones has a configuration file for [travis-ci](https://travis-ci.org/). You can add your forked repo to Travis to have it check your code against PEP8, PyLint, and PyFlakes for you. Your pull request will show a green check mark or a red cross on each tested commit, depending on if linting passes.
|
||||||
|
|
||||||
|
### HTML/Template code
|
||||||
|
|
||||||
|
#### Compatibility
|
||||||
|
HTML5 compatible browsers are targetted. There is no specific mobile version of Headphones yet.
|
||||||
|
|
||||||
|
#### Conventions
|
||||||
|
* 4 space indentation
|
||||||
|
* `methodName`
|
||||||
|
* `variableName`
|
||||||
|
* `ClassName`
|
||||||
@@ -14,31 +14,31 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
# Ensure lib added to path, before any other imports
|
# Ensure lib added to path, before any other imports
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
||||||
|
|
||||||
|
from headphones import webstart, logger
|
||||||
|
|
||||||
import locale
|
import locale
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
|
import argparse
|
||||||
from lib.configobj import ConfigObj
|
|
||||||
|
|
||||||
import headphones
|
import headphones
|
||||||
|
|
||||||
from headphones import webstart, logger
|
# Register signals, such as CTRL + C
|
||||||
|
|
||||||
try:
|
|
||||||
import argparse
|
|
||||||
except ImportError:
|
|
||||||
import lib.argparse as argparse
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, headphones.sig_handler)
|
signal.signal(signal.SIGINT, headphones.sig_handler)
|
||||||
signal.signal(signal.SIGTERM, headphones.sig_handler)
|
signal.signal(signal.SIGTERM, headphones.sig_handler)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""
|
||||||
|
Headphones application entry point. Parses arguments, setups encoding and
|
||||||
|
initializes the application.
|
||||||
|
"""
|
||||||
|
|
||||||
# Fixed paths to Headphones
|
# Fixed paths to Headphones
|
||||||
if hasattr(sys, 'frozen'):
|
if hasattr(sys, 'frozen'):
|
||||||
headphones.FULL_PATH = os.path.abspath(sys.executable)
|
headphones.FULL_PATH = os.path.abspath(sys.executable)
|
||||||
@@ -63,112 +63,146 @@ def main():
|
|||||||
headphones.SYS_ENCODING = 'UTF-8'
|
headphones.SYS_ENCODING = 'UTF-8'
|
||||||
|
|
||||||
# Set up and gather command line arguments
|
# Set up and gather command line arguments
|
||||||
parser = argparse.ArgumentParser(description='Music add-on for SABnzbd+')
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Music add-on for SABnzbd+, Transmission and more.')
|
||||||
|
|
||||||
parser.add_argument('-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
parser.add_argument(
|
||||||
parser.add_argument('-q', '--quiet', action='store_true', help='Turn off console logging')
|
'-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
||||||
parser.add_argument('-d', '--daemon', action='store_true', help='Run as a daemon')
|
parser.add_argument(
|
||||||
parser.add_argument('-p', '--port', type=int, help='Force Headphones to run on a specified port')
|
'-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||||
parser.add_argument('--datadir', help='Specify a directory where to store your data files')
|
parser.add_argument(
|
||||||
|
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||||
|
parser.add_argument(
|
||||||
|
'-p', '--port', type=int, help='Force Headphones to run on a specified port')
|
||||||
|
parser.add_argument(
|
||||||
|
'--datadir', help='Specify a directory where to store your data files')
|
||||||
parser.add_argument('--config', help='Specify a config file to use')
|
parser.add_argument('--config', help='Specify a config file to use')
|
||||||
parser.add_argument('--nolaunch', action='store_true', help='Prevent browser from launching on startup')
|
parser.add_argument('--nolaunch', action='store_true',
|
||||||
parser.add_argument('--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
help='Prevent browser from launching on startup')
|
||||||
|
parser.add_argument(
|
||||||
|
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
headphones.VERBOSE = 2
|
headphones.VERBOSE = True
|
||||||
elif args.quiet:
|
if args.quiet:
|
||||||
headphones.VERBOSE = 0
|
headphones.QUIET = True
|
||||||
|
|
||||||
|
# Do an intial setup of the logger.
|
||||||
|
logger.initLogger(console=not headphones.QUIET, log_dir=False,
|
||||||
|
verbose=headphones.VERBOSE)
|
||||||
|
|
||||||
if args.daemon:
|
if args.daemon:
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
print "Daemonize not supported under Windows, starting normally"
|
sys.stderr.write(
|
||||||
|
"Daemonizing not supported under Windows, starting normally\n")
|
||||||
else:
|
else:
|
||||||
headphones.DAEMON=True
|
headphones.DAEMON = True
|
||||||
headphones.VERBOSE = False
|
headphones.QUIET = True
|
||||||
|
|
||||||
if args.pidfile:
|
if args.pidfile:
|
||||||
headphones.PIDFILE = str(args.pidfile)
|
headphones.PIDFILE = str(args.pidfile)
|
||||||
|
|
||||||
# If the pidfile already exists, headphones may still be running, so exit
|
# If the pidfile already exists, headphones may still be running, so
|
||||||
|
# exit
|
||||||
if os.path.exists(headphones.PIDFILE):
|
if os.path.exists(headphones.PIDFILE):
|
||||||
sys.exit("PID file '" + headphones.PIDFILE + "' already exists. Exiting.")
|
raise SystemExit("PID file '%s' already exists. Exiting." %
|
||||||
|
headphones.PIDFILE)
|
||||||
|
|
||||||
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
# The pidfile is only useful in daemon mode, make sure we can write the
|
||||||
|
# file properly
|
||||||
if headphones.DAEMON:
|
if headphones.DAEMON:
|
||||||
headphones.CREATEPID = True
|
headphones.CREATEPID = True
|
||||||
try:
|
|
||||||
file(headphones.PIDFILE, 'w').write("pid\n")
|
|
||||||
except IOError, e:
|
|
||||||
raise SystemExit("Unable to write PID file: %s [%d]" % (e.strerror, e.errno))
|
|
||||||
else:
|
|
||||||
logger.warn("Not running in daemon mode. PID file creation disabled.")
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(headphones.PIDFILE, 'w') as fp:
|
||||||
|
fp.write("pid\n")
|
||||||
|
except IOError as e:
|
||||||
|
raise SystemExit("Unable to write PID file: %s", e)
|
||||||
|
else:
|
||||||
|
logger.warn("Not running in daemon mode. PID file creation " \
|
||||||
|
"disabled.")
|
||||||
|
|
||||||
|
# Determine which data directory and config file to use
|
||||||
if args.datadir:
|
if args.datadir:
|
||||||
headphones.DATA_DIR = args.datadir
|
headphones.DATA_DIR = args.datadir
|
||||||
else:
|
else:
|
||||||
headphones.DATA_DIR = headphones.PROG_DIR
|
headphones.DATA_DIR = headphones.PROG_DIR
|
||||||
|
|
||||||
if args.config:
|
if args.config:
|
||||||
headphones.CONFIG_FILE = args.config
|
config_file = args.config
|
||||||
else:
|
else:
|
||||||
headphones.CONFIG_FILE = os.path.join(headphones.DATA_DIR, 'config.ini')
|
config_file = os.path.join(headphones.DATA_DIR, 'config.ini')
|
||||||
|
|
||||||
# Try to create the DATA_DIR if it doesn't exist
|
# Try to create the DATA_DIR if it doesn't exist
|
||||||
if not os.path.exists(headphones.DATA_DIR):
|
if not os.path.exists(headphones.DATA_DIR):
|
||||||
try:
|
try:
|
||||||
os.makedirs(headphones.DATA_DIR)
|
os.makedirs(headphones.DATA_DIR)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise SystemExit('Could not create data directory: ' + headphones.DATA_DIR + '. Exiting....')
|
raise SystemExit(
|
||||||
|
'Could not create data directory: ' + headphones.DATA_DIR + '. Exiting....')
|
||||||
|
|
||||||
# Make sure the DATA_DIR is writeable
|
# Make sure the DATA_DIR is writeable
|
||||||
if not os.access(headphones.DATA_DIR, os.W_OK):
|
if not os.access(headphones.DATA_DIR, os.W_OK):
|
||||||
raise SystemExit('Cannot write to the data directory: ' + headphones.DATA_DIR + '. Exiting...')
|
raise SystemExit(
|
||||||
|
'Cannot write to the data directory: ' + headphones.DATA_DIR + '. Exiting...')
|
||||||
|
|
||||||
# Put the database in the DATA_DIR
|
# Put the database in the DATA_DIR
|
||||||
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
|
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
|
||||||
|
|
||||||
headphones.CFG = ConfigObj(headphones.CONFIG_FILE, encoding='utf-8')
|
# Read config and start logging
|
||||||
|
try:
|
||||||
# Read config & start logging
|
headphones.initialize(config_file)
|
||||||
headphones.initialize()
|
except headphones.exceptions.SoftChrootError as e:
|
||||||
|
raise SystemExit('FATAL ERROR')
|
||||||
|
|
||||||
if headphones.DAEMON:
|
if headphones.DAEMON:
|
||||||
if sys.platform == "win32":
|
headphones.daemonize()
|
||||||
print "Daemonize not supported under Windows, starting normally"
|
|
||||||
else:
|
|
||||||
headphones.daemonize()
|
|
||||||
|
|
||||||
#configure the connection to the musicbrainz database
|
# Configure the connection to the musicbrainz database
|
||||||
headphones.mb.startmb()
|
headphones.mb.startmb()
|
||||||
|
|
||||||
# Force the http port if neccessary
|
# Force the http port if neccessary
|
||||||
if args.port:
|
if args.port:
|
||||||
http_port = args.port
|
http_port = args.port
|
||||||
logger.info('Using forced port: %i', http_port)
|
logger.info('Using forced web server port: %i', http_port)
|
||||||
else:
|
else:
|
||||||
http_port = int(headphones.HTTP_PORT)
|
http_port = int(headphones.CONFIG.HTTP_PORT)
|
||||||
|
|
||||||
# Try to start the server.
|
# Check if pyOpenSSL is installed. It is required for certificate generation
|
||||||
webstart.initialize({
|
# and for CherryPy.
|
||||||
'http_port': http_port,
|
if headphones.CONFIG.ENABLE_HTTPS:
|
||||||
'http_host': headphones.HTTP_HOST,
|
try:
|
||||||
'http_root': headphones.HTTP_ROOT,
|
import OpenSSL
|
||||||
'http_proxy': headphones.HTTP_PROXY,
|
except ImportError:
|
||||||
'enable_https': headphones.ENABLE_HTTPS,
|
logger.warn("The pyOpenSSL module is missing. Install this " \
|
||||||
'https_cert': headphones.HTTPS_CERT,
|
"module to enable HTTPS. HTTPS will be disabled.")
|
||||||
'https_key': headphones.HTTPS_KEY,
|
headphones.CONFIG.ENABLE_HTTPS = False
|
||||||
'http_username': headphones.HTTP_USERNAME,
|
|
||||||
'http_password': headphones.HTTP_PASSWORD,
|
|
||||||
})
|
|
||||||
|
|
||||||
if headphones.LAUNCH_BROWSER and not args.nolaunch:
|
# Try to start the server. Will exit here is address is already in use.
|
||||||
headphones.launch_browser(headphones.HTTP_HOST, http_port, headphones.HTTP_ROOT)
|
web_config = {
|
||||||
|
'http_port': http_port,
|
||||||
|
'http_host': headphones.CONFIG.HTTP_HOST,
|
||||||
|
'http_root': headphones.CONFIG.HTTP_ROOT,
|
||||||
|
'http_proxy': headphones.CONFIG.HTTP_PROXY,
|
||||||
|
'enable_https': headphones.CONFIG.ENABLE_HTTPS,
|
||||||
|
'https_cert': headphones.CONFIG.HTTPS_CERT,
|
||||||
|
'https_key': headphones.CONFIG.HTTPS_KEY,
|
||||||
|
'http_username': headphones.CONFIG.HTTP_USERNAME,
|
||||||
|
'http_password': headphones.CONFIG.HTTP_PASSWORD,
|
||||||
|
}
|
||||||
|
webstart.initialize(web_config)
|
||||||
|
|
||||||
# Start the background threads
|
# Start the background threads
|
||||||
headphones.start()
|
headphones.start()
|
||||||
|
|
||||||
|
# Open webbrowser
|
||||||
|
if headphones.CONFIG.LAUNCH_BROWSER and not args.nolaunch:
|
||||||
|
headphones.launch_browser(headphones.CONFIG.HTTP_HOST, http_port,
|
||||||
|
headphones.CONFIG.HTTP_ROOT)
|
||||||
|
|
||||||
|
# Wait endlessy for a signal to happen
|
||||||
while True:
|
while True:
|
||||||
if not headphones.SIGNAL:
|
if not headphones.SIGNAL:
|
||||||
try:
|
try:
|
||||||
@@ -177,6 +211,7 @@ def main():
|
|||||||
headphones.SIGNAL = 'shutdown'
|
headphones.SIGNAL = 'shutdown'
|
||||||
else:
|
else:
|
||||||
logger.info('Received signal: %s', headphones.SIGNAL)
|
logger.info('Received signal: %s', headphones.SIGNAL)
|
||||||
|
|
||||||
if headphones.SIGNAL == 'shutdown':
|
if headphones.SIGNAL == 'shutdown':
|
||||||
headphones.shutdown()
|
headphones.shutdown()
|
||||||
elif headphones.SIGNAL == 'restart':
|
elif headphones.SIGNAL == 'restart':
|
||||||
@@ -186,7 +221,6 @@ def main():
|
|||||||
|
|
||||||
headphones.SIGNAL = None
|
headphones.SIGNAL = None
|
||||||
|
|
||||||
return
|
# Call main()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,44 +1,67 @@
|
|||||||
#Headphones
|
## Headphones
|
||||||
|
|
||||||
###Installation and Notes
|
**Master Branch:** [](https://travis-ci.org/rembo10/headphones)
|
||||||
|
**Develop Branch:** [](https://travis-ci.org/rembo10/headphones)
|
||||||
This is a relatively early release of a third-party add-on for SABnzbd. It's been around for about a year, and while
|
|
||||||
quite functional, there are still a lot of kinks to work out!
|
Headphones is an automated music downloader for NZB and Torrent, written in Python. It supports SABnzbd, NZBget, Transmission, µTorrent, Deluge and Blackhole.
|
||||||
|
|
||||||
To run it, just double-click the Headphones.py file (in Windows - you may need to right click and click 'Open With' -> Python) or launch a terminal, cd into the Headphones directory and run 'python headphones.py'.
|
## Support & Discuss
|
||||||
|
You are free to join the Headphones support community on IRC where you can ask questions, hang around and discuss anything related to HP.
|
||||||
For additional startup options, type 'python Headphones.py -h' or 'python Headphones.py --help'
|
|
||||||
|
1. Use any IRC client and connect to the Freenode server, `irc.freenode.net`.
|
||||||
###Screenshots
|
2. Join the `#headphones` channel.
|
||||||
|
|
||||||
Homepage (Artist Overview)
|
## Installation and Notes
|
||||||
|
|
||||||

|
* [Installation page](../../wiki/Installation) shows you how to install Headphones.
|
||||||
|
* [Usage guide](../../wiki/Usage-guide) introduces you to Headphones.
|
||||||
One of the many settings pages....
|
* [Troubleshooting page](../../wiki/TroubleShooting) in the wiki can help you with common problems.
|
||||||
|
|
||||||

|
**Issues** can be reported on the GitHub issue tracker considering these rules:
|
||||||
|
|
||||||
It might even know you better than you know yourself:
|
1. Analyze your log, you just might find the solution yourself!
|
||||||
|
2. You read the wiki and searched existing issues, but this is not solving your problem.
|
||||||

|
3. Post the issue with a clear title, description and the HP log and use [proper markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your text (code/log in code blocks).
|
||||||
|
4. Close your issue when it's solved! If you found the solution yourself, please comment so that others benefit from it.
|
||||||
Import Your Favorite Artists:
|
|
||||||
|
**Feature requests** can be reported on the GitHub issue tracker too:
|
||||||

|
|
||||||
|
1. Search for similar existing 'issues', feature requests can be recognized by the label 'Request'.
|
||||||
Artist Search Results (also search by album!):
|
2. If a similar Request exists, post a comment (+1, or add a new idea to the existing request), otherwise you can create a new one.
|
||||||
|
|
||||||

|
If you **comply with these rules** you can [post your request/issue](http://github.com/rembo10/headphones/issues).
|
||||||
|
|
||||||
Artist Page with Bio & Album Overview:
|
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
||||||
|
|
||||||

|
## Screenshots
|
||||||
|
|
||||||
Album Page with track overview:
|
Homepage (Artist Overview):
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you run into any issues, use the GitHub issue tracker at: http://github.com/rembo10/headphones/issues.
|
One of the many settings pages:
|
||||||
|
|
||||||
This is free software under the GPL v3 open source license - so feel free to do with it what you wish.
|

|
||||||
|
|
||||||
|
It might even know you better than you know yourself:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Import Your Favorite Artists:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Search Results:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Artist Page with Bio & Album Overview:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Album Page with track overview:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## License
|
||||||
|
This is free software under the GPL v3 open source license. Feel free to do with it what you wish, but any modification must be open sourced. A copy of the license is included.
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Display information
|
||||||
|
echo "This script will remove *.pyc files. These files are generated by Python, but they can cause conflicts after an upgrade. It's safe to remove them, because they will be regenerated."
|
||||||
|
echo "Press enter to continue, or CTRL + C to quit."
|
||||||
|
read
|
||||||
|
|
||||||
|
# Remove the *.pyc
|
||||||
|
find "`dirname $0`/.." -type f -name "*.pyc" -exec rm -rf {} \;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Parameter check
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Syntax: $0 <data directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Version file check
|
||||||
|
if [ ! -s "$1/version.lock" ]; then
|
||||||
|
echo "Missing the version.lock file in the data folder, or the file is empty. Did you start Headphones at least once?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Git installation check
|
||||||
|
if [ ! -x "$(command -v git)" ]; then
|
||||||
|
echo "Git is required to downgrade."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display information
|
||||||
|
HASH=$(cat $1/version.lock)
|
||||||
|
|
||||||
|
echo "This script will try to downgrade Headphones to the last version that started, version $HASH. Make sure you have a backup of your config file and database, just in case!"
|
||||||
|
echo "Press enter to continue, or CTRL + C to quit."
|
||||||
|
read
|
||||||
|
|
||||||
|
# Downgrade
|
||||||
|
cd "`dirname $0`/.."
|
||||||
|
git reset --hard "$HASH"
|
||||||
|
|
||||||
|
echo "All done, Headphones should be downgraded to the last version that started."
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Ensure that we use the Headphones provided libraries.
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib"))
|
||||||
|
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
def can_import(module):
|
||||||
|
"""
|
||||||
|
Return True if a given module can be imported or not.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
__import__(module)
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Module can be imported
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_installation():
|
||||||
|
"""
|
||||||
|
Check if some core modules are available. Info is based on this topic:
|
||||||
|
https://github.com/rembo10/headphones/issues/2210.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if can_import("requests"):
|
||||||
|
import requests
|
||||||
|
requests_version = requests.__version__
|
||||||
|
else:
|
||||||
|
requests_version = "no"
|
||||||
|
|
||||||
|
if can_import("OpenSSL"):
|
||||||
|
import OpenSSL
|
||||||
|
openssl_version = OpenSSL.__version__
|
||||||
|
else:
|
||||||
|
openssl_version = "no"
|
||||||
|
|
||||||
|
if can_import("cryptography"):
|
||||||
|
import cryptography
|
||||||
|
cryptography_version = cryptography.__version__
|
||||||
|
else:
|
||||||
|
cryptography_version = "no"
|
||||||
|
|
||||||
|
if can_import("pyasn1"):
|
||||||
|
import pyasn1
|
||||||
|
pyasn1_version = pyasn1.__version__
|
||||||
|
else:
|
||||||
|
pyasn1_version = "no"
|
||||||
|
|
||||||
|
if can_import("ndg.httpsclient"):
|
||||||
|
from ndg import httpsclient
|
||||||
|
ndg_version = httpsclient.__date__
|
||||||
|
else:
|
||||||
|
ndg_version = "no"
|
||||||
|
|
||||||
|
# Print some system information.
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking Python version: %s.%s.%s\n" % sys.version_info[:3])
|
||||||
|
sys.stdout.write("* Operating system: %s\n" % sys.platform)
|
||||||
|
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking if requests can be imported: %s\n" % requests_version)
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking if pyOpenSSL is installed: %s\n" % openssl_version)
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking if cryptography is installed: %s\n" % cryptography_version)
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking if pyasn1 is installed: %s\n" % pyasn1_version)
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Checking if ndg.httpsclient is installed: %s\n" % ndg_version)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Test if the current Headphones installation can connect to SNI-enabled
|
||||||
|
servers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Read the URL to test.
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
url = "https://sni.velox.ch/"
|
||||||
|
else:
|
||||||
|
url = sys.argv[1]
|
||||||
|
|
||||||
|
# Check if it is a HTTPS website.
|
||||||
|
parts = urlparse.urlparse(url)
|
||||||
|
|
||||||
|
if parts.scheme.lower() != "https":
|
||||||
|
sys.stderr.write(
|
||||||
|
"Error: provided URL does not start with https://\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Gather information
|
||||||
|
check_installation()
|
||||||
|
|
||||||
|
# Do the request.
|
||||||
|
if not can_import("requests"):
|
||||||
|
sys.stderr.exit("Error: cannot continue without requests module!\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
sys.stdout.write("* Performing request: %s\n" % url)
|
||||||
|
|
||||||
|
import requests
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
except requests.exceptions.SSLError as e:
|
||||||
|
sys.stdout.write(
|
||||||
|
"- Server certificate seems invalid. I will disable "
|
||||||
|
"certificate check and try again. You'll see the real "
|
||||||
|
"exception if it fails again.\n")
|
||||||
|
sys.stdout.write(
|
||||||
|
"* Retrying request with certificate verification off.\n")
|
||||||
|
response = requests.get(url)
|
||||||
|
except Exception as e:
|
||||||
|
sys.stdout.write(
|
||||||
|
"- An error occured while performing the request. The "
|
||||||
|
"exception was: %s\n" % e.message)
|
||||||
|
sys.stdout.write(
|
||||||
|
"- Consult the Troubleshooting wiki (https://github.com/"
|
||||||
|
"rembo10/headphones/wiki/Troubleshooting) before you post an "
|
||||||
|
"issue!")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Verify the response.
|
||||||
|
if response.status_code == 200:
|
||||||
|
sys.stdout.write("+ Got a valid response. All seems OK!\n")
|
||||||
|
else:
|
||||||
|
sys.stdout.write(
|
||||||
|
"- Server returned status code %s. Expected a status code 200.\n",
|
||||||
|
response.status_code)
|
||||||
|
sys.stdout.write(
|
||||||
|
"- However, I was able to communicate to the server!\n")
|
||||||
|
|
||||||
|
# E.g. `python sni_test.py https://example.org'.
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
.dataTables_wrapper {
|
|
||||||
border-radius: 20px;
|
|
||||||
-webkit-border-radius: 20px;
|
|
||||||
-moz-border-radius: 20px;
|
|
||||||
width: 88%;
|
|
||||||
margin: 20px auto 0 auto;
|
|
||||||
padding: 25px;
|
|
||||||
background-color: white;
|
|
||||||
position: relative;
|
|
||||||
min-height: 155px;
|
|
||||||
clear: both;
|
|
||||||
_height: 302px;
|
|
||||||
zoom: 1; /* Feeling sorry for IE */
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_processing {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
width: 20px;
|
|
||||||
height: 30px;
|
|
||||||
margin-left: -125px;
|
|
||||||
margin-top: -15px;
|
|
||||||
padding: 14px 0 2px 0;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
text-align: center;
|
|
||||||
color: #999;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_length {
|
|
||||||
width: 40%;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_filter {
|
|
||||||
width: 50%;
|
|
||||||
float: right;
|
|
||||||
text-align: right;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_info {
|
|
||||||
width: 50%;
|
|
||||||
float: left;
|
|
||||||
background-color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_paginate {
|
|
||||||
width: 44px;
|
|
||||||
* width: 50px;
|
|
||||||
float: right;
|
|
||||||
text-align: right;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pagination nested */
|
|
||||||
.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
|
|
||||||
height: 19px;
|
|
||||||
width: 19px;
|
|
||||||
margin-left: 3px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paginate_disabled_previous {
|
|
||||||
background-image: url('../images/back_disabled.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.paginate_enabled_previous {
|
|
||||||
background-image: url('../images/back_enabled.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.paginate_disabled_next {
|
|
||||||
background-image: url('../images/forward_disabled.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.paginate_enabled_next {
|
|
||||||
background-image: url('../images/forward_enabled.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
* DataTables display
|
|
||||||
*/
|
|
||||||
table.display {
|
|
||||||
margin: 20px auto;
|
|
||||||
clear: both;
|
|
||||||
|
|
||||||
/* Note Firefox 3.5 and before have a bug with border-collapse
|
|
||||||
* ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 )
|
|
||||||
* border-spacing: 0; is one possible option. Conditional-css.com is
|
|
||||||
* useful for this kind of thing
|
|
||||||
*
|
|
||||||
* Further note IE 6/7 has problems when calculating widths with border width.
|
|
||||||
* It subtracts one px relative to the other browsers from the first column, and
|
|
||||||
* adds one to the end...
|
|
||||||
*
|
|
||||||
* If you want that effect I'd suggest setting a border-top/left on th/td's and
|
|
||||||
* then filling in the gaps with other borders.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display thead th {
|
|
||||||
padding: 3px 18px 3px 10px;
|
|
||||||
background-color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
* cursor: hand;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tfoot th {
|
|
||||||
padding: 3px 18px 3px 10px;
|
|
||||||
border-top: 1px solid black;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.heading2 td {
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display td {
|
|
||||||
padding: 3px 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display td.center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
* DataTables sorting
|
|
||||||
*/
|
|
||||||
|
|
||||||
.sorting_asc {
|
|
||||||
background: url('../images/sort_asc.png') no-repeat center right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sorting_desc {
|
|
||||||
background: url('../images/sort_desc.png') no-repeat center right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sorting {
|
|
||||||
background: url('../images/sort_both.png') no-repeat center right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sorting_asc_disabled {
|
|
||||||
background: url('../images/sort_asc_disabled.png') no-repeat center right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sorting_desc_disabled {
|
|
||||||
background: url('../images/sort_desc_disabled.png') no-repeat center right;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
* DataTables row classes
|
|
||||||
*/
|
|
||||||
table.display tr.odd.gradeA {
|
|
||||||
background-color: #ddffdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.gradeA {
|
|
||||||
background-color: #ddffdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.odd.gradeC {
|
|
||||||
background-color: #ddddff;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.gradeC {
|
|
||||||
background-color: #ddddff;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.odd.gradeX {
|
|
||||||
background-color: #ffdddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.gradeX {
|
|
||||||
background-color: #ffdddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.odd.gradeU {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.gradeU {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
table.display tr.odd.gradeZ {
|
|
||||||
background-color: #FCFAFA;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.gradeZ {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
* Misc
|
|
||||||
*/
|
|
||||||
.dataTables_scroll {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_scrollBody {
|
|
||||||
*margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top, .bottom {
|
|
||||||
padding: 15px;
|
|
||||||
background-color: #F5F5F5;
|
|
||||||
border: 1px solid #CCCCCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top .dataTables_info {
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTables_empty {
|
|
||||||
font-size: 24px;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
background-color: white;
|
|
||||||
height: 50px;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
tfoot input {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
width: 100%;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
tfoot input.search_init {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.group {
|
|
||||||
background-color: #d1cfd0;
|
|
||||||
border-bottom: 2px solid #A19B9E;
|
|
||||||
border-top: 2px solid #A19B9E;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.details {
|
|
||||||
background-color: #d1cfd0;
|
|
||||||
border: 2px solid #A19B9E;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paging_full_numbers {
|
|
||||||
width: 400px;
|
|
||||||
height: 22px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paging_full_numbers span.paginate_button,
|
|
||||||
.paging_full_numbers span.paginate_active {
|
|
||||||
border: 1px solid #aaa;
|
|
||||||
-webkit-border-radius: 5px;
|
|
||||||
-moz-border-radius: 5px;
|
|
||||||
padding: 2px 5px;
|
|
||||||
margin: 0 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
*cursor: hand;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paging_full_numbers span.paginate_button {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paging_full_numbers span.paginate_button:hover {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paging_full_numbers span.paginate_active {
|
|
||||||
background-color: #99B3FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.even.row_selected td {
|
|
||||||
background-color: #B0BED9;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.display tr.odd.row_selected td {
|
|
||||||
background-color: #9FAFD1;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.box {
|
|
||||||
height: 100px;
|
|
||||||
padding: 10px;
|
|
||||||
overflow: auto;
|
|
||||||
border: 1px solid #8080FF;
|
|
||||||
background-color: #E5E5FF;
|
|
||||||
}
|
|
||||||
@@ -1,315 +0,0 @@
|
|||||||
/* HTML5 ✰ Boilerplate */
|
|
||||||
|
|
||||||
html, body, div, span, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
|
|
||||||
small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote, q { quotes: none; }
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after { content: ''; content: none; }
|
|
||||||
ins { background-color: #ff9; color: #000; text-decoration: none; }
|
|
||||||
mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
|
|
||||||
del { text-decoration: line-through; }
|
|
||||||
abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
|
|
||||||
table { border-collapse: collapse; border-spacing: 0; }
|
|
||||||
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
|
|
||||||
input, select { vertical-align: middle; }
|
|
||||||
|
|
||||||
body { font:13px/1.231 sans-serif; *font-size:small; }
|
|
||||||
select, input, textarea, button { font:99% sans-serif; }
|
|
||||||
pre, code, kbd, samp { font-family: monospace, sans-serif; }
|
|
||||||
|
|
||||||
html { overflow-y: scroll; }
|
|
||||||
a:hover, a:active { outline: none; }
|
|
||||||
ul, ol { margin-left: 2em; }
|
|
||||||
ol { list-style-type: decimal; }
|
|
||||||
nav ul, nav li { margin: 0; list-style:none; list-style-image: none; }
|
|
||||||
small { font-size: 85%; }
|
|
||||||
strong, th { font-weight: bold; }
|
|
||||||
td { vertical-align: top; }
|
|
||||||
|
|
||||||
sub, sup { font-size: 75%; line-height: 0; position: relative; }
|
|
||||||
sup { top: -0.5em; }
|
|
||||||
sub { bottom: -0.25em; }
|
|
||||||
|
|
||||||
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; }
|
|
||||||
textarea { overflow: auto; }
|
|
||||||
.ie6 legend, .ie7 legend { margin-left: -7px; }
|
|
||||||
input[type="radio"] { vertical-align: text-bottom; }
|
|
||||||
input[type="checkbox"] { vertical-align: bottom; }
|
|
||||||
.ie7 input[type="checkbox"] { vertical-align: baseline; }
|
|
||||||
.ie6 input { vertical-align: text-bottom; }
|
|
||||||
label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; }
|
|
||||||
button, input, select, textarea { margin: 0; }
|
|
||||||
input:valid, textarea:valid { }
|
|
||||||
input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
|
|
||||||
.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
|
|
||||||
|
|
||||||
::-moz-selection{ background: grey; color:#fff; text-shadow: none; }
|
|
||||||
::selection { background: grey; color:#fff; text-shadow: none; }
|
|
||||||
|
|
||||||
button { width: auto; overflow: visible; }
|
|
||||||
.ie7 img { -ms-interpolation-mode: bicubic; }
|
|
||||||
|
|
||||||
body, select, input, textarea { color: #444; }
|
|
||||||
h1, h2, h3, h4, h5, h6 { font-weight: bold; }
|
|
||||||
|
|
||||||
/*
|
|
||||||
// ========================================== \\
|
|
||||||
|| ||
|
|
||||||
|| Custom Styles ||
|
|
||||||
|| ||
|
|
||||||
\\ ========================================== //
|
|
||||||
*/
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #5E2612;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:visited {
|
|
||||||
color: #5E2612;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:hover { /*this effect is not shown in NN4.xx*/
|
|
||||||
color: #999999;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
a:active {/*colour in NN4.xx is red*/
|
|
||||||
color: #5E2612;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
a.blue {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
container { }
|
|
||||||
|
|
||||||
body { background-color: #EBF4FB; min-width: 935px; }
|
|
||||||
|
|
||||||
header { min-height: 68px; width: 100%; min-width: 935px; padding-left: 0px; padding-right: 10px; background-color: #CDC9C9; position: fixed; z-index: 998; }
|
|
||||||
|
|
||||||
h1 { font-size: 24px; }
|
|
||||||
h2 { font-size: 20px; }
|
|
||||||
h3 { font-size: 16px; }
|
|
||||||
h4 { font-size: 14px; }
|
|
||||||
|
|
||||||
p.indented { padding-top: 20px; margin-left: 20px; font-size: 14px; }
|
|
||||||
p.center { text-align: center; font-size: 18px; }
|
|
||||||
.smalltext2 { font-size: 12px; margin-left: 45px; }
|
|
||||||
|
|
||||||
div#updatebar { text-align: center; min-width: 970px; width: 100%; background-color: light-blue; float: left; }
|
|
||||||
div#logo { float: left; padding-left: 10px; }
|
|
||||||
|
|
||||||
ul#nav { margin: 25px 0 0 0; float: left; list-style-type: none; }
|
|
||||||
ul#nav li { margin: 40px 0px auto 10px; display: inline; }
|
|
||||||
ul#nav li a { padding: 5px; font-size: 16px; font-weight: bold; color: #330000; text-decoration: none; }
|
|
||||||
ul#nav li a:hover { background-color: #a3e532; }
|
|
||||||
|
|
||||||
div#subhead_container { height: 30px; width:100%; min-width: 1000px; background-color:#330000; float: left; list-style-type: none; z-index: 998; overflow: hidden; }
|
|
||||||
ul#subhead_menu { margin-top: 5px; }
|
|
||||||
ul#subhead_menu li { width: 100%; height: 100%; display: inline; }
|
|
||||||
ul#subhead_menu li a { padding: 5px 15px 10px 15px; vertical-align: middle; color: white; font-size: 16px; text-decoration: none; }
|
|
||||||
ul#subhead_menu li a:hover { width: 100%; height: 100%; background-color: #grey; }
|
|
||||||
|
|
||||||
div#searchbar { margin: 24px 30px auto auto; float: right; }
|
|
||||||
|
|
||||||
div#main { margin: 0; padding: 80px 0 0 0; }
|
|
||||||
|
|
||||||
.table_wrapper { border-radius: 20px; -webkit-border-radius: 20px; -moz-border-radius: 20px; width: 88%; margin: 20px auto 0 auto; padding: 25px; background-color: white; position: relative; min-height: 200px; clear: both; _height: 302px; zoom: 1; }
|
|
||||||
.manage_wrapper { width: 88%; margin: 20px auto 0 auto; padding: 25px; min-height: 150px; clear: both; _height: 302px; zoom: 1; }
|
|
||||||
.table_wrapper_left { padding: 25px; background-color: #ffffff; float: left; width: 40%; min-height: 100px; margin-top: 25px; margin-left: 30px; margin-right: auto; -moz-border-radius: 20px; border-radius: 20px; }
|
|
||||||
.table_wrapper_right{ padding: 25px; background-color: #ffffff; width: 40%; min-height: 100px; margin-top: 25px; margin-left: auto; margin-right: 30px; -moz-border-radius: 20px; border-radius: 20px; }
|
|
||||||
|
|
||||||
.configtable { font-size: 14px; line-height:18px; }
|
|
||||||
.configtable td { width: 350px; padding: 10px; }
|
|
||||||
.configtable td#middle { vertical-align: middle; }
|
|
||||||
|
|
||||||
table#artist_table { background-color: white; width: 100%; padding: 20px; }
|
|
||||||
|
|
||||||
table#artist_table th#select { text-align: left; }
|
|
||||||
table#artist_table th#name { text-align: left; min-width: 200px; }
|
|
||||||
table#artist_table th#status { text-align: left; min-width: 50px; }
|
|
||||||
table#artist_table th#album { text-align: left; min-width: 300px; }
|
|
||||||
table#artist_table th#have { text-align: center; }
|
|
||||||
table#artist_table td#name { vertical-align: middle; text-align: left; min-width:200px; }
|
|
||||||
table#artist_table td#status { vertical-align: middle; text-align: left; min-width: 50px; }
|
|
||||||
table#artist_table td#album { vertical-align: middle; text-align: left; min-width: 300px; }
|
|
||||||
table#artist_table td#have { vertical-align: middle; }
|
|
||||||
|
|
||||||
div#paddingheader { padding-top: 48px; font-size: 24px; font-weight: bold; text-align: center; }
|
|
||||||
div#nopaddingheader { font-size: 24px; font-weight: bold; text-align: center; }
|
|
||||||
table#album_table { background-color: white; }
|
|
||||||
|
|
||||||
table#album_table th#select { vertical-align: middle; text-align: left; min-width: 10px; }
|
|
||||||
table#album_table th#albumart { text-align: left; min-width: 50px; }
|
|
||||||
table#album_table th#albumname { text-align: center; min-width: 150px; }
|
|
||||||
table#album_table th#reldate { width: 175px; text-align: center; min-width: 70px; }
|
|
||||||
table#album_table th#status { width: 175px; text-align: center; min-width: 80px; }
|
|
||||||
table#album_table th#type { width: 175px; text-align: center; min-width: 100px; }
|
|
||||||
table#album_table th#bitrate { text-align: center; min-width: 60px; }
|
|
||||||
table#album_table th#albumformat { text-align: center; min-width: 60px; }
|
|
||||||
table#album_table th#wantlossless { width: 80px; text-align: center; min-width: 80px; }
|
|
||||||
table#album_table td#select { vertical-align: middle; text-align: left; }
|
|
||||||
table#album_table td#albumart { vertical-align: middle; text-align: left; }
|
|
||||||
table#album_table td#albumname { vertical-align: middle; text-align: center; }
|
|
||||||
table#album_table td#reldate { vertical-align: middle; text-align: center; }
|
|
||||||
table#album_table td#status { vertical-align: middle; text-align: center; font-size: 13px; }
|
|
||||||
table#album_table td#status a#wantlossless { white-space: nowrap; }
|
|
||||||
table#album_table td#type { vertical-align: middle; text-align: center; }
|
|
||||||
table#album_table td#have { vertical-align: middle; }
|
|
||||||
table#album_table td#bitrate { vertical-align: middle; text-align: center; font-size: 13px; }
|
|
||||||
table#album_table td#albumformat { vertical-align: middle; text-align: center; font-size: 13px; }
|
|
||||||
table#album_table td#wantlossless { vertical-align: middle; text-align: center; font-size: 13px; }
|
|
||||||
|
|
||||||
img.albumArt { float: left; padding-right: 5px; }
|
|
||||||
div#albumheader { padding-top: 48px; height: 200px; }
|
|
||||||
div#track_wrapper { margin-left: -50px; padding-top: 20px; font-size: 16px; width: 100%; }
|
|
||||||
|
|
||||||
table#track_table th#number { text-align: right; min-width: 10px; }
|
|
||||||
table#track_table th#name { text-align: center; min-width: 350px; }
|
|
||||||
table#track_table th#duration { width: 175px; text-align: center; min-width: 100px; }
|
|
||||||
table#track_table th#location { text-align: center; width: 250px; }
|
|
||||||
table#track_table th#bitrate { text-align: center; min-width: 75px; }
|
|
||||||
table#track_table th#format { text-align: center; min-width: 75px; }
|
|
||||||
|
|
||||||
table#track_table td#number { vertical-align: middle; text-align: right; }
|
|
||||||
table#track_table td#name { vertical-align: middle; text-align: center; font-size: 15px; }
|
|
||||||
table#track_table td#duration { vertical-align: middle; text-align: center; }
|
|
||||||
table#track_table td#location { vertical-align: middle; text-align: center; font-size: 11px; }
|
|
||||||
table#track_table td#bitrate { vertical-align: middle; text-align: center; font-size: 12px; }
|
|
||||||
table#track_table td#format { vertical-align: middle; text-align: center; font-size: 12px; }
|
|
||||||
|
|
||||||
table#history_table { background-color: white; width: 100%; font-size: 13px; }
|
|
||||||
|
|
||||||
table#history_table td#dateadded { vertical-align: middle; text-align: center; min-width: 150px; font-size: 14px; }
|
|
||||||
table#history_table td#filename { vertical-align: middle; text-align: center; min-width: 100px; font-size: 15px; }
|
|
||||||
table#history_table td#size { vertical-align: middle; text-align: center; min-width: 75px; font-size: 14px; }
|
|
||||||
table#history_table td#status { vertical-align: middle; text-align: center; font-size: 14px; }
|
|
||||||
table#history_table td#action { vertical-align: middle; text-align: center; font-size: 14px; }
|
|
||||||
|
|
||||||
table#log_table { background-color: white; }
|
|
||||||
|
|
||||||
table#log_table th#timestamp { text-align: left; min-width: 150px; }
|
|
||||||
table#log_table th#level { text-align: left; min-width: 60px; }
|
|
||||||
table#log_table th#message { text-align: left; min-width: 500px; }
|
|
||||||
|
|
||||||
table#upcoming_table th#albumart { text-align: center; min-width: 50px; }
|
|
||||||
table#upcoming_table th#albumname { text-align: center; min-width: 200px; }
|
|
||||||
table#upcoming_table th#artistname { text-align: center; min-width: 150px; }
|
|
||||||
table#upcoming_table th#reldate { text-align: center; min-width: 100px; }
|
|
||||||
table#upcoming_table th#type { text-align: center; min-width: 75px; }
|
|
||||||
|
|
||||||
table#upcoming_table td#select { vertical-align: middle; text-align: center; }
|
|
||||||
table#upcoming_table td#albumart { vertical-align: middle; text-align: center; min-width: 50px; }
|
|
||||||
table#upcoming_table td#albumname { vertical-align: middle; text-align: center; min-width: 200px; }
|
|
||||||
table#upcoming_table td#artistname { vertical-align: middle; text-align: center; min-width: 150px; }
|
|
||||||
table#upcoming_table td#reldate { vertical-align: middle; text-align: center; min-width: 100px; }
|
|
||||||
table#upcoming_table td#type { vertical-align: middle; text-align: center; min-width: 75px; }
|
|
||||||
table#upcoming_table td#status { vertical-align: middle; text-align: center; }
|
|
||||||
|
|
||||||
table#wanted_table th#albumart { text-align: center; min-width: 50px; }
|
|
||||||
table#wanted_table th#albumname { text-align: center; min-width: 200px; }
|
|
||||||
table#wanted_table th#artistname { text-align: center; min-width: 150px; }
|
|
||||||
table#wanted_table th#reldate { text-align: center; min-width: 100px; }
|
|
||||||
table#wanted_table th#type { text-align: center; min-width: 75px; }
|
|
||||||
|
|
||||||
table#wanted_table td#select { vertical-align: middle; text-align: center; }
|
|
||||||
table#wanted_table td#albumart { vertical-align: middle; text-align: center; min-width: 50px; }
|
|
||||||
table#wanted_table td#albumname { vertical-align: middle; text-align: center; min-width: 200px; }
|
|
||||||
table#wanted_table td#artistname { vertical-align: middle; text-align: center; min-width: 150px; }
|
|
||||||
table#wanted_table td#reldate { vertical-align: middle; text-align: center; min-width: 100px; }
|
|
||||||
table#wanted_table td#type { vertical-align: middle; text-align: center; min-width: 75px; }
|
|
||||||
table#wanted_table td#status { vertical-align: middle; text-align: center; }
|
|
||||||
|
|
||||||
table#searchresults_table th#albumname { text-align: left; min-width: 225px; }
|
|
||||||
table#searchresults_table th#artistname { text-align: center; min-width: 325px; }
|
|
||||||
table#searchresults_table th#score { text-align: center; min-width: 75px; }
|
|
||||||
|
|
||||||
table#searchresults_table td#albumname { vertical-align: middle; text-align: left; min-width: 200px; }
|
|
||||||
table#searchresults_table td#artistname { vertical-align: middle; text-align: left; min-width: 300px; }
|
|
||||||
table#searchresults_table td#score { vertical-align: middle; text-align: center; min-width: 75px; }
|
|
||||||
|
|
||||||
div.progress-container { border: 1px solid #ccc; width: 100px; height: 14px; margin: 2px 5px 2px 0; padding: 1px; float: left; background: white; }
|
|
||||||
div.progress-container > div { background-color: #a3e532; height: 14px; }
|
|
||||||
.havetracks { font-size: 13px; margin-left: 36px; padding-bottom: 3px; vertical-align: middle; }
|
|
||||||
|
|
||||||
footer { margin: 20px auto 20px auto; }
|
|
||||||
div#version { text-align: center; font-weight: bold; }
|
|
||||||
div#donate { text-align: center; margin: 20px auto 20px auto; }
|
|
||||||
|
|
||||||
div#shutdown{ text-align: center; vertical-align: middle; }
|
|
||||||
|
|
||||||
.cloudtag { padding-top: 30px; font-size:16px; }
|
|
||||||
#cloud a.tag1 { font-size: 0.7em; font-weight: 100; }
|
|
||||||
#cloud a.tag2 { font-size: 0.8em; font-weight: 200; }
|
|
||||||
#cloud a.tag3 { font-size: 0.9em; font-weight: 300; }
|
|
||||||
#cloud a.tag4 { font-size: 1.0em; font-weight: 400; }
|
|
||||||
#cloud a.tag5 { font-size: 1.2em; font-weight: 500; }
|
|
||||||
#cloud a.tag6 { font-size: 1.4em; font-weight: 600; }
|
|
||||||
#cloud a.tag7 { font-size: 1.6em; font-weight: 700; }
|
|
||||||
#cloud a.tag8 { font-size: 1.8em; font-weight: 800; }
|
|
||||||
#cloud a.tag9 { font-size: 2.2em; font-weight: 900; }
|
|
||||||
#cloud a.tag10 { font-size: 2.5em; font-weight: 900; }
|
|
||||||
|
|
||||||
#cloud { padding: 2px; line-height: 1.5em; text-align: center; }
|
|
||||||
#cloud a { padding: 0px; }
|
|
||||||
#cloud { margin: 0; }
|
|
||||||
#cloud li { display: inline; }
|
|
||||||
|
|
||||||
.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
|
|
||||||
.hidden { display: none; visibility: hidden; }
|
|
||||||
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
|
||||||
.visuallyhidden.focusable:active,
|
|
||||||
.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
|
|
||||||
.invisible { visibility: hidden; }
|
|
||||||
.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
|
|
||||||
.clearfix:after { clear: both; }
|
|
||||||
.clearfix { zoom: 1; }
|
|
||||||
|
|
||||||
|
|
||||||
@media all and (orientation:portrait) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (orientation:landscape) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-device-width: 480px) {
|
|
||||||
|
|
||||||
html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
* { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important;
|
|
||||||
-ms-filter: none !important; }
|
|
||||||
a, a:visited { color: #444 !important; text-decoration: underline; }
|
|
||||||
a[href]:after { content: " (" attr(href) ")"; }
|
|
||||||
abbr[title]:after { content: " (" attr(title) ")"; }
|
|
||||||
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
|
|
||||||
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
|
|
||||||
thead { display: table-header-group; }
|
|
||||||
tr, img { page-break-inside: avoid; }
|
|
||||||
@page { margin: 0.5cm; }
|
|
||||||
p, h2, h3 { orphans: 3; widows: 3; }
|
|
||||||
h2, h3{ page-break-after: avoid; }
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 253 B |
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -13,16 +13,16 @@
|
|||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
|
|
||||||
<a id="menu_link_delete" href="deleteAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}"><i class="fa fa-trash-o"></i> Delete Album</a>
|
<a id="menu_link_delete" href="deleteAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}"><i class="fa fa-trash-o"></i> Delete Album</a>
|
||||||
%if album['Status'] == 'Skipped':
|
%if album['Status'] == 'Skipped' or album['Status'] == 'Ignored':
|
||||||
<a id="menu_link_wanted" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=False', $(this),true)" data-success="'${album['AlbumTitle']}' added to queue"><i class="fa fa-heart"></i> Mark Album as Wanted</a>
|
<a id="menu_link_wanted" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=False', $(this),true)" data-success="'${album['AlbumTitle']}' added to queue"><i class="fa fa-heart"></i> Mark Album as Wanted</a>
|
||||||
%elif album['Status'] == 'Wanted':
|
%elif album['Status'] == 'Wanted':
|
||||||
<a id="menu_link_check" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this));" data-success="Forced checking successful"><i class="fa fa-search"></i> Force Check</a>
|
<a id="menu_link_check" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this));" data-success="Forced checking successful"><i class="fa fa-search"></i> Force Check</a>
|
||||||
<a id="menu_link_skipped" href="#" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),true);" data-success="'${album['AlbumTitle']}' marked as Skipped"><i class="fa fa-step-forward"></i> Mark Album as Skipped</a>
|
<a id="menu_link_skipped" href="javascript:void(0)" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),true);" data-success="'${album['AlbumTitle']}' marked as Skipped"><i class="fa fa-step-forward"></i> Mark Album as Skipped</a>
|
||||||
%else:
|
%else:
|
||||||
<a id="menu_link_retry" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=False', $(this),true);" data-success="Retrying the same version of '${album['AlbumTitle']}'"><i class="fa fa-refresh"></i> Retry Download</a>
|
<a id="menu_link_retry" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=False', $(this),true);" data-success="Retrying the same version of '${album['AlbumTitle']}'"><i class="fa fa-refresh"></i> Retry Download</a>
|
||||||
<a id="menu_link_new" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this),true);" data-success="Looking for a new version of '${album['AlbumTitle']}'"><i class="fa fa-download"></i> Try New Version</a>
|
<a id="menu_link_new" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this),true);" data-success="Looking for a new version of '${album['AlbumTitle']}'"><i class="fa fa-download"></i> Try New Version</a>
|
||||||
%endif
|
%endif
|
||||||
<a class="menu_link_edit" id="album_chooser" href="#"><i class="fa fa-pencil"></i> Choose Alternate Release</a>
|
<a class="menu_link_edit" id="album_chooser" href="javascript:void(0)"><i class="fa fa-pencil"></i> Choose Alternate Release</a>
|
||||||
<div id="dialog" title="Choose an Alternate Release" style="display:none" class="configtable">
|
<div id="dialog" title="Choose an Alternate Release" style="display:none" class="configtable">
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<%
|
<%
|
||||||
@@ -30,11 +30,12 @@
|
|||||||
%>
|
%>
|
||||||
%if not alternate_albums:
|
%if not alternate_albums:
|
||||||
<p>No alternate releases found. Try refreshing the artist (if the artist is being refreshed, please wait until it's finished)</p>
|
<p>No alternate releases found. Try refreshing the artist (if the artist is being refreshed, please wait until it's finished)</p>
|
||||||
<h2><a id="refresh_artist" onclick="doAjaxCall('refreshArtist?ArtistID=${album['ArtistID']}', $(this)), true" href="#" data-success="'${album['ArtistName']}' is being refreshed">Refresh Artist</a></h2>
|
<h2><a id="refresh_artist" onclick="doAjaxCall('refreshArtist?ArtistID=${album['ArtistID']}', $(this)), true" href="javascript:void(0)" data-success="'${album['ArtistName']}' is being refreshed">Refresh Artist</a></h2>
|
||||||
%else:
|
%else:
|
||||||
%for alternate_album in alternate_albums:
|
%for alternate_album in alternate_albums:
|
||||||
<%
|
<%
|
||||||
track_count = len(myDB.select("SELECT * from alltracks WHERE ReleaseID=?", [alternate_album['ReleaseID']]))
|
track_count = len(myDB.select("SELECT * from alltracks WHERE ReleaseID=?", [alternate_album['ReleaseID']]))
|
||||||
|
mb_link = "http://musicbrainz.org/release/" + alternate_album['ReleaseID']
|
||||||
have_track_count = len(myDB.select("SELECT * from alltracks WHERE ReleaseID=? AND Location IS NOT NULL", [alternate_album['ReleaseID']]))
|
have_track_count = len(myDB.select("SELECT * from alltracks WHERE ReleaseID=? AND Location IS NOT NULL", [alternate_album['ReleaseID']]))
|
||||||
if alternate_album['AlbumID'] == alternate_album['ReleaseID']:
|
if alternate_album['AlbumID'] == alternate_album['ReleaseID']:
|
||||||
alternate_album_name = "Headphones Default Release (" + str(alternate_album['ReleaseDate']) + ") [" + str(have_track_count) + "/" + str(track_count) + " tracks]"
|
alternate_album_name = "Headphones Default Release (" + str(alternate_album['ReleaseDate']) + ") [" + str(have_track_count) + "/" + str(track_count) + " tracks]"
|
||||||
@@ -42,56 +43,62 @@
|
|||||||
alternate_album_name = alternate_album['AlbumTitle'] + " (" + alternate_album['ReleaseCountry'] + ", " + str(alternate_album['ReleaseDate']) + ", " + alternate_album['ReleaseFormat'] + ") [" + str(have_track_count) + "/" + str(track_count) + " tracks]"
|
alternate_album_name = alternate_album['AlbumTitle'] + " (" + alternate_album['ReleaseCountry'] + ", " + str(alternate_album['ReleaseDate']) + ", " + alternate_album['ReleaseFormat'] + ") [" + str(have_track_count) + "/" + str(track_count) + " tracks]"
|
||||||
|
|
||||||
%>
|
%>
|
||||||
<a href="#" onclick="doAjaxCall('switchAlbum?AlbumID=${album['AlbumID']}&ReleaseID=${alternate_album['ReleaseID']}', $(this), 'table');" data-success="Switched release to: ${alternate_album_name}">${alternate_album_name}</a><br>
|
<a href="javascript:void(0)" onclick="doAjaxCall('switchAlbum?AlbumID=${album['AlbumID']}&ReleaseID=${alternate_album['ReleaseID']}', $(this), 'table');" data-success="Switched release to: ${alternate_album_name}">${alternate_album_name}</a><a href="${mb_link}" target="_blank">MB</a><br>
|
||||||
%endfor
|
%endfor
|
||||||
%endif
|
%endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="menu_link_edit" id="edit_search_term" href="#"><i class="fa fa-pencil"></i> Edit Search Term</a>
|
<a class="menu_link_edit" id="edit_search_term" href="javascript:void(0)"><i class="fa fa-pencil"></i> Edit Search Term</a>
|
||||||
<div id="dialog2" title="Enter your own search term for this album" style="display:none" class="configtable">
|
<div id="dialog2" title="Enter your own search term for this album" style="display:none" class="configtable">
|
||||||
<form action="editSearchTerm" method="GET" id="editSearchTerm">
|
<form action="editSearchTerm" method="GET" id="editSearchTerm">
|
||||||
<input type="hidden" name="AlbumID" value="${album['AlbumID']}">
|
<input type="hidden" name="AlbumID" value="${album['AlbumID']}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<%
|
<%
|
||||||
if not album['SearchTerm']:
|
if not album['SearchTerm']:
|
||||||
search_term = ""
|
search_term = ""
|
||||||
else:
|
else:
|
||||||
search_term = album['SearchTerm']
|
search_term = album['SearchTerm']
|
||||||
|
%>
|
||||||
%>
|
<input type="text" value="${search_term}" name="SearchTerm" size="40" />
|
||||||
<input type="text" value="${search_term}" name="SearchTerm" size="40" />
|
</div>
|
||||||
</div>
|
<input type="button" value="Save changes" onclick="doAjaxCall('editSearchTerm',$(this),'tabs',true);return false;" data-success="Search term updated"/>
|
||||||
<input type="button" value="Save changes" onclick="doAjaxCall('editSearchTerm',$(this),'tabs',true);return false;" data-success="Search term updated"/>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
<a class="menu_link_edit" id="choose_specific_download" href="javascript:void(0)" onclick="getAvailableDownloads()"><i class="fa fa-search"></i> Choose Specific Download</a>
|
||||||
<a class="menu_link_edit" id="choose_specific_download" href="#" onclick="getAvailableDownloads()"><i class="fa fa-search"></i> Choose Specific Download</a>
|
<div id="choose_specific_download_dialog" title="Choose a specific download for this album" style="display:none" class="configtable">
|
||||||
<div id="choose_specific_download_dialog" title="Choose a specific download for this album" style="display:none" class="configtable">
|
<table class="display" id="downloads_table">
|
||||||
<table class="display" id="downloads_table">
|
<thead>
|
||||||
<thead>
|
<tr>
|
||||||
<tr>
|
<th id="title">Title</th>
|
||||||
<th id="title">Title</th>
|
<th id="size">Size</th>
|
||||||
<th id="size">Size</th>
|
<th id="provider">Provider</th>
|
||||||
<th id="provider">Provider</th>
|
<th id="kind">Kind</th>
|
||||||
<th id="kind">Kind</th>
|
<th id="matches" title="Matches quality and/or seed settings.">Matches</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="downloads_table_body">
|
<tbody id="downloads_table_body">
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
<div class="table_wrapper">
|
<div class="table_wrapper">
|
||||||
<div id="albumheader" class="clearfix">
|
<div id="albumheader" class="clearfix">
|
||||||
<div id="albumImg">
|
<div id="albumImg">
|
||||||
<img height="200" alt="" class="albumArt" src="artwork/album/${album['AlbumID']}">
|
<img height="200" alt="" class="albumArt" src="artwork/album/${album['AlbumID']}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1><a href="http://musicbrainz.org/release-group/${album['AlbumID']}">${album['AlbumTitle']}</a></h1>
|
<h1 id="albumname">
|
||||||
<h2><a href="http://musicbrainz.org/artist/${album['ArtistID']}">${album['ArtistName']}</a></h2>
|
<a href="http://musicbrainz.org/release-group/${album['AlbumID']}" id="albumnamelink">${album['AlbumTitle']}</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h2 id="artistname">
|
||||||
|
<a href="http://musicbrainz.org/artist/${album['ArtistID']}" id="artistnamelink">${album['ArtistName']}</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
totalduration = myDB.action("SELECT SUM(TrackDuration) FROM tracks WHERE AlbumID=?", [album['AlbumID']]).fetchone()[0]
|
totalduration = myDB.action("SELECT SUM(TrackDuration) FROM tracks WHERE AlbumID=?", [album['AlbumID']]).fetchone()[0]
|
||||||
totaltracks = len(myDB.select("SELECT TrackTitle from tracks WHERE AlbumID=?", [album['AlbumID']]))
|
totaltracks = len(myDB.select("SELECT TrackTitle from tracks WHERE AlbumID=?", [album['AlbumID']]))
|
||||||
@@ -191,14 +198,20 @@
|
|||||||
var elem = $("#albumInfo");
|
var elem = $("#albumInfo");
|
||||||
getInfo(elem,id,'album');
|
getInfo(elem,id,'album');
|
||||||
}
|
}
|
||||||
|
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
$('#album_chooser').click(function() {
|
$('#album_chooser').click(function() {
|
||||||
$('#dialog').dialog({ width: "500px" });
|
$('#dialog').dialog({
|
||||||
|
width: 500,
|
||||||
|
maxHeight: 500
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
$('#edit_search_term').click(function() {
|
$('#edit_search_term').click(function() {
|
||||||
$('#dialog2').dialog({ width: "500px" });
|
$('#dialog2').dialog({
|
||||||
|
width: 500,
|
||||||
|
maxHeight: 500
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
$('#refresh_artist').click(function() {
|
$('#refresh_artist').click(function() {
|
||||||
@@ -207,8 +220,8 @@
|
|||||||
initActions();
|
initActions();
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
initFancybox();
|
initFancybox();
|
||||||
},1000);
|
}, 1000);
|
||||||
|
|
||||||
$('#track_table').dataTable({
|
$('#track_table').dataTable({
|
||||||
"aaSorting": [],
|
"aaSorting": [],
|
||||||
"bFilter": false,
|
"bFilter": false,
|
||||||
@@ -225,21 +238,26 @@
|
|||||||
feedback.fadeOut();
|
feedback.fadeOut();
|
||||||
search_results = data
|
search_results = data
|
||||||
for( var i = 0, len = data.length; i < len; i++ ) {
|
for( var i = 0, len = data.length; i < len; i++ ) {
|
||||||
$('#downloads_table_body').append('<tr><td id="title"><a href="#" onclick="downloadSpecificRelease('+i+')">'+data[i].title+'</a></td><td id="size"><span title='+data[i].size+'></span>'+(data[i].size / (1024*1024)).toFixed(2)+' MB</td><td id="provider">'+data[i].provider+'</td><td id="kind">'+data[i].kind+'</td></tr>');
|
$('#downloads_table_body').append('<tr><td id="title"><a href="javascript:void(0)" onclick="downloadSpecificRelease('+i+')">'+data[i].title+'</a></td><td id="size"><span title='+data[i].size+'></span>'+(data[i].size / (1024*1024)).toFixed(2)+' MB</td><td id="provider">'+data[i].provider+'</td><td id="kind">'+data[i].kind+'</td><td id="matches">'+data[i].matches+'</td></tr>');
|
||||||
}
|
}
|
||||||
$('#downloads_table').dataTable({
|
$('#downloads_table').dataTable({
|
||||||
"aoColumns": [
|
"aoColumns": [
|
||||||
null,
|
null,
|
||||||
{ "sType": "title-numeric"},
|
{"sType": "title-numeric"},
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
|
{"sType": "string"}
|
||||||
],
|
],
|
||||||
"aaSorting": [],
|
"aaSorting": [[ 4, 'desc']],
|
||||||
"bFilter": false,
|
"bFilter": false,
|
||||||
"bInfo": false,
|
"bInfo": false,
|
||||||
"bPaginate": false,
|
"bPaginate": false,
|
||||||
|
"bDestroy": true
|
||||||
});
|
});
|
||||||
$("#choose_specific_download_dialog").dialog({ width: "1000px" });
|
$("#choose_specific_download_dialog").dialog({
|
||||||
|
width: "80%",
|
||||||
|
maxHeight: 500
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -275,9 +293,73 @@
|
|||||||
feedback.fadeIn();
|
feedback.fadeIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var loadingMessage = false;
|
||||||
|
var spinner_active = false;
|
||||||
|
var loadingtext_active = false;
|
||||||
|
var refreshInterval;
|
||||||
|
var wasLoading = false;
|
||||||
|
var x = 0;
|
||||||
|
|
||||||
|
function checkAlbumStatus() {
|
||||||
|
$.getJSON("getAlbumjson?AlbumID=${album['AlbumID']}", function(data) {
|
||||||
|
if (data['Status'] == "Loading"){
|
||||||
|
wasLoading = true;
|
||||||
|
$('#albumnamelink').text(data["AlbumTitle"]);
|
||||||
|
$('#artistnamelink').text(data["ArtistName"]);
|
||||||
|
if (loadingMessage == false){
|
||||||
|
$("#ajaxMsg").after( "<div id='ajaxMsg2' class='ajaxMsg'></div>" );
|
||||||
|
showArtistMsg("Getting album information");
|
||||||
|
loadingMessage = true;
|
||||||
|
}
|
||||||
|
if (spinner_active == false){
|
||||||
|
$('#albumname').prepend('<i class="fa fa-refresh fa-spin" id="albumnamespinner"></i>')
|
||||||
|
spinner_active = true;
|
||||||
|
}
|
||||||
|
if (loadingtext_active == false){
|
||||||
|
$('#albumname').append('<h3 id="loadingtext"><i>(Album information is currently being loaded)</i></h3>')
|
||||||
|
loadingtext_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (++x === 10) {
|
||||||
|
clearInterval(refreshInterval);
|
||||||
|
}
|
||||||
|
var sts = $("#artistname").text();
|
||||||
|
if (wasLoading == true || sts == "Loading"){
|
||||||
|
location.reload();
|
||||||
|
$('#albumnamespinner').remove()
|
||||||
|
$('#loadingtext').remove()
|
||||||
|
$('#ajaxMsg2').remove()
|
||||||
|
spinner_active = false
|
||||||
|
loadingtext_active = false
|
||||||
|
loadingMessage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
|
||||||
|
"title-numeric-pre": function ( a ) {
|
||||||
|
var x = a.match(/title="*(-?[0-9\.]+)/)[1];
|
||||||
|
return parseFloat( x );
|
||||||
|
},
|
||||||
|
|
||||||
|
"title-numeric-asc": function ( a, b ) {
|
||||||
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
"title-numeric-desc": function ( a, b ) {
|
||||||
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
getAlbumInfo();
|
getAlbumInfo();
|
||||||
initThisPage();
|
initThisPage();
|
||||||
|
checkAlbumStatus();
|
||||||
|
refreshInterval = setInterval(function(){
|
||||||
|
checkAlbumStatus();
|
||||||
|
}, 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,30 +8,31 @@
|
|||||||
<%def name="headerIncludes()">
|
<%def name="headerIncludes()">
|
||||||
<div id="subhead_container">
|
<div id="subhead_container">
|
||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
<a id="menu_link_refresh" onclick="doAjaxCall('refreshArtist?ArtistID=${artist['ArtistID']}', $(this)),'table'" href="#" data-success="'${artist['ArtistName']}' is being refreshed"><i class="fa fa-refresh"></i> Refresh Artist</a>
|
<a id="menu_link_refresh" onclick="doSimpleAjaxCall('refreshArtist?ArtistID=${artist['ArtistID']}')" href="javascript:void(0)"><i class="fa fa-refresh"></i> Refresh Artist</a>
|
||||||
<a id="menu_link_delete" href="deleteArtist?ArtistID=${artist['ArtistID']}"><i class="fa fa-trash-o"></i> Delete Artist</a>
|
<a id="menu_link_delete" href="deleteArtist?ArtistID=${artist['ArtistID']}"><i class="fa fa-trash-o"></i> Delete Artist</a>
|
||||||
|
<a id="menu_link_scan" onclick="doAjaxCall('scanArtist?ArtistID=${artist['ArtistID']}', $(this)),'table'" href="javascript:void(0)" data-success="'${artist['ArtistName']}' was scanned"><i class="fa fa-refresh"></i> Scan Artist</a>
|
||||||
%if artist['Status'] == 'Paused':
|
%if artist['Status'] == 'Paused':
|
||||||
<a id="menu_link_resume" href="#" onclick="doAjaxCall('resumeArtist?ArtistID=${artist['ArtistID']}',$(this),true)" data-success="${artist['ArtistName']} resumed"><i class="fa fa-play"></i> Resume Artist</a>
|
<a id="menu_link_resume" href="javascript:void(0)" onclick="doAjaxCall('resumeArtist?ArtistID=${artist['ArtistID']}',$(this),true)" data-success="${artist['ArtistName']} resumed"><i class="fa fa-play"></i> Resume Artist</a>
|
||||||
%else:
|
%else:
|
||||||
<a id="menu_link_pauze" href="#" onclick="doAjaxCall('pauseArtist?ArtistID=${artist['ArtistID']}',$(this),true)" data-success="${artist['ArtistName']} paused"><i class="fa fa-pause"></i> Pause Artist</a>
|
<a id="menu_link_pauze" href="javascript:void(0)" onclick="doAjaxCall('pauseArtist?ArtistID=${artist['ArtistID']}',$(this),true)" data-success="${artist['ArtistName']} paused"><i class="fa fa-pause"></i> Pause Artist</a>
|
||||||
%endif
|
%endif
|
||||||
%if artist['IncludeExtras']:
|
%if artist['IncludeExtras']:
|
||||||
<a id="menu_link_removeextra" href="#" onclick="doAjaxCall('removeExtras?ArtistID=${artist['ArtistID']}',$(this),true)" data-success="Extras removed for ${artist['ArtistName']}"><i class="fa fa-minus"></i> Remove Extras</a>
|
<a id="menu_link_removeextra" href="javascript:void(0)" onclick="doAjaxCall('removeExtras?ArtistID=${artist['ArtistID']}&ArtistName=${artist['ArtistName']}',$(this),'submenu&table')" data-success="Extras removed for ${artist['ArtistName']}"><i class="fa fa-minus"></i> Remove Extras</a>
|
||||||
<a class="menu_link_edit" id="menu_link_modifyextra" href="#"><i class="fa fa-pencil"></i> Modify Extras</a>
|
<a class="menu_link_edit" id="menu_link_modifyextra" href="javascript:void(0)"><i class="fa fa-pencil"></i> Modify Extras</a>
|
||||||
%else:
|
%else:
|
||||||
<a id="menu_link_getextra" href="#"><i class="fa fa-plus"></i> Get Extras</a>
|
<a id="menu_link_getextra" href="javascript:void(0)"><i class="fa fa-plus"></i> Get Extras</a>
|
||||||
%endif
|
%endif
|
||||||
<div id="dialog" title="Choose Which Extras to Fetch" style="display:none" class="configtable">
|
<div id="dialog" title="Choose Which Extras to Fetch" style="display:none" class="configtable">
|
||||||
<form action="getExtras" method="get" class="form">
|
<form action="getExtras" method="get" class="form">
|
||||||
<input type="hidden" name="ArtistID" value="${artist['ArtistID']}">
|
<input type="hidden" name="ArtistID" value="${artist['ArtistID']}">
|
||||||
<input type="hidden" name="newstyle" value="true">
|
<input type="hidden" name="newstyle" value="true">
|
||||||
%for extra in extras:
|
%for extra in extras:
|
||||||
<input type="checkbox" id="${extra}" name="${extra}" value="1" ${extras[extra]} />${string.capwords(extra)}<br>
|
<input type="checkbox" id="${extra}" name="${extra}" value="1" ${extras[extra]} />${string.capwords(extra)}<br>
|
||||||
%endfor
|
%endfor
|
||||||
<br>
|
<br>
|
||||||
<input id="submit" type="submit" value="Fetch Extras">
|
<input id="submit" type="submit" value="Fetch Extras">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="home" class="back">« Back to overview</a>
|
<a href="home" class="back">« Back to overview</a>
|
||||||
@@ -42,14 +43,8 @@
|
|||||||
<div id="artistImg">
|
<div id="artistImg">
|
||||||
<img id="artistImage" class="albumArt" alt="" src="artwork/artist/${artist['ArtistID']}"/>
|
<img id="artistImage" class="albumArt" alt="" src="artwork/artist/${artist['ArtistID']}"/>
|
||||||
</div>
|
</div>
|
||||||
<h1>
|
<h1 id="artistname">
|
||||||
%if artist['Status'] == 'Loading':
|
<a href="http://musicbrainz.org/artist/${artist['ArtistID']}" id="artistnamelink">${artist['ArtistName']}</a>
|
||||||
<i class="fa fa-refresh fa-spin"></i>
|
|
||||||
%endif
|
|
||||||
<a href="http://musicbrainz.org/artist/${artist['ArtistID']}">${artist['ArtistName']}</a>
|
|
||||||
%if artist['Status'] == 'Loading':
|
|
||||||
<h3><i>(Album information for this artist is currently being loaded)</i></h3>
|
|
||||||
%endif
|
|
||||||
</h1>
|
</h1>
|
||||||
<div id="artistBio"></div>
|
<div id="artistBio"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +57,7 @@
|
|||||||
<option value="Wanted">Wanted</option>
|
<option value="Wanted">Wanted</option>
|
||||||
<option value="WantedNew">Wanted (new only)</option>
|
<option value="WantedNew">Wanted (new only)</option>
|
||||||
<option value="Skipped">Skipped</option>
|
<option value="Skipped">Skipped</option>
|
||||||
|
<option value="Ignored">Ignored</option>
|
||||||
<option value="Downloaded">Downloaded</option>
|
<option value="Downloaded">Downloaded</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" value="Go">
|
<input type="hidden" value="Go">
|
||||||
@@ -74,6 +70,7 @@
|
|||||||
<th id="albumname">Name</th>
|
<th id="albumname">Name</th>
|
||||||
<th id="reldate">Date</th>
|
<th id="reldate">Date</th>
|
||||||
<th id="type">Type</th>
|
<th id="type">Type</th>
|
||||||
|
<th id="score">Metacritic</th>
|
||||||
<th id="status">Status</th>
|
<th id="status">Status</th>
|
||||||
<th id="have">Have</th>
|
<th id="have">Have</th>
|
||||||
<th id="bitrate">Bitrate</th>
|
<th id="bitrate">Bitrate</th>
|
||||||
@@ -89,6 +86,8 @@
|
|||||||
grade = 'X'
|
grade = 'X'
|
||||||
elif album['Status'] == 'Snatched':
|
elif album['Status'] == 'Snatched':
|
||||||
grade = 'C'
|
grade = 'C'
|
||||||
|
elif album['Status'] == 'Ignored':
|
||||||
|
grade = 'I'
|
||||||
else:
|
else:
|
||||||
grade = 'A'
|
grade = 'A'
|
||||||
|
|
||||||
@@ -123,22 +122,23 @@
|
|||||||
%>
|
%>
|
||||||
<tr class="grade${grade}">
|
<tr class="grade${grade}">
|
||||||
<td id="select"><input type="checkbox" name="${album['AlbumID']}" class="checkbox" /></td>
|
<td id="select"><input type="checkbox" name="${album['AlbumID']}" class="checkbox" /></td>
|
||||||
<td id="albumart"><img class="albumArt" id="${album['AlbumID']}" src="artwork/thumbs/album/${album['AlbumID']}" height="64" width="64"></td>
|
<td id="albumart"><img class="albumArtnostretch" id="${album['AlbumID']}" src="artwork/thumbs/album/${album['AlbumID']}" height="64" width="64"></td>
|
||||||
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
||||||
<td id="reldate">${album['ReleaseDate']}</td>
|
<td id="reldate">${album['ReleaseDate']}</td>
|
||||||
<td id="type">${album['Type']}</td>
|
<td id="type">${album['Type']}</td>
|
||||||
|
<td id="score">${album['CriticScore']}/${album['UserScore']}</td>
|
||||||
<td id="status">${album['Status']}
|
<td id="status">${album['Status']}
|
||||||
%if album['Status'] == 'Skipped':
|
%if album['Status'] == 'Skipped' or album['Status'] == 'Ignored':
|
||||||
[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' added to Wanted list">want</a>]
|
[<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' added to Wanted list">want</a>]
|
||||||
%elif (album['Status'] == 'Wanted' or album['Status'] == 'Wanted Lossless'):
|
%elif (album['Status'] == 'Wanted' or album['Status'] == 'Wanted Lossless'):
|
||||||
[<a href="#" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' skipped">skip</a>]
|
[<a href="javascript:void(0)" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' skipped">skip</a>] [<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),'table')" data-success="Trying to download'${album['AlbumTitle']}'" title="Search if available for download">search</a>]
|
||||||
%else:
|
%else:
|
||||||
[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),'table')" data-success="Retrying the same version of '${album['AlbumTitle']}'" title="Retry the same download again">retry</a>][<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this),'table')" title="Try a new download, skipping all previously tried nzbs" data-success="Downloading new version for '${album['AlbumTitle']}'" data-success="Looking for a new version of '${album['AlbumTitle']}'">new</a>]
|
[<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),'table')" data-success="Retrying the same version of '${album['AlbumTitle']}'" title="Retry the same download again">retry</a>][<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this),'table')" title="Try a new download, skipping all previously tried nzbs" data-success="Downloading new version for '${album['AlbumTitle']}'" data-success="Looking for a new version of '${album['AlbumTitle']}'">new</a>]
|
||||||
%endif
|
%endif
|
||||||
%if albumformat in lossy_formats and album['Status'] == 'Skipped':
|
%if albumformat in lossy_formats and album['Status'] == 'Skipped':
|
||||||
[<a id="wantlossless" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&lossless=True', $(this),'table')" data-success="Lossless version of '${album['AlbumTitle']}' added to queue">want lossless</a>]
|
[<a id="wantlossless" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&lossless=True', $(this),'table')" data-success="Lossless version of '${album['AlbumTitle']}' added to queue">want lossless</a>]
|
||||||
%elif albumformat in lossy_formats and (album['Status'] == 'Snatched' or album['Status'] == 'Downloaded'):
|
%elif albumformat in lossy_formats and (album['Status'] == 'Snatched' or album['Status'] == 'Downloaded'):
|
||||||
[<a id="wantlossless" href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&lossless=True', $(this),'table')" data-success="Retrying the same lossless version of '${album['AlbumTitle']}'">retry lossless</a>]
|
[<a id="wantlossless" href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&lossless=True', $(this),'table')" data-success="Retrying the same lossless version of '${album['AlbumTitle']}'">retry lossless</a>]
|
||||||
%endif
|
%endif
|
||||||
</td>
|
</td>
|
||||||
<td id="have"><span title="${percent}"><span><div class="progress-container"><div style="width:${percent}%"><div class="havetracks">${havetracks}/${totaltracks}</div></div></div></td>
|
<td id="have"><span title="${percent}"><span><div class="progress-container"><div style="width:${percent}%"><div class="havetracks">${havetracks}/${totaltracks}</div></div></div></td>
|
||||||
@@ -153,9 +153,6 @@
|
|||||||
|
|
||||||
<%def name="headIncludes()">
|
<%def name="headIncludes()">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/data_table.css">
|
<link rel="stylesheet" href="interfaces/default/css/data_table.css">
|
||||||
%if artist['Status'] == 'Loading':
|
|
||||||
<meta http-equiv="refresh" content="5">
|
|
||||||
%endif
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
@@ -168,23 +165,22 @@
|
|||||||
getInfo(elem,id,'artist');
|
getInfo(elem,id,'artist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<%
|
<%
|
||||||
if headphones.SONGKICK_FILTER_ENABLED:
|
if headphones.CONFIG.SONGKICK_FILTER_ENABLED:
|
||||||
songkick_filter_enabled = "true"
|
songkick_filter_enabled = "true"
|
||||||
else:
|
else:
|
||||||
songkick_filter_enabled = "false"
|
songkick_filter_enabled = "false"
|
||||||
|
|
||||||
if not headphones.SONGKICK_LOCATION:
|
if not headphones.CONFIG.SONGKICK_LOCATION:
|
||||||
songkick_location = "none"
|
songkick_location = "none"
|
||||||
else:
|
else:
|
||||||
songkick_location = headphones.SONGKICK_LOCATION
|
songkick_location = headphones.CONFIG.SONGKICK_LOCATION
|
||||||
|
|
||||||
if headphones.SONGKICK_ENABLED:
|
if headphones.CONFIG.SONGKICK_ENABLED:
|
||||||
songkick_enabled = "true"
|
songkick_enabled = "true"
|
||||||
else:
|
else:
|
||||||
songkick_enabled = "false"
|
songkick_enabled = "false"
|
||||||
|
|
||||||
%>
|
%>
|
||||||
function getArtistsCalendar() {
|
function getArtistsCalendar() {
|
||||||
var template, calendarDomNode;
|
var template, calendarDomNode;
|
||||||
@@ -193,62 +189,93 @@
|
|||||||
|
|
||||||
template = '<li><a target="_blank" href="URI"><span class="sk-name">NAME</span><span class="sk-location">LOC</span></a></li>';
|
template = '<li><a target="_blank" href="URI"><span class="sk-name">NAME</span><span class="sk-location">LOC</span></a></li>';
|
||||||
|
|
||||||
$.getJSON("https://api.songkick.com/api/3.0/artists/mbid:${artist['ArtistID']}/calendar.json?apikey=${headphones.SONGKICK_APIKEY}&jsoncallback=?",
|
$.getJSON("https://api.songkick.com/api/3.0/artists/mbid:${artist['ArtistID']}/calendar.json?apikey=${headphones.CONFIG.SONGKICK_APIKEY}&jsoncallback=?",
|
||||||
function(data){
|
function(data){
|
||||||
if (data['resultsPage'].totalEntries >= 1) {
|
if (data['resultsPage'].totalEntries >= 1) {
|
||||||
|
if (${songkick_filter_enabled}) {
|
||||||
if( ${songkick_filter_enabled} ) {
|
data.resultsPage.results.event = $.grep(data.resultsPage.results.event, function(element,index){
|
||||||
data.resultsPage.results.event = $.grep(data.resultsPage.results.event, function(element,index){
|
return element.venue.metroArea.id == ${songkick_location};
|
||||||
return element.venue.metroArea.id == ${songkick_location};
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tourDate;
|
|
||||||
|
|
||||||
calendarDomNode.show();
|
if (data.resultsPage.results.event.length > 0) {
|
||||||
$("#artistImg").addClass('on-tour');
|
var tourDate;
|
||||||
|
|
||||||
jQuery.each( data['resultsPage'].results.event, function( i, event ) {
|
calendarDomNode.show();
|
||||||
tourDate = template;
|
$("#artistImg").addClass('on-tour');
|
||||||
tourDate = tourDate.replace('URI',event.uri);
|
|
||||||
tourDate = tourDate.replace('NAME',event.displayName);
|
|
||||||
tourDate = tourDate.replace('LOC',event.location.city);
|
|
||||||
|
|
||||||
calendarDomNode.append(tourDate);
|
jQuery.each(data.resultsPage.results.event, function(i, event) {
|
||||||
});
|
tourDate = template;
|
||||||
|
tourDate = tourDate.replace('URI',event.uri);
|
||||||
|
tourDate = tourDate.replace('NAME',event.displayName);
|
||||||
|
tourDate = tourDate.replace('LOC',event.location.city);
|
||||||
|
|
||||||
calendarDomNode.append('<li><img src="interfaces/default/images/songkick.png" alt="concerts by songkick" class="sk-logo" /></li>');
|
calendarDomNode.append(tourDate);
|
||||||
|
});
|
||||||
$(function() {
|
|
||||||
$("#artistCalendar").each(function() {
|
|
||||||
$("li:gt(4)", this).hide(); /* :gt() is zero-indexed */
|
|
||||||
$("li:nth-child(5)", this).after("<br><li class='more'><a href='#'>More...</a></li>"); /* :nth-child() is one-indexed */
|
|
||||||
});
|
|
||||||
$("li.more a").live("click", function() {
|
|
||||||
var li = $(this).parents("li:first");
|
|
||||||
li.parent().children().show();
|
|
||||||
li.remove();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
calendarDomNode.append('<li><img src="interfaces/default/images/songkick.png" alt="concerts by songkick" class="sk-logo" /></li>');
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#artistCalendar").each(function() {
|
||||||
|
$("li:gt(4)", this).hide(); /* :gt() is zero-indexed */
|
||||||
|
$("li:nth-child(5)", this).after("<br><li class='more'><a href='#'>More...</a></li>"); /* :nth-child() is one-indexed */
|
||||||
|
});
|
||||||
|
$("li.more").on("click", 'a', function() {
|
||||||
|
var li = $(this).parents("li:first");
|
||||||
|
li.parent().children().show();
|
||||||
|
li.remove();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// data is JSON response object
|
}
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadingMessage = false;
|
||||||
|
var spinner_active = false;
|
||||||
|
var loadingtext_active = false;
|
||||||
|
|
||||||
|
function checkArtistStatus() {
|
||||||
|
$.getJSON("getArtistjson?ArtistID=${artist['ArtistID']}", function(data) {
|
||||||
|
if (data['Status'] == "Loading"){
|
||||||
|
refreshTable();
|
||||||
|
$('#artistnamelink').text(data["ArtistName"]);
|
||||||
|
if (loadingMessage == false){
|
||||||
|
$("#ajaxMsg").after( "<div id='ajaxMsg2' class='ajaxMsg'></div>" );
|
||||||
|
showArtistMsg("Getting artist information");
|
||||||
|
loadingMessage = true;
|
||||||
|
}
|
||||||
|
if (spinner_active == false){
|
||||||
|
$('#artistname').prepend('<i class="fa fa-refresh fa-spin" id="artistnamespinner"></i>')
|
||||||
|
spinner_active = true;
|
||||||
|
}
|
||||||
|
if (loadingtext_active == false){
|
||||||
|
$('#artistname').append('<h3 id="loadingtext"><i>(Album information for this artist is currently being loaded)</i></h3>')
|
||||||
|
loadingtext_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$('#artistnamespinner').remove()
|
||||||
|
$('#loadingtext').remove()
|
||||||
|
$('#ajaxMsg2').remove()
|
||||||
|
spinner_active = false
|
||||||
|
loadingtext_active = false
|
||||||
|
loadingMessage = false
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
$('#menu_link_getextra').click(function() {
|
$('#menu_link_getextra').click(function(event) {
|
||||||
$('#dialog').dialog();
|
$('#dialog').dialog();
|
||||||
return false;
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
$('#menu_link_modifyextra').click(function() {
|
$('#menu_link_modifyextra').click(function(event) {
|
||||||
$('#dialog').dialog();
|
$('#dialog').dialog();
|
||||||
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
%if artist['Status'] == 'Loading':
|
|
||||||
showMsg("Getting artist information",true);
|
|
||||||
%endif
|
|
||||||
$('#album_table').dataTable({
|
$('#album_table').dataTable({
|
||||||
"bDestroy": true,
|
"bDestroy": true,
|
||||||
"aoColumns": [
|
"aoColumns": [
|
||||||
@@ -258,6 +285,7 @@
|
|||||||
{ "sType": "date" },
|
{ "sType": "date" },
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
{ "sType": "title-numeric"},
|
{ "sType": "title-numeric"},
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
@@ -280,7 +308,7 @@
|
|||||||
resetFilters("albums");
|
resetFilters("albums");
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
initFancybox();
|
initFancybox();
|
||||||
},1500)
|
},1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@@ -288,8 +316,12 @@
|
|||||||
initThisPage();
|
initThisPage();
|
||||||
getArtistBio();
|
getArtistBio();
|
||||||
if( ${songkick_enabled} ){
|
if( ${songkick_enabled} ){
|
||||||
getArtistsCalendar();
|
getArtistsCalendar();
|
||||||
}
|
}
|
||||||
|
checkArtistStatus();
|
||||||
|
setInterval(function(){
|
||||||
|
checkArtistStatus();
|
||||||
|
}, 1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
from headphones import version
|
from headphones import version
|
||||||
%>
|
%>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
|
||||||
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
||||||
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
||||||
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
||||||
@@ -20,24 +19,26 @@
|
|||||||
|
|
||||||
<link rel="shortcut icon" href="images/favicon.ico">
|
<link rel="shortcut icon" href="images/favicon.ico">
|
||||||
<link rel="apple-touch-icon" href="images/headphoneslogo.png">
|
<link rel="apple-touch-icon" href="images/headphoneslogo.png">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="css/jquery-ui.min.css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="interfaces/default/css/style.css">
|
<link rel="stylesheet" href="interfaces/default/css/style.css">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/jquery-ui.css">
|
|
||||||
<link rel="stylesheet" href="interfaces/default/css/font-awesome.min.css">
|
<link rel="stylesheet" href="interfaces/default/css/font-awesome.min.css">
|
||||||
${next.headIncludes()}
|
${next.headIncludes()}
|
||||||
|
|
||||||
<script src="js/libs/modernizr-1.7.min.js"></script>
|
<script src="js/libs/modernizr-2.8.3.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div id="ajaxMsg"></div>
|
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||||
% if not headphones.CURRENT_VERSION:
|
% if headphones.CONFIG.CHECK_GITHUB and not headphones.CURRENT_VERSION:
|
||||||
<div id="updatebar">
|
<div id="updatebar">
|
||||||
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
||||||
<a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
<a href="javascript:void(0)" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||||
</div>
|
</div>
|
||||||
% elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.COMMITS_BEHIND > 0 and headphones.INSTALL_TYPE != 'win':
|
% elif headphones.CONFIG.CHECK_GITHUB and headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.COMMITS_BEHIND > 0 and headphones.INSTALL_TYPE != 'win':
|
||||||
<div id="updatebar">
|
<div id="updatebar">
|
||||||
A <a href="https://github.com/${headphones.GIT_USER}/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
A <a href="https://github.com/${headphones.CONFIG.GIT_USER}/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="javascript:void(0)" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
@@ -51,18 +52,19 @@
|
|||||||
<li><a href="extras">extras</a></li>
|
<li><a href="extras">extras</a></li>
|
||||||
<li><a href="manage">manage</a></li>
|
<li><a href="manage">manage</a></li>
|
||||||
<li><a href="history">history</a></li>
|
<li><a href="history">history</a></li>
|
||||||
<li><a href="logs" class="log">logs</a></li>
|
<li><a href="logs">logs</a></li>
|
||||||
<li><a href="config" class="config"><i class="fa fa-gear fa-lg"></i></a></li>
|
<li><a href="config" class="config"><i class="fa fa-gear fa-lg"></i></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="searchbar">
|
<div id="searchbar">
|
||||||
<form action="search" method="get">
|
<form action="search" method="get">
|
||||||
<input type="text" value="" placeholder="Search" onfocus="if(this.value==this.defaultValue) this.value='';" name="name" />
|
<input type="text" value="" placeholder="Search" onfocus="if(this.value==this.defaultValue) this.value='';" name="name" />
|
||||||
<i class='fa fa-search mini-icon'></i>
|
<i class='fa fa-search mini-icon'></i>
|
||||||
<select name="type">
|
<select name="type" id="search_type">
|
||||||
<option value="artist">Artist</option>
|
<option value="artist">Artist</option>
|
||||||
<option value="album">Album</option>
|
<option value="album">Album</option>
|
||||||
|
<option value="series">Series</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" value="Add"/>
|
<input type="submit" value="Search"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -77,11 +79,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
<div id="info">
|
||||||
|
<small>
|
||||||
|
<a href="https://github.com/rembo10/headphones"><i class="fa fa-headphones"></i> Website</a> |
|
||||||
|
<a href="https://github.com/rembo10/headphones/wiki/TroubleShooting"><i class="fa fa-ambulance"></i> Help</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
<div id="actions">
|
<div id="actions">
|
||||||
<small>
|
<small>
|
||||||
<a href="shutdown"><i class="fa fa-power-off"></i> Shutdown</a> |
|
<a href="shutdown"><i class="fa fa-power-off"></i> Shutdown</a> |
|
||||||
<a href="restart"><i class="fa fa-power-off"></i> Restart</a> |
|
<a href="restart"><i class="fa fa-power-off"></i> Restart</a> |
|
||||||
<a href="#" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><i class="fa fa-refresh"></i> Check for new version</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><i class="fa fa-refresh"></i> Check for new version</a>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div id="version">
|
<div id="version">
|
||||||
@@ -89,26 +97,44 @@
|
|||||||
%if version.HEADPHONES_VERSION != 'master':
|
%if version.HEADPHONES_VERSION != 'master':
|
||||||
(${version.HEADPHONES_VERSION})
|
(${version.HEADPHONES_VERSION})
|
||||||
%endif
|
%endif
|
||||||
%if headphones.GIT_BRANCH != 'master':
|
%if headphones.CONFIG.GIT_BRANCH != 'master':
|
||||||
(${headphones.GIT_BRANCH})
|
(${headphones.CONFIG.GIT_BRANCH})
|
||||||
%endif
|
%endif
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<a href="#main" id="toTop"><i class="fa fa-angle-double-up"></i> <span>Back to top</span></a>
|
<a href="#main" id="toTop"><i class="fa fa-angle-double-up"></i> <span>Back to top</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/libs/jquery-1.7.2.min.js"></script>
|
<script src="js/libs/jquery-1.11.1.min.js"></script>
|
||||||
<script src="js/libs/jquery-ui.min.js"></script>
|
<script src="js/libs/jquery-ui.min.js"></script>
|
||||||
|
<script src="js/common.js"></script>
|
||||||
|
|
||||||
${next.javascriptIncludes()}
|
${next.javascriptIncludes()}
|
||||||
|
|
||||||
<script src="js/plugins.js"></script>
|
|
||||||
<script src="interfaces/default/js/script.js"></script>
|
<script src="interfaces/default/js/script.js"></script>
|
||||||
<!--[if lt IE 7 ]>
|
|
||||||
<script src="js/libs/dd_belatedpng.js"></script>
|
|
||||||
<script> DD_belatedPNG.fix('img, .png_bg');</script>
|
|
||||||
<![endif]-->
|
|
||||||
<!-- This template is made by Elmar Kouwenhoven -->
|
<!-- This template is made by Elmar Kouwenhoven -->
|
||||||
|
|
||||||
|
<!-- Persist search type using local storage -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('form:first *:input[type!=hidden]:first').focus();
|
||||||
|
try{
|
||||||
|
var type = window.localStorage.getItem('search_type') || "artist";
|
||||||
|
$("#search_type").val(type);
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('select[id=search_type]').change(function() {
|
||||||
|
var type = $(this).val()
|
||||||
|
try{
|
||||||
|
window.localStorage.setItem('search_type', type);
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Variables */
|
/* Variables */
|
||||||
@base-font-face: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
@base-font-face: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||||
@alt-font-face: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
@alt-font-face: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
||||||
@base-font-size: 12px;
|
@base-font-size: 12px;
|
||||||
@@ -10,20 +10,20 @@
|
|||||||
@msg-bg: #FFF6A9;
|
@msg-bg: #FFF6A9;
|
||||||
@msg-bg-success: #D3FFD7;
|
@msg-bg-success: #D3FFD7;
|
||||||
@msg-bg-error: #FFD3D3;
|
@msg-bg-error: #FFD3D3;
|
||||||
|
|
||||||
/* Mixins */
|
/* Mixins */
|
||||||
.rounded(@radius: 5px) {
|
.rounded(@radius: 5px) {
|
||||||
-moz-border-radius: @radius;
|
-moz-border-radius: @radius;
|
||||||
-webkit-border-radius: @radius;
|
-webkit-border-radius: @radius;
|
||||||
border-radius: @radius;
|
border-radius: @radius;
|
||||||
}
|
}
|
||||||
.roundedTop(@radius: 5px) {
|
.roundedTop(@radius: 5px) {
|
||||||
-moz-border-radius-topleft: @radius;
|
-moz-border-radius-topleft: @radius;
|
||||||
-moz-border-radius-topright: @radius;
|
-moz-border-radius-topright: @radius;
|
||||||
-webkit-border-top-right-radius: @radius;
|
-webkit-border-top-right-radius: @radius;
|
||||||
-webkit-border-top-left-radius: @radius;
|
-webkit-border-top-left-radius: @radius;
|
||||||
border-top-left-radius: @radius;
|
border-top-left-radius: @radius;
|
||||||
border-top-right-radius: @radius;
|
border-top-right-radius: @radius;
|
||||||
}
|
}
|
||||||
.roundedLeftTop(@radius: 5px) {
|
.roundedLeftTop(@radius: 5px) {
|
||||||
-moz-border-radius-topleft: @radius;
|
-moz-border-radius-topleft: @radius;
|
||||||
@@ -34,14 +34,14 @@
|
|||||||
-moz-border-radius-topright: @radius;
|
-moz-border-radius-topright: @radius;
|
||||||
-webkit-border-top-right-radius: @radius;
|
-webkit-border-top-right-radius: @radius;
|
||||||
border-top-right-radius: @radius;
|
border-top-right-radius: @radius;
|
||||||
}
|
}
|
||||||
.roundedBottom(@radius: 5px) {
|
.roundedBottom(@radius: 5px) {
|
||||||
-moz-border-radius-bottomleft: @radius;
|
-moz-border-radius-bottomleft: @radius;
|
||||||
-moz-border-radius-bottomright: @radius;
|
-moz-border-radius-bottomright: @radius;
|
||||||
-webkit-border-bottom-right-radius: @radius;
|
-webkit-border-bottom-right-radius: @radius;
|
||||||
-webkit-border-bottom-left-radius: @radius;
|
-webkit-border-bottom-left-radius: @radius;
|
||||||
border-bottom-left-radius: @radius;
|
border-bottom-left-radius: @radius;
|
||||||
border-bottom-right-radius: @radius;
|
border-bottom-right-radius: @radius;
|
||||||
}
|
}
|
||||||
.roundedLeftBottom(@radius: 5px) {
|
.roundedLeftBottom(@radius: 5px) {
|
||||||
-moz-border-radius-bottomleft: @radius;
|
-moz-border-radius-bottomleft: @radius;
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
-moz-border-radius-bottomright: @radius;
|
-moz-border-radius-bottomright: @radius;
|
||||||
-webkit-border-bottom-right-radius: @radius;
|
-webkit-border-bottom-right-radius: @radius;
|
||||||
border-bottom-right-radius: @radius;
|
border-bottom-right-radius: @radius;
|
||||||
}
|
}
|
||||||
.shadow(@shadow: 0 17px 11px -1px #ced8d9) {
|
.shadow(@shadow: 0 17px 11px -1px #ced8d9) {
|
||||||
-moz-box-shadow: @shadow;
|
-moz-box-shadow: @shadow;
|
||||||
-webkit-box-shadow: @shadow;
|
-webkit-box-shadow: @shadow;
|
||||||
@@ -74,4 +74,4 @@
|
|||||||
-o-opacity:@opacity_percent / 100 !important;
|
-o-opacity:@opacity_percent / 100 !important;
|
||||||
opacity:@opacity_percent / 100 !important;
|
opacity:@opacity_percent / 100 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,14 @@ table.display tr.even.gradeA {
|
|||||||
background-color: #ddffdd;
|
background-color: #ddffdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.display tr.odd.gradeI {
|
||||||
|
background-color: #bebebe;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.display tr.even.gradeI {
|
||||||
|
background-color: #bebebe;
|
||||||
|
}
|
||||||
|
|
||||||
table.display tr.odd.gradeC {
|
table.display tr.odd.gradeC {
|
||||||
background-color: #ebf5ff;
|
background-color: #ebf5ff;
|
||||||
}
|
}
|
||||||
@@ -254,6 +262,13 @@ table.display tr.even.gradeU {
|
|||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.display tr.odd.gradeW {
|
||||||
|
background-color: #ffffaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.display tr.even.gradeW {
|
||||||
|
background-color: #ffffaa;
|
||||||
|
}
|
||||||
|
|
||||||
table.display tr.odd.gradeZ {
|
table.display tr.odd.gradeZ {
|
||||||
background-color: #FAFAFA;
|
background-color: #FAFAFA;
|
||||||
@@ -269,6 +284,7 @@ table.display tr.gradeL #status {
|
|||||||
}
|
}
|
||||||
table.display tr.gradeA td,
|
table.display tr.gradeA td,
|
||||||
table.display tr.gradeC td,
|
table.display tr.gradeC td,
|
||||||
|
table.display tr.gradeI td,
|
||||||
table.display tr.gradeX td,
|
table.display tr.gradeX td,
|
||||||
table.display tr.gradeU td,
|
table.display tr.gradeU td,
|
||||||
table.display tr.gradeZ td {border-bottom: 1px solid #FFF;}
|
table.display tr.gradeZ td {border-bottom: 1px solid #FFF;}
|
||||||
|
|||||||
@@ -1,547 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery UI CSS Framework 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Theming/API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Layout helpers
|
|
||||||
----------------------------------*/
|
|
||||||
.ui-helper-hidden { display: none; }
|
|
||||||
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
|
|
||||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
|
||||||
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
|
|
||||||
.ui-helper-clearfix:after { clear: both; }
|
|
||||||
.ui-helper-clearfix { zoom: 1; }
|
|
||||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
|
||||||
|
|
||||||
|
|
||||||
/* Interaction Cues
|
|
||||||
----------------------------------*/
|
|
||||||
.ui-state-disabled { cursor: default !important; }
|
|
||||||
|
|
||||||
|
|
||||||
/* Icons
|
|
||||||
----------------------------------*/
|
|
||||||
|
|
||||||
/* states and images */
|
|
||||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
|
||||||
|
|
||||||
|
|
||||||
/* Misc visuals
|
|
||||||
----------------------------------*/
|
|
||||||
|
|
||||||
/* Overlays */
|
|
||||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* jQuery UI CSS Framework 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Theming/API
|
|
||||||
*
|
|
||||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Helvetica,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=dddddd&bgTextureHeader=02_glass.png&bgImgOpacityHeader=35&borderColorHeader=bbbbbb&fcHeader=444444&iconColorHeader=999999&bgColorContent=c9c9c9&bgTextureContent=05_inset_soft.png&bgImgOpacityContent=50&borderColorContent=aaaaaa&fcContent=333333&iconColorContent=999999&bgColorDefault=eeeeee&bgTextureDefault=02_glass.png&bgImgOpacityDefault=60&borderColorDefault=cccccc&fcDefault=3383bb&iconColorDefault=70b2e1&bgColorHover=f8f8f8&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=bbbbbb&fcHover=599fcf&iconColorHover=3383bb&bgColorActive=999999&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=75&borderColorActive=999999&fcActive=ffffff&iconColorActive=454545&bgColorHighlight=eeeeee&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=ffffff&fcHighlight=444444&iconColorHighlight=3383bb&bgColorError=c0402a&bgTextureError=01_flat.png&bgImgOpacityError=55&borderColorError=c0402a&fcError=ffffff&iconColorError=fbc856&bgColorOverlay=eeeeee&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=80&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=60&thicknessShadow=4px&offsetTopShadow=-4px&offsetLeftShadow=-4px&cornerRadiusShadow=0pxdow=0px
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* Component containers
|
|
||||||
----------------------------------*/
|
|
||||||
.ui-widget { font-family: Trebuchet MS, Helvetica, Arial, sans-serif; font-size: 1.1em; }
|
|
||||||
.ui-widget .ui-widget { font-size: 1em; }
|
|
||||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Helvetica, Arial, sans-serif; font-size: 1em; }
|
|
||||||
.ui-widget-content { border: 1px solid #aaaaaa; background: #f2f2f2; color: #333333; }
|
|
||||||
.ui-widget-content a { /*color: #333333;*/ }
|
|
||||||
.ui-widget-header { border: 1px solid #bbbbbb; background: #dddddd url(../images/ui-bg_glass_35_dddddd_1x400.png) 50% 50% repeat-x; color: #444444; font-weight: bold; }
|
|
||||||
.ui-widget-header a { color: #444444; }
|
|
||||||
|
|
||||||
/* Interaction states
|
|
||||||
----------------------------------*/
|
|
||||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #eeeeee url(../images/ui-bg_glass_60_eeeeee_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #3383bb; }
|
|
||||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #3383bb; text-decoration: none; }
|
|
||||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #bbbbbb; background: #f8f8f8 url(../images/ui-bg_glass_100_f8f8f8_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #599fcf; }
|
|
||||||
.ui-state-hover a, .ui-state-hover a:hover { color: #599fcf; text-decoration: none; }
|
|
||||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #999999; background: #ccc; font-weight: bold; color: #ffffff; }
|
|
||||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; }
|
|
||||||
.ui-widget :active { outline: none; }
|
|
||||||
|
|
||||||
/* Interaction Cues
|
|
||||||
----------------------------------*/
|
|
||||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #ffffff; background: #eeeeee url(../images/ui-bg_flat_55_eeeeee_40x100.png) 50% 50% repeat-x; color: #444444; }
|
|
||||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #444444; }
|
|
||||||
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #c0402a; background: #c0402a url(../images/ui-bg_flat_55_c0402a_40x100.png) 50% 50% repeat-x; color: #ffffff; }
|
|
||||||
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
|
|
||||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
|
|
||||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
|
|
||||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
|
||||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
|
||||||
|
|
||||||
/* Icons
|
|
||||||
----------------------------------*/
|
|
||||||
|
|
||||||
/* states and images */
|
|
||||||
.ui-icon { width: 16px; height: 16px; background-image: url(../images/icon_sprite_black.png); }
|
|
||||||
.ui-widget-content .ui-icon {background-image: url(../images/icon_sprite_black.png); }
|
|
||||||
.ui-widget-header .ui-icon {background-image: url(../images/icon_sprite_black.png); }
|
|
||||||
.ui-button.ui-state-hover .ui-icon { background-image: url(../images/icon_sprite_white.png); }
|
|
||||||
.ui-state-default .ui-icon { background-image: url(../images/ui-icons_70b2e1_256x240.png); }
|
|
||||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(../images/ui-icons_3383bb_256x240.png); }
|
|
||||||
.ui-state-active .ui-icon {background-image: url(../images/ui-icons_454545_256x240.png); }
|
|
||||||
.ui-state-highlight .ui-icon {background-image: url(../images/ui-icons_3383bb_256x240.png); }
|
|
||||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(../images/ui-icons_fbc856_256x240.png); }
|
|
||||||
|
|
||||||
/* positioning */
|
|
||||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
|
||||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
|
||||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
|
||||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
|
||||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
|
||||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
|
||||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
|
||||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
|
||||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
|
||||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
|
||||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
|
||||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
|
||||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
|
||||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
|
||||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
|
||||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
|
||||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
|
||||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
|
||||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
|
||||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
|
||||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
|
||||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
|
||||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
|
||||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
|
||||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
|
||||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
|
||||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
|
||||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
|
||||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
|
||||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
|
||||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
|
||||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
|
||||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
|
||||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
|
||||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
|
||||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
|
||||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
|
||||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
|
||||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
|
||||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
|
||||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
|
||||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
|
||||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
|
||||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
|
||||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
|
||||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
|
||||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
|
||||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
|
||||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
|
||||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
|
||||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
|
||||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
|
||||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
|
||||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
|
||||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
|
||||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
|
||||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
|
||||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
|
||||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
|
||||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
|
||||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
|
||||||
.ui-icon-extlink { background-position: -32px -80px; }
|
|
||||||
.ui-icon-newwin { background-position: -48px -80px; }
|
|
||||||
.ui-icon-refresh { background-position: -64px -80px; }
|
|
||||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
|
||||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
|
||||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
|
||||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
|
||||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
|
||||||
.ui-icon-document { background-position: -32px -96px; }
|
|
||||||
.ui-icon-document-b { background-position: -48px -96px; }
|
|
||||||
.ui-icon-note { background-position: -64px -96px; }
|
|
||||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
|
||||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
|
||||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
|
||||||
.ui-icon-comment { background-position: -128px -96px; }
|
|
||||||
.ui-icon-person { background-position: -144px -96px; }
|
|
||||||
.ui-icon-print { background-position: -160px -96px; }
|
|
||||||
.ui-icon-trash { background-position: -176px -96px; }
|
|
||||||
.ui-icon-locked { background-position: -192px -96px; }
|
|
||||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
|
||||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
|
||||||
.ui-icon-tag { background-position: -240px -96px; }
|
|
||||||
.ui-icon-home { background-position: 0 -112px; }
|
|
||||||
.ui-icon-flag { background-position: -16px -112px; }
|
|
||||||
.ui-icon-calendar { background-position: -32px -112px; }
|
|
||||||
.ui-icon-cart { background-position: -48px -112px; }
|
|
||||||
.ui-icon-pencil { background-position: -64px -112px; }
|
|
||||||
.ui-icon-clock { background-position: -80px -112px; }
|
|
||||||
.ui-icon-disk { background-position: -96px -112px; }
|
|
||||||
.ui-icon-calculator { background-position: -112px -112px; }
|
|
||||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
|
||||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
|
||||||
.ui-icon-search { background-position: -160px -112px; }
|
|
||||||
.ui-icon-wrench { background-position: -176px -112px; }
|
|
||||||
.ui-icon-gear { background-position: -192px -112px; }
|
|
||||||
.ui-icon-heart { background-position: -208px -112px; }
|
|
||||||
.ui-icon-star { background-position: -224px -112px; }
|
|
||||||
.ui-icon-link { background-position: -240px -112px; }
|
|
||||||
.ui-icon-cancel { background-position: 0 -128px; }
|
|
||||||
.ui-icon-plus { background-position: -16px -128px; }
|
|
||||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
|
||||||
.ui-icon-minus { background-position: -48px -128px; }
|
|
||||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
|
||||||
.ui-icon-close { background-position: -80px -128px; }
|
|
||||||
.ui-icon-closethick { background-position: -96px -128px; }
|
|
||||||
.ui-icon-key { background-position: -112px -128px; }
|
|
||||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
|
||||||
.ui-icon-scissors { background-position: -144px -128px; }
|
|
||||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
|
||||||
.ui-icon-copy { background-position: -176px -128px; }
|
|
||||||
.ui-icon-contact { background-position: -192px -128px; }
|
|
||||||
.ui-icon-image { background-position: -208px -128px; }
|
|
||||||
.ui-icon-video { background-position: -224px -128px; }
|
|
||||||
.ui-icon-script { background-position: -240px -128px; }
|
|
||||||
.ui-icon-alert { background-position: 0 -144px; }
|
|
||||||
.ui-icon-info { background-position: -16px -144px; }
|
|
||||||
.ui-icon-notice { background-position: -32px -144px; }
|
|
||||||
.ui-icon-help { background-position: -48px -144px; }
|
|
||||||
.ui-icon-check { background-position: -64px -144px; }
|
|
||||||
.ui-icon-bullet { background-position: -80px -144px; }
|
|
||||||
.ui-icon-radio-off { background-position: -96px -144px; }
|
|
||||||
.ui-icon-radio-on { background-position: -112px -144px; }
|
|
||||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
|
||||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
|
||||||
.ui-icon-play { background-position: 0 -160px; }
|
|
||||||
.ui-icon-pause { background-position: -16px -160px; }
|
|
||||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
|
||||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
|
||||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
|
||||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
|
||||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
|
||||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
|
||||||
.ui-icon-stop { background-position: -96px -160px; }
|
|
||||||
.ui-icon-eject { background-position: -112px -160px; }
|
|
||||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
|
||||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
|
||||||
.ui-icon-power { background-position: 0 -176px; }
|
|
||||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
|
||||||
.ui-icon-signal { background-position: -32px -176px; }
|
|
||||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
|
||||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
|
||||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
|
||||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
|
||||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
|
||||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
|
||||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
|
||||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
|
||||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
|
||||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
|
||||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
|
||||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
|
||||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
|
||||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
|
||||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
|
||||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
|
||||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
|
||||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
|
||||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
|
||||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
|
||||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
|
||||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
|
||||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
|
||||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
|
||||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
|
||||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
|
||||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
|
||||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
|
||||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
|
||||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
|
||||||
|
|
||||||
|
|
||||||
/* Misc visuals
|
|
||||||
----------------------------------*/
|
|
||||||
|
|
||||||
/* Corner radius */
|
|
||||||
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; -khtml-border-top-left-radius: 6px; border-top-left-radius: 6px; }
|
|
||||||
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; -khtml-border-top-right-radius: 6px; border-top-right-radius: 6px; }
|
|
||||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; -khtml-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
|
|
||||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; -khtml-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
|
|
||||||
|
|
||||||
/* Overlays */
|
|
||||||
.ui-widget-overlay { background: #eeeeee url(../images/ui-bg_flat_0_eeeeee_40x100.png) 50% 50% repeat-x; opacity: .80;filter:Alpha(Opacity=80); }
|
|
||||||
.ui-widget-shadow { margin: -4px 0 0 -4px; padding: 4px; background: #aaaaaa url(../images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); -moz-border-radius: 0pxdow=0px; -khtml-border-radius: 0pxdow=0px; -webkit-border-radius: 0pxdow=0px; border-radius: 0pxdow=0px; }/*!
|
|
||||||
* jQuery UI Resizable 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Resizable#theming
|
|
||||||
*/
|
|
||||||
.ui-resizable { position: relative;}
|
|
||||||
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
|
|
||||||
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
|
|
||||||
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
|
|
||||||
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
|
|
||||||
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
|
|
||||||
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
|
|
||||||
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
|
|
||||||
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
|
|
||||||
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
|
|
||||||
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*!
|
|
||||||
* jQuery UI Selectable 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Selectable#theming
|
|
||||||
*/
|
|
||||||
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
|
|
||||||
/*!
|
|
||||||
* jQuery UI Accordion 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Accordion#theming
|
|
||||||
*/
|
|
||||||
/* IE/Win - Fix animation bug - #4615 */
|
|
||||||
.ui-accordion { width: 100%; }
|
|
||||||
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
|
|
||||||
.ui-accordion .ui-accordion-li-fix { display: inline; }
|
|
||||||
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
|
|
||||||
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
|
|
||||||
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
|
|
||||||
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
|
|
||||||
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
|
|
||||||
.ui-accordion .ui-accordion-content-active { display: block; }
|
|
||||||
/*!
|
|
||||||
* jQuery UI Autocomplete 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Autocomplete#theming
|
|
||||||
*/
|
|
||||||
.ui-autocomplete { position: absolute; cursor: default; }
|
|
||||||
|
|
||||||
/* workarounds */
|
|
||||||
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* jQuery UI Menu @VERSION
|
|
||||||
*
|
|
||||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Menu#theming
|
|
||||||
*/
|
|
||||||
.ui-menu {
|
|
||||||
list-style:none;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 0;
|
|
||||||
display:block;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.ui-menu .ui-menu {
|
|
||||||
margin-top: -3px;
|
|
||||||
}
|
|
||||||
.ui-menu .ui-menu-item {
|
|
||||||
margin:0;
|
|
||||||
padding: 0;
|
|
||||||
zoom: 1;
|
|
||||||
float: left;
|
|
||||||
clear: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.ui-menu .ui-menu-item a {
|
|
||||||
text-decoration:none;
|
|
||||||
display:block;
|
|
||||||
padding:.2em .4em;
|
|
||||||
line-height:1.5;
|
|
||||||
zoom:1;
|
|
||||||
}
|
|
||||||
.ui-menu .ui-menu-item a.ui-state-hover,
|
|
||||||
.ui-menu .ui-menu-item a.ui-state-active {
|
|
||||||
font-weight: normal;
|
|
||||||
margin: -1px;
|
|
||||||
}
|
|
||||||
/*!
|
|
||||||
* jQuery UI Button 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Button#theming
|
|
||||||
*/
|
|
||||||
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
|
|
||||||
.ui-button:hover {
|
|
||||||
color:#3383BB;
|
|
||||||
}
|
|
||||||
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
|
|
||||||
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
|
|
||||||
.ui-button-icons-only { width: 3.4em; }
|
|
||||||
button.ui-button-icons-only { width: 3.7em; }
|
|
||||||
|
|
||||||
/*button text element */
|
|
||||||
.ui-button .ui-button-text { display: block; line-height: 1.4; }
|
|
||||||
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
|
|
||||||
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
|
|
||||||
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 0.2em 0.5em 0.2em 1.8em }
|
|
||||||
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 0.2em 1.8em 0.2em 0.5em }
|
|
||||||
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
|
|
||||||
/* no icon support for input elements, provide padding by default */
|
|
||||||
input.ui-button { padding: .4em 1em; }
|
|
||||||
|
|
||||||
/*button icon element(s) */
|
|
||||||
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
|
|
||||||
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
|
|
||||||
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .3em; }
|
|
||||||
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
|
|
||||||
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
|
|
||||||
|
|
||||||
/*button sets*/
|
|
||||||
.ui-buttonset { margin-right: 7px; }
|
|
||||||
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
|
|
||||||
|
|
||||||
/* workarounds */
|
|
||||||
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
|
|
||||||
/*!
|
|
||||||
* jQuery UI Dialog 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Dialog#theming
|
|
||||||
*/
|
|
||||||
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
|
|
||||||
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
|
|
||||||
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
|
|
||||||
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
|
|
||||||
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
|
|
||||||
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
|
|
||||||
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
|
|
||||||
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
|
|
||||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
|
|
||||||
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
|
|
||||||
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
|
|
||||||
.ui-draggable .ui-dialog-titlebar { cursor: move; }
|
|
||||||
/*!
|
|
||||||
* jQuery UI Tabs 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Tabs#theming
|
|
||||||
*/
|
|
||||||
.ui-tabs { position: relative; padding: 0em; zoom: 1; background: transparent; border: none;} /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
|
|
||||||
.ui-tabs .ui-tabs-nav { margin: 0; padding: 0; background: none; border:none;}
|
|
||||||
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap;background: #FFF; }
|
|
||||||
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; font-weight: normal; }
|
|
||||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; background-color: #f7f7f7; border-color: #CCC; }
|
|
||||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a { color: #454545; }
|
|
||||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
|
|
||||||
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
|
|
||||||
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: #f7f7f7; border: 1px solid #CCC; }
|
|
||||||
.ui-tabs .ui-tabs-hide { display: none !important; }
|
|
||||||
/*!
|
|
||||||
* jQuery UI Datepicker 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Datepicker#theming
|
|
||||||
*/
|
|
||||||
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
|
|
||||||
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
|
|
||||||
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
|
|
||||||
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
|
|
||||||
.ui-datepicker .ui-datepicker-prev { left:2px; }
|
|
||||||
.ui-datepicker .ui-datepicker-next { right:2px; }
|
|
||||||
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
|
|
||||||
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
|
|
||||||
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
|
|
||||||
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
|
|
||||||
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
|
|
||||||
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
|
|
||||||
.ui-datepicker select.ui-datepicker-month,
|
|
||||||
.ui-datepicker select.ui-datepicker-year { width: 49%;}
|
|
||||||
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
|
|
||||||
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
|
|
||||||
.ui-datepicker td { border: 0; padding: 1px; }
|
|
||||||
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
|
|
||||||
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
|
|
||||||
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
|
||||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
|
|
||||||
|
|
||||||
/* with multiple calendars */
|
|
||||||
.ui-datepicker.ui-datepicker-multi { width:auto; }
|
|
||||||
.ui-datepicker-multi .ui-datepicker-group { float:left; }
|
|
||||||
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
|
|
||||||
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
|
|
||||||
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
|
|
||||||
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
|
|
||||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
|
|
||||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
|
|
||||||
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
|
|
||||||
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
|
|
||||||
|
|
||||||
/* RTL support */
|
|
||||||
.ui-datepicker-rtl { direction: rtl; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
|
||||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
|
||||||
|
|
||||||
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
|
|
||||||
.ui-datepicker-cover {
|
|
||||||
display: none; /*sorry for IE5*/
|
|
||||||
display/**/: block; /*sorry for IE5*/
|
|
||||||
position: absolute; /*must have*/
|
|
||||||
z-index: -1; /*must have*/
|
|
||||||
filter: mask(); /*must have*/
|
|
||||||
top: -4px; /*must have*/
|
|
||||||
left: -4px; /*must have*/
|
|
||||||
width: 200px; /*must have*/
|
|
||||||
height: 200px; /*must have*/
|
|
||||||
}/*!
|
|
||||||
* jQuery UI Progressbar 1.8.19
|
|
||||||
*
|
|
||||||
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Progressbar#theming
|
|
||||||
*/
|
|
||||||
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
|
|
||||||
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
|
|
||||||
@@ -163,6 +163,14 @@ img.albumArt {
|
|||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
img.albumArt-nostretch {
|
||||||
|
float: left;
|
||||||
|
min-height: 64px;
|
||||||
|
min-width: 64px;
|
||||||
|
max-width: 250px;
|
||||||
|
max-height: 300px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
@@ -221,6 +229,17 @@ textarea,
|
|||||||
button {
|
button {
|
||||||
font: 99%;
|
font: 99%;
|
||||||
}
|
}
|
||||||
|
select {
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #4F4F4F;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||||
|
color: #fff;
|
||||||
|
padding: 3px 10px;
|
||||||
|
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
textarea {
|
textarea {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
@@ -317,7 +336,7 @@ form fieldset small.heading {
|
|||||||
margin-top: -15px;
|
margin-top: -15px;
|
||||||
}
|
}
|
||||||
form .row {
|
form .row {
|
||||||
font-family: Helvetica, Arial;
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
form .row label {
|
form .row label {
|
||||||
@@ -326,7 +345,11 @@ form .row label {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
padding-top: 7px;
|
padding-top: 7px;
|
||||||
width: 175px;
|
width: 170px;
|
||||||
|
}
|
||||||
|
form .row label.inline {
|
||||||
|
margin-right: 5px;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
form .row input {
|
form .row input {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
@@ -356,7 +379,7 @@ form .row small {
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
margin-left: 175px;
|
margin-left: 170px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
form .left label {
|
form .left label {
|
||||||
@@ -396,6 +419,18 @@ form .checkbox small {
|
|||||||
form .indent input {
|
form .indent input {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
form .suboptions {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
.option{
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
input.bigcheck[type="checkbox"] {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
margin-left: 2em;
|
margin-left: 2em;
|
||||||
@@ -495,7 +530,7 @@ header .wrapper {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 960px;
|
width: 990px;
|
||||||
}
|
}
|
||||||
header #logo {
|
header #logo {
|
||||||
float: left;
|
float: left;
|
||||||
@@ -537,7 +572,7 @@ footer {
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
}
|
}
|
||||||
#ajaxMsg {
|
.ajaxMsg {
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
background-image: -moz-linear-gradient(#ffffff, #eeeeee) !important;
|
background-image: -moz-linear-gradient(#ffffff, #eeeeee) !important;
|
||||||
background-image: linear-gradient(#ffffff, #eeeeee) !important;
|
background-image: linear-gradient(#ffffff, #eeeeee) !important;
|
||||||
@@ -568,16 +603,16 @@ footer {
|
|||||||
-o-opacity: 0.8 !important;
|
-o-opacity: 0.8 !important;
|
||||||
opacity: 0.8 !important;
|
opacity: 0.8 !important;
|
||||||
}
|
}
|
||||||
#ajaxMsg .msg {
|
.ajaxMsg .msg {
|
||||||
font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
#ajaxMsg .loader {
|
.ajaxMsg .loader {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
#ajaxMsg.success {
|
.ajaxMsg .success {
|
||||||
background-image: -moz-linear-gradient(#d3ffd7, #c2edc6) !important;
|
background-image: -moz-linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||||
background-image: linear-gradient(#d3ffd7, #c2edc6) !important;
|
background-image: linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||||
background-image: -webkit-linear-gradient(#d3ffd7, #c2edc6) !important;
|
background-image: -webkit-linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||||
@@ -587,7 +622,7 @@ footer {
|
|||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
#ajaxMsg.error {
|
.ajaxMsg .error {
|
||||||
background-image: -moz-linear-gradient(#ffd3d3, #edc4c4) !important;
|
background-image: -moz-linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||||
background-image: linear-gradient(#ffd3d3, #edc4c4) !important;
|
background-image: linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||||
background-image: -webkit-linear-gradient(#ffd3d3, #edc4c4) !important;
|
background-image: -webkit-linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||||
@@ -597,7 +632,7 @@ footer {
|
|||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
#ajaxMsg .ui-icon {
|
.ajaxMsg .ui-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
@@ -750,7 +785,7 @@ div#searchbar input[type=text] {
|
|||||||
line-height: normal;
|
line-height: normal;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
padding: 4px 5px 4px 25px;
|
padding: 4px 5px 4px 25px;
|
||||||
width: 150px;
|
width: 185px;
|
||||||
}
|
}
|
||||||
div#searchbar .mini-icon {
|
div#searchbar .mini-icon {
|
||||||
color: #999;
|
color: #999;
|
||||||
@@ -1118,13 +1153,34 @@ div#artistheader h2 a {
|
|||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
}
|
}
|
||||||
#searchresults_table th#albumname {
|
#searchresults_table th#albumname {
|
||||||
min-width: 225px;
|
min-width: 250px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
#searchresults_table th#artistname {
|
#searchresults_table th#artistname {
|
||||||
min-width: 325px;
|
min-width: 325px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
#searchresults_table th#artistnamesmall {
|
||||||
|
min-width: 125px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table th#reldate {
|
||||||
|
min-width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table th#format {
|
||||||
|
min-width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table th#tracks {
|
||||||
|
min-width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
#searchresults_table #artistImg {
|
#searchresults_table #artistImg {
|
||||||
background: url("../images/loader_black.gif") no-repeat scroll center center #ffffff;
|
background: url("../images/loader_black.gif") no-repeat scroll center center #ffffff;
|
||||||
border: 3px solid #FFFFFF;
|
border: 3px solid #FFFFFF;
|
||||||
@@ -1136,15 +1192,40 @@ div#artistheader h2 a {
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
#searchresults_table td#albumname {
|
#searchresults_table td#albumname {
|
||||||
min-width: 200px;
|
min-width: 250px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
#searchresults_table td#artistname {
|
#searchresults_table td#artistname {
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
#searchresults_table td#artistnamesmall {
|
||||||
|
min-width: 100px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table td#reldate {
|
||||||
|
min-width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table td#format {
|
||||||
|
min-width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#searchresults_table td#tracks {
|
||||||
|
min-width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
#searchresults_table td#add {
|
#searchresults_table td#add {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
@@ -1207,6 +1288,13 @@ div#artistheader h2 a {
|
|||||||
float: left;
|
float: left;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
#info {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-top: -5px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
#actions {
|
#actions {
|
||||||
float: right;
|
float: right;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@@ -1355,6 +1443,13 @@ div#artistheader h2 a {
|
|||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
.nopad {
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
margin-top: 0px !important;
|
||||||
|
padding-top: 0px !important;
|
||||||
|
padding-bottom: 0px !important;
|
||||||
|
padding: 0px 0px !important;
|
||||||
|
}
|
||||||
#album_table th#albumname,
|
#album_table th#albumname,
|
||||||
#upcoming_table th#artistname,
|
#upcoming_table th#artistname,
|
||||||
#wanted_table th#artistname {
|
#wanted_table th#artistname {
|
||||||
@@ -1380,6 +1475,7 @@ div#artistheader h2 a {
|
|||||||
#album_table td#albumname,
|
#album_table td#albumname,
|
||||||
#album_table td#reldate,
|
#album_table td#reldate,
|
||||||
#album_table td#type,
|
#album_table td#type,
|
||||||
|
#album_table td#score,
|
||||||
#track_table td#duration,
|
#track_table td#duration,
|
||||||
#upcoming_table td#select,
|
#upcoming_table td#select,
|
||||||
#upcoming_table td#status,
|
#upcoming_table td#status,
|
||||||
@@ -1390,6 +1486,7 @@ div#artistheader h2 a {
|
|||||||
}
|
}
|
||||||
#album_table td#status,
|
#album_table td#status,
|
||||||
#album_table td#bitrate,
|
#album_table td#bitrate,
|
||||||
|
#album_table td#score,
|
||||||
#album_table td#albumformat,
|
#album_table td#albumformat,
|
||||||
#album_table td#wantlossless {
|
#album_table td#wantlossless {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -1408,6 +1505,11 @@ div#artistheader h3 span {
|
|||||||
min-width: 75px;
|
min-width: 75px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
#searchresults_table th#scoresmall {
|
||||||
|
min-width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
#track_table td#bitrate,
|
#track_table td#bitrate,
|
||||||
#track_table td#format {
|
#track_table td#format {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@@ -124,7 +124,23 @@ table {
|
|||||||
|
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
select, input, textarea, button { font: 99%;}
|
select, input, textarea, button
|
||||||
|
{
|
||||||
|
font: 99%;
|
||||||
|
}
|
||||||
|
|
||||||
|
select
|
||||||
|
{
|
||||||
|
.rounded(5px);
|
||||||
|
background: #4F4F4F;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||||
|
color: #fff;
|
||||||
|
padding: 3px 10px;
|
||||||
|
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
textarea {overflow: auto;}
|
textarea {overflow: auto;}
|
||||||
input { .rounded(3px);}
|
input { .rounded(3px);}
|
||||||
input:invalid, textarea:invalid {
|
input:invalid, textarea:invalid {
|
||||||
@@ -190,7 +206,12 @@ form {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
padding-top: 7px;
|
padding-top: 7px;
|
||||||
width: 175px;
|
width: 170px;
|
||||||
|
|
||||||
|
&.inline {
|
||||||
|
margin-right: 5px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input { margin-right: 5px; }
|
input { margin-right: 5px; }
|
||||||
input[type=text], input[type=password] {
|
input[type=text], input[type=password] {
|
||||||
@@ -211,7 +232,7 @@ form {
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
margin-left: 175px;
|
margin-left: 170px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -938,6 +959,13 @@ div#artistheader {
|
|||||||
float: left;
|
float: left;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
#info {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-top: -5px;
|
||||||
|
color: @border-color;
|
||||||
|
}
|
||||||
#actions {
|
#actions {
|
||||||
float: right;
|
float: right;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
<%inherit file="base.html"/>
|
<%inherit file="base.html"/>
|
||||||
<%!
|
<%!
|
||||||
from headphones import helpers
|
from headphones import helpers
|
||||||
|
import cgi
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%def name="headerIncludes()">
|
<%def name="headerIncludes()">
|
||||||
<div id="subhead_container">
|
<div id="subhead_container">
|
||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=all',$(this),'table')" data-success="All History cleared"><i class="fa fa-trash-o"></i> Clear All History</a>
|
<a id="menu_link_delete" href="javascript:void(0)" onclick="doAjaxCall('clearhistory?type=all',$(this),'table')" data-success="All History cleared"><i class="fa fa-trash-o"></i> Clear All History</a>
|
||||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Processed',$(this),'table')" data-success="All Processed cleared"><i class="fa fa-trash-o"></i> Clear Processed</a>
|
<a id="menu_link_delete" href="javascript:void(0)" onclick="doAjaxCall('clearhistory?type=Processed',$(this),'table')" data-success="All Processed cleared"><i class="fa fa-trash-o"></i> Clear Processed</a>
|
||||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Unprocessed',$(this),'table')" data-success="All Unprocessed cleared"><i class="fa fa-trash-o"></i> Clear Unprocessed</a>
|
<a id="menu_link_delete" href="javascript:void(0)" onclick="doAjaxCall('clearhistory?type=Unprocessed',$(this),'table')" data-success="All Unprocessed cleared"><i class="fa fa-trash-o"></i> Clear Unprocessed</a>
|
||||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Snatched',$(this),'table')" data-success="All Snatched cleared"><i class="fa fa-trash-o"></i> Clear Snatched</a>
|
<a id="menu_link_delete" href="javascript:void(0)" onclick="doAjaxCall('clearhistory?type=Frozen',$(this),'table')" data-success="All Frozen cleared"><i class="fa fa-trash-o"></i> Clear Frozen</a>
|
||||||
|
<a id="menu_link_delete" href="javascript:void(0)" onclick="doAjaxCall('clearhistory?type=Snatched',$(this),'table')" data-success="All Snatched cleared"><i class="fa fa-trash-o"></i> Clear Snatched</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
@@ -30,32 +32,41 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
%for item in history:
|
%for item in history:
|
||||||
<%
|
<%
|
||||||
if item['Status'] == 'Processed':
|
if item['Status'] == 'Processed':
|
||||||
grade = 'A'
|
grade = 'A'
|
||||||
elif item['Status'] == 'Snatched':
|
elif item['Status'] == 'Snatched':
|
||||||
grade = 'C'
|
grade = 'C'
|
||||||
elif item['Status'] == 'Unprocessed':
|
elif item['Status'] == 'Unprocessed':
|
||||||
grade = 'X'
|
grade = 'X'
|
||||||
else:
|
elif item['Status'] == 'Frozen':
|
||||||
grade = 'U'
|
grade = 'X'
|
||||||
|
else:
|
||||||
fileid = 'unknown'
|
grade = 'U'
|
||||||
if item['URL'].find('nzb') != -1:
|
|
||||||
fileid = 'nzb'
|
fileid = 'unknown'
|
||||||
if item['URL'].find('torrent') != -1:
|
if item['URL'].find('nzb') != -1:
|
||||||
fileid = 'torrent'
|
fileid = 'nzb'
|
||||||
if item['URL'].find('rutracker') != -1:
|
if item['URL'].find('torrent') != -1:
|
||||||
fileid = 'torrent'
|
fileid = 'torrent'
|
||||||
%>
|
if item['URL'].find('magnet:') != -1:
|
||||||
|
fileid = 'torrent'
|
||||||
|
if item['URL'].find('rutracker') != -1:
|
||||||
|
fileid = 'torrent'
|
||||||
|
if item['URL'].find('codeshy') != -1:
|
||||||
|
fileid = 'nzb'
|
||||||
|
|
||||||
|
folder = 'Folder: ' + item['FolderName']
|
||||||
|
|
||||||
|
%>
|
||||||
<tr class="grade${grade}">
|
<tr class="grade${grade}">
|
||||||
<td id="dateadded">${item['DateAdded']}</td>
|
<td id="dateadded">${item['DateAdded']}</td>
|
||||||
<td id="filename">${item['Title']} [<a href="${item['URL']}">${fileid}</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
|
<td id="filename">${cgi.escape(item['Title'], quote=True)} [<a href="${item['URL']}">${fileid}</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
|
||||||
<td id="size">${helpers.bytes_to_mb(item['Size'])}</td>
|
<td id="size">${helpers.bytes_to_mb(item['Size'])}</td>
|
||||||
<td id="status">${item['Status']}</td>
|
<td title="${folder}" id="status">${item['Status']}</td>
|
||||||
<td id="action">[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&redirect=history', $(this),'table')" data-success="Retrying download of '${item['Title']}'">retry</a>][<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history',$(this),'table')" data-success="Looking for a new version of '${item['Title']}'">new</a>]</td>
|
<td id="action">[<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&redirect=history', $(this),'table')" data-success="Retrying download of '${cgi.escape(item['Title'], quote=True)}'">retry</a>][<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history',$(this),'table')" data-success="Looking for a new version of '${cgi.escape(item['Title'], quote=True)}'">new</a>]</td>
|
||||||
<td id="delete"><a href="#" onclick="doAjaxCall('clearhistory?date_added=${item['DateAdded']}&title=${item['Title']}',$(this),'table')" data-success="${item['Title']} cleared from history"><img src="interfaces/default/images/trashcan.png" height="18" width="18" id="trashcan" title="Clear this item from the history"></a>
|
<td id="delete"><a href="javascript:void(0)" onclick="doAjaxCall('clearhistory?date_added=${item['DateAdded']}&title=${cgi.escape(item['Title'], quote=True)}',$(this),'table')" data-success="${cgi.escape(item['Title'], quote=True)} cleared from history"><img src="interfaces/default/images/trashcan.png" height="18" width="18" id="trashcan" title="Clear this item from the history"></a>
|
||||||
</tr>
|
</tr>
|
||||||
%endfor
|
%endfor
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -80,8 +91,11 @@
|
|||||||
"sInfoFiltered":"(filtered from _MAX_ total items)"},
|
"sInfoFiltered":"(filtered from _MAX_ total items)"},
|
||||||
"iDisplayLength": 25,
|
"iDisplayLength": 25,
|
||||||
"sPaginationType": "full_numbers",
|
"sPaginationType": "full_numbers",
|
||||||
"aaSorting": []
|
"aaSorting": [],
|
||||||
|
"fnDrawCallback": function (o) {
|
||||||
|
// Jump to top of page
|
||||||
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
resetFilters("history");
|
resetFilters("history");
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 109 B |
|
Before Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 114 B |
|
Before Width: | Height: | Size: 96 B |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -10,7 +10,7 @@
|
|||||||
<th id="albumart"></th>
|
<th id="albumart"></th>
|
||||||
<th id="name">Artist Name</th>
|
<th id="name">Artist Name</th>
|
||||||
<th id="status">Status</th>
|
<th id="status">Status</th>
|
||||||
<th id="album">Latest Album</th>
|
<th id="album">Latest Release</th>
|
||||||
<th id="have">Have</th>
|
<th id="have">Have</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -24,129 +24,132 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
|
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
$('#artist_table').dataTable({
|
$('#artist_table').dataTable({
|
||||||
"bDestroy": true,
|
"bDestroy": true,
|
||||||
"aoColumnDefs": [
|
"aoColumnDefs": [
|
||||||
{
|
{
|
||||||
"bSortable": false,
|
"bSortable": false,
|
||||||
"aTargets": [ 0 ],
|
"aTargets": [0],
|
||||||
"mData":"ArtistID",
|
"mData":"ArtistID",
|
||||||
"mRender": function ( data, type, full ) {
|
"mRender": function ( data, type, full ) {
|
||||||
return '<div id="artistImg"><img class="albumArt" alt="" id="'+ data + '" src="artwork/thumbs/artist/' + data + '"/></div>';
|
return '<div id="artistImg"><img class="albumArt" alt="" id="'+ data + '" data-src="artwork/thumbs/artist/' + data + '"/></div>';
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"aTargets":[1],
|
|
||||||
"mDataProp":"ArtistSortName",
|
|
||||||
"mRender":function (data,type,full) {
|
|
||||||
|
|
||||||
return '<span title="' + full['ArtistID'] + '"></span><a href="artistPage?ArtistID=' + full['ArtistID'] + '">' + full['ArtistName'] + '</a>'
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{"aTargets":[2],"mDataProp":"Status"},
|
|
||||||
{
|
|
||||||
"aTargets":[3],
|
|
||||||
"mDataProp":"LatestAlbum",
|
|
||||||
"mRender":function(data,type,full){
|
|
||||||
artist = full;
|
|
||||||
if (artist['ReleaseDate'] && artist['LatestAlbum'])
|
|
||||||
{
|
|
||||||
releasedate = artist['ReleaseDate'];
|
|
||||||
albumdisplay = '<i>' + artist['LatestAlbum'] + '</i> (' + artist['ReleaseDate'] + ')';
|
|
||||||
}
|
|
||||||
else if(artist['LatestAlbum'])
|
|
||||||
{
|
|
||||||
releasedate = '';
|
|
||||||
albumdisplay = '<i>' + artist['LatestAlbum'] + '</i>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
releasedate = '';
|
|
||||||
albumdisplay = '<i>None</i>';
|
|
||||||
}
|
|
||||||
if (artist['ReleaseInFuture'] === 'True')
|
|
||||||
{
|
|
||||||
grade = 'gradeA';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
grade = 'gradeZ';
|
|
||||||
}
|
|
||||||
artist['Grade'] = grade;
|
|
||||||
return '<span title="' + releasedate + '"></span><a href="albumPage?AlbumID=' + full['AlbumID'] + '">' + albumdisplay + '</a>'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"aTargets":[4],
|
|
||||||
"mDataProp":"HaveTracks",
|
|
||||||
"mRender":function(data,type,full){
|
|
||||||
if(full['TotalTracks'] > 0)
|
|
||||||
{
|
|
||||||
percent = (full['HaveTracks']*100.0)/full['TotalTracks']
|
|
||||||
if(percent > 100){
|
|
||||||
percent = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
full['TotalTracks'] = '?';
|
|
||||||
percent = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '<span title="' + percent + '"></span><div class="progress-container"><div style="width:' + percent + '%"><div class="havetracks">' + full['HaveTracks'] + '/' + full['TotalTracks'] + '</div></div></div>';
|
|
||||||
|
|
||||||
}},
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
"oLanguage": {
|
|
||||||
"sSearch": "",
|
|
||||||
"sLengthMenu":"Show _MENU_ artists per page",
|
|
||||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ artists",
|
|
||||||
"sInfoEmpty":"Showing 0 to 0 of 0 artists",
|
|
||||||
"sInfoFiltered":"(filtered from _MAX_ total artists)",
|
|
||||||
"sEmptyTable": " ",
|
|
||||||
},
|
},
|
||||||
"bStateSave": true,
|
{
|
||||||
"iDisplayLength": 50,
|
"aTargets":[1],
|
||||||
"sPaginationType": "full_numbers",
|
"mDataProp":"ArtistSortName",
|
||||||
"bProcessing": true,
|
"mRender":function (data,type,full) {
|
||||||
"bServerSide": true,
|
return '<span title="' + full['ArtistID'] + '"></span><a href="artistPage?ArtistID=' + full['ArtistID'] + '">' + full['ArtistName'] + '</a>'
|
||||||
"sAjaxSource": 'getArtists.json',
|
}
|
||||||
"fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ){
|
},
|
||||||
$('td', nRow).closest('tr').addClass(aData['Grade'])
|
{
|
||||||
nRow.children[0].id = 'albumart';
|
"aTargets":[2],"mDataProp":"Status"
|
||||||
nRow.children[1].id = 'name';
|
},
|
||||||
nRow.children[2].id = 'status'
|
{
|
||||||
nRow.children[3].id = 'album'
|
"aTargets":[3],
|
||||||
nRow.children[4].id = 'have'
|
"mDataProp":"LatestAlbum",
|
||||||
return nRow;
|
"mRender":function(data,type,full){
|
||||||
},
|
artist = full;
|
||||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
if (artist['ReleaseDate'] && artist['LatestAlbum'])
|
||||||
/* Add some extra data to the sender */
|
{
|
||||||
$.getJSON( sSource, aoData, function (json) { fnCallback(json) } )
|
releasedate = artist['ReleaseDate'];
|
||||||
|
albumdisplay = '<i>' + artist['LatestAlbum'] + '</i> (' + artist['ReleaseDate'] + ')';
|
||||||
|
}
|
||||||
|
else if(artist['LatestAlbum'])
|
||||||
|
{
|
||||||
|
releasedate = '';
|
||||||
|
albumdisplay = '<i>' + artist['LatestAlbum'] + '</i>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
releasedate = '';
|
||||||
|
albumdisplay = '<i>None</i>';
|
||||||
|
}
|
||||||
|
if (artist['ReleaseInFuture'] === 'True')
|
||||||
|
{
|
||||||
|
grade = 'gradeA';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
grade = 'gradeZ';
|
||||||
|
}
|
||||||
|
artist['Grade'] = grade;
|
||||||
|
return '<span title="' + releasedate + '"></span><a href="albumPage?AlbumID=' + full['AlbumID'] + '">' + albumdisplay + '</a>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aTargets":[4],
|
||||||
|
"mDataProp":"HaveTracks",
|
||||||
|
"mRender":function(data,type,full){
|
||||||
|
if(full['TotalTracks'] > 0)
|
||||||
|
{
|
||||||
|
percent = (full['HaveTracks']*100.0)/full['TotalTracks']
|
||||||
|
if(percent > 100){
|
||||||
|
percent = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
full['TotalTracks'] = '?';
|
||||||
|
percent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<span title="' + percent + '"></span><div class="progress-container"><div style="width:' + percent + '%"><div class="havetracks">' + full['HaveTracks'] + '/' + full['TotalTracks'] + '</div></div></div>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"oLanguage": {
|
||||||
|
"sSearch": "",
|
||||||
|
"sLengthMenu":"Show _MENU_ artists per page",
|
||||||
|
"sInfo":"Showing _START_ to _END_ of _TOTAL_ artists",
|
||||||
|
"sInfoEmpty":"Showing 0 to 0 of 0 artists",
|
||||||
|
"sInfoFiltered":"(filtered from _MAX_ total artists)",
|
||||||
|
"sEmptyTable": " ",
|
||||||
},
|
},
|
||||||
"fnInitComplete": function(oSettings, json)
|
"bStateSave": true,
|
||||||
{
|
"iDisplayLength": 50,
|
||||||
}});
|
"sPaginationType": "full_numbers",
|
||||||
|
"bProcessing": true,
|
||||||
|
"bServerSide": true,
|
||||||
|
"sAjaxSource": 'getArtists.json',
|
||||||
|
"fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
||||||
|
$('td', nRow).closest('tr').addClass(aData['Grade'])
|
||||||
|
nRow.children[0].id = 'albumart';
|
||||||
|
nRow.children[1].id = 'name';
|
||||||
|
nRow.children[2].id = 'status'
|
||||||
|
nRow.children[3].id = 'album'
|
||||||
|
nRow.children[4].id = 'have'
|
||||||
|
return nRow;
|
||||||
|
},
|
||||||
|
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||||
|
/* Add some extra data to the sender */
|
||||||
|
$.getJSON( sSource, aoData, function (json) { fnCallback(json) } )
|
||||||
|
},
|
||||||
|
"fnInitComplete": function(oSettings, json)
|
||||||
|
{
|
||||||
|
},
|
||||||
|
"fnDrawCallback": function (o) {
|
||||||
|
// Jump to top of page
|
||||||
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#artist_table').on("draw.dt", function () {
|
||||||
|
$("img").unveil();
|
||||||
|
});
|
||||||
|
|
||||||
|
resetFilters("artist or album");
|
||||||
|
}
|
||||||
|
|
||||||
resetFilters("artist or album");
|
$(document).ready(function() {
|
||||||
}
|
initThisPage();
|
||||||
|
});
|
||||||
$(document).ready(function() {
|
$(window).load(function(){
|
||||||
initThisPage();
|
initFancybox();
|
||||||
});
|
refreshLoadArtist();
|
||||||
$(window).load(function(){
|
});
|
||||||
initFancybox();
|
|
||||||
refreshLoadArtist();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|||||||
@@ -1,648 +0,0 @@
|
|||||||
/*
|
|
||||||
* File: jquery.dataTables.min.js
|
|
||||||
* Version: 1.8.1
|
|
||||||
* Author: Allan Jardine (www.sprymedia.co.uk)
|
|
||||||
* Info: www.datatables.net
|
|
||||||
*
|
|
||||||
* Copyright 2008-2011 Allan Jardine, all rights reserved.
|
|
||||||
*
|
|
||||||
* This source file is free software, under either the GPL v2 license or a
|
|
||||||
* BSD style license, as supplied with this software.
|
|
||||||
*
|
|
||||||
* This source file 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 license files for details.
|
|
||||||
*/
|
|
||||||
(function (i, wa, p) {
|
|
||||||
i.fn.dataTableSettings = [];
|
|
||||||
var D = i.fn.dataTableSettings; i.fn.dataTableExt = {};
|
|
||||||
var o = i.fn.dataTableExt;
|
|
||||||
o.sVersion = "1.8.1";
|
|
||||||
o.sErrMode = "alert";
|
|
||||||
o.iApiIndex = 0; o.oApi = {};
|
|
||||||
o.afnFiltering = [];
|
|
||||||
o.aoFeatures = [];
|
|
||||||
o.ofnSearch = {};
|
|
||||||
o.afnSortData = [];
|
|
||||||
o.oStdClasses = {
|
|
||||||
sPagePrevEnabled: "paginate_enabled_previous",
|
|
||||||
sPagePrevDisabled: "paginate_disabled_previous",
|
|
||||||
sPageNextEnabled: "paginate_enabled_next",
|
|
||||||
sPageNextDisabled: "paginate_disabled_next",
|
|
||||||
sPageJUINext: "", sPageJUIPrev: "",
|
|
||||||
sPageButton: "paginate_button",
|
|
||||||
sPageButtonActive: "paginate_active",
|
|
||||||
sPageButtonStaticDisabled: "paginate_button paginate_button_disabled",
|
|
||||||
sPageFirst: "first",
|
|
||||||
sPagePrevious: "previous",
|
|
||||||
sPageNext: "next",
|
|
||||||
sPageLast: "last",
|
|
||||||
sStripOdd: "odd",
|
|
||||||
sStripEven: "even",
|
|
||||||
sRowEmpty: "dataTables_empty",
|
|
||||||
sWrapper: "dataTables_wrapper",
|
|
||||||
sFilter: "dataTables_filter",
|
|
||||||
sInfo: "dataTables_info",
|
|
||||||
sPaging: "dataTables_paginate paging_",
|
|
||||||
sLength: "dataTables_length",
|
|
||||||
sProcessing: "dataTables_processing",
|
|
||||||
sSortAsc: "sorting_asc",
|
|
||||||
sSortDesc: "sorting_desc",
|
|
||||||
sSortable: "sorting",
|
|
||||||
sSortableAsc: "sorting_asc_disabled",
|
|
||||||
sSortableDesc: "sorting_desc_disabled",
|
|
||||||
sSortableNone: "sorting_disabled",
|
|
||||||
sSortColumn: "sorting_",
|
|
||||||
sSortJUIAsc: "",
|
|
||||||
sSortJUIDesc: "",
|
|
||||||
sSortJUI: "",
|
|
||||||
sSortJUIAscAllowed: "",
|
|
||||||
sSortJUIDescAllowed: "", sSortJUIWrapper: "", sSortIcon: "", sScrollWrapper: "dataTables_scroll",
|
|
||||||
sScrollHead: "dataTables_scrollHead",
|
|
||||||
sScrollHeadInner: "dataTables_scrollHeadInner",
|
|
||||||
sScrollBody: "dataTables_scrollBody",
|
|
||||||
sScrollFoot: "dataTables_scrollFoot",
|
|
||||||
sScrollFootInner: "dataTables_scrollFootInner",
|
|
||||||
sFooterTH: ""
|
|
||||||
}; o.oJUIClasses = { sPagePrevEnabled: "fg-button ui-button ui-state-default ui-corner-left",
|
|
||||||
sPagePrevDisabled: "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
|
|
||||||
sPageNextEnabled: "fg-button ui-button ui-state-default ui-corner-right",
|
|
||||||
sPageNextDisabled: "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
|
|
||||||
sPageJUINext: "ui-icon ui-icon-circle-arrow-e",
|
|
||||||
sPageJUIPrev: "ui-icon ui-icon-circle-arrow-w",
|
|
||||||
sPageButton: "fg-button ui-button ui-state-default",
|
|
||||||
sPageButtonActive: "fg-button ui-button ui-state-default ui-state-disabled",
|
|
||||||
sPageButtonStaticDisabled: "fg-button ui-button ui-state-default ui-state-disabled",
|
|
||||||
sPageFirst: "first ui-corner-tl ui-corner-bl",
|
|
||||||
sPagePrevious: "previous",
|
|
||||||
sPageNext: "next",
|
|
||||||
sPageLast: "last ui-corner-tr ui-corner-br",
|
|
||||||
sStripOdd: "odd",
|
|
||||||
sStripEven: "even",
|
|
||||||
sRowEmpty: "dataTables_empty",
|
|
||||||
sWrapper: "dataTables_wrapper",
|
|
||||||
sFilter: "dataTables_filter",
|
|
||||||
sInfo: "dataTables_info",
|
|
||||||
sPaging: "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",
|
|
||||||
sLength: "dataTables_length",
|
|
||||||
sProcessing: "dataTables_processing",
|
|
||||||
sSortAsc: "ui-state-default",
|
|
||||||
sSortDesc: "ui-state-default",
|
|
||||||
sSortable: "ui-state-default",
|
|
||||||
sSortableAsc: "ui-state-default",
|
|
||||||
sSortableDesc: "ui-state-default",
|
|
||||||
sSortableNone: "ui-state-default",
|
|
||||||
sSortColumn: "sorting_",
|
|
||||||
sSortJUIAsc: "css_right ui-icon ui-icon-triangle-1-n",
|
|
||||||
sSortJUIDesc: "css_right ui-icon ui-icon-triangle-1-s",
|
|
||||||
sSortJUI: "css_right ui-icon ui-icon-carat-2-n-s",
|
|
||||||
sSortJUIAscAllowed: "css_right ui-icon ui-icon-carat-1-n",
|
|
||||||
sSortJUIDescAllowed: "css_right ui-icon ui-icon-carat-1-s",
|
|
||||||
sSortJUIWrapper: "DataTables_sort_wrapper",
|
|
||||||
sSortIcon: "DataTables_sort_icon",
|
|
||||||
sScrollWrapper: "dataTables_scroll",
|
|
||||||
sScrollHead: "dataTables_scrollHead ui-state-default",
|
|
||||||
sScrollHeadInner: "dataTables_scrollHeadInner",
|
|
||||||
sScrollBody: "dataTables_scrollBody",
|
|
||||||
sScrollFoot: "dataTables_scrollFoot ui-state-default",
|
|
||||||
sScrollFootInner: "dataTables_scrollFootInner",
|
|
||||||
sFooterTH: "ui-state-default"
|
|
||||||
}; o.oPagination = { two_button: { fnInit: function (g, l, r) {
|
|
||||||
var s, w, y; if (g.bJUI) {
|
|
||||||
s = p.createElement("a"); w = p.createElement("a"); y = p.createElement("span"); y.className = g.oClasses.sPageJUINext; w.appendChild(y); y = p.createElement("span"); y.className = g.oClasses.sPageJUIPrev;
|
|
||||||
s.appendChild(y)
|
|
||||||
}
|
|
||||||
else { s = p.createElement("div"); w = p.createElement("div") } s.className = g.oClasses.sPagePrevDisabled; w.className = g.oClasses.sPageNextDisabled; s.title = g.oLanguage.oPaginate.sPrevious; w.title = g.oLanguage.oPaginate.sNext; l.appendChild(s); l.appendChild(w); i(s).bind("click.DT", function () { g.oApi._fnPageChange(g, "previous") && r(g) }); i(w).bind("click.DT", function () { g.oApi._fnPageChange(g, "next") && r(g) }); i(s).bind("selectstart.DT", function () { return false }); i(w).bind("selectstart.DT", function () { return false });
|
|
||||||
|
|
||||||
if (g.sTableId !== "" && typeof g.aanFeatures.p == "undefined") {
|
|
||||||
l.setAttribute("id", g.sTableId + "_paginate");
|
|
||||||
s.setAttribute("id", g.sTableId + "_previous");
|
|
||||||
w.setAttribute("id", g.sTableId + "_next")
|
|
||||||
}
|
|
||||||
}, fnUpdate: function (g) {
|
|
||||||
if (g.aanFeatures.p) for (var l = g.aanFeatures.p, r = 0, s = l.length; r < s; r++)
|
|
||||||
if (l[r].childNodes.length !== 0) {
|
|
||||||
l[r].childNodes[0].className = g._iDisplayStart === 0 ? g.oClasses.sPagePrevDisabled : g.oClasses.sPagePrevEnabled; l[r].childNodes[1].className = g.fnDisplayEnd() == g.fnRecordsDisplay() ? g.oClasses.sPageNextDisabled :
|
|
||||||
g.oClasses.sPageNextEnabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, iFullNumbersShowPages: 5, full_numbers: { fnInit: function (g, l, r) {
|
|
||||||
var s = p.createElement("span"), w = p.createElement("span"), y = p.createElement("span"), G = p.createElement("span"), x = p.createElement("span"); s.innerHTML = g.oLanguage.oPaginate.sFirst; w.innerHTML = g.oLanguage.oPaginate.sPrevious; G.innerHTML = g.oLanguage.oPaginate.sNext; x.innerHTML = g.oLanguage.oPaginate.sLast; var v = g.oClasses; s.className = v.sPageButton + " " + v.sPageFirst; w.className = v.sPageButton + " " + v.sPagePrevious; G.className =
|
|
||||||
v.sPageButton + " " + v.sPageNext; x.className = v.sPageButton + " " + v.sPageLast; l.appendChild(s); l.appendChild(w); l.appendChild(y); l.appendChild(G); l.appendChild(x); i(s).bind("click.DT", function () { g.oApi._fnPageChange(g, "first") && r(g) }); i(w).bind("click.DT", function () { g.oApi._fnPageChange(g, "previous") && r(g) }); i(G).bind("click.DT", function () { g.oApi._fnPageChange(g, "next") && r(g) }); i(x).bind("click.DT", function () { g.oApi._fnPageChange(g, "last") && r(g) }); i("span", l).bind("mousedown.DT", function () { return false }).bind("selectstart.DT",
|
|
||||||
|
|
||||||
function () { return false }); if (g.sTableId !== "" && typeof g.aanFeatures.p == "undefined") { l.setAttribute("id", g.sTableId + "_paginate"); s.setAttribute("id", g.sTableId + "_first"); w.setAttribute("id", g.sTableId + "_previous"); G.setAttribute("id", g.sTableId + "_next"); x.setAttribute("id", g.sTableId + "_last") }
|
|
||||||
}, fnUpdate: function (g, l) {
|
|
||||||
if (g.aanFeatures.p) {
|
|
||||||
var r = o.oPagination.iFullNumbersShowPages, s = Math.floor(r / 2), w = Math.ceil(g.fnRecordsDisplay() / g._iDisplayLength), y = Math.ceil(g._iDisplayStart / g._iDisplayLength) + 1, G =
|
|
||||||
"", x, v = g.oClasses; if (w < r) { s = 1; x = w } else if (y <= s) { s = 1; x = r } else if (y >= w - s) { s = w - r + 1; x = w } else { s = y - Math.ceil(r / 2) + 1; x = s + r - 1 } for (r = s; r <= x; r++) G += y != r ? '<span class="' + v.sPageButton + '">' + r + "</span>" : '<span class="' + v.sPageButtonActive + '">' + r + "</span>"; x = g.aanFeatures.p; var z, Y = function (L) { g._iDisplayStart = (this.innerHTML * 1 - 1) * g._iDisplayLength; l(g); L.preventDefault() }, V = function () { return false }; r = 0; for (s = x.length; r < s; r++) if (x[r].childNodes.length !== 0) {
|
|
||||||
z = i("span:eq(2)", x[r]); z.html(G); i("span", z).bind("click.DT",
|
|
||||||
Y).bind("mousedown.DT", V).bind("selectstart.DT", V); z = x[r].getElementsByTagName("span"); z = [z[0], z[1], z[z.length - 2], z[z.length - 1]]; i(z).removeClass(v.sPageButton + " " + v.sPageButtonActive + " " + v.sPageButtonStaticDisabled); if (y == 1) { z[0].className += " " + v.sPageButtonStaticDisabled; z[1].className += " " + v.sPageButtonStaticDisabled } else { z[0].className += " " + v.sPageButton; z[1].className += " " + v.sPageButton } if (w === 0 || y == w || g._iDisplayLength == -1) {
|
|
||||||
z[2].className += " " + v.sPageButtonStaticDisabled; z[3].className += " " +
|
|
||||||
v.sPageButtonStaticDisabled
|
|
||||||
} else { z[2].className += " " + v.sPageButton; z[3].className += " " + v.sPageButton }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; o.oSort = { "string-asc": function (g, l) { if (typeof g != "string") g = ""; if (typeof l != "string") l = ""; g = g.toLowerCase(); l = l.toLowerCase(); return g < l ? -1 : g > l ? 1 : 0 }, "string-desc": function (g, l) { if (typeof g != "string") g = ""; if (typeof l != "string") l = ""; g = g.toLowerCase(); l = l.toLowerCase(); return g < l ? 1 : g > l ? -1 : 0 }, "html-asc": function (g, l) {
|
|
||||||
g = g.replace(/<.*?>/g, "").toLowerCase(); l = l.replace(/<.*?>/g, "").toLowerCase(); return g <
|
|
||||||
l ? -1 : g > l ? 1 : 0
|
|
||||||
}, "html-desc": function (g, l) { g = g.replace(/<.*?>/g, "").toLowerCase(); l = l.replace(/<.*?>/g, "").toLowerCase(); return g < l ? 1 : g > l ? -1 : 0 }, "date-asc": function (g, l) { g = Date.parse(g); l = Date.parse(l); if (isNaN(g) || g === "") g = Date.parse("01/01/1970 00:00:00"); if (isNaN(l) || l === "") l = Date.parse("01/01/1970 00:00:00"); return g - l }, "date-desc": function (g, l) {
|
|
||||||
g = Date.parse(g); l = Date.parse(l); if (isNaN(g) || g === "") g = Date.parse("01/01/1970 00:00:00"); if (isNaN(l) || l === "") l = Date.parse("01/01/1970 00:00:00"); return l -
|
|
||||||
g
|
|
||||||
}, "numeric-asc": function (g, l) { return (g == "-" || g === "" ? 0 : g * 1) - (l == "-" || l === "" ? 0 : l * 1) }, "numeric-desc": function (g, l) { return (l == "-" || l === "" ? 0 : l * 1) - (g == "-" || g === "" ? 0 : g * 1) }
|
|
||||||
}; o.aTypes = [function (g) { if (typeof g == "number") return "numeric"; else if (typeof g != "string") return null; var l, r = false; l = g.charAt(0); if ("0123456789-".indexOf(l) == -1) return null; for (var s = 1; s < g.length; s++) { l = g.charAt(s); if ("0123456789.".indexOf(l) == -1) return null; if (l == ".") { if (r) return null; r = true } } return "numeric" }, function (g) {
|
|
||||||
var l = Date.parse(g);
|
|
||||||
|
|
||||||
if (l !== null && !isNaN(l) || typeof g == "string" && g.length === 0) return "date"; return null
|
|
||||||
}, function (g) { if (typeof g == "string" && g.indexOf("<") != -1 && g.indexOf(">") != -1) return "html"; return null } ]; o.fnVersionCheck = function (g) { var l = function (x, v) { for (; x.length < v; ) x += "0"; return x }, r = o.sVersion.split("."); g = g.split("."); for (var s = "", w = "", y = 0, G = g.length; y < G; y++) { s += l(r[y], 3); w += l(g[y], 3) } return parseInt(s, 10) >= parseInt(w, 10) }; o._oExternConfig = { iNextUnique: 0 }; i.fn.dataTable = function (g) {
|
|
||||||
function l() {
|
|
||||||
this.fnRecordsTotal =
|
|
||||||
function () { return this.oFeatures.bServerSide ? parseInt(this._iRecordsTotal, 10) : this.aiDisplayMaster.length }; this.fnRecordsDisplay = function () { return this.oFeatures.bServerSide ? parseInt(this._iRecordsDisplay, 10) : this.aiDisplay.length }; this.fnDisplayEnd = function () { return this.oFeatures.bServerSide ? this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ? this._iDisplayStart + this.aiDisplay.length : Math.min(this._iDisplayStart + this._iDisplayLength, this._iRecordsDisplay) : this._iDisplayEnd }; this.sInstance =
|
|
||||||
this.oInstance = null; this.oFeatures = { bPaginate: true, bLengthChange: true, bFilter: true, bSort: true, bInfo: true, bAutoWidth: true, bProcessing: false, bSortClasses: true, bStateSave: false, bServerSide: false, bDeferRender: false }; this.oScroll = { sX: "", sXInner: "", sY: "", bCollapse: false, bInfinite: false, iLoadGap: 100, iBarWidth: 0, bAutoCss: true }; this.aanFeatures = [];
|
|
||||||
this.oLanguage = { sProcessing: "Processing...", sLengthMenu: "Show _MENU_ artists per page", sZeroRecords: "No matching records found", sEmptyTable: "",
|
|
||||||
sLoadingRecords: "Loading...", sInfo: "Showing _START_ to _END_ of _TOTAL_ artists", sInfoEmpty: "Showing 0 to 0 of 0 artists", sInfoFiltered: "(filtered from _MAX_ total artists)", sInfoPostFix: "", sSearch: "Filter:", sUrl: "", oPaginate: { sFirst: "First", sPrevious: "Previous", sNext: "Next", sLast: "Last" }, fnInfoCallback: null
|
|
||||||
}; this.aoData = []; this.aiDisplay = []; this.aiDisplayMaster = []; this.aoColumns = []; this.aoHeader = []; this.aoFooter = []; this.iNextId = 0; this.asDataSearch = []; this.oPreviousSearch = { sSearch: "", bRegex: false,
|
|
||||||
bSmart: true
|
|
||||||
}; this.aoPreSearchCols = []; this.aaSorting = [[0, "asc", 0]]; this.aaSortingFixed = null; this.asStripClasses = []; this.asDestoryStrips = []; this.sDestroyWidth = 0; this.fnFooterCallback = this.fnHeaderCallback = this.fnRowCallback = null; this.aoDrawCallback = []; this.fnInitComplete = this.fnPreDrawCallback = null; this.sTableId = ""; this.nTableWrapper = this.nTBody = this.nTFoot = this.nTHead = this.nTable = null; this.bInitialised = this.bDeferLoading = false; this.aoOpenRows = []; this.sDom = "lfrtip"; this.sPaginationType = "two_button";
|
|
||||||
this.iCookieDuration = 7200; this.sCookiePrefix = "SpryMedia_DataTables_"; this.fnCookieCallback = null; this.aoStateSave = []; this.aoStateLoad = []; this.sAjaxSource = this.oLoadedState = null; this.sAjaxDataProp = "aaData"; this.bAjaxDataGet = true; this.jqXHR = null; this.fnServerData = function (a, b, c, d) { d.jqXHR = i.ajax({ url: a, data: b, success: c, dataType: "json", cache: false, error: function (f, e) { e == "parsererror" && alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.") } }) };
|
|
||||||
this.fnFormatNumber = function (a) { if (a < 1E3) return a; else { var b = a + ""; a = b.split(""); var c = ""; b = b.length; for (var d = 0; d < b; d++) { if (d % 3 === 0 && d !== 0) c = "," + c; c = a[b - d - 1] + c } } return c }; this.aLengthMenu = [10, 25, 50, 100]; this.bDrawing = this.iDraw = 0; this.iDrawError = -1; this._iDisplayLength = 10; this._iDisplayStart = 0; this._iDisplayEnd = 10; this._iRecordsDisplay = this._iRecordsTotal = 0; this.bJUI = false; this.oClasses = o.oStdClasses; this.bSortCellsTop = this.bSorted = this.bFiltered = false; this.oInit = null
|
|
||||||
} function r(a) {
|
|
||||||
return function () {
|
|
||||||
var b =
|
|
||||||
[A(this[o.iApiIndex])].concat(Array.prototype.slice.call(arguments)); return o.oApi[a].apply(this, b)
|
|
||||||
}
|
|
||||||
} function s(a) {
|
|
||||||
var b, c, d = a.iInitDisplayStart; if (a.bInitialised === false) setTimeout(function () { s(a) }, 200); else {
|
|
||||||
xa(a); V(a); L(a, a.aoHeader); a.nTFoot && L(a, a.aoFooter);
|
|
||||||
K(a, true); a.oFeatures.bAutoWidth && ea(a);
|
|
||||||
b = 0; for (c = a.aoColumns.length; b < c; b++)
|
|
||||||
if (a.aoColumns[b].sWidth !== null) a.aoColumns[b].nTh.style.width = u(a.aoColumns[b].sWidth);
|
|
||||||
if (a.oFeatures.bSort) R(a);
|
|
||||||
else if (a.oFeatures.bFilter) M(a, a.oPreviousSearch);
|
|
||||||
else { a.aiDisplay = a.aiDisplayMaster.slice(); E(a); C(a) } if (a.sAjaxSource !== null && !a.oFeatures.bServerSide) a.fnServerData.call(a.oInstance, a.sAjaxSource, [], function (f) { var e = f; if (a.sAjaxDataProp !== "") e = Z(a.sAjaxDataProp)(f); for (b = 0; b < e.length; b++) v(a, e[b]); a.iInitDisplayStart = d; if (a.oFeatures.bSort) R(a); else { a.aiDisplay = a.aiDisplayMaster.slice(); E(a); C(a) } K(a, false); w(a, f) }, a); else if (!a.oFeatures.bServerSide) { K(a, false); w(a) }
|
|
||||||
}
|
|
||||||
} function w(a, b) {
|
|
||||||
a._bInitComplete = true; if (typeof a.fnInitComplete == "function") typeof b !=
|
|
||||||
"undefined" ? a.fnInitComplete.call(a.oInstance, a, b) : a.fnInitComplete.call(a.oInstance, a)
|
|
||||||
} function y(a, b, c) {
|
|
||||||
n(a.oLanguage, b, "sProcessing"); n(a.oLanguage, b, "sLengthMenu"); n(a.oLanguage, b, "sEmptyTable"); n(a.oLanguage, b, "sLoadingRecords"); n(a.oLanguage, b, "sZeroRecords"); n(a.oLanguage, b, "sInfo"); n(a.oLanguage, b, "sInfoEmpty"); n(a.oLanguage, b, "sInfoFiltered"); n(a.oLanguage, b, "sInfoPostFix"); n(a.oLanguage, b, "sSearch"); if (typeof b.oPaginate != "undefined") {
|
|
||||||
n(a.oLanguage.oPaginate, b.oPaginate, "sFirst"); n(a.oLanguage.oPaginate,
|
|
||||||
b.oPaginate, "sPrevious"); n(a.oLanguage.oPaginate, b.oPaginate, "sNext"); n(a.oLanguage.oPaginate, b.oPaginate, "sLast")
|
|
||||||
} typeof b.sEmptyTable == "undefined" && typeof b.sZeroRecords != "undefined" && n(a.oLanguage, b, "sZeroRecords", "sEmptyTable"); typeof b.sLoadingRecords == "undefined" && typeof b.sZeroRecords != "undefined" && n(a.oLanguage, b, "sZeroRecords", "sLoadingRecords"); c && s(a)
|
|
||||||
} function G(a, b) {
|
|
||||||
var c = a.aoColumns.length; b = { sType: null, _bAutoType: true, bVisible: true, bSearchable: true, bSortable: true, asSorting: ["asc", "desc"],
|
|
||||||
sSortingClass: a.oClasses.sSortable, sSortingClassJUI: a.oClasses.sSortJUI, sTitle: b ? b.innerHTML : "", sName: "", sWidth: null, sWidthOrig: null, sClass: null, fnRender: null, bUseRendered: true, iDataSort: c, mDataProp: c, fnGetData: null, fnSetData: null, sSortDataType: "std", sDefaultContent: null, sContentPadding: "", nTh: b ? b : p.createElement("th"), nTf: null
|
|
||||||
}; a.aoColumns.push(b); if (typeof a.aoPreSearchCols[c] == "undefined" || a.aoPreSearchCols[c] === null) a.aoPreSearchCols[c] = { sSearch: "", bRegex: false, bSmart: true }; else {
|
|
||||||
if (typeof a.aoPreSearchCols[c].bRegex ==
|
|
||||||
"undefined") a.aoPreSearchCols[c].bRegex = true; if (typeof a.aoPreSearchCols[c].bSmart == "undefined") a.aoPreSearchCols[c].bSmart = true
|
|
||||||
} x(a, c, null)
|
|
||||||
} function x(a, b, c) {
|
|
||||||
b = a.aoColumns[b]; if (typeof c != "undefined" && c !== null) {
|
|
||||||
if (typeof c.sType != "undefined") { b.sType = c.sType; b._bAutoType = false } n(b, c, "bVisible"); n(b, c, "bSearchable"); n(b, c, "bSortable"); n(b, c, "sTitle"); n(b, c, "sName"); n(b, c, "sWidth"); n(b, c, "sWidth", "sWidthOrig"); n(b, c, "sClass"); n(b, c, "fnRender"); n(b, c, "bUseRendered"); n(b, c, "iDataSort"); n(b, c, "mDataProp");
|
|
||||||
n(b, c, "asSorting"); n(b, c, "sSortDataType"); n(b, c, "sDefaultContent"); n(b, c, "sContentPadding")
|
|
||||||
} b.fnGetData = Z(b.mDataProp); b.fnSetData = ya(b.mDataProp); if (!a.oFeatures.bSort) b.bSortable = false; if (!b.bSortable || i.inArray("asc", b.asSorting) == -1 && i.inArray("desc", b.asSorting) == -1) { b.sSortingClass = a.oClasses.sSortableNone; b.sSortingClassJUI = "" } else if (b.bSortable || i.inArray("asc", b.asSorting) == -1 && i.inArray("desc", b.asSorting) == -1) { b.sSortingClass = a.oClasses.sSortable; b.sSortingClassJUI = a.oClasses.sSortJUI } else if (i.inArray("asc",
|
|
||||||
b.asSorting) != -1 && i.inArray("desc", b.asSorting) == -1) { b.sSortingClass = a.oClasses.sSortableAsc; b.sSortingClassJUI = a.oClasses.sSortJUIAscAllowed } else if (i.inArray("asc", b.asSorting) == -1 && i.inArray("desc", b.asSorting) != -1) { b.sSortingClass = a.oClasses.sSortableDesc; b.sSortingClassJUI = a.oClasses.sSortJUIDescAllowed }
|
|
||||||
} function v(a, b) {
|
|
||||||
var c; c = typeof b.length == "number" ? b.slice() : i.extend(true, {}, b); b = a.aoData.length; var d = { nTr: null, _iId: a.iNextId++, _aData: c, _anHidden: [], _sRowStripe: "" }; a.aoData.push(d); for (var f,
|
|
||||||
e = 0, h = a.aoColumns.length; e < h; e++) { c = a.aoColumns[e]; typeof c.fnRender == "function" && c.bUseRendered && c.mDataProp !== null && N(a, b, e, c.fnRender({ iDataRow: b, iDataColumn: e, aData: d._aData, oSettings: a })); if (c._bAutoType && c.sType != "string") { f = H(a, b, e, "type"); if (f !== null && f !== "") { f = fa(f); if (c.sType === null) c.sType = f; else if (c.sType != f) c.sType = "string" } } } a.aiDisplayMaster.push(b); a.oFeatures.bDeferRender || z(a, b); return b
|
|
||||||
} function z(a, b) {
|
|
||||||
var c = a.aoData[b], d; if (c.nTr === null) {
|
|
||||||
c.nTr = p.createElement("tr"); typeof c._aData.DT_RowId !=
|
|
||||||
"undefined" && c.nTr.setAttribute("id", c._aData.DT_RowId); typeof c._aData.DT_RowClass != "undefined" && i(c.nTr).addClass(c._aData.DT_RowClass); for (var f = 0, e = a.aoColumns.length; f < e; f++) {
|
|
||||||
var h = a.aoColumns[f]; d = p.createElement("td"); d.innerHTML = typeof h.fnRender == "function" && (!h.bUseRendered || h.mDataProp === null) ? h.fnRender({ iDataRow: b, iDataColumn: f, aData: c._aData, oSettings: a }) : H(a, b, f, "display"); if (h.sClass !== null) d.className = h.sClass; if (h.bVisible) { c.nTr.appendChild(d); c._anHidden[f] = null } else c._anHidden[f] =
|
|
||||||
d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} function Y(a) {
|
|
||||||
var b, c, d, f, e, h, j, k, m; if (a.bDeferLoading || a.sAjaxSource === null) { j = a.nTBody.childNodes; b = 0; for (c = j.length; b < c; b++) if (j[b].nodeName.toUpperCase() == "TR") { k = a.aoData.length; a.aoData.push({ nTr: j[b], _iId: a.iNextId++, _aData: [], _anHidden: [], _sRowStripe: "" }); a.aiDisplayMaster.push(k); h = j[b].childNodes; d = e = 0; for (f = h.length; d < f; d++) { m = h[d].nodeName.toUpperCase(); if (m == "TD" || m == "TH") { N(a, k, e, i.trim(h[d].innerHTML)); e++ } } } } j = $(a); h = []; b = 0; for (c = j.length; b < c; b++) {
|
|
||||||
d = 0; for (f = j[b].childNodes.length; d <
|
|
||||||
f; d++) { e = j[b].childNodes[d]; m = e.nodeName.toUpperCase(); if (m == "TD" || m == "TH") h.push(e) }
|
|
||||||
} h.length != j.length * a.aoColumns.length && J(a, 1, "Unexpected number of TD elements. Expected " + j.length * a.aoColumns.length + " and got " + h.length + ". DataTables does not support rowspan / colspan in the table body, and there must be one cell for each row/column combination."); d = 0; for (f = a.aoColumns.length; d < f; d++) {
|
|
||||||
if (a.aoColumns[d].sTitle === null) a.aoColumns[d].sTitle = a.aoColumns[d].nTh.innerHTML; j = a.aoColumns[d]._bAutoType;
|
|
||||||
m = typeof a.aoColumns[d].fnRender == "function"; e = a.aoColumns[d].sClass !== null; k = a.aoColumns[d].bVisible; var t, q; if (j || m || e || !k) {
|
|
||||||
b = 0; for (c = a.aoData.length; b < c; b++) {
|
|
||||||
t = h[b * f + d]; if (j && a.aoColumns[d].sType != "string") { q = H(a, b, d, "type"); if (q !== "") { q = fa(q); if (a.aoColumns[d].sType === null) a.aoColumns[d].sType = q; else if (a.aoColumns[d].sType != q) a.aoColumns[d].sType = "string" } } if (m) {
|
|
||||||
q = a.aoColumns[d].fnRender({ iDataRow: b, iDataColumn: d, aData: a.aoData[b]._aData, oSettings: a }); t.innerHTML = q; a.aoColumns[d].bUseRendered &&
|
|
||||||
N(a, b, d, q)
|
|
||||||
} if (e) t.className += " " + a.aoColumns[d].sClass; if (k) a.aoData[b]._anHidden[d] = null; else { a.aoData[b]._anHidden[d] = t; t.parentNode.removeChild(t) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} function V(a) {
|
|
||||||
var b, c, d; a.nTHead.getElementsByTagName("tr"); if (a.nTHead.getElementsByTagName("th").length !== 0) { b = 0; for (d = a.aoColumns.length; b < d; b++) { c = a.aoColumns[b].nTh; a.aoColumns[b].sClass !== null && i(c).addClass(a.aoColumns[b].sClass); if (a.aoColumns[b].sTitle != c.innerHTML) c.innerHTML = a.aoColumns[b].sTitle } } else {
|
|
||||||
var f = p.createElement("tr"); b = 0;
|
|
||||||
for (d = a.aoColumns.length; b < d; b++) { c = a.aoColumns[b].nTh; c.innerHTML = a.aoColumns[b].sTitle; a.aoColumns[b].sClass !== null && i(c).addClass(a.aoColumns[b].sClass); f.appendChild(c) } i(a.nTHead).html("")[0].appendChild(f); W(a.aoHeader, a.nTHead)
|
|
||||||
} if (a.bJUI) { b = 0; for (d = a.aoColumns.length; b < d; b++) { c = a.aoColumns[b].nTh; f = p.createElement("div"); f.className = a.oClasses.sSortJUIWrapper; i(c).contents().appendTo(f); var e = p.createElement("span"); e.className = a.oClasses.sSortIcon; f.appendChild(e); c.appendChild(f) } } d = function () {
|
|
||||||
this.onselectstart =
|
|
||||||
function () { return false }; return false
|
|
||||||
}; if (a.oFeatures.bSort) for (b = 0; b < a.aoColumns.length; b++) if (a.aoColumns[b].bSortable !== false) { ga(a, a.aoColumns[b].nTh, b); i(a.aoColumns[b].nTh).bind("mousedown.DT", d) } else i(a.aoColumns[b].nTh).addClass(a.oClasses.sSortableNone); a.oClasses.sFooterTH !== "" && i(">tr>th", a.nTFoot).addClass(a.oClasses.sFooterTH); if (a.nTFoot !== null) { c = S(a, null, a.aoFooter); b = 0; for (d = a.aoColumns.length; b < d; b++) if (typeof c[b] != "undefined") a.aoColumns[b].nTf = c[b] }
|
|
||||||
} function L(a, b, c) {
|
|
||||||
var d, f,
|
|
||||||
e, h = [], j = [], k = a.aoColumns.length; if (typeof c == "undefined") c = false; d = 0; for (f = b.length; d < f; d++) { h[d] = b[d].slice(); h[d].nTr = b[d].nTr; for (e = k - 1; e >= 0; e--) !a.aoColumns[e].bVisible && !c && h[d].splice(e, 1); j.push([]) } d = 0; for (f = h.length; d < f; d++) {
|
|
||||||
if (h[d].nTr) { a = 0; for (e = h[d].nTr.childNodes.length; a < e; a++) h[d].nTr.removeChild(h[d].nTr.childNodes[0]) } e = 0; for (b = h[d].length; e < b; e++) {
|
|
||||||
k = c = 1; if (typeof j[d][e] == "undefined") {
|
|
||||||
h[d].nTr.appendChild(h[d][e].cell); for (j[d][e] = 1; typeof h[d + c] != "undefined" && h[d][e].cell == h[d +
|
|
||||||
c][e].cell; ) { j[d + c][e] = 1; c++ } for (; typeof h[d][e + k] != "undefined" && h[d][e].cell == h[d][e + k].cell; ) { for (a = 0; a < c; a++) j[d + a][e + k] = 1; k++ } h[d][e].cell.setAttribute("rowspan", c); h[d][e].cell.setAttribute("colspan", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} function C(a) {
|
|
||||||
var b, c, d = [], f = 0, e = false; b = a.asStripClasses.length; c = a.aoOpenRows.length; if (!(a.fnPreDrawCallback !== null && a.fnPreDrawCallback.call(a.oInstance, a) === false)) {
|
|
||||||
a.bDrawing = true; if (typeof a.iInitDisplayStart != "undefined" && a.iInitDisplayStart != -1) {
|
|
||||||
a._iDisplayStart = a.oFeatures.bServerSide ?
|
|
||||||
a.iInitDisplayStart : a.iInitDisplayStart >= a.fnRecordsDisplay() ? 0 : a.iInitDisplayStart; a.iInitDisplayStart = -1; E(a)
|
|
||||||
} if (a.bDeferLoading) { a.bDeferLoading = false; a.iDraw++ } else if (a.oFeatures.bServerSide) { if (!a.bDestroying && !za(a)) return } else a.iDraw++; if (a.aiDisplay.length !== 0) {
|
|
||||||
var h = a._iDisplayStart, j = a._iDisplayEnd; if (a.oFeatures.bServerSide) { h = 0; j = a.aoData.length } for (h = h; h < j; h++) {
|
|
||||||
var k = a.aoData[a.aiDisplay[h]]; k.nTr === null && z(a, a.aiDisplay[h]); var m = k.nTr; if (b !== 0) {
|
|
||||||
var t = a.asStripClasses[f % b]; if (k._sRowStripe !=
|
|
||||||
t) { i(m).removeClass(k._sRowStripe).addClass(t); k._sRowStripe = t }
|
|
||||||
} if (typeof a.fnRowCallback == "function") { m = a.fnRowCallback.call(a.oInstance, m, a.aoData[a.aiDisplay[h]]._aData, f, h); if (!m && !e) { J(a, 0, "A node was not returned by fnRowCallback"); e = true } } d.push(m); f++; if (c !== 0) for (k = 0; k < c; k++) m == a.aoOpenRows[k].nParent && d.push(a.aoOpenRows[k].nTr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d[0] = p.createElement("tr"); if (typeof a.asStripClasses[0] != "undefined") d[0].className = a.asStripClasses[0]; e = a.oLanguage.sZeroRecords.replace("_MAX_", a.fnFormatNumber(a.fnRecordsTotal()));
|
|
||||||
if (a.iDraw == 1 && a.sAjaxSource !== null && !a.oFeatures.bServerSide) e = a.oLanguage.sLoadingRecords; else if (typeof a.oLanguage.sEmptyTable != "undefined" && a.fnRecordsTotal() === 0) e = a.oLanguage.sEmptyTable; b = p.createElement("td"); b.setAttribute("valign", "top"); b.colSpan = X(a); b.className = a.oClasses.sRowEmpty; b.innerHTML = e; d[f].appendChild(b)
|
|
||||||
} typeof a.fnHeaderCallback == "function" && a.fnHeaderCallback.call(a.oInstance, i(">tr", a.nTHead)[0], aa(a), a._iDisplayStart, a.fnDisplayEnd(), a.aiDisplay); typeof a.fnFooterCallback ==
|
|
||||||
"function" && a.fnFooterCallback.call(a.oInstance, i(">tr", a.nTFoot)[0], aa(a), a._iDisplayStart, a.fnDisplayEnd(), a.aiDisplay); f = p.createDocumentFragment(); b = p.createDocumentFragment(); if (a.nTBody) { e = a.nTBody.parentNode; b.appendChild(a.nTBody); if (!a.oScroll.bInfinite || !a._bInitComplete || a.bSorted || a.bFiltered) { c = a.nTBody.childNodes; for (b = c.length - 1; b >= 0; b--) c[b].parentNode.removeChild(c[b]) } b = 0; for (c = d.length; b < c; b++) f.appendChild(d[b]); a.nTBody.appendChild(f); e !== null && e.appendChild(a.nTBody) } for (b = a.aoDrawCallback.length -
|
|
||||||
1; b >= 0; b--) a.aoDrawCallback[b].fn.call(a.oInstance, a); a.bSorted = false; a.bFiltered = false; a.bDrawing = false; if (a.oFeatures.bServerSide) { K(a, false); typeof a._bInitComplete == "undefined" && w(a) }
|
|
||||||
}
|
|
||||||
} function ba(a) { if (a.oFeatures.bSort) R(a, a.oPreviousSearch); else if (a.oFeatures.bFilter) M(a, a.oPreviousSearch); else { E(a); C(a) } } function za(a) {
|
|
||||||
if (a.bAjaxDataGet) {
|
|
||||||
K(a, true); var b = a.aoColumns.length, c = [], d, f; a.iDraw++; c.push({ name: "sEcho", value: a.iDraw }); c.push({ name: "iColumns", value: b }); c.push({ name: "sColumns", value: ha(a) });
|
|
||||||
c.push({ name: "iDisplayStart", value: a._iDisplayStart }); c.push({ name: "iDisplayLength", value: a.oFeatures.bPaginate !== false ? a._iDisplayLength : -1 }); for (f = 0; f < b; f++) { d = a.aoColumns[f].mDataProp; c.push({ name: "mDataProp_" + f, value: typeof d == "function" ? "function" : d }) } if (a.oFeatures.bFilter !== false) {
|
|
||||||
c.push({ name: "sSearch", value: a.oPreviousSearch.sSearch }); c.push({ name: "bRegex", value: a.oPreviousSearch.bRegex }); for (f = 0; f < b; f++) {
|
|
||||||
c.push({ name: "sSearch_" + f, value: a.aoPreSearchCols[f].sSearch }); c.push({ name: "bRegex_" +
|
|
||||||
f, value: a.aoPreSearchCols[f].bRegex
|
|
||||||
}); c.push({ name: "bSearchable_" + f, value: a.aoColumns[f].bSearchable })
|
|
||||||
}
|
|
||||||
} if (a.oFeatures.bSort !== false) {
|
|
||||||
d = a.aaSortingFixed !== null ? a.aaSortingFixed.length : 0; var e = a.aaSorting.length; c.push({ name: "iSortingCols", value: d + e }); for (f = 0; f < d; f++) { c.push({ name: "iSortCol_" + f, value: a.aaSortingFixed[f][0] }); c.push({ name: "sSortDir_" + f, value: a.aaSortingFixed[f][1] }) } for (f = 0; f < e; f++) { c.push({ name: "iSortCol_" + (f + d), value: a.aaSorting[f][0] }); c.push({ name: "sSortDir_" + (f + d), value: a.aaSorting[f][1] }) } for (f =
|
|
||||||
0; f < b; f++) c.push({ name: "bSortable_" + f, value: a.aoColumns[f].bSortable })
|
|
||||||
} a.fnServerData.call(a.oInstance, a.sAjaxSource, c, function (h) { Aa(a, h) }, a); return false
|
|
||||||
} else return true
|
|
||||||
} function Aa(a, b) {
|
|
||||||
if (typeof b.sEcho != "undefined") if (b.sEcho * 1 < a.iDraw) return; else a.iDraw = b.sEcho * 1; if (!a.oScroll.bInfinite || a.oScroll.bInfinite && (a.bSorted || a.bFiltered)) ia(a); a._iRecordsTotal = b.iTotalRecords; a._iRecordsDisplay = b.iTotalDisplayRecords; var c = ha(a); if (c = typeof b.sColumns != "undefined" && c !== "" && b.sColumns != c) var d =
|
|
||||||
Ba(a, b.sColumns); b = Z(a.sAjaxDataProp)(b); for (var f = 0, e = b.length; f < e; f++) if (c) { for (var h = [], j = 0, k = a.aoColumns.length; j < k; j++) h.push(b[f][d[j]]); v(a, h) } else v(a, b[f]); a.aiDisplay = a.aiDisplayMaster.slice(); a.bAjaxDataGet = false; C(a); a.bAjaxDataGet = true; K(a, false)
|
|
||||||
} function xa(a) {
|
|
||||||
var b = p.createElement("div"); a.nTable.parentNode.insertBefore(b, a.nTable); a.nTableWrapper = p.createElement("div"); a.nTableWrapper.className = a.oClasses.sWrapper; a.sTableId !== "" && a.nTableWrapper.setAttribute("id", a.sTableId + "_wrapper");
|
|
||||||
a.nTableReinsertBefore = a.nTable.nextSibling; for (var c = a.nTableWrapper, d = a.sDom.split(""), f, e, h, j, k, m, t, q = 0; q < d.length; q++) {
|
|
||||||
e = 0; h = d[q]; if (h == "<") {
|
|
||||||
j = p.createElement("div"); k = d[q + 1]; if (k == "'" || k == '"') {
|
|
||||||
m = ""; for (t = 2; d[q + t] != k; ) { m += d[q + t]; t++ } if (m == "H") m = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix"; else if (m == "F") m = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"; if (m.indexOf(".") != -1) {
|
|
||||||
k = m.split("."); j.setAttribute("id", k[0].substr(1,
|
|
||||||
k[0].length - 1)); j.className = k[1]
|
|
||||||
} else if (m.charAt(0) == "#") j.setAttribute("id", m.substr(1, m.length - 1)); else j.className = m; q += t
|
|
||||||
} c.appendChild(j); c = j
|
|
||||||
} else if (h == ">") c = c.parentNode; else if (h == "l" && a.oFeatures.bPaginate && a.oFeatures.bLengthChange) { f = Ca(a); e = 1 } else if (h == "f" && a.oFeatures.bFilter) { f = Da(a); e = 1 } else if (h == "r" && a.oFeatures.bProcessing) { f = Ea(a); e = 1 } else if (h == "t") { f = Fa(a); e = 1 } else if (h == "i" && a.oFeatures.bInfo) { f = Ga(a); e = 1 } else if (h == "p" && a.oFeatures.bPaginate) { f = Ha(a); e = 1 } else if (o.aoFeatures.length !==
|
|
||||||
0) { j = o.aoFeatures; t = 0; for (k = j.length; t < k; t++) if (h == j[t].cFeature) { if (f = j[t].fnInit(a)) e = 1; break } } if (e == 1 && f !== null) { if (typeof a.aanFeatures[h] != "object") a.aanFeatures[h] = []; a.aanFeatures[h].push(f); c.appendChild(f) }
|
|
||||||
} b.parentNode.replaceChild(a.nTableWrapper, b)
|
|
||||||
} function Fa(a) {
|
|
||||||
if (a.oScroll.sX === "" && a.oScroll.sY === "") return a.nTable; var b = p.createElement("div"), c = p.createElement("div"), d = p.createElement("div"), f = p.createElement("div"), e = p.createElement("div"), h = p.createElement("div"), j = a.nTable.cloneNode(false),
|
|
||||||
k = a.nTable.cloneNode(false), m = a.nTable.getElementsByTagName("thead")[0], t = a.nTable.getElementsByTagName("tfoot").length === 0 ? null : a.nTable.getElementsByTagName("tfoot")[0], q = typeof g.bJQueryUI != "undefined" && g.bJQueryUI ? o.oJUIClasses : o.oStdClasses; c.appendChild(d); e.appendChild(h); f.appendChild(a.nTable); b.appendChild(c); b.appendChild(f); d.appendChild(j); j.appendChild(m); if (t !== null) { b.appendChild(e); h.appendChild(k); k.appendChild(t) } b.className = q.sScrollWrapper; c.className = q.sScrollHead; d.className =
|
|
||||||
q.sScrollHeadInner; f.className = q.sScrollBody; e.className = q.sScrollFoot; h.className = q.sScrollFootInner; if (a.oScroll.bAutoCss) { c.style.overflow = "hidden"; c.style.position = "relative"; e.style.overflow = "hidden"; f.style.overflow = "auto" } c.style.border = "0"; c.style.width = "100%"; e.style.border = "0"; d.style.width = "150%"; j.removeAttribute("id"); j.style.marginLeft = "0"; a.nTable.style.marginLeft = "0"; if (t !== null) { k.removeAttribute("id"); k.style.marginLeft = "0" } d = i(">caption", a.nTable); h = 0; for (k = d.length; h < k; h++) j.appendChild(d[h]);
|
|
||||||
if (a.oScroll.sX !== "") { c.style.width = u(a.oScroll.sX); f.style.width = u(a.oScroll.sX); if (t !== null) e.style.width = u(a.oScroll.sX); i(f).scroll(function () { c.scrollLeft = this.scrollLeft; if (t !== null) e.scrollLeft = this.scrollLeft }) } if (a.oScroll.sY !== "") f.style.height = u(a.oScroll.sY); a.aoDrawCallback.push({ fn: Ia, sName: "scrolling" }); a.oScroll.bInfinite && i(f).scroll(function () {
|
|
||||||
if (!a.bDrawing) if (i(this).scrollTop() + i(this).height() > i(a.nTable).height() - a.oScroll.iLoadGap) if (a.fnDisplayEnd() < a.fnRecordsDisplay()) {
|
|
||||||
ja(a,
|
|
||||||
"next"); E(a); C(a)
|
|
||||||
}
|
|
||||||
}); a.nScrollHead = c; a.nScrollFoot = e; return b
|
|
||||||
} function Ia(a) {
|
|
||||||
var b = a.nScrollHead.getElementsByTagName("div")[0], c = b.getElementsByTagName("table")[0], d = a.nTable.parentNode, f, e, h, j, k, m, t, q, I = []; h = a.nTable.getElementsByTagName("thead"); h.length > 0 && a.nTable.removeChild(h[0]); if (a.nTFoot !== null) { k = a.nTable.getElementsByTagName("tfoot"); k.length > 0 && a.nTable.removeChild(k[0]) } h = a.nTHead.cloneNode(true); a.nTable.insertBefore(h, a.nTable.childNodes[0]); if (a.nTFoot !== null) {
|
|
||||||
k = a.nTFoot.cloneNode(true);
|
|
||||||
a.nTable.insertBefore(k, a.nTable.childNodes[1])
|
|
||||||
} if (a.oScroll.sX === "") { d.style.width = "100%"; b.parentNode.style.width = "100%" } var O = S(a, h); f = 0; for (e = O.length; f < e; f++) { t = Ja(a, f); O[f].style.width = a.aoColumns[t].sWidth } a.nTFoot !== null && P(function (B) { B.style.width = "" }, k.getElementsByTagName("tr")); f = i(a.nTable).outerWidth(); if (a.oScroll.sX === "") { a.nTable.style.width = "100%"; if (i.browser.msie && i.browser.version <= 7) a.nTable.style.width = u(i(a.nTable).outerWidth() - a.oScroll.iBarWidth) } else if (a.oScroll.sXInner !==
|
|
||||||
"") a.nTable.style.width = u(a.oScroll.sXInner); else if (f == i(d).width() && i(d).height() < i(a.nTable).height()) { a.nTable.style.width = u(f - a.oScroll.iBarWidth); if (i(a.nTable).outerWidth() > f - a.oScroll.iBarWidth) a.nTable.style.width = u(f) } else a.nTable.style.width = u(f); f = i(a.nTable).outerWidth(); if (a.oScroll.sX === "") { d.style.width = u(f + a.oScroll.iBarWidth); b.parentNode.style.width = u(f + a.oScroll.iBarWidth) } e = a.nTHead.getElementsByTagName("tr"); h = h.getElementsByTagName("tr"); P(function (B, F) {
|
|
||||||
m = B.style; m.paddingTop =
|
|
||||||
"0"; m.paddingBottom = "0"; m.borderTopWidth = "0"; m.borderBottomWidth = "0"; m.height = 0; q = i(B).width(); F.style.width = u(q); I.push(q)
|
|
||||||
}, h, e); i(h).height(0); if (a.nTFoot !== null) { j = k.getElementsByTagName("tr"); k = a.nTFoot.getElementsByTagName("tr"); P(function (B, F) { m = B.style; m.paddingTop = "0"; m.paddingBottom = "0"; m.borderTopWidth = "0"; m.borderBottomWidth = "0"; m.height = 0; q = i(B).width(); F.style.width = u(q); I.push(q) }, j, k); i(j).height(0) } P(function (B) { B.innerHTML = ""; B.style.width = u(I.shift()) }, h); a.nTFoot !== null && P(function (B) {
|
|
||||||
B.innerHTML =
|
|
||||||
""; B.style.width = u(I.shift())
|
|
||||||
}, j); if (i(a.nTable).outerWidth() < f) if (a.oScroll.sX === "") J(a, 1, "The table cannot fit into the current element which will cause column misalignment. It is suggested that you enable x-scrolling or increase the width the table has in which to be drawn"); else a.oScroll.sXInner !== "" && J(a, 1, "The table cannot fit into the current element which will cause column misalignment. It is suggested that you increase the sScrollXInner property to allow it to draw in a larger area, or simply remove that parameter to allow automatic calculation");
|
|
||||||
if (a.oScroll.sY === "") if (i.browser.msie && i.browser.version <= 7) d.style.height = u(a.nTable.offsetHeight + a.oScroll.iBarWidth); if (a.oScroll.sY !== "" && a.oScroll.bCollapse) { d.style.height = u(a.oScroll.sY); j = a.oScroll.sX !== "" && a.nTable.offsetWidth > d.offsetWidth ? a.oScroll.iBarWidth : 0; if (a.nTable.offsetHeight < d.offsetHeight) d.style.height = u(i(a.nTable).height() + j) } j = i(a.nTable).outerWidth(); c.style.width = u(j); b.style.width = u(j + a.oScroll.iBarWidth); if (a.nTFoot !== null) {
|
|
||||||
b = a.nScrollFoot.getElementsByTagName("div")[0];
|
|
||||||
c = b.getElementsByTagName("table")[0]; b.style.width = u(a.nTable.offsetWidth + a.oScroll.iBarWidth); c.style.width = u(a.nTable.offsetWidth)
|
|
||||||
} if (a.bSorted || a.bFiltered) d.scrollTop = 0
|
|
||||||
}
|
|
||||||
// sFilter
|
|
||||||
function ca(a) { if (a.oFeatures.bAutoWidth === false) return false; ea(a); for (var b = 0, c = a.aoColumns.length; b < c; b++) a.aoColumns[b].nTh.style.width = a.aoColumns[b].sWidth } function Da(a) {
|
|
||||||
var b = a.oLanguage.sSearch;
|
|
||||||
b = b.indexOf("_INPUT_") !== -1 ? b.replace("_INPUT_", '<input type="text" />') : b === "" ? '<input type="text" />' : b + ' <input type="text" />';
|
|
||||||
var c = p.createElement("div");
|
|
||||||
c.className = a.oClasses.sFilter;
|
|
||||||
c.innerHTML = "<div>" + b + "</div>";
|
|
||||||
a.sTableId !== "" && typeof a.aanFeatures.f == "undefined" && c.setAttribute("id", a.sTableId + "_filter");
|
|
||||||
b = i("input", c);
|
|
||||||
b.val(a.oPreviousSearch.sSearch.replace('"', """));
|
|
||||||
b.bind("keyup.DT", function () {
|
|
||||||
for (var d = a.aanFeatures.f, f = 0, e = d.length;
|
|
||||||
f < e; f++)
|
|
||||||
d[f] != this.parentNode && i("input", d[f]).val(this.value);
|
|
||||||
this.value != a.oPreviousSearch.sSearch && M(a, {
|
|
||||||
sSearch: this.value, bRegex: a.oPreviousSearch.bRegex, bSmart: a.oPreviousSearch.bSmart
|
|
||||||
})
|
|
||||||
});
|
|
||||||
b.bind("keypress.DT", function (d) {
|
|
||||||
if (d.keyCode == 13) return false
|
|
||||||
});
|
|
||||||
return c
|
|
||||||
|
|
||||||
} function M(a, b, c) { Ka(a, b.sSearch, c, b.bRegex, b.bSmart); for (b = 0; b < a.aoPreSearchCols.length; b++) La(a, a.aoPreSearchCols[b].sSearch, b, a.aoPreSearchCols[b].bRegex, a.aoPreSearchCols[b].bSmart); o.afnFiltering.length !== 0 && Ma(a); a.bFiltered = true; a._iDisplayStart = 0; E(a); C(a); ka(a, 0) } function Ma(a) {
|
|
||||||
for (var b = o.afnFiltering, c = 0, d = b.length; c < d; c++) for (var f = 0, e = 0, h = a.aiDisplay.length; e < h; e++) {
|
|
||||||
var j = a.aiDisplay[e - f];
|
|
||||||
if (!b[c](a, da(a, j, "filter"),
|
|
||||||
j)) {
|
|
||||||
a.aiDisplay.splice(e - f, 1); f++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} function La(a, b, c, d, f) { if (b !== "") { var e = 0; b = la(b, d, f); for (d = a.aiDisplay.length - 1; d >= 0; d--) { f = ma(H(a, a.aiDisplay[d], c, "filter"), a.aoColumns[c].sType); if (!b.test(f)) { a.aiDisplay.splice(d, 1); e++ } } } } function Ka(a, b, c, d, f) {
|
|
||||||
var e = la(b, d, f); if (typeof c == "undefined" || c === null) c = 0; if (o.afnFiltering.length !== 0) c = 1; if (b.length <= 0) { a.aiDisplay.splice(0, a.aiDisplay.length); a.aiDisplay = a.aiDisplayMaster.slice() } else if (a.aiDisplay.length == a.aiDisplayMaster.length || a.oPreviousSearch.sSearch.length >
|
|
||||||
b.length || c == 1 || b.indexOf(a.oPreviousSearch.sSearch) !== 0) { a.aiDisplay.splice(0, a.aiDisplay.length); ka(a, 1); for (c = 0; c < a.aiDisplayMaster.length; c++) e.test(a.asDataSearch[c]) && a.aiDisplay.push(a.aiDisplayMaster[c]) } else { var h = 0; for (c = 0; c < a.asDataSearch.length; c++) if (!e.test(a.asDataSearch[c])) { a.aiDisplay.splice(c - h, 1); h++ } } a.oPreviousSearch.sSearch = b; a.oPreviousSearch.bRegex = d; a.oPreviousSearch.bSmart = f
|
|
||||||
} function ka(a, b) {
|
|
||||||
a.asDataSearch.splice(0, a.asDataSearch.length); b = typeof b != "undefined" && b == 1 ? a.aiDisplayMaster :
|
|
||||||
a.aiDisplay; for (var c = 0, d = b.length; c < d; c++) a.asDataSearch[c] = na(a, da(a, b[c], "filter"))
|
|
||||||
} function na(a, b) { var c = ""; if (typeof a.__nTmpFilter == "undefined") a.__nTmpFilter = p.createElement("div"); for (var d = a.__nTmpFilter, f = 0, e = a.aoColumns.length; f < e; f++) if (a.aoColumns[f].bSearchable) c += ma(b[f], a.aoColumns[f].sType) + " "; if (c.indexOf("&") !== -1) { d.innerHTML = c; c = d.textContent ? d.textContent : d.innerText; c = c.replace(/\n/g, " ").replace(/\r/g, "") } return c } function la(a, b, c) {
|
|
||||||
if (c) {
|
|
||||||
a = b ? a.split(" ") : oa(a).split(" ");
|
|
||||||
a = "^(?=.*?" + a.join(")(?=.*?") + ").*$"; return new RegExp(a, "i")
|
|
||||||
} else { a = b ? a : oa(a); return new RegExp(a, "i") }
|
|
||||||
} function ma(a, b) { if (typeof o.ofnSearch[b] == "function") return o.ofnSearch[b](a); else if (b == "html") return a.replace(/\n/g, " ").replace(/<.*?>/g, ""); else if (typeof a == "string") return a.replace(/\n/g, " "); else if (a === null) return ""; return a } function R(a, b) {
|
|
||||||
var c, d, f, e, h = [], j = [], k = o.oSort; d = a.aoData; var m = a.aoColumns; if (!a.oFeatures.bServerSide && (a.aaSorting.length !== 0 || a.aaSortingFixed !== null)) {
|
|
||||||
h = a.aaSortingFixed !==
|
|
||||||
null ? a.aaSortingFixed.concat(a.aaSorting) : a.aaSorting.slice(); for (c = 0; c < h.length; c++) { var t = h[c][0]; f = pa(a, t); e = a.aoColumns[t].sSortDataType; if (typeof o.afnSortData[e] != "undefined") { var q = o.afnSortData[e](a, t, f); f = 0; for (e = d.length; f < e; f++) N(a, f, t, q[f]) } } c = 0; for (d = a.aiDisplayMaster.length; c < d; c++) j[a.aiDisplayMaster[c]] = c; var I = h.length; a.aiDisplayMaster.sort(function (O, B) {
|
|
||||||
var F, qa; for (c = 0; c < I; c++) {
|
|
||||||
F = m[h[c][0]].iDataSort; qa = m[F].sType; F = k[(qa ? qa : "string") + "-" + h[c][1]](H(a, O, F, "sort"), H(a, B, F, "sort"));
|
|
||||||
if (F !== 0) return F
|
|
||||||
} return k["numeric-asc"](j[O], j[B])
|
|
||||||
})
|
|
||||||
} if ((typeof b == "undefined" || b) && !a.oFeatures.bDeferRender) T(a); a.bSorted = true; if (a.oFeatures.bFilter) M(a, a.oPreviousSearch, 1); else { a.aiDisplay = a.aiDisplayMaster.slice(); a._iDisplayStart = 0; E(a); C(a) }
|
|
||||||
} function ga(a, b, c, d) {
|
|
||||||
i(b).bind("click.DT", function (f) {
|
|
||||||
if (a.aoColumns[c].bSortable !== false) {
|
|
||||||
var e = function () {
|
|
||||||
var h, j; if (f.shiftKey) {
|
|
||||||
for (var k = false, m = 0; m < a.aaSorting.length; m++) if (a.aaSorting[m][0] == c) {
|
|
||||||
k = true; h = a.aaSorting[m][0]; j = a.aaSorting[m][2] +
|
|
||||||
1; if (typeof a.aoColumns[h].asSorting[j] == "undefined") a.aaSorting.splice(m, 1); else { a.aaSorting[m][1] = a.aoColumns[h].asSorting[j]; a.aaSorting[m][2] = j } break
|
|
||||||
} k === false && a.aaSorting.push([c, a.aoColumns[c].asSorting[0], 0])
|
|
||||||
} else if (a.aaSorting.length == 1 && a.aaSorting[0][0] == c) { h = a.aaSorting[0][0]; j = a.aaSorting[0][2] + 1; if (typeof a.aoColumns[h].asSorting[j] == "undefined") j = 0; a.aaSorting[0][1] = a.aoColumns[h].asSorting[j]; a.aaSorting[0][2] = j } else {
|
|
||||||
a.aaSorting.splice(0, a.aaSorting.length); a.aaSorting.push([c, a.aoColumns[c].asSorting[0],
|
|
||||||
0])
|
|
||||||
} R(a)
|
|
||||||
}; if (a.oFeatures.bProcessing) { K(a, true); setTimeout(function () { e(); a.oFeatures.bServerSide || K(a, false) }, 0) } else e(); typeof d == "function" && d(a)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} function T(a) {
|
|
||||||
var b, c, d, f, e, h = a.aoColumns.length, j = a.oClasses; for (b = 0; b < h; b++) a.aoColumns[b].bSortable && i(a.aoColumns[b].nTh).removeClass(j.sSortAsc + " " + j.sSortDesc + " " + a.aoColumns[b].sSortingClass); f = a.aaSortingFixed !== null ? a.aaSortingFixed.concat(a.aaSorting) : a.aaSorting.slice(); for (b = 0; b < a.aoColumns.length; b++) if (a.aoColumns[b].bSortable) {
|
|
||||||
e = a.aoColumns[b].sSortingClass;
|
|
||||||
d = -1; for (c = 0; c < f.length; c++) if (f[c][0] == b) { e = f[c][1] == "asc" ? j.sSortAsc : j.sSortDesc; d = c; break } i(a.aoColumns[b].nTh).addClass(e); if (a.bJUI) { c = i("span", a.aoColumns[b].nTh); c.removeClass(j.sSortJUIAsc + " " + j.sSortJUIDesc + " " + j.sSortJUI + " " + j.sSortJUIAscAllowed + " " + j.sSortJUIDescAllowed); c.addClass(d == -1 ? a.aoColumns[b].sSortingClassJUI : f[d][1] == "asc" ? j.sSortJUIAsc : j.sSortJUIDesc) }
|
|
||||||
} else i(a.aoColumns[b].nTh).addClass(a.aoColumns[b].sSortingClass); e = j.sSortColumn; if (a.oFeatures.bSort && a.oFeatures.bSortClasses) {
|
|
||||||
d =
|
|
||||||
Q(a); if (a.oFeatures.bDeferRender) i(d).removeClass(e + "1 " + e + "2 " + e + "3"); else if (d.length >= h) for (b = 0; b < h; b++) if (d[b].className.indexOf(e + "1") != -1) { c = 0; for (a = d.length / h; c < a; c++) d[h * c + b].className = i.trim(d[h * c + b].className.replace(e + "1", "")) } else if (d[b].className.indexOf(e + "2") != -1) { c = 0; for (a = d.length / h; c < a; c++) d[h * c + b].className = i.trim(d[h * c + b].className.replace(e + "2", "")) } else if (d[b].className.indexOf(e + "3") != -1) {
|
|
||||||
c = 0; for (a = d.length / h; c < a; c++) d[h * c + b].className = i.trim(d[h * c + b].className.replace(" " +
|
|
||||||
e + "3", ""))
|
|
||||||
} j = 1; var k; for (b = 0; b < f.length; b++) { k = parseInt(f[b][0], 10); c = 0; for (a = d.length / h; c < a; c++) d[h * c + k].className += " " + e + j; j < 3 && j++ }
|
|
||||||
}
|
|
||||||
} function Ha(a) { if (a.oScroll.bInfinite) return null; var b = p.createElement("div"); b.className = a.oClasses.sPaging + a.sPaginationType; o.oPagination[a.sPaginationType].fnInit(a, b, function (c) { E(c); C(c) }); typeof a.aanFeatures.p == "undefined" && a.aoDrawCallback.push({ fn: function (c) { o.oPagination[c.sPaginationType].fnUpdate(c, function (d) { E(d); C(d) }) }, sName: "pagination" }); return b }
|
|
||||||
function ja(a, b) {
|
|
||||||
var c = a._iDisplayStart; if (b == "first") a._iDisplayStart = 0; else if (b == "previous") { a._iDisplayStart = a._iDisplayLength >= 0 ? a._iDisplayStart - a._iDisplayLength : 0; if (a._iDisplayStart < 0) a._iDisplayStart = 0 } else if (b == "next") if (a._iDisplayLength >= 0) { if (a._iDisplayStart + a._iDisplayLength < a.fnRecordsDisplay()) a._iDisplayStart += a._iDisplayLength } else a._iDisplayStart = 0; else if (b == "last") if (a._iDisplayLength >= 0) { b = parseInt((a.fnRecordsDisplay() - 1) / a._iDisplayLength, 10) + 1; a._iDisplayStart = (b - 1) * a._iDisplayLength } else a._iDisplayStart =
|
|
||||||
0; else J(a, 0, "Unknown paging action: " + b); return c != a._iDisplayStart
|
|
||||||
} function Ga(a) { var b = p.createElement("div"); b.className = a.oClasses.sInfo; if (typeof a.aanFeatures.i == "undefined") { a.aoDrawCallback.push({ fn: Na, sName: "information" }); a.sTableId !== "" && b.setAttribute("id", a.sTableId + "_info") } return b } function Na(a) {
|
|
||||||
if (!(!a.oFeatures.bInfo || a.aanFeatures.i.length === 0)) {
|
|
||||||
var b = a._iDisplayStart + 1, c = a.fnDisplayEnd(), d = a.fnRecordsTotal(), f = a.fnRecordsDisplay(), e = a.fnFormatNumber(b), h = a.fnFormatNumber(c), j =
|
|
||||||
a.fnFormatNumber(d), k = a.fnFormatNumber(f); if (a.oScroll.bInfinite) e = a.fnFormatNumber(1); e = a.fnRecordsDisplay() === 0 && a.fnRecordsDisplay() == a.fnRecordsTotal() ? a.oLanguage.sInfoEmpty + a.oLanguage.sInfoPostFix : a.fnRecordsDisplay() === 0 ? a.oLanguage.sInfoEmpty + " " + a.oLanguage.sInfoFiltered.replace("_MAX_", j) + a.oLanguage.sInfoPostFix : a.fnRecordsDisplay() == a.fnRecordsTotal() ? a.oLanguage.sInfo.replace("_START_", e).replace("_END_", h).replace("_TOTAL_", k) + a.oLanguage.sInfoPostFix : a.oLanguage.sInfo.replace("_START_",
|
|
||||||
e).replace("_END_", h).replace("_TOTAL_", k) + " " + a.oLanguage.sInfoFiltered.replace("_MAX_", a.fnFormatNumber(a.fnRecordsTotal())) + a.oLanguage.sInfoPostFix; if (a.oLanguage.fnInfoCallback !== null) e = a.oLanguage.fnInfoCallback(a, b, c, d, f, e); a = a.aanFeatures.i; b = 0; for (c = a.length; b < c; b++) i(a[b]).html(e)
|
|
||||||
}
|
|
||||||
} function Ca(a) {
|
|
||||||
if (a.oScroll.bInfinite) return null; var b = '<select size="1" ' + (a.sTableId === "" ? "" : 'name="' + a.sTableId + '_length"') + ">", c, d; if (a.aLengthMenu.length == 2 && typeof a.aLengthMenu[0] == "object" && typeof a.aLengthMenu[1] ==
|
|
||||||
"object") { c = 0; for (d = a.aLengthMenu[0].length; c < d; c++) b += '<option value="' + a.aLengthMenu[0][c] + '">' + a.aLengthMenu[1][c] + "</option>" } else { c = 0; for (d = a.aLengthMenu.length; c < d; c++) b += '<option value="' + a.aLengthMenu[c] + '">' + a.aLengthMenu[c] + "</option>" } b += "</select>"; var f = p.createElement("div"); a.sTableId !== "" && typeof a.aanFeatures.l == "undefined" && f.setAttribute("id", a.sTableId + "_length"); f.className = a.oClasses.sLength; f.innerHTML = "<label>" + a.oLanguage.sLengthMenu.replace("_MENU_", b) + "</label>"; i('select option[value="' +
|
|
||||||
a._iDisplayLength + '"]', f).attr("selected", true); i("select", f).bind("change.DT", function () { var e = i(this).val(), h = a.aanFeatures.l; c = 0; for (d = h.length; c < d; c++) h[c] != this.parentNode && i("select", h[c]).val(e); a._iDisplayLength = parseInt(e, 10); E(a); if (a.fnDisplayEnd() == a.fnRecordsDisplay()) { a._iDisplayStart = a.fnDisplayEnd() - a._iDisplayLength; if (a._iDisplayStart < 0) a._iDisplayStart = 0 } if (a._iDisplayLength == -1) a._iDisplayStart = 0; C(a) }); return f
|
|
||||||
} function Ea(a) {
|
|
||||||
var b = p.createElement("div"); a.sTableId !== "" && typeof a.aanFeatures.r ==
|
|
||||||
"undefined" && b.setAttribute("id", a.sTableId + "_processing"); b.innerHTML = a.oLanguage.sProcessing; b.className = a.oClasses.sProcessing; a.nTable.parentNode.insertBefore(b, a.nTable); return b
|
|
||||||
} function K(a, b) { if (a.oFeatures.bProcessing) { a = a.aanFeatures.r; for (var c = 0, d = a.length; c < d; c++) a[c].style.visibility = b ? "visible" : "hidden" } } function Ja(a, b) { for (var c = -1, d = 0; d < a.aoColumns.length; d++) { a.aoColumns[d].bVisible === true && c++; if (c == b) return d } return null } function pa(a, b) {
|
|
||||||
for (var c = -1, d = 0; d < a.aoColumns.length; d++) {
|
|
||||||
a.aoColumns[d].bVisible ===
|
|
||||||
true && c++; if (d == b) return a.aoColumns[d].bVisible === true ? c : null
|
|
||||||
} return null
|
|
||||||
} function U(a, b) { var c, d; c = a._iDisplayStart; for (d = a._iDisplayEnd; c < d; c++) if (a.aoData[a.aiDisplay[c]].nTr == b) return a.aiDisplay[c]; c = 0; for (d = a.aoData.length; c < d; c++) if (a.aoData[c].nTr == b) return c; return null } function X(a) { for (var b = 0, c = 0; c < a.aoColumns.length; c++) a.aoColumns[c].bVisible === true && b++; return b } function E(a) {
|
|
||||||
a._iDisplayEnd = a.oFeatures.bPaginate === false ? a.aiDisplay.length : a._iDisplayStart + a._iDisplayLength > a.aiDisplay.length ||
|
|
||||||
a._iDisplayLength == -1 ? a.aiDisplay.length : a._iDisplayStart + a._iDisplayLength
|
|
||||||
} function Oa(a, b) { if (!a || a === null || a === "") return 0; if (typeof b == "undefined") b = p.getElementsByTagName("body")[0]; var c = p.createElement("div"); c.style.width = u(a); b.appendChild(c); a = c.offsetWidth; b.removeChild(c); return a } function ea(a) {
|
|
||||||
var b = 0, c, d = 0, f = a.aoColumns.length, e, h = i("th", a.nTHead); for (e = 0; e < f; e++) if (a.aoColumns[e].bVisible) {
|
|
||||||
d++; if (a.aoColumns[e].sWidth !== null) {
|
|
||||||
c = Oa(a.aoColumns[e].sWidthOrig, a.nTable.parentNode); if (c !==
|
|
||||||
null) a.aoColumns[e].sWidth = u(c); b++
|
|
||||||
}
|
|
||||||
} if (f == h.length && b === 0 && d == f && a.oScroll.sX === "" && a.oScroll.sY === "") for (e = 0; e < a.aoColumns.length; e++) { c = i(h[e]).width(); if (c !== null) a.aoColumns[e].sWidth = u(c) } else {
|
|
||||||
b = a.nTable.cloneNode(false); e = a.nTHead.cloneNode(true); d = p.createElement("tbody"); c = p.createElement("tr"); b.removeAttribute("id"); b.appendChild(e); if (a.nTFoot !== null) { b.appendChild(a.nTFoot.cloneNode(true)); P(function (k) { k.style.width = "" }, b.getElementsByTagName("tr")) } b.appendChild(d); d.appendChild(c);
|
|
||||||
d = i("thead th", b); if (d.length === 0) d = i("tbody tr:eq(0)>td", b); h = S(a, e); for (e = d = 0; e < f; e++) { var j = a.aoColumns[e]; if (j.bVisible && j.sWidthOrig !== null && j.sWidthOrig !== "") h[e - d].style.width = u(j.sWidthOrig); else if (j.bVisible) h[e - d].style.width = ""; else d++ } for (e = 0; e < f; e++) if (a.aoColumns[e].bVisible) { d = Pa(a, e); if (d !== null) { d = d.cloneNode(true); if (a.aoColumns[e].sContentPadding !== "") d.innerHTML += a.aoColumns[e].sContentPadding; c.appendChild(d) } } f = a.nTable.parentNode; f.appendChild(b); if (a.oScroll.sX !== "" && a.oScroll.sXInner !==
|
|
||||||
"") b.style.width = u(a.oScroll.sXInner); else if (a.oScroll.sX !== "") { b.style.width = ""; if (i(b).width() < f.offsetWidth) b.style.width = u(f.offsetWidth) } else if (a.oScroll.sY !== "") b.style.width = u(f.offsetWidth); b.style.visibility = "hidden"; Qa(a, b); f = i("tbody tr:eq(0)", b).children(); if (f.length === 0) f = S(a, i("thead", b)[0]); if (a.oScroll.sX !== "") {
|
|
||||||
for (e = d = c = 0; e < a.aoColumns.length; e++) if (a.aoColumns[e].bVisible) {
|
|
||||||
c += a.aoColumns[e].sWidthOrig === null ? i(f[d]).outerWidth() : parseInt(a.aoColumns[e].sWidth.replace("px", ""),
|
|
||||||
10) + (i(f[d]).outerWidth() - i(f[d]).width()); d++
|
|
||||||
} b.style.width = u(c); a.nTable.style.width = u(c)
|
|
||||||
} for (e = d = 0; e < a.aoColumns.length; e++) if (a.aoColumns[e].bVisible) { c = i(f[d]).width(); if (c !== null && c > 0) a.aoColumns[e].sWidth = u(c); d++ } a.nTable.style.width = u(i(b).outerWidth()); b.parentNode.removeChild(b)
|
|
||||||
}
|
|
||||||
} function Qa(a, b) { if (a.oScroll.sX === "" && a.oScroll.sY !== "") { i(b).width(); b.style.width = u(i(b).outerWidth() - a.oScroll.iBarWidth) } else if (a.oScroll.sX !== "") b.style.width = u(i(b).outerWidth()) } function Pa(a, b) {
|
|
||||||
var c =
|
|
||||||
Ra(a, b); if (c < 0) return null; if (a.aoData[c].nTr === null) { var d = p.createElement("td"); d.innerHTML = H(a, c, b, ""); return d } return Q(a, c)[b]
|
|
||||||
} function Ra(a, b) { for (var c = -1, d = -1, f = 0; f < a.aoData.length; f++) { var e = H(a, f, b, "display") + ""; e = e.replace(/<.*?>/g, ""); if (e.length > c) { c = e.length; d = f } } return d } function u(a) { if (a === null) return "0px"; if (typeof a == "number") { if (a < 0) return "0px"; return a + "px" } var b = a.charCodeAt(a.length - 1); if (b < 48 || b > 57) return a; return a + "px" } function Va(a, b) {
|
|
||||||
if (a.length != b.length) return 1; for (var c =
|
|
||||||
0; c < a.length; c++) if (a[c] != b[c]) return 2; return 0
|
|
||||||
} function fa(a) { for (var b = o.aTypes, c = b.length, d = 0; d < c; d++) { var f = b[d](a); if (f !== null) return f } return "string" } function A(a) { for (var b = 0; b < D.length; b++) if (D[b].nTable == a) return D[b]; return null } function aa(a) { for (var b = [], c = a.aoData.length, d = 0; d < c; d++) b.push(a.aoData[d]._aData); return b } function $(a) { for (var b = [], c = 0, d = a.aoData.length; c < d; c++) a.aoData[c].nTr !== null && b.push(a.aoData[c].nTr); return b } function Q(a, b) {
|
|
||||||
var c = [], d, f, e, h, j; f = 0; var k = a.aoData.length;
|
|
||||||
if (typeof b != "undefined") { f = b; k = b + 1 } for (f = f; f < k; f++) { j = a.aoData[f]; if (j.nTr !== null) { b = []; e = 0; for (h = j.nTr.childNodes.length; e < h; e++) { d = j.nTr.childNodes[e].nodeName.toLowerCase(); if (d == "td" || d == "th") b.push(j.nTr.childNodes[e]) } e = d = 0; for (h = a.aoColumns.length; e < h; e++) if (a.aoColumns[e].bVisible) c.push(b[e - d]); else { c.push(j._anHidden[e]); d++ } } } return c
|
|
||||||
} function oa(a) { return a.replace(new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^)", "g"), "\\$1") } function ra(a, b) {
|
|
||||||
for (var c = -1, d =
|
|
||||||
0, f = a.length; d < f; d++) if (a[d] == b) c = d; else a[d] > b && a[d]--; c != -1 && a.splice(c, 1)
|
|
||||||
} function Ba(a, b) { b = b.split(","); for (var c = [], d = 0, f = a.aoColumns.length; d < f; d++) for (var e = 0; e < f; e++) if (a.aoColumns[d].sName == b[e]) { c.push(e); break } return c } function ha(a) { for (var b = "", c = 0, d = a.aoColumns.length; c < d; c++) b += a.aoColumns[c].sName + ","; if (b.length == d) return ""; return b.slice(0, -1) } function J(a, b, c) {
|
|
||||||
a = a.sTableId === "" ? "DataTables warning: " + c : "DataTables warning (table id = '" + a.sTableId + "'): " + c; if (b === 0) if (o.sErrMode ==
|
|
||||||
"alert") alert(a); else throw a; else typeof console != "undefined" && typeof console.log != "undefined" && console.log(a)
|
|
||||||
} function ia(a) { a.aoData.splice(0, a.aoData.length); a.aiDisplayMaster.splice(0, a.aiDisplayMaster.length); a.aiDisplay.splice(0, a.aiDisplay.length); E(a) } function sa(a) {
|
|
||||||
if (!(!a.oFeatures.bStateSave || typeof a.bDestroying != "undefined")) {
|
|
||||||
var b, c, d, f = "{"; f += '"iCreate":' + (new Date).getTime() + ","; f += '"iStart":' + (a.oScroll.bInfinite ? 0 : a._iDisplayStart) + ","; f += '"iEnd":' + (a.oScroll.bInfinite ? a._iDisplayLength :
|
|
||||||
a._iDisplayEnd) + ","; f += '"iLength":' + a._iDisplayLength + ","; f += '"sFilter":"' + encodeURIComponent(a.oPreviousSearch.sSearch) + '",'; f += '"sFilterEsc":' + !a.oPreviousSearch.bRegex + ","; f += '"aaSorting":[ '; for (b = 0; b < a.aaSorting.length; b++) f += "[" + a.aaSorting[b][0] + ',"' + a.aaSorting[b][1] + '"],'; f = f.substring(0, f.length - 1); f += "],"; f += '"aaSearchCols":[ '; for (b = 0; b < a.aoPreSearchCols.length; b++) f += '["' + encodeURIComponent(a.aoPreSearchCols[b].sSearch) + '",' + !a.aoPreSearchCols[b].bRegex + "],"; f = f.substring(0, f.length -
|
|
||||||
1); f += "],"; f += '"abVisCols":[ '; for (b = 0; b < a.aoColumns.length; b++) f += a.aoColumns[b].bVisible + ","; f = f.substring(0, f.length - 1); f += "]"; b = 0; for (c = a.aoStateSave.length; b < c; b++) { d = a.aoStateSave[b].fn(a, f); if (d !== "") f = d } f += "}"; Sa(a.sCookiePrefix + a.sInstance, f, a.iCookieDuration, a.sCookiePrefix, a.fnCookieCallback)
|
|
||||||
}
|
|
||||||
} function Ta(a, b) {
|
|
||||||
if (a.oFeatures.bStateSave) {
|
|
||||||
var c, d, f; d = ta(a.sCookiePrefix + a.sInstance); if (d !== null && d !== "") {
|
|
||||||
try { c = typeof i.parseJSON == "function" ? i.parseJSON(d.replace(/'/g, '"')) : eval("(" + d + ")") } catch (e) { return } d =
|
|
||||||
0; for (f = a.aoStateLoad.length; d < f; d++) if (!a.aoStateLoad[d].fn(a, c)) return; a.oLoadedState = i.extend(true, {}, c); a._iDisplayStart = c.iStart; a.iInitDisplayStart = c.iStart; a._iDisplayEnd = c.iEnd; a._iDisplayLength = c.iLength; a.oPreviousSearch.sSearch = decodeURIComponent(c.sFilter); a.aaSorting = c.aaSorting.slice(); a.saved_aaSorting = c.aaSorting.slice(); if (typeof c.sFilterEsc != "undefined") a.oPreviousSearch.bRegex = !c.sFilterEsc; if (typeof c.aaSearchCols != "undefined") for (d = 0; d < c.aaSearchCols.length; d++) a.aoPreSearchCols[d] =
|
|
||||||
{ sSearch: decodeURIComponent(c.aaSearchCols[d][0]), bRegex: !c.aaSearchCols[d][1] }; if (typeof c.abVisCols != "undefined") { b.saved_aoColumns = []; for (d = 0; d < c.abVisCols.length; d++) { b.saved_aoColumns[d] = {}; b.saved_aoColumns[d].bVisible = c.abVisCols[d] } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} function Sa(a, b, c, d, f) {
|
|
||||||
var e = new Date; e.setTime(e.getTime() + c * 1E3); c = wa.location.pathname.split("/"); a = a + "_" + c.pop().replace(/[\/:]/g, "").toLowerCase(); var h; if (f !== null) {
|
|
||||||
h = typeof i.parseJSON == "function" ? i.parseJSON(b) : eval("(" + b + ")"); b = f(a, h, e.toGMTString(),
|
|
||||||
c.join("/") + "/")
|
|
||||||
} else b = a + "=" + encodeURIComponent(b) + "; expires=" + e.toGMTString() + "; path=" + c.join("/") + "/"; f = ""; e = 9999999999999; if ((ta(a) !== null ? p.cookie.length : b.length + p.cookie.length) + 10 > 4096) {
|
|
||||||
a = p.cookie.split(";"); for (var j = 0, k = a.length; j < k; j++) if (a[j].indexOf(d) != -1) { var m = a[j].split("="); try { h = eval("(" + decodeURIComponent(m[1]) + ")") } catch (t) { continue } if (typeof h.iCreate != "undefined" && h.iCreate < e) { f = m[0]; e = h.iCreate } } if (f !== "") p.cookie = f + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=" + c.join("/") +
|
|
||||||
"/"
|
|
||||||
} p.cookie = b
|
|
||||||
} function ta(a) { var b = wa.location.pathname.split("/"); a = a + "_" + b[b.length - 1].replace(/[\/:]/g, "").toLowerCase() + "="; b = p.cookie.split(";"); for (var c = 0; c < b.length; c++) { for (var d = b[c]; d.charAt(0) == " "; ) d = d.substring(1, d.length); if (d.indexOf(a) === 0) return decodeURIComponent(d.substring(a.length, d.length)) } return null } function W(a, b) {
|
|
||||||
b = b.getElementsByTagName("tr"); var c, d, f, e, h, j, k, m, t = function (O, B, F) { for (; typeof O[B][F] != "undefined"; ) F++; return F }; a.splice(0, a.length); d = 0; for (j = b.length; d <
|
|
||||||
j; d++) a.push([]); d = 0; for (j = b.length; d < j; d++) { f = 0; for (k = b[d].childNodes.length; f < k; f++) { c = b[d].childNodes[f]; if (c.nodeName.toUpperCase() == "TD" || c.nodeName.toUpperCase() == "TH") { var q = c.getAttribute("colspan") * 1, I = c.getAttribute("rowspan") * 1; q = !q || q === 0 || q === 1 ? 1 : q; I = !I || I === 0 || I === 1 ? 1 : I; m = t(a, d, 0); for (h = 0; h < q; h++) for (e = 0; e < I; e++) { a[d + e][m + h] = { cell: c, unique: q == 1 ? true : false }; a[d + e].nTr = b[d] } } } }
|
|
||||||
} function S(a, b, c) {
|
|
||||||
var d = []; if (typeof c == "undefined") { c = a.aoHeader; if (typeof b != "undefined") { c = []; W(c, b) } } b = 0;
|
|
||||||
for (var f = c.length; b < f; b++) for (var e = 0, h = c[b].length; e < h; e++) if (c[b][e].unique && (typeof d[e] == "undefined" || !a.bSortCellsTop)) d[e] = c[b][e].cell; return d
|
|
||||||
} function Ua() {
|
|
||||||
var a = p.createElement("p"), b = a.style; b.width = "100%"; b.height = "200px"; var c = p.createElement("div"); b = c.style; b.position = "absolute"; b.top = "0px"; b.left = "0px"; b.visibility = "hidden"; b.width = "200px"; b.height = "150px"; b.overflow = "hidden"; c.appendChild(a); p.body.appendChild(c); b = a.offsetWidth; c.style.overflow = "scroll"; a = a.offsetWidth; if (b == a) a =
|
|
||||||
c.clientWidth; p.body.removeChild(c); return b - a
|
|
||||||
} function P(a, b, c) { for (var d = 0, f = b.length; d < f; d++) for (var e = 0, h = b[d].childNodes.length; e < h; e++) if (b[d].childNodes[e].nodeType == 1) typeof c != "undefined" ? a(b[d].childNodes[e], c[d].childNodes[e]) : a(b[d].childNodes[e]) } function n(a, b, c, d) { if (typeof d == "undefined") d = c; if (typeof b[c] != "undefined") a[d] = b[c] } function da(a, b, c) { for (var d = [], f = 0, e = a.aoColumns.length; f < e; f++) d.push(H(a, b, f, c)); return d } function H(a, b, c, d) {
|
|
||||||
var f = a.aoColumns[c]; if ((c = f.fnGetData(a.aoData[b]._aData)) ===
|
|
||||||
undefined) { if (a.iDrawError != a.iDraw && f.sDefaultContent === null) { J(a, 0, "Requested unknown parameter '" + f.mDataProp + "' from the data source for row " + b); a.iDrawError = a.iDraw } return f.sDefaultContent } if (c === null && f.sDefaultContent !== null) c = f.sDefaultContent; if (d == "display" && c === null) return ""; return c
|
|
||||||
} function N(a, b, c, d) { a.aoColumns[c].fnSetData(a.aoData[b]._aData, d) } function Z(a) {
|
|
||||||
if (a === null) return function () { return null }; else if (typeof a == "function") return function (c) { return a(c) }; else if (typeof a ==
|
|
||||||
"string" && a.indexOf(".") != -1) { var b = a.split("."); return b.length == 2 ? function (c) { return c[b[0]][b[1]] } : b.length == 3 ? function (c) { return c[b[0]][b[1]][b[2]] } : function (c) { for (var d = 0, f = b.length; d < f; d++) c = c[b[d]]; return c } } else return function (c) { return c[a] }
|
|
||||||
} function ya(a) {
|
|
||||||
if (a === null) return function () { }; else if (typeof a == "function") return function (c, d) { return a(c, d) }; else if (typeof a == "string" && a.indexOf(".") != -1) {
|
|
||||||
var b = a.split("."); return b.length == 2 ? function (c, d) { c[b[0]][b[1]] = d } : b.length == 3 ? function (c,
|
|
||||||
d) { c[b[0]][b[1]][b[2]] = d } : function (c, d) { for (var f = 0, e = b.length - 1; f < e; f++) c = c[b[f]]; c[b[b.length - 1]] = d }
|
|
||||||
} else return function (c, d) { c[a] = d }
|
|
||||||
} this.oApi = {}; this.fnDraw = function (a) { var b = A(this[o.iApiIndex]); if (typeof a != "undefined" && a === false) { E(b); C(b) } else ba(b) }; this.fnFilter = function (a, b, c, d, f) {
|
|
||||||
var e = A(this[o.iApiIndex]); if (e.oFeatures.bFilter) {
|
|
||||||
if (typeof c == "undefined") c = false; if (typeof d == "undefined") d = true; if (typeof f == "undefined") f = true; if (typeof b == "undefined" || b === null) {
|
|
||||||
M(e, { sSearch: a, bRegex: c,
|
|
||||||
bSmart: d
|
|
||||||
}, 1); if (f && typeof e.aanFeatures.f != "undefined") { b = e.aanFeatures.f; c = 0; for (d = b.length; c < d; c++) i("input", b[c]).val(a) }
|
|
||||||
} else { e.aoPreSearchCols[b].sSearch = a; e.aoPreSearchCols[b].bRegex = c; e.aoPreSearchCols[b].bSmart = d; M(e, e.oPreviousSearch, 1) }
|
|
||||||
}
|
|
||||||
}; this.fnSettings = function () { return A(this[o.iApiIndex]) }; this.fnVersionCheck = o.fnVersionCheck; this.fnSort = function (a) { var b = A(this[o.iApiIndex]); b.aaSorting = a; R(b) }; this.fnSortListener = function (a, b, c) { ga(A(this[o.iApiIndex]), a, b, c) }; this.fnAddData = function (a,
|
|
||||||
b) { if (a.length === 0) return []; var c = [], d, f = A(this[o.iApiIndex]); if (typeof a[0] == "object") for (var e = 0; e < a.length; e++) { d = v(f, a[e]); if (d == -1) return c; c.push(d) } else { d = v(f, a); if (d == -1) return c; c.push(d) } f.aiDisplay = f.aiDisplayMaster.slice(); if (typeof b == "undefined" || b) ba(f); return c }; this.fnDeleteRow = function (a, b, c) {
|
|
||||||
var d = A(this[o.iApiIndex]); a = typeof a == "object" ? U(d, a) : a; var f = d.aoData.splice(a, 1), e = i.inArray(a, d.aiDisplay); d.asDataSearch.splice(e, 1); ra(d.aiDisplayMaster, a); ra(d.aiDisplay, a); typeof b ==
|
|
||||||
"function" && b.call(this, d, f); if (d._iDisplayStart >= d.aiDisplay.length) { d._iDisplayStart -= d._iDisplayLength; if (d._iDisplayStart < 0) d._iDisplayStart = 0 } if (typeof c == "undefined" || c) { E(d); C(d) } return f
|
|
||||||
}; this.fnClearTable = function (a) { var b = A(this[o.iApiIndex]); ia(b); if (typeof a == "undefined" || a) C(b) }; this.fnOpen = function (a, b, c) {
|
|
||||||
var d = A(this[o.iApiIndex]); this.fnClose(a); var f = p.createElement("tr"), e = p.createElement("td"); f.appendChild(e); e.className = c; e.colSpan = X(d); if (typeof b.jquery != "undefined" || typeof b ==
|
|
||||||
"object") e.appendChild(b); else e.innerHTML = b; b = i("tr", d.nTBody); i.inArray(a, b) != -1 && i(f).insertAfter(a); d.aoOpenRows.push({ nTr: f, nParent: a }); return f
|
|
||||||
}; this.fnClose = function (a) { for (var b = A(this[o.iApiIndex]), c = 0; c < b.aoOpenRows.length; c++) if (b.aoOpenRows[c].nParent == a) { (a = b.aoOpenRows[c].nTr.parentNode) && a.removeChild(b.aoOpenRows[c].nTr); b.aoOpenRows.splice(c, 1); return 0 } return 1 }; this.fnGetData = function (a, b) {
|
|
||||||
var c = A(this[o.iApiIndex]); if (typeof a != "undefined") {
|
|
||||||
a = typeof a == "object" ? U(c, a) : a; if (typeof b !=
|
|
||||||
"undefined") return H(c, a, b, ""); return typeof c.aoData[a] != "undefined" ? c.aoData[a]._aData : null
|
|
||||||
} return aa(c)
|
|
||||||
}; this.fnGetNodes = function (a) { var b = A(this[o.iApiIndex]); if (typeof a != "undefined") return typeof b.aoData[a] != "undefined" ? b.aoData[a].nTr : null; return $(b) }; this.fnGetPosition = function (a) { var b = A(this[o.iApiIndex]), c = a.nodeName.toUpperCase(); if (c == "TR") return U(b, a); else if (c == "TD" || c == "TH") { c = U(b, a.parentNode); for (var d = Q(b, c), f = 0; f < b.aoColumns.length; f++) if (d[f] == a) return [c, pa(b, f), f] } return null };
|
|
||||||
this.fnUpdate = function (a, b, c, d, f) {
|
|
||||||
var e = A(this[o.iApiIndex]); b = typeof b == "object" ? U(e, b) : b; if (i.isArray(a) && typeof a == "object") { e.aoData[b]._aData = a.slice(); for (c = 0; c < e.aoColumns.length; c++) this.fnUpdate(H(e, b, c), b, c, false, false) } else if (typeof a == "object") { e.aoData[b]._aData = i.extend(true, {}, a); for (c = 0; c < e.aoColumns.length; c++) this.fnUpdate(H(e, b, c), b, c, false, false) } else {
|
|
||||||
a = a; N(e, b, c, a); if (e.aoColumns[c].fnRender !== null) {
|
|
||||||
a = e.aoColumns[c].fnRender({ iDataRow: b, iDataColumn: c, aData: e.aoData[b]._aData,
|
|
||||||
oSettings: e
|
|
||||||
}); e.aoColumns[c].bUseRendered && N(e, b, c, a)
|
|
||||||
} if (e.aoData[b].nTr !== null) Q(e, b)[c].innerHTML = a
|
|
||||||
} c = i.inArray(b, e.aiDisplay); e.asDataSearch[c] = na(e, da(e, b, "filter")); if (typeof f == "undefined" || f) ca(e); if (typeof d == "undefined" || d) ba(e); return 0
|
|
||||||
}; this.fnSetColumnVis = function (a, b, c) {
|
|
||||||
var d = A(this[o.iApiIndex]), f, e; e = d.aoColumns.length; var h, j; if (d.aoColumns[a].bVisible != b) {
|
|
||||||
if (b) {
|
|
||||||
for (f = j = 0; f < a; f++) d.aoColumns[f].bVisible && j++; j = j >= X(d); if (!j) for (f = a; f < e; f++) if (d.aoColumns[f].bVisible) { h = f; break } f = 0;
|
|
||||||
for (e = d.aoData.length; f < e; f++) if (d.aoData[f].nTr !== null) j ? d.aoData[f].nTr.appendChild(d.aoData[f]._anHidden[a]) : d.aoData[f].nTr.insertBefore(d.aoData[f]._anHidden[a], Q(d, f)[h])
|
|
||||||
} else { f = 0; for (e = d.aoData.length; f < e; f++) if (d.aoData[f].nTr !== null) { h = Q(d, f)[a]; d.aoData[f]._anHidden[a] = h; h.parentNode.removeChild(h) } } d.aoColumns[a].bVisible = b; L(d, d.aoHeader); d.nTFoot && L(d, d.aoFooter); f = 0; for (e = d.aoOpenRows.length; f < e; f++) d.aoOpenRows[f].nTr.colSpan = X(d); if (typeof c == "undefined" || c) { ca(d); C(d) } sa(d)
|
|
||||||
}
|
|
||||||
}; this.fnPageChange =
|
|
||||||
function (a, b) { var c = A(this[o.iApiIndex]); ja(c, a); E(c); if (typeof b == "undefined" || b) C(c) }; this.fnDestroy = function () {
|
|
||||||
var a = A(this[o.iApiIndex]), b = a.nTableWrapper.parentNode, c = a.nTBody, d, f; a.bDestroying = true; d = 0; for (f = a.aoColumns.length; d < f; d++) a.aoColumns[d].bVisible === false && this.fnSetColumnVis(d, true); i(a.nTableWrapper).find("*").andSelf().unbind(".DT"); i("tbody>tr>td." + a.oClasses.sRowEmpty, a.nTable).parent().remove(); if (a.nTable != a.nTHead.parentNode) { i(">thead", a.nTable).remove(); a.nTable.appendChild(a.nTHead) } if (a.nTFoot &&
|
|
||||||
a.nTable != a.nTFoot.parentNode) { i(">tfoot", a.nTable).remove(); a.nTable.appendChild(a.nTFoot) } a.nTable.parentNode.removeChild(a.nTable); i(a.nTableWrapper).remove(); a.aaSorting = []; a.aaSortingFixed = []; T(a); i($(a)).removeClass(a.asStripClasses.join(" ")); if (a.bJUI) {
|
|
||||||
i("th", a.nTHead).removeClass([o.oStdClasses.sSortable, o.oJUIClasses.sSortableAsc, o.oJUIClasses.sSortableDesc, o.oJUIClasses.sSortableNone].join(" ")); i("th span." + o.oJUIClasses.sSortIcon, a.nTHead).remove(); i("th", a.nTHead).each(function () {
|
|
||||||
var e =
|
|
||||||
i("div." + o.oJUIClasses.sSortJUIWrapper, this), h = e.contents(); i(this).append(h); e.remove()
|
|
||||||
})
|
|
||||||
} else i("th", a.nTHead).removeClass([o.oStdClasses.sSortable, o.oStdClasses.sSortableAsc, o.oStdClasses.sSortableDesc, o.oStdClasses.sSortableNone].join(" ")); a.nTableReinsertBefore ? b.insertBefore(a.nTable, a.nTableReinsertBefore) : b.appendChild(a.nTable); d = 0; for (f = a.aoData.length; d < f; d++) a.aoData[d].nTr !== null && c.appendChild(a.aoData[d].nTr); if (a.oFeatures.bAutoWidth === true) a.nTable.style.width = u(a.sDestroyWidth);
|
|
||||||
i(">tr:even", c).addClass(a.asDestoryStrips[0]); i(">tr:odd", c).addClass(a.asDestoryStrips[1]); d = 0; for (f = D.length; d < f; d++) D[d] == a && D.splice(d, 1); a = null
|
|
||||||
};
|
|
||||||
this.fnAdjustColumnSizing = function (a) { var b = A(this[o.iApiIndex]); ca(b); if (typeof a == "undefined" || a) this.fnDraw(false); else if (b.oScroll.sX !== "" || b.oScroll.sY !== "") this.oApi._fnScrollDraw(b) }; for (var ua in o.oApi) if (ua) this[ua] = r(ua); this.oApi._fnExternApiFunc = r; this.oApi._fnInitalise = s; this.oApi._fnInitComplete = w; this.oApi._fnLanguageProcess = y; this.oApi._fnAddColumn =
|
|
||||||
G;
|
|
||||||
this.oApi._fnColumnOptions = x; this.oApi._fnAddData = v; this.oApi._fnCreateTr = z; this.oApi._fnGatherData = Y; this.oApi._fnBuildHead = V; this.oApi._fnDrawHead = L; this.oApi._fnDraw = C; this.oApi._fnReDraw = ba; this.oApi._fnAjaxUpdate = za; this.oApi._fnAjaxUpdateDraw = Aa; this.oApi._fnAddOptionsHtml = xa; this.oApi._fnFeatureHtmlTable = Fa; this.oApi._fnScrollDraw = Ia; this.oApi._fnAjustColumnSizing = ca; this.oApi._fnFeatureHtmlFilter = Da; this.oApi._fnFilterComplete = M; this.oApi._fnFilterCustom = Ma; this.oApi._fnFilterColumn = La;
|
|
||||||
this.oApi._fnFilter = Ka; this.oApi._fnBuildSearchArray = ka; this.oApi._fnBuildSearchRow = na; this.oApi._fnFilterCreateSearch = la; this.oApi._fnDataToSearch = ma; this.oApi._fnSort = R; this.oApi._fnSortAttachListener = ga; this.oApi._fnSortingClasses = T; this.oApi._fnFeatureHtmlPaginate = Ha; this.oApi._fnPageChange = ja; this.oApi._fnFeatureHtmlInfo = Ga; this.oApi._fnUpdateInfo = Na; this.oApi._fnFeatureHtmlLength = Ca; this.oApi._fnFeatureHtmlProcessing = Ea; this.oApi._fnProcessingDisplay = K; this.oApi._fnVisibleToColumnIndex = Ja; this.oApi._fnColumnIndexToVisible =
|
|
||||||
pa;
|
|
||||||
this.oApi._fnNodeToDataIndex = U;
|
|
||||||
this.oApi._fnVisbleColumns = X; this.oApi._fnCalculateEnd = E; this.oApi._fnConvertToWidth = Oa; this.oApi._fnCalculateColumnWidths = ea; this.oApi._fnScrollingWidthAdjust = Qa; this.oApi._fnGetWidestNode = Pa; this.oApi._fnGetMaxLenString = Ra; this.oApi._fnStringToCss = u; this.oApi._fnArrayCmp = Va; this.oApi._fnDetectType = fa; this.oApi._fnSettingsFromNode = A; this.oApi._fnGetDataMaster = aa; this.oApi._fnGetTrNodes = $; this.oApi._fnGetTdNodes = Q; this.oApi._fnEscapeRegex = oa; this.oApi._fnDeleteIndex =
|
|
||||||
ra;
|
|
||||||
this.oApi._fnReOrderIndex = Ba;
|
|
||||||
this.oApi._fnColumnOrdering = ha;
|
|
||||||
this.oApi._fnLog = J;
|
|
||||||
this.oApi._fnClearTable = ia;
|
|
||||||
this.oApi._fnSaveState = sa;
|
|
||||||
this.oApi._fnLoadState = Ta;
|
|
||||||
this.oApi._fnCreateCookie = Sa;
|
|
||||||
this.oApi._fnReadCookie = ta;
|
|
||||||
this.oApi._fnDetectHeader = W; this.oApi._fnGetUniqueThs = S; this.oApi._fnScrollBarWidth = Ua; this.oApi._fnApplyToChildren = P; this.oApi._fnMap = n; this.oApi._fnGetRowData = da; this.oApi._fnGetCellData = H; this.oApi._fnSetCellData = N; this.oApi._fnGetObjectDataFn = Z; this.oApi._fnSetObjectDataFn = ya; var va =
|
|
||||||
this;
|
|
||||||
return this.each(function () {
|
|
||||||
var a = 0, b, c, d, f; a = 0; for (b = D.length; a < b; a++) {
|
|
||||||
if (D[a].nTable == this) if (typeof g == "undefined" || typeof g.bRetrieve != "undefined" && g.bRetrieve === true) return D[a].oInstance; else if (typeof g.bDestroy != "undefined" && g.bDestroy === true) { D[a].oInstance.fnDestroy(); break } else {
|
|
||||||
J(D[a], 0, "Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destory the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");
|
|
||||||
return
|
|
||||||
} if (D[a].sTableId !== "" && D[a].sTableId == this.getAttribute("id")) { D.splice(a, 1); break }
|
|
||||||
} var e = new l; D.push(e); var h = false, j = false; a = this.getAttribute("id"); if (a !== null) { e.sTableId = a; e.sInstance = a } else e.sInstance = o._oExternConfig.iNextUnique++; if (this.nodeName.toLowerCase() != "table") J(e, 0, "Attempted to initialise DataTables on a node which is not a table: " + this.nodeName); else {
|
|
||||||
e.nTable = this; e.oInstance = va.length == 1 ? va : i(this).dataTable(); e.oApi = va.oApi; e.sDestroyWidth = i(this).width(); if (typeof g !=
|
|
||||||
"undefined" && g !== null) {
|
|
||||||
e.oInit = g; n(e.oFeatures, g, "bPaginate");
|
|
||||||
n(e.oFeatures, g, "bLengthChange");
|
|
||||||
n(e.oFeatures, g, "bFilter");
|
|
||||||
n(e.oFeatures, g, "bSort");
|
|
||||||
n(e.oFeatures, g, "bInfo");
|
|
||||||
n(e.oFeatures, g, "bProcessing");
|
|
||||||
n(e.oFeatures, g, "bAutoWidth");
|
|
||||||
n(e.oFeatures, g, "bSortClasses");
|
|
||||||
n(e.oFeatures, g, "bServerSide");
|
|
||||||
n(e.oFeatures, g, "bDeferRender");
|
|
||||||
n(e.oScroll, g, "sScrollX", "sX");
|
|
||||||
n(e.oScroll, g, "sScrollXInner", "sXInner");
|
|
||||||
n(e.oScroll, g, "sScrollY", "sY");
|
|
||||||
n(e.oScroll, g, "bScrollCollapse", "bCollapse");
|
|
||||||
n(e.oScroll, g, "bScrollInfinite", "bInfinite");
|
|
||||||
n(e.oScroll, g, "iScrollLoadGap", "iLoadGap");
|
|
||||||
n(e.oScroll, g, "bScrollAutoCss", "bAutoCss");
|
|
||||||
n(e, g, "asStripClasses");
|
|
||||||
n(e, g, "fnPreDrawCallback");
|
|
||||||
n(e, g, "fnRowCallback"); n(e, g, "fnHeaderCallback"); n(e, g, "fnFooterCallback"); n(e, g, "fnCookieCallback"); n(e, g, "fnInitComplete"); n(e, g, "fnServerData"); n(e, g, "fnFormatNumber"); n(e, g, "aaSorting"); n(e, g, "aaSortingFixed"); n(e, g, "aLengthMenu"); n(e, g, "sPaginationType"); n(e, g, "sAjaxSource"); n(e, g, "sAjaxDataProp"); n(e, g, "iCookieDuration"); n(e, g, "sCookiePrefix");
|
|
||||||
n(e, g, "sDom");
|
|
||||||
n(e, g, "bSortCellsTop");
|
|
||||||
n(e, g, "oSearch", "oPreviousSearch");
|
|
||||||
n(e, g, "aoSearchCols", "aoPreSearchCols");
|
|
||||||
n(e, g, "iDisplayLength", "_iDisplayLength");
|
|
||||||
n(e, g, "bJQueryUI", "bJUI");
|
|
||||||
n(e.oLanguage, g, "fnInfoCallback");
|
|
||||||
typeof g.fnDrawCallback == "function" && e.aoDrawCallback.push({
|
|
||||||
fn: g.fnDrawCallback, sName: "user"
|
|
||||||
});
|
|
||||||
typeof g.fnStateSaveCallback == "function" && e.aoStateSave.push({
|
|
||||||
fn: g.fnStateSaveCallback, sName: "user"
|
|
||||||
});
|
|
||||||
typeof g.fnStateLoadCallback == "function" && e.aoStateLoad.push({
|
|
||||||
fn: g.fnStateLoadCallback, sName: "user"
|
|
||||||
});
|
|
||||||
if (e.oFeatures.bServerSide && e.oFeatures.bSort && e.oFeatures.bSortClasses) e.aoDrawCallback.push({ fn: T, sName: "server_side_sort_classes" }); else e.oFeatures.bDeferRender && e.aoDrawCallback.push({ fn: T, sName: "defer_sort_classes" }); if (typeof g.bJQueryUI != "undefined" && g.bJQueryUI) { e.oClasses = o.oJUIClasses; if (typeof g.sDom == "undefined") e.sDom = '<"H"lfr>t<"F"ip>' } if (e.oScroll.sX !== "" || e.oScroll.sY !== "") e.oScroll.iBarWidth = Ua(); if (typeof g.iDisplayStart != "undefined" && typeof e.iInitDisplayStart == "undefined") {
|
|
||||||
e.iInitDisplayStart = g.iDisplayStart; e._iDisplayStart = g.iDisplayStart
|
|
||||||
} if (typeof g.bStateSave != "undefined") { e.oFeatures.bStateSave = g.bStateSave; Ta(e, g); e.aoDrawCallback.push({ fn: sa, sName: "state_save" }) } if (typeof g.iDeferLoading != "undefined") { e.bDeferLoading = true; e._iRecordsTotal = g.iDeferLoading; e._iRecordsDisplay = g.iDeferLoading } if (typeof g.aaData != "undefined") j = true; if (typeof g != "undefined" && typeof g.aoData != "undefined") g.aoColumns = g.aoData; if (typeof g.oLanguage != "undefined") if (typeof g.oLanguage.sUrl != "undefined" && g.oLanguage.sUrl !==
|
|
||||||
"") { e.oLanguage.sUrl = g.oLanguage.sUrl; i.getJSON(e.oLanguage.sUrl, null, function (t) { y(e, t, true) }); h = true } else y(e, g.oLanguage, false)
|
|
||||||
} else g = {}; if (typeof g.asStripClasses == "undefined") { e.asStripClasses.push(e.oClasses.sStripOdd); e.asStripClasses.push(e.oClasses.sStripEven) } c = false; d = i(">tbody>tr", this); a = 0; for (b = e.asStripClasses.length; a < b; a++) if (d.filter(":lt(2)").hasClass(e.asStripClasses[a])) { c = true; break } if (c) {
|
|
||||||
e.asDestoryStrips = ["", ""]; if (i(d[0]).hasClass(e.oClasses.sStripOdd)) e.asDestoryStrips[0] +=
|
|
||||||
e.oClasses.sStripOdd + " "; if (i(d[0]).hasClass(e.oClasses.sStripEven)) e.asDestoryStrips[0] += e.oClasses.sStripEven; if (i(d[1]).hasClass(e.oClasses.sStripOdd)) e.asDestoryStrips[1] += e.oClasses.sStripOdd + " "; if (i(d[1]).hasClass(e.oClasses.sStripEven)) e.asDestoryStrips[1] += e.oClasses.sStripEven; d.removeClass(e.asStripClasses.join(" "))
|
|
||||||
} c = []; var k; a = this.getElementsByTagName("thead"); if (a.length !== 0) { W(e.aoHeader, a[0]); c = S(e) } if (typeof g.aoColumns == "undefined") { k = []; a = 0; for (b = c.length; a < b; a++) k.push(null) } else k =
|
|
||||||
g.aoColumns; a = 0; for (b = k.length; a < b; a++) { if (typeof g.saved_aoColumns != "undefined" && g.saved_aoColumns.length == b) { if (k[a] === null) k[a] = {}; k[a].bVisible = g.saved_aoColumns[a].bVisible } G(e, c ? c[a] : null) } if (typeof g.aoColumnDefs != "undefined") for (a = g.aoColumnDefs.length - 1; a >= 0; a--) {
|
|
||||||
var m = g.aoColumnDefs[a].aTargets; i.isArray(m) || J(e, 1, "aTargets must be an array of targets, not a " + typeof m); c = 0; for (d = m.length; c < d; c++) if (typeof m[c] == "number" && m[c] >= 0) { for (; e.aoColumns.length <= m[c]; ) G(e); x(e, m[c], g.aoColumnDefs[a]) } else if (typeof m[c] ==
|
|
||||||
"number" && m[c] < 0) x(e, e.aoColumns.length + m[c], g.aoColumnDefs[a]); else if (typeof m[c] == "string") { b = 0; for (f = e.aoColumns.length; b < f; b++) if (m[c] == "_all" || i(e.aoColumns[b].nTh).hasClass(m[c])) x(e, b, g.aoColumnDefs[a]) }
|
|
||||||
} if (typeof k != "undefined") { a = 0; for (b = k.length; a < b; a++) x(e, a, k[a]) } a = 0; for (b = e.aaSorting.length; a < b; a++) {
|
|
||||||
if (e.aaSorting[a][0] >= e.aoColumns.length)
|
|
||||||
e.aaSorting[a][0] = 0; k = e.aoColumns[e.aaSorting[a][0]];
|
|
||||||
if (typeof e.aaSorting[a][2] == "undefined") e.aaSorting[a][2] = 0;
|
|
||||||
if (typeof g.aaSorting == "undefined" && typeof e.saved_aaSorting == "undefined")
|
|
||||||
e.aaSorting[a][1] = k.asSorting[0];
|
|
||||||
c = 0; for (d = k.asSorting.length;
|
|
||||||
c < d; c++) if (e.aaSorting[a][1] == k.asSorting[c]) {
|
|
||||||
e.aaSorting[a][2] = c; break
|
|
||||||
}
|
|
||||||
} T(e); a = i(">thead", this); if (a.length === 0) {
|
|
||||||
a = [p.createElement("thead")]; this.appendChild(a[0])
|
|
||||||
} e.nTHead = a[0]; a = i(">tbody", this);
|
|
||||||
if (a.length === 0) {
|
|
||||||
a = [p.createElement("tbody")];
|
|
||||||
this.appendChild(a[0])
|
|
||||||
} e.nTBody = a[0]; a = i(">tfoot", this);
|
|
||||||
if (a.length > 0) { e.nTFoot = a[0]; W(e.aoFooter, e.nTFoot) }
|
|
||||||
if (j)
|
|
||||||
for (a = 0; a < g.aaData.length; a++) v(e, g.aaData[a]);
|
|
||||||
else Y(e); e.aiDisplay = e.aiDisplayMaster.slice(); e.bInitialised = true; h === false && s(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})(jQuery, window, document);
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
window.log = function(){
|
|
||||||
log.history = log.history || [];
|
|
||||||
log.history.push(arguments);
|
|
||||||
arguments.callee = arguments.callee.caller;
|
|
||||||
if(this.console) console.log( Array.prototype.slice.call(arguments) );
|
|
||||||
};
|
|
||||||
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c})(window.console=window.console||{});
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-string-asc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
var y = b.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-string-desc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
var y = b.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-numeric-asc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
var y = b.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
x = parseFloat( x );
|
|
||||||
y = parseFloat( y );
|
|
||||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-numeric-desc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
var y = b.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
x = parseFloat( x );
|
|
||||||
y = parseFloat( y );
|
|
||||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
function toggle(source) {
|
|
||||||
checkboxes = document.getElementsByClassName('checkbox');
|
|
||||||
for(var i in checkboxes)
|
|
||||||
checkboxes[i].checked = source.checked;
|
|
||||||
}
|
|
||||||
@@ -67,37 +67,49 @@ function getInfo(elem,id,type) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImageLinks(elem,id,type) {
|
function getImageLinks(elem,id,type,unveil) {
|
||||||
|
|
||||||
if ( type == 'artist' ) {
|
if ( type == 'artist' ) {
|
||||||
var infoURL = "getImageLinks?ArtistID=" + id;
|
var infoURL = "getImageLinks?ArtistID=" + id;
|
||||||
} else {
|
} else {
|
||||||
var infoURL = "getImageLinks?AlbumID=" + id;
|
var infoURL = "getImageLinks?AlbumID=" + id;
|
||||||
}
|
}
|
||||||
// Get Data from the cache by ID
|
|
||||||
|
// Get Data from the cache by ID
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: infoURL,
|
url: infoURL,
|
||||||
cache: true,
|
cache: true,
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if ( data.thumbnail == "" || data.thumbnail == undefined ) {
|
if (!data) {
|
||||||
|
// Invalid response
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.thumbnail) {
|
||||||
var thumbnail = "interfaces/default/images/no-cover-artist.png";
|
var thumbnail = "interfaces/default/images/no-cover-artist.png";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var thumbnail = data.thumbnail;
|
var thumbnail = data.thumbnail;
|
||||||
}
|
}
|
||||||
if ( data.artwork == "" || data.artwork == undefined ) {
|
if (!data.artwork) {
|
||||||
var artwork = "interfaces/default/images/no-cover-artist.png";
|
var artwork = "interfaces/default/images/no-cover-artist.png";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var artwork = data.artwork;
|
var artwork = data.artwork;
|
||||||
}
|
}
|
||||||
$(elem).attr("src", thumbnail);
|
|
||||||
|
if (unveil) {
|
||||||
|
$(elem).attr("data-src", thumbnail);
|
||||||
|
$(elem).unveil();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(elem).attr("src", thumbnail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initHeader() {
|
function initHeader() {
|
||||||
//settings
|
//settings
|
||||||
var header = $("#container header");
|
var header = $("#container header");
|
||||||
var fadeSpeed = 100, fadeTo = 0.5, topDistance = 20;
|
var fadeSpeed = 100, fadeTo = 0.5, topDistance = 20;
|
||||||
@@ -228,6 +240,21 @@ function showMsg(msg,loader,timeout,ms) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showArtistMsg(msg) {
|
||||||
|
var feedback = $("#ajaxMsg2");
|
||||||
|
update = $("#updatebar");
|
||||||
|
if ( update.is(":visible") ) {
|
||||||
|
var height = update.height() + 35;
|
||||||
|
feedback.css("bottom",height + "px");
|
||||||
|
} else {
|
||||||
|
feedback.removeAttr("style");
|
||||||
|
}
|
||||||
|
feedback.fadeIn();
|
||||||
|
var message = $("<i class='fa fa-refresh fa-spin'></i> " + msg + "</div>");
|
||||||
|
feedback.css("padding","14px 10px")
|
||||||
|
$(feedback).prepend(message);
|
||||||
|
}
|
||||||
|
|
||||||
function doAjaxCall(url,elem,reload,form) {
|
function doAjaxCall(url,elem,reload,form) {
|
||||||
// Set Message
|
// Set Message
|
||||||
feedback = $("#ajaxMsg");
|
feedback = $("#ajaxMsg");
|
||||||
@@ -316,6 +343,10 @@ function doAjaxCall(url,elem,reload,form) {
|
|||||||
}
|
}
|
||||||
if ( reload == "tabs") refreshTab();
|
if ( reload == "tabs") refreshTab();
|
||||||
if ( reload == "page") location.reload();
|
if ( reload == "page") location.reload();
|
||||||
|
if ( reload == "submenu&table") {
|
||||||
|
refreshSubmenu();
|
||||||
|
refreshTable();
|
||||||
|
}
|
||||||
if ( form ) {
|
if ( form ) {
|
||||||
// Change the option to 'choose...'
|
// Change the option to 'choose...'
|
||||||
$(formID + " select").children('option[disabled=disabled]').attr('selected','selected');
|
$(formID + " select").children('option[disabled=disabled]').attr('selected','selected');
|
||||||
@@ -330,18 +361,16 @@ function doAjaxCall(url,elem,reload,form) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function doSimpleAjaxCall(url) {
|
||||||
|
$.ajax(url);
|
||||||
|
}
|
||||||
|
|
||||||
function resetFilters(text){
|
function resetFilters(text){
|
||||||
if ( $(".dataTables_filter").length > 0 ) {
|
if ( $(".dataTables_filter").length > 0 ) {
|
||||||
$(".dataTables_filter input").attr("placeholder","filter " + text + "");
|
$(".dataTables_filter input").attr("placeholder","filter " + text + "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function preventDefault(){
|
|
||||||
$("a[href='#']").live('click', function(){
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initFancybox() {
|
function initFancybox() {
|
||||||
if ( $("a[rel=dialog]").length > 0 ) {
|
if ( $("a[rel=dialog]").length > 0 ) {
|
||||||
$.getScript('interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js', function() {
|
$.getScript('interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js', function() {
|
||||||
@@ -351,11 +380,6 @@ function initFancybox() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
|
||||||
initHeader();
|
|
||||||
preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
init();
|
initHeader();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
lossless<%inherit file="base.html"/>
|
<%inherit file="base.html"/>
|
||||||
<%!
|
<%!
|
||||||
from headphones import helpers
|
from headphones import helpers
|
||||||
%>
|
%>
|
||||||
|
|
||||||
|
<%def name="headerIncludes()">
|
||||||
|
<div id="subhead_container">
|
||||||
|
<div id="subhead_menu">
|
||||||
|
<a class="menu_link_edit" href="clearLogs"><i class="fa fa-trash-o"></i> Clear log</a>
|
||||||
|
<a class="menu_link_edit" href="toggleVerbose"><i class="fa fa-pencil"></i> Toggle Debug Log</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
<div class="title">
|
<div id="paddingheader">
|
||||||
<h1 class="clearfix"><i class="fa fa-flag"></i> Logs</h1>
|
<h1 class="clearfix"><i class="fa fa-flag"></i> Logs</h1>
|
||||||
</div>
|
</div>
|
||||||
<table class="display" id="log_table">
|
<table class="display" id="log_table">
|
||||||
@@ -19,7 +28,7 @@ lossless<%inherit file="base.html"/>
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br>
|
<br>
|
||||||
<div align="center">Refresh rate:
|
<div align="center">Refresh rate:
|
||||||
<select id="refreshrate" onchange="setRefresh()">
|
<select id="refreshrate" onchange="setRefresh()">
|
||||||
<option value="0" selected="selected">No Refresh</option>
|
<option value="0" selected="selected">No Refresh</option>
|
||||||
<option value="5">5 Seconds</option>
|
<option value="5">5 Seconds</option>
|
||||||
@@ -29,7 +38,6 @@ lossless<%inherit file="base.html"/>
|
|||||||
<option value="300">5 Minutes</option>
|
<option value="300">5 Minutes</option>
|
||||||
<option value="600">10 Minutes</option>
|
<option value="600">10 Minutes</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="headIncludes()">
|
<%def name="headIncludes()">
|
||||||
@@ -39,46 +47,50 @@ lossless<%inherit file="base.html"/>
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#log_table').dataTable( {
|
initActions();
|
||||||
"bProcessing": true,
|
|
||||||
"bServerSide": true,
|
$('#log_table').dataTable( {
|
||||||
"sAjaxSource": 'getLog',
|
"bProcessing": true,
|
||||||
"sPaginationType": "full_numbers",
|
"bServerSide": true,
|
||||||
"aaSorting": [[0, 'desc']],
|
"sAjaxSource": 'getLog',
|
||||||
"iDisplayLength": 25,
|
"sPaginationType": "full_numbers",
|
||||||
"bStateSave": true,
|
"aaSorting": [[0, 'desc']],
|
||||||
"oLanguage": {
|
"iDisplayLength": 25,
|
||||||
"sSearch":"",
|
"bStateSave": true,
|
||||||
"sLengthMenu":"Show _MENU_ lines per page",
|
"oLanguage": {
|
||||||
"sEmptyTable": "No log information available",
|
"sSearch":"Filter:",
|
||||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ lines",
|
"sLengthMenu":"Show _MENU_ lines per page",
|
||||||
"sInfoEmpty":"Showing 0 to 0 of 0 lines",
|
"sEmptyTable": "No log information available",
|
||||||
"sInfoFiltered":"(filtered from _MAX_ total lines)"},
|
"sInfo":"Showing _START_ to _END_ of _TOTAL_ lines",
|
||||||
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
"sInfoEmpty":"Showing 0 to 0 of 0 lines",
|
||||||
if (aData[1] === "WARNING" || aData[1] === "ERROR")
|
"sInfoFiltered":"(filtered from _MAX_ total lines)"},
|
||||||
{
|
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
||||||
$('td', nRow).closest('tr').addClass("gradeX");
|
if (aData[1] === "ERROR") {
|
||||||
}
|
$('td', nRow).closest('tr').addClass("gradeX");
|
||||||
else
|
} else if (aData[1] === "WARNING") {
|
||||||
{
|
$('td', nRow).closest('tr').addClass("gradeW");
|
||||||
$('td', nRow).closest('tr').addClass("gradeZ");
|
} else {
|
||||||
}
|
$('td', nRow).closest('tr').addClass("gradeZ");
|
||||||
|
}
|
||||||
|
|
||||||
return nRow;
|
return nRow;
|
||||||
},
|
},
|
||||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
"fnDrawCallback": function (o) {
|
||||||
/* Add some extra data to the sender */
|
// Jump to top of page
|
||||||
$.getJSON( sSource, aoData, function (json) {
|
$('html,body').scrollTop(0);
|
||||||
fnCallback(json)
|
},
|
||||||
} );
|
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||||
}
|
/* Add some extra data to the sender */
|
||||||
} );
|
$.getJSON(sSource, aoData, function (json) {
|
||||||
} );
|
fnCallback(json)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var timer;
|
var timer;
|
||||||
function setRefresh()
|
function setRefresh()
|
||||||
{
|
{
|
||||||
refreshrate = document.getElementById('refreshrate');
|
refreshrate = document.getElementById('refreshrate');
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<%def name="headerIncludes()">
|
<%def name="headerIncludes()">
|
||||||
<div id="subhead_container">
|
<div id="subhead_container">
|
||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
<a class="menu_link_edit" id="manage_albums" href="#"><i class="fa fa-pencil"></i> Manage Albums</a>
|
<a class="menu_link_edit" id="manage_albums" href="javascript:void(0)"><i class="fa fa-pencil"></i> Manage Albums</a>
|
||||||
<div id="dialog" title="Choose Album Filter" style="display:none" class="configtable">
|
<div id="dialog" title="Choose Album Filter" style="display:none" class="configtable">
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a href="manageAlbums?Status=Downloaded"><i class="fa fa-check fa-fw"></i> Manage Downloaded Albums</a><br>
|
<a href="manageAlbums?Status=Downloaded"><i class="fa fa-check fa-fw"></i> Manage Downloaded Albums</a><br>
|
||||||
@@ -14,12 +14,13 @@
|
|||||||
<a href="manageAlbums?Status=Snatched"><i class="fa fa-cloud-download fa-fw"></i> </span>Manage Snatched Albums</a><br>
|
<a href="manageAlbums?Status=Snatched"><i class="fa fa-cloud-download fa-fw"></i> </span>Manage Snatched Albums</a><br>
|
||||||
<a href="manageAlbums?Status=Upcoming"><i class="fa fa-calendar fa-fw"></i> Manage Upcoming Albums</a><br>
|
<a href="manageAlbums?Status=Upcoming"><i class="fa fa-calendar fa-fw"></i> Manage Upcoming Albums</a><br>
|
||||||
<a href="manageAlbums?Status=Wanted"><i class="fa fa-heart fa-fw"></i> </span>Manage Wanted Albums</a><br>
|
<a href="manageAlbums?Status=Wanted"><i class="fa fa-heart fa-fw"></i> </span>Manage Wanted Albums</a><br>
|
||||||
|
<a href="manageAlbums?Status=Ignored"><i class="fa fa-meh-o fa-fw"></i> </span>Manage Ignored Albums</a><br>
|
||||||
<br><br>
|
<br><br>
|
||||||
<a href="manageAlbums">Manage All Albums</a>
|
<a href="manageAlbums">Manage All Albums</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="menu_link_edit" href="manageArtists"><i class="fa fa-pencil"></i> Manage Artists</a>
|
<a class="menu_link_edit" href="manageArtists"><i class="fa fa-pencil"></i> Manage Artists</a>
|
||||||
%if not headphones.ADD_ARTISTS:
|
%if not headphones.CONFIG.AUTO_ADD_ARTISTS:
|
||||||
<a class="menu_link_edit" href="manageNew"><i class="fa fa-pencil"></i> Manage New Artists</a>
|
<a class="menu_link_edit" href="manageNew"><i class="fa fa-pencil"></i> Manage New Artists</a>
|
||||||
%endif
|
%endif
|
||||||
<a class="menu_link_edit" href="manageUnmatched"><i class="fa fa-pencil"></i> Manage Unmatched</a>
|
<a class="menu_link_edit" href="manageUnmatched"><i class="fa fa-pencil"></i> Manage Unmatched</a>
|
||||||
@@ -52,18 +53,18 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="">Path to directory</label>
|
<label for="">Path to directory</label>
|
||||||
%if headphones.MUSIC_DIR:
|
%if headphones.CONFIG.MUSIC_DIR:
|
||||||
<input type="text" value="${headphones.MUSIC_DIR}" name="path" size="70" />
|
<input type="text" value="${headphones.CONFIG.MUSIC_DIR}" name="path" size="70" />
|
||||||
%else:
|
%else:
|
||||||
<input type="text" value="Enter a Music Directory to scan" onfocus="if
|
<input type="text" value="Enter a Music Directory to scan" onfocus="if
|
||||||
(this.value==this.defaultValue) this.value='';" name="path" size="70" />
|
(this.value==this.defaultValue) this.value='';" name="path" size="70" />
|
||||||
%endif
|
%endif
|
||||||
</div>
|
</div>
|
||||||
<div class="row checkbox">
|
<div class="row checkbox">
|
||||||
<input type="checkbox" name="libraryscan" id="libraryscan" value="1" ${checked(headphones.LIBRARYSCAN)}><label>Automatically scan library</label>
|
<input type="checkbox" name="libraryscan" id="libraryscan" value="1" ${checked(headphones.CONFIG.LIBRARYSCAN)}><label>Automatically scan library</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="row checkbox">
|
<div class="row checkbox">
|
||||||
<input type="checkbox" name="autoadd" id="autoadd" value="1" ${checked(headphones.ADD_ARTISTS)}><label>Auto-add new artists</label>
|
<input type="checkbox" name="autoadd" id="autoadd" value="1" ${checked(headphones.CONFIG.AUTO_ADD_ARTISTS)}><label>Auto-add new artists</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -82,14 +83,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="">Username</label>
|
<label for="">Username</label>
|
||||||
<%
|
<%
|
||||||
if headphones.LASTFM_USERNAME:
|
if headphones.CONFIG.LASTFM_USERNAME:
|
||||||
lastfmvalue = headphones.LASTFM_USERNAME
|
lastfmvalue = headphones.CONFIG.LASTFM_USERNAME
|
||||||
else:
|
else:
|
||||||
lastfmvalue = ''
|
lastfmvalue = ''
|
||||||
%>
|
%>
|
||||||
<input type="text" value="${lastfmvalue}" placeholder="Last.fm username" onfocus="if
|
<input type="text" value="${lastfmvalue}" placeholder="Last.fm username" onfocus="if
|
||||||
(this.value==this.defaultValue) this.value='';" name="username" id="username" size="18" />
|
(this.value==this.defaultValue) this.value='';" name="username" id="username" size="18" />
|
||||||
<a href="#" onclick="doAjaxCall('importLastFM?username=',$(this),'tabs');return false;" data-success="Last.fm username has been reset"><i class="fa fa-reply"></i> Reset username</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('importLastFM?username=',$(this),'tabs');return false;" data-success="Last.fm username has been reset"><i class="fa fa-reply"></i> Reset username</a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<input type="button" value="Save changes" onclick="doAjaxCall('importLastFM',$(this),'tabs',true);return false;" data-success="Last.fm artists will be imported" data-error="Fill in a last.fm username"/>
|
<input type="button" value="Save changes" onclick="doAjaxCall('importLastFM',$(this),'tabs',true);return false;" data-success="Last.fm artists will be imported" data-error="Fill in a last.fm username"/>
|
||||||
@@ -120,10 +121,10 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Force Search</legend>
|
<legend>Force Search</legend>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a href="#" onclick="doAjaxCall('forceSearch',$(this))" data-success="Checking for wanted albums successful" data-error="Error checking wanted albums"><i class="fa fa-search fa-fw"></i> Force Check for Wanted Albums</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('forceSearch',$(this))" data-success="Checking for wanted albums successful" data-error="Error checking wanted albums"><i class="fa fa-search fa-fw"></i> Force Check for Wanted Albums</a>
|
||||||
<a href="#" onclick="doAjaxCall('forceUpdate',$(this))" data-success="Update active artists successful" data-error="Error forcing update artists"><i class="fa fa-heart fa-fw"></i> Force Update Active Artists [Fast]</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('forceUpdate',$(this))" data-success="Update active artists successful" data-error="Error forcing update artists"><i class="fa fa-heart fa-fw"></i> Force Update Active Artists [Fast]</a>
|
||||||
<a href="#" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><i class="fa fa-refresh fa-fw"></i> Check for Headphones Updates</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><i class="fa fa-refresh fa-fw"></i> Check for Headphones Updates</a>
|
||||||
<a href="#" id="delete_empty_artists"><i class="fa fa-trash-o fa-fw"></i> Delete empty Artists</a>
|
<a href="javascript:void(0)" id="delete_empty_artists"><i class="fa fa-trash-o fa-fw"></i> Delete empty Artists</a>
|
||||||
<div id="emptyartistdialog" title="Confirm Artist Deletion" style="display:none" class="configtable">
|
<div id="emptyartistdialog" title="Confirm Artist Deletion" style="display:none" class="configtable">
|
||||||
%if emptyArtists:
|
%if emptyArtists:
|
||||||
<h3>The following artists will be deleted:</h3>
|
<h3>The following artists will be deleted:</h3>
|
||||||
@@ -136,28 +137,27 @@
|
|||||||
No empty artists found.
|
No empty artists found.
|
||||||
%endif
|
%endif
|
||||||
</div>
|
</div>
|
||||||
<a href="#" onclick="doAjaxCall('forcePostProcess',$(this))" data-success="Post-Processor is being loaded" data-error="Error during Post-Processing"><i class="fa fa-wrench fa-fw"></i> Force Post-Process Albums in Download Folder</a>
|
<div id="post_process">
|
||||||
|
<a href="javascript:void(0)" class="btnOpenDialog"><i class="fa fa-wrench fa-fw"></i> Force Post-Process Albums in Download Folder</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<form action="forcePostProcess" method="GET">
|
<fieldset>
|
||||||
<fieldset>
|
<div class="row" id="post_process_alternate">
|
||||||
<div class="row">
|
<label>Force Post-Process Albums in Alternate Folder</label>
|
||||||
<label>Force Post-Process Albums in Alternate Folder</label>
|
<input type="text" value="" name="dir" id="dir" size="50" />
|
||||||
<input type="text" value="" name="dir" id="dir" size="50" /><input type="submit" />
|
<input type="button" class="btnOpenDialog" value="Submit" />
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
</form>
|
<fieldset>
|
||||||
<form action="forcePostProcess" method="GET">
|
<div class="row" id="post_process_single">
|
||||||
<fieldset>
|
<label>Post-Process Single Folder</label>
|
||||||
<div class="row">
|
<input type="text" value="" name="album_dir" id="album_dir" size="50" />
|
||||||
<label>Post-Process Single Folder</label>
|
<input type="button" class="btnOpenDialog" value="Submit" />
|
||||||
<input type="text" value="" name="album_dir" id="album_dir" size="50" /><input type="submit" />
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -166,9 +166,9 @@
|
|||||||
<legend>Force Legacy</legend>
|
<legend>Force Legacy</legend>
|
||||||
<p>Please note that these functions will take a significant amount of time to complete.</p>
|
<p>Please note that these functions will take a significant amount of time to complete.</p>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a href="#" onclick="doAjaxCall('forceFullUpdate',$(this))" data-success="Update active artists successful" data-error="Error forcing update artists"><i class="fa fa-heart fa-fw"></i> Force Update Active Artists [Comprehensive]</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('forceFullUpdate',$(this))" data-success="Update active artists successful" data-error="Error forcing update artists"><i class="fa fa-heart fa-fw"></i> Force Update Active Artists [Comprehensive]</a>
|
||||||
<BR>
|
<BR>
|
||||||
<a href="#" onclick="doAjaxCall('forceScan',$(this))" data-success="Library scan successful" data-error="Error forcing library scan"><i class="fa fa-refresh fa-fw"></i> Force Re-scan Library [Comprehensive]</a>
|
<a href="javascript:void(0)" onclick="doAjaxCall('forceScan',$(this))" data-success="Library scan successful" data-error="Error forcing library scan"><i class="fa fa-refresh fa-fw"></i> Force Re-scan Library [Comprehensive]</a>
|
||||||
<BR>
|
<BR>
|
||||||
<small>*Warning: If you choose [Force Re-scan Library], any manually ignored/matched artists/albums will be reset to "unmatched".</small>
|
<small>*Warning: If you choose [Force Re-scan Library], any manually ignored/matched artists/albums will be reset to "unmatched".</small>
|
||||||
|
|
||||||
@@ -176,8 +176,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dialog-confirm"></div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
@@ -186,6 +185,55 @@
|
|||||||
function addScanAction() {
|
function addScanAction() {
|
||||||
$('#autoadd').append('<input type="hidden" name="scan" value=1 />');
|
$('#autoadd').append('<input type="hidden" name="scan" value=1 />');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function fnOpenNormalDialog(id) {
|
||||||
|
if (id === "post_process"){
|
||||||
|
var url = "forcePostProcess?"
|
||||||
|
}
|
||||||
|
if (id === "post_process_alternate"){
|
||||||
|
var dir = $('#dir').val();
|
||||||
|
var url = "forcePostProcess?dir=" + dir + "&"
|
||||||
|
}
|
||||||
|
if (id === "post_process_single"){
|
||||||
|
var dir = $('#album_dir').val();
|
||||||
|
var url = "forcePostProcess?album_dir=" + dir + "&"
|
||||||
|
}
|
||||||
|
var t = $('<a data-success="Post-Processor is being loaded" data-error="Error during Post-Processing">');
|
||||||
|
$("#dialog-confirm").html("Do you want to keep the original folder(s) after post-processing? If you click no, the folders still might be kept depending on your global settings");
|
||||||
|
|
||||||
|
// Define the Dialog and its properties.
|
||||||
|
$("#dialog-confirm").dialog({
|
||||||
|
resizable: false,
|
||||||
|
modal: true,
|
||||||
|
title: "Keep original folder(s)?",
|
||||||
|
height: 170,
|
||||||
|
width: 400,
|
||||||
|
buttons: {
|
||||||
|
"Yes": function () {
|
||||||
|
$(this).dialog('close');
|
||||||
|
doAjaxCall(url + "keep_original_folder=True", t);
|
||||||
|
},
|
||||||
|
"No": function () {
|
||||||
|
$(this).dialog('close');
|
||||||
|
doAjaxCall(url + "keep_original_folder=False", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.btnOpenDialog').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var parentId = $(this).closest('div').prop('id');
|
||||||
|
fnOpenNormalDialog(parentId);
|
||||||
|
});
|
||||||
|
|
||||||
|
function callback(value) {
|
||||||
|
if (value) {
|
||||||
|
alert("Confirmed");
|
||||||
|
} else {
|
||||||
|
alert("Rejected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
$('#manage_albums').click(function() {
|
$('#manage_albums').click(function() {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<option value="WantedNew">Wanted (new only)</option>
|
<option value="WantedNew">Wanted (new only)</option>
|
||||||
<option value="WantedLossless">Wanted (lossless)</option>
|
<option value="WantedLossless">Wanted (lossless)</option>
|
||||||
<option value="Skipped">Skipped</option>
|
<option value="Skipped">Skipped</option>
|
||||||
|
<option value="Ignored">Ignored</option>
|
||||||
<option value="Downloaded">Downloaded</option>
|
<option value="Downloaded">Downloaded</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" value="Go">
|
<input type="hidden" value="Go">
|
||||||
@@ -50,6 +51,8 @@
|
|||||||
grade = 'Z'
|
grade = 'Z'
|
||||||
elif album['Status'] == 'Wanted':
|
elif album['Status'] == 'Wanted':
|
||||||
grade = 'X'
|
grade = 'X'
|
||||||
|
elif album['Status'] == 'Ignored':
|
||||||
|
grade = 'I'
|
||||||
elif album['Status'] == 'Snatched':
|
elif album['Status'] == 'Snatched':
|
||||||
grade = 'C'
|
grade = 'C'
|
||||||
else:
|
else:
|
||||||
@@ -109,40 +112,43 @@
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
function initThisPage() {
|
||||||
|
$('#album_table').dataTable({
|
||||||
|
"bDestroy": true,
|
||||||
|
"aoColumns": [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ "sType": "title-numeric"},
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"aoColumnDefs": [
|
||||||
|
{ 'bSortable': false, 'aTargets': [ 0 ] }
|
||||||
|
],
|
||||||
|
"oLanguage": {
|
||||||
|
"sLengthMenu":"Show _MENU_ albums per page",
|
||||||
|
"sEmptyTable": "No album information available",
|
||||||
|
"sInfo":"Showing _TOTAL_ albums",
|
||||||
|
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||||
|
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||||
|
"sSearch": ""},
|
||||||
|
"bPaginate": false,
|
||||||
|
"aaSorting": [[5, 'desc']],
|
||||||
|
"fnDrawCallback": function (o) {
|
||||||
|
// Jump to top of page
|
||||||
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
|
|
||||||
function initThisPage() {
|
});
|
||||||
$('#album_table').dataTable({
|
resetFilters("albums");
|
||||||
"bDestroy": true,
|
}
|
||||||
"aoColumns": [
|
$(document).ready(function() {
|
||||||
null,
|
initThisPage();
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{ "sType": "title-numeric"},
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"aoColumnDefs": [
|
|
||||||
{ 'bSortable': false, 'aTargets': [ 0 ] }
|
|
||||||
],
|
|
||||||
"oLanguage": {
|
|
||||||
"sLengthMenu":"Show _MENU_ albums per page",
|
|
||||||
"sEmptyTable": "No album information available",
|
|
||||||
"sInfo":"Showing _TOTAL_ albums",
|
|
||||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
|
||||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
|
||||||
"sSearch": ""},
|
|
||||||
"bPaginate": false,
|
|
||||||
"aaSorting": [[5, 'desc']]
|
|
||||||
|
|
||||||
});
|
});
|
||||||
resetFilters("albums");
|
|
||||||
}
|
|
||||||
$(document).ready(function() {
|
|
||||||
initThisPage();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|||||||
@@ -86,31 +86,31 @@
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
$('#artist_table').dataTable({
|
$('#artist_table').dataTable({
|
||||||
"bDestroy":true,
|
"bDestroy": true,
|
||||||
"aoColumns": [
|
"aoColumns": [
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{ "sType": "title-string"},
|
{ "sType": "title-string"},
|
||||||
null,
|
null,
|
||||||
{ "sType": "title-string"},
|
{ "sType": "title-string"},
|
||||||
null
|
null
|
||||||
],
|
],
|
||||||
"oLanguage": {
|
"oLanguage": {
|
||||||
"sSearch" : "",
|
"sSearch" : "",
|
||||||
"sEmptyTable": " "
|
"sEmptyTable": " "
|
||||||
},
|
},
|
||||||
"bStateSave": true,
|
"bStateSave": true,
|
||||||
"bPaginate": false
|
"bPaginate": false
|
||||||
|
});
|
||||||
|
resetFilters("artists");
|
||||||
|
}
|
||||||
|
$(document).ready(function() {
|
||||||
|
initThisPage();
|
||||||
|
});
|
||||||
|
$(window).load(function(){
|
||||||
|
initFancybox();
|
||||||
});
|
});
|
||||||
resetFilters("artists");
|
|
||||||
}
|
|
||||||
$(document).ready(function() {
|
|
||||||
initThisPage();
|
|
||||||
});
|
|
||||||
$(window).load(function(){
|
|
||||||
initFancybox();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
<div class="table_wrapper">
|
<div class="table_wrapper">
|
||||||
<div id="manageheader" class="title">
|
<div id="manageheader" class="title">
|
||||||
<h1 class="clearfix"><i class="fa fa-music"></i> Manage Manually Changed Albums</h1>
|
<h1 class="clearfix"><i class="fa fa-music"></i> Manage Manually Matched Albums</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="display" id="artist_table">
|
<table class="display" id="artist_table">
|
||||||
@@ -37,30 +37,30 @@
|
|||||||
old_album_clean = album['AlbumTitle'].replace('&','%26').replace('+', '%2B').replace("'","%27")
|
old_album_clean = album['AlbumTitle'].replace('&','%26').replace('+', '%2B').replace("'","%27")
|
||||||
%>
|
%>
|
||||||
<td id="artist">${album['ArtistName']}<BR>
|
<td id="artist">${album['ArtistName']}<BR>
|
||||||
<button id="restore_artist${count_albums}" onClick="restore_Artist(this.id)">(<-) Restore Artist</button>
|
<button id="reset_artist${count_albums}" onClick="reset_Artist(this.id)">(<-) Reset Artist</button>
|
||||||
<div id="restore_artist_dialog${count_albums}" title="Restore Artist" style="display:none">
|
<div id="reset_artist_dialog${count_albums}" title="Reset Artist" style="display:none">
|
||||||
<table>
|
<table>
|
||||||
<tr><td>Are you sure you want to restore Local Artist: ${album['ArtistName']} to unmatched?</td></tr>
|
<tr><td>Are you sure you want to reset Local Artist: ${album['ArtistName']} to unmatched?</td></tr>
|
||||||
<tr><td align="right"><BR>
|
<tr><td align="right"><BR>
|
||||||
%if album['AlbumStatus'] == "Ignored":
|
%if album['AlbumStatus'] == "Ignored":
|
||||||
<button href="#" onclick="doAjaxCall('markManual?action=unignoreArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully restored ${album['ArtistName']} to unmatched">Restore Artist</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markManual?action=unignoreArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully reset ${album['ArtistName']} to unmatched">Reset Artist</button>
|
||||||
%elif album['AlbumStatus'] == "Matched":
|
%elif album['AlbumStatus'] == "Matched":
|
||||||
<button href="#" onclick="doAjaxCall('markManual?action=unmatchArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully restored ${album['ArtistName']} to unmatched">Restore Artist</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markManual?action=unmatchArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully restored ${album['ArtistName']} to unmatched">Reset Artist</button>
|
||||||
%endif
|
%endif
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td id="album">${album['AlbumTitle']}<BR>
|
<td id="album">${album['AlbumTitle']}<BR>
|
||||||
<button id="restore_album${count_albums}" onClick="restore_Album(this.id)">(<-) Restore Album</button>
|
<button id="reset_album${count_albums}" onClick="reset_Album(this.id)">(<-) Reset Album</button>
|
||||||
<div id="restore_album_dialog${count_albums}" title="Restore Album" style="display:none">
|
<div id="reset_album_dialog${count_albums}" title="Reset Album" style="display:none">
|
||||||
<table>
|
<table>
|
||||||
<tr><td>Are you sure you want to restore Local Album: ${album['AlbumTitle']} to unmatched?</td></tr>
|
<tr><td>Are you sure you want to reset Local Album: ${album['AlbumTitle']} to unmatched?</td></tr>
|
||||||
<tr><td align="right"><BR>
|
<tr><td align="right"><BR>
|
||||||
%if album['AlbumStatus'] == "Ignored":
|
%if album['AlbumStatus'] == "Ignored":
|
||||||
<button href="#" onclick="doAjaxCall('markManual?action=unignoreAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully restored ${album['AlbumTitle']} to unmatched">Restore Album</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markManual?action=unignoreAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully reset ${album['AlbumTitle']} to unmatched">Reset Album</button>
|
||||||
%elif album['AlbumStatus'] == "Matched":
|
%elif album['AlbumStatus'] == "Matched":
|
||||||
<button href="#" onclick="doAjaxCall('markManual?action=unmatchAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully restored ${album['AlbumTitle']} to unmatched">Restore Album</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markManual?action=unmatchAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully reset ${album['AlbumTitle']} to unmatched">Reset Album</button>
|
||||||
%endif
|
%endif
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -85,35 +85,37 @@
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function()
|
$(document).ready(function() {
|
||||||
{
|
$('#artist_table').dataTable({
|
||||||
$('#artist_table').dataTable(
|
"bStateSave": true,
|
||||||
{
|
"bPaginate": true,
|
||||||
"bStateSave": true,
|
"oLanguage": {
|
||||||
"bPaginate": true,
|
"sSearch": "",
|
||||||
"oLanguage": {
|
"sLengthMenu":"Show _MENU_ albums per page",
|
||||||
"sSearch": "",
|
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||||
"sLengthMenu":"Show _MENU_ albums per page",
|
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
"sEmptyTable": " ",
|
||||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
},
|
||||||
"sEmptyTable": " ",
|
"sPaginationType": "full_numbers",
|
||||||
},
|
"fnDrawCallback": function (o) {
|
||||||
"sPaginationType": "full_numbers",
|
// Jump to top of page
|
||||||
});
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
initActions();
|
initActions();
|
||||||
});
|
});
|
||||||
|
|
||||||
function restore_Artist(clicked_id) {
|
function reset_Artist(clicked_id) {
|
||||||
n=clicked_id.replace("restore_artist","");
|
n=clicked_id.replace("reset_artist","");
|
||||||
$("#restore_artist_dialog"+n).dialog();
|
$("#reset_artist_dialog"+n).dialog();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function restore_Album(clicked_id) {
|
function reset_Album(clicked_id) {
|
||||||
n=clicked_id.replace("restore_album","");
|
n=clicked_id.replace("reset_album","");
|
||||||
$("#restore_album_dialog"+n).dialog();
|
$("#reset_album_dialog"+n).dialog();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<%def name="headerIncludes()">
|
<%def name="headerIncludes()">
|
||||||
<div id="subhead_container">
|
<div id="subhead_container">
|
||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
<a id="menu_link_scan" onclick="doAjaxCall('musicScan?path=${headphones.MUSIC_DIR}&redirect=manageNew',$(this))" data-success="Music library is getting scanned">Scan Music Library</a>
|
<a id="menu_link_scan" onclick="doAjaxCall('musicScan?path=${headphones.CONFIG.MUSIC_DIR}&redirect=manageNew',$(this))" data-success="Music library is getting scanned">Scan Music Library</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="manage" class="back">« Back to manage overview</a>
|
<a href="manage" class="back">« Back to manage overview</a>
|
||||||
@@ -53,18 +53,16 @@
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function()
|
$(document).ready(function() {
|
||||||
{
|
$('#artist_table').dataTable({
|
||||||
$('#artist_table').dataTable(
|
"aaSorting": [[1, 'asc']],
|
||||||
{
|
"bStateSave": false,
|
||||||
"aaSorting": [[1, 'asc']],
|
"bPaginate": false,
|
||||||
"bStateSave": false,
|
"oLanguage": {
|
||||||
"bPaginate": false,
|
"sSearch" : ""},
|
||||||
"oLanguage": {
|
});
|
||||||
"sSearch" : ""},
|
|
||||||
|
initActions();
|
||||||
});
|
|
||||||
initActions();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr><td>Are you sure you want to ignore Local Artist: ${album['ArtistName']} from future matching?</td></tr>
|
<tr><td>Are you sure you want to ignore Local Artist: ${album['ArtistName']} from future matching?</td></tr>
|
||||||
<tr><td align="right"><BR>
|
<tr><td align="right"><BR>
|
||||||
<button href="#" onclick="doAjaxCall('markUnmatched?action=ignoreArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully ignored ${album['ArtistName']} from future matching">Ignore Artist</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markUnmatched?action=ignoreArtist&existing_artist=${old_artist_clean}', $(this), 'page');" data-success="Successfully ignored ${album['ArtistName']} from future matching">Ignore Artist</button>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td></td><td align="right"><BR>
|
<tr><td></td><td align="right"><BR>
|
||||||
<button href="#" onclick="artist_matcher(${count_albums}, '${old_artist_js}')">Match Artist</button>
|
<button href="javascript:void(0)" onclick="artist_matcher(${count_albums}, '${old_artist_js}')">Match Artist</button>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr><td>Are you sure you want to ignore Local Album: ${album['AlbumTitle']} from future matching?</td></tr>
|
<tr><td>Are you sure you want to ignore Local Album: ${album['AlbumTitle']} from future matching?</td></tr>
|
||||||
<tr><td align="right"><BR>
|
<tr><td align="right"><BR>
|
||||||
<button href="#" onclick="doAjaxCall('markUnmatched?action=ignoreAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully ignored ${album['AlbumTitle']} from future matching">Ignore Album</button>
|
<button href="javascript:void(0)" onclick="doAjaxCall('markUnmatched?action=ignoreAlbum&existing_artist=${old_artist_clean}&existing_album=${old_album_clean}', $(this), 'page');" data-success="Successfully ignored ${album['AlbumTitle']} from future matching">Ignore Album</button>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td></td><td align="right"><BR>
|
<tr><td></td><td align="right"><BR>
|
||||||
<button href="#" onclick="album_matcher(${count_albums}, '${old_artist_js}', '${old_album_js}')">Match Album</button>
|
<button href="javascript:void(0)" onclick="album_matcher(${count_albums}, '${old_artist_js}', '${old_album_js}')">Match Album</button>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -118,22 +118,24 @@
|
|||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function()
|
$(document).ready(function() {
|
||||||
{
|
$('#artist_table').dataTable({
|
||||||
$('#artist_table').dataTable(
|
"bStateSave": true,
|
||||||
{
|
"bPaginate": true,
|
||||||
"bStateSave": true,
|
"oLanguage": {
|
||||||
"bPaginate": true,
|
"sSearch": "",
|
||||||
"oLanguage": {
|
"sLengthMenu":"Show _MENU_ albums per page",
|
||||||
"sSearch": "",
|
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||||
"sLengthMenu":"Show _MENU_ albums per page",
|
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
"sEmptyTable": " ",
|
||||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
},
|
||||||
"sEmptyTable": " ",
|
"sPaginationType": "full_numbers",
|
||||||
},
|
"fnDrawCallback": function (o) {
|
||||||
"sPaginationType": "full_numbers",
|
// Jump to top of page
|
||||||
});
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
initActions();
|
initActions();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,36 +11,62 @@
|
|||||||
<th id="albumart"></th>
|
<th id="albumart"></th>
|
||||||
%if type == 'album':
|
%if type == 'album':
|
||||||
<th id="albumname">Album Name</th>
|
<th id="albumname">Album Name</th>
|
||||||
|
<th id="artistnamesmall">Artist Name</th>
|
||||||
|
<th id="format">Format</th>
|
||||||
|
<th id="tracks">Tracks</th>
|
||||||
|
<th id="reldate">Date</th>
|
||||||
|
<th id="scoresmall">Score</th>
|
||||||
|
<th id="mbrelid" style="display:none;"</th>
|
||||||
|
%elif type == 'artist':
|
||||||
|
<th id="artistname">Artist Name</th>
|
||||||
|
<th id="score">Score</th>
|
||||||
|
%else:
|
||||||
|
<th id="seriesname">Series Name</th>
|
||||||
|
<th id="type">Type</th>
|
||||||
|
<th id="score">Score</th>
|
||||||
%endif
|
%endif
|
||||||
<th id="artistname">Artist Name</th>
|
<th id="mb"></th>
|
||||||
<th id="score">Score</th>
|
|
||||||
<th id="add"></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
%if searchresults:
|
%if searchresults:
|
||||||
%for result in searchresults:
|
%for result in searchresults:
|
||||||
<%
|
<%
|
||||||
if result['score'] == 100:
|
if result['score'] == 100:
|
||||||
grade = 'A'
|
grade = 'A'
|
||||||
else:
|
else:
|
||||||
grade = 'Z'
|
grade = 'Z'
|
||||||
%>
|
|
||||||
|
if type == 'album':
|
||||||
|
albuminfo = 'Type: ' + result['rgtype'] + ', Country: ' + result['country']
|
||||||
|
caa_group_url = "http://coverartarchive.org/release-group/%s/front-250.jpg" %result['rgid']
|
||||||
|
%>
|
||||||
<tr class="grade${grade}">
|
<tr class="grade${grade}">
|
||||||
%if type == 'album':
|
%if type == 'album':
|
||||||
<td id="albumart"><div id="artistImg"><img title="${result['albumid']}" class="albumArt" height="50" width="50"></div></td>
|
<td id="albumart" style=" text-align: center; vertical-align: middle;"><div id="artistImg"><img title="${result['albumid']}" class="albumArt" height="50" width="50" onerror="tryCCA(this, '${caa_group_url}')"></div></td>
|
||||||
%else:
|
%elif type == 'artist':
|
||||||
<td id="albumart"><div id="artistImg"><img title="${result['id']}" class="albumArt" height="50" width="50"></div></td>
|
<td id="albumart"><div id="artistImg"><img title="${result['id']}" class="albumArt" height="50" width="50"></div></td>
|
||||||
|
%else:
|
||||||
|
<td id="albumart"></td>
|
||||||
%endif
|
%endif
|
||||||
%if type == 'album':
|
%if type == 'album':
|
||||||
<td id="albumname"><a href="${result['albumurl']}">${result['title']}</a></td>
|
<td id="albumname"><a href="addReleaseById?rid=${result['albumid']}&rgid=${result['rgid']}" title="${albuminfo}">${result['title']}</a></td>
|
||||||
%endif
|
<td id="artistnamesmall"><a href="addArtist?artistid=${result['id']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
||||||
<td id="artistname"><a href="${result['url']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
<td id="format">${result['formats']}</td>
|
||||||
<td id="score"><div class="bar"><div class="score" style="width: ${result['score']}px">${result['score']}</div></div></td>
|
<td id="tracks">${result['tracks']}</td>
|
||||||
%if type == 'album':
|
<td id="reldate">${result['date']}</td>
|
||||||
<td id="add"><a href="addReleaseById?rid=${result['albumid']}"><i class="fa fa-plus"></i> Add this album</a></td>
|
<td id="score"><a href="${result['albumurl']} "title="View on MusicBrainz"><div class="bar"><div class="score" style="width: ${result['score']}px">${result['score']}</div></div></a></td>
|
||||||
%else:
|
<td id="musicbrainz" style=" text-align: center; line-height: 0; vertical-align: middle;"><a href="${result['albumurl']}"><img src="interfaces/default/images/MusicBrainz_Album_Icon.png" title="View on MusicBrainz" height="20" width="20"></a></td>
|
||||||
<td id="add"><a href="addArtist?artistid=${result['id']}"><i class="fa fa-plus"></i> Add this artist</a></td>
|
<td id="mbrelid" style="display:none;">${result['albumid']}</td>
|
||||||
|
%elif type == 'artist':
|
||||||
|
<td id="artistname"><a href="addArtist?artistid=${result['id']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
||||||
|
<td id="score"><a href="${result['url']} "title="View on MusicBrainz"><div class="bar"><div class="score" style="width: ${result['score']}px">${result['score']}</div></div></a></td>
|
||||||
|
<td id="musicbrainz" style=" text-align: center; line-height: 0; vertical-align: middle;"><a href="${result['url']}"><img src="interfaces/default/images/MusicBrainz_Artist_Icon.png" title="View on MusicBrainz" height="20" width="20"></a></td>
|
||||||
|
%else:
|
||||||
|
<td id="seriesname"><a href="addSeries?seriesid=${result['id']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
||||||
|
<td id="type">${result['type']}</td>
|
||||||
|
<td id="score"><a href="${result['url']} "title="View on MusicBrainz"><div class="bar"><div class="score" style="width: ${result['score']}px">${result['score']}</div></div></a></td>
|
||||||
|
<td id="musicbrainz" style=" text-align: center; line-height: 0; vertical-align: middle;"><a href="${result['url']}"><img src="interfaces/default/images/MusicBrainz_Artist_Icon.png" title="View on MusicBrainz" height="20" width="20"></a></td>
|
||||||
%endif
|
%endif
|
||||||
</tr>
|
</tr>
|
||||||
%endfor
|
%endfor
|
||||||
@@ -55,28 +81,34 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
|
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script type="text/javascript">
|
||||||
function getArt() {
|
function tryCCA(element, url) {
|
||||||
$("table#searchresults_table tr td#albumart img").each(function(){
|
element.onerror = function() {
|
||||||
var id = $(this).attr('title');
|
element.onerror = null;
|
||||||
var image = $(this);
|
element.src = "interfaces/default/images/no-cover-art.png";
|
||||||
if ( !image.hasClass('done') ) {
|
};
|
||||||
image.addClass('done');
|
element.src = url;
|
||||||
getImageLinks(image,id,"${type}");
|
}
|
||||||
}
|
function getArt() {
|
||||||
});
|
$("table#searchresults_table tr td#albumart img").each(function(){
|
||||||
}
|
var id = $(this).attr('title');
|
||||||
function initThisPage() {
|
var image = $(this);
|
||||||
getArt();
|
|
||||||
$('#searchresults_table').dataTable(
|
if (!image.hasClass('done')) {
|
||||||
{
|
image.addClass('done');
|
||||||
|
getImageLinks(image, id, "${type}", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function initThisPage() {
|
||||||
|
$('#searchresults_table').dataTable({
|
||||||
"bDestroy": true,
|
"bDestroy": true,
|
||||||
"aoColumnDefs": [
|
"aoColumnDefs": [
|
||||||
{ 'bSortable': false, 'aTargets': [ 0,3 ] }
|
{ 'bSortable': false, 'aTargets': [ 0 ] }
|
||||||
],
|
],
|
||||||
"oLanguage": {
|
"oLanguage": {
|
||||||
"sLengthMenu":"Show _MENU_ results per page",
|
"sLengthMenu":"Show _MENU_ results per page",
|
||||||
"sEmptyTable": "No results",
|
"sEmptyTable": "No results",
|
||||||
@@ -86,15 +118,36 @@
|
|||||||
"sSearch" : ""},
|
"sSearch" : ""},
|
||||||
"iDisplayLength": 25,
|
"iDisplayLength": 25,
|
||||||
"sPaginationType": "full_numbers",
|
"sPaginationType": "full_numbers",
|
||||||
"aaSorting": []
|
"aaSorting": [],
|
||||||
|
"fnDrawCallback": function (o) {
|
||||||
|
// Jump to top of page
|
||||||
|
$('html,body').scrollTop(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
$('#searchresults_table').on("draw.dt", function () {
|
||||||
|
getArt();
|
||||||
|
$("img").unveil();
|
||||||
|
});
|
||||||
|
getArt();
|
||||||
resetFilters("album");
|
resetFilters("album");
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initThisPage();
|
initFancybox();
|
||||||
});
|
initThisPage();
|
||||||
$(window).load(function(){
|
});
|
||||||
initFancybox();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
<%!
|
||||||
|
# Abuse JSON module for escaping JavaScript
|
||||||
|
import json
|
||||||
|
%>
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Search parameter
|
||||||
|
$("#searchbar input[name=name]").val(${name | json.dumps});
|
||||||
|
|
||||||
|
// Album or artist
|
||||||
|
$("#searchbar select[name=type]").val(${type | json.dumps});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|||||||
@@ -3,23 +3,23 @@
|
|||||||
<%def name="headerIncludes()">
|
<%def name="headerIncludes()">
|
||||||
<div id="subhead_container">
|
<div id="subhead_container">
|
||||||
<div id="subhead_menu">
|
<div id="subhead_menu">
|
||||||
<a href="#" id="menu_link_scan" onclick="doAjaxCall('forceSearch',$(this))" data-success="Checking for wanted albums successful" data-error="Error checking wanted albums"><i class="fa fa-search"></i> Force Check</a>
|
<a href="javascript:void(0)" id="menu_link_scan" onclick="doAjaxCall('forceSearch',$(this))" data-success="Checking for wanted albums successful" data-error="Error checking wanted albums"><i class="fa fa-search"></i> Force Check</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
|
<div id="paddingheader">
|
||||||
<div class="title">
|
|
||||||
<h1 class="clearfix"><i class="fa fa-heart"></i> Wanted Albums</h1>
|
<h1 class="clearfix"><i class="fa fa-heart"></i> Wanted Albums</h1>
|
||||||
</div>
|
</div>
|
||||||
<form action="markAlbums" method="get" id="markAlbums">
|
<form action="markAlbums" method="get" id="markAlbums">
|
||||||
<div id="markalbum" style="top:0;">
|
<div id="markalbum" style="top:0;">
|
||||||
Mark selected albums as
|
Mark selected albums as
|
||||||
<select name="action" onChange="doAjaxCall('markAlbums',$(this),'table',true);" data-error="You didn't select any albums">
|
<select name="action" onChange="doAjaxCall('markAlbums',$(this),'table',true);" data-error="You didn't select any albums">
|
||||||
<option disabled="disabled" selected="selected">Choose...</option>
|
<option disabled="disabled" selected="selected">Choose...</option>
|
||||||
<option value="Skipped">Skipped</option>
|
<option value="Skipped">Skipped</option>
|
||||||
|
<option value="Ignored">Ignored</option>
|
||||||
<option value="Downloaded">Downloaded</option>
|
<option value="Downloaded">Downloaded</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" value="Go">
|
<input type="hidden" value="Go">
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
%for album in wanted:
|
%for album in wanted:
|
||||||
<tr class="gradeZ">
|
<tr class="gradeZ">
|
||||||
<td id="select"><input type="checkbox" name="${album['AlbumID']}" class="checkbox" /></th>
|
<td id="select"><input type="checkbox" name="${album['AlbumID']}" class="checkbox" /></th>
|
||||||
<td id="albumart"><img title="${album['AlbumID']}" height="64" width="64" src="artwork/thumbs/album/${album['AlbumID']}"></td>
|
<td id="albumart"><img title="${album['AlbumID']}" height="64" width="64" src="interfaces/default/images/no-cover-art.png" data-src="artwork/thumbs/album/${album['AlbumID']}"></td>
|
||||||
<td id="artistname"><a href="artistPage?ArtistID=${album['ArtistID']}">${album['ArtistName']}</a></td>
|
<td id="artistname"><a href="artistPage?ArtistID=${album['ArtistID']}">${album['ArtistName']}</a></td>
|
||||||
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
||||||
<td id="reldate">${album['ReleaseDate']}</td>
|
<td id="reldate">${album['ReleaseDate']}</td>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
%endfor
|
%endfor
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
%for album in upcoming:
|
%for album in upcoming:
|
||||||
<tr class="gradeZ">
|
<tr class="gradeZ">
|
||||||
<td id="albumart"><img title="${album['AlbumID']}" height="64" width="64" src="artwork/thumbs/album/${album['AlbumID']}"></td>
|
<td id="albumart"><img title="${album['AlbumID']}" height="64" width="64" data-src="artwork/thumbs/album/${album['AlbumID']}"></td>
|
||||||
<td id="artistname"><a href="artistPage?ArtistID=${album['ArtistID']}">${album['ArtistName']}</a></td>
|
<td id="artistname"><a href="artistPage?ArtistID=${album['ArtistID']}">${album['ArtistName']}</a></td>
|
||||||
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
||||||
<td id="reldate">${album['ReleaseDate']}</td>
|
<td id="reldate">${album['ReleaseDate']}</td>
|
||||||
@@ -88,24 +88,25 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
|
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function initThisPage() {
|
function initThisPage() {
|
||||||
|
$("img").unveil();
|
||||||
$('#wanted_table').dataTable({
|
$('#wanted_table').dataTable({
|
||||||
"oLanguage": {
|
"oLanguage": {
|
||||||
"sEmptyTable": " "
|
"sEmptyTable": " "
|
||||||
},
|
},
|
||||||
"bDestroy":true,
|
"bDestroy": true,
|
||||||
"bFilter": false,
|
"bFilter": false,
|
||||||
"bInfo": false,
|
"bInfo": false,
|
||||||
"bPaginate": false
|
"bPaginate": false
|
||||||
|
});
|
||||||
|
resetFilters("artists");
|
||||||
|
initActions();
|
||||||
|
}
|
||||||
|
$(document).ready(function() {
|
||||||
|
initThisPage();
|
||||||
});
|
});
|
||||||
resetFilters("artists");
|
|
||||||
initActions();
|
|
||||||
}
|
|
||||||
$(document).ready(function() {
|
|
||||||
initThisPage();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
window.log = function(){
|
||||||
|
log.history = log.history || [];
|
||||||
|
log.history.push(arguments);
|
||||||
|
arguments.callee = arguments.callee.caller;
|
||||||
|
|
||||||
|
if (this.console) {
|
||||||
|
console.log(Array.prototype.slice.call(arguments));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggle(source) {
|
||||||
|
checkboxes = document.getElementsByClassName('checkbox');
|
||||||
|
|
||||||
|
for (var i in checkboxes) {
|
||||||
|
checkboxes[i].checked = source.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
!function(t){t.fn.unveil=function(i,e){function n(){var i=a.filter(function(){var i=t(this);if(!i.is(":hidden")){var e=o.scrollTop(),n=e+o.height(),r=i.offset().top,s=r+i.height();return s>=e-u&&n+u>=r}});r=i.trigger("unveil"),a=a.not(r)}var r,o=t(window),u=i||0,s=window.devicePixelRatio>1,l=s?"data-src-retina":"data-src",a=this;return this.one("unveil",function(){var t=this.getAttribute(l);t=t||this.getAttribute("data-src"),t&&(this.setAttribute("src",t),"function"==typeof e&&e.call(this))}),o.on("scroll.unveil resize.unveil lookup.unveil",n),n(),this}}(window.jQuery||window.Zepto);
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
window.log = function(){
|
|
||||||
log.history = log.history || [];
|
|
||||||
log.history.push(arguments);
|
|
||||||
arguments.callee = arguments.callee.caller;
|
|
||||||
if(this.console) console.log( Array.prototype.slice.call(arguments) );
|
|
||||||
};
|
|
||||||
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c})(window.console=window.console||{});
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-string-asc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
var y = b.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-string-desc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
var y = b.match(/title="(.*?)"/)[1].toLowerCase();
|
|
||||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-numeric-asc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
var y = b.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
x = parseFloat( x );
|
|
||||||
y = parseFloat( y );
|
|
||||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
jQuery.fn.dataTableExt.oSort['title-numeric-desc'] = function(a,b) {
|
|
||||||
var x = a.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
var y = b.match(/title="*(-?[0-9]+)/)[1];
|
|
||||||
x = parseFloat( x );
|
|
||||||
y = parseFloat( y );
|
|
||||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
function toggle(source) {
|
|
||||||
checkboxes = document.getElementsByClassName('checkbox');
|
|
||||||
for(var i in checkboxes)
|
|
||||||
checkboxes[i].checked = source.checked;
|
|
||||||
}
|
|
||||||
@@ -13,15 +13,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from headphones import request, db
|
from headphones import request, db, logger
|
||||||
|
|
||||||
|
|
||||||
def getAlbumArt(albumid):
|
def getAlbumArt(albumid):
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
asin = myDB.action('SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0]
|
asin = myDB.action(
|
||||||
|
'SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0]
|
||||||
|
|
||||||
if asin:
|
if asin:
|
||||||
return 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin
|
return 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin
|
||||||
|
|
||||||
|
|
||||||
def getCachedArt(albumid):
|
def getCachedArt(albumid):
|
||||||
from headphones import cache
|
from headphones import cache
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#import unittest
|
||||||
|
#import mock
|
||||||
|
from headphones.unittestcompat import TestCase
|
||||||
|
|
||||||
|
import headphones.albumart
|
||||||
|
|
||||||
|
|
||||||
|
# no tests...
|
||||||
|
class AlbumArtTest(TestCase):
|
||||||
|
def test_nothing(self):
|
||||||
|
x = 100 - 2 * 50
|
||||||
|
if x:
|
||||||
|
headphones.albumart.getAlbumArt('asdf')
|
||||||
|
self.assertTrue(True)
|
||||||
@@ -14,69 +14,86 @@
|
|||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import headphones
|
import headphones
|
||||||
from headphones import db, logger
|
from headphones import db, logger, cache
|
||||||
|
|
||||||
|
|
||||||
def switch(AlbumID, ReleaseID):
|
def switch(AlbumID, ReleaseID):
|
||||||
'''
|
"""
|
||||||
Takes the contents from allalbums & alltracks (based on ReleaseID) and switches them into
|
Takes the contents from allalbums & alltracks (based on ReleaseID) and switches them into
|
||||||
the albums & tracks table.
|
the albums & tracks table.
|
||||||
'''
|
"""
|
||||||
|
logger.debug('Switching allalbums and alltracks')
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
oldalbumdata = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone()
|
oldalbumdata = myDB.action(
|
||||||
newalbumdata = myDB.action('SELECT * from allalbums WHERE ReleaseID=?', [ReleaseID]).fetchone()
|
'SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone()
|
||||||
newtrackdata = myDB.action('SELECT * from alltracks WHERE ReleaseID=?', [ReleaseID]).fetchall()
|
newalbumdata = myDB.action(
|
||||||
|
'SELECT * from allalbums WHERE ReleaseID=?', [ReleaseID]).fetchone()
|
||||||
|
newtrackdata = myDB.action(
|
||||||
|
'SELECT * from alltracks WHERE ReleaseID=?', [ReleaseID]).fetchall()
|
||||||
myDB.action('DELETE from tracks WHERE AlbumID=?', [AlbumID])
|
myDB.action('DELETE from tracks WHERE AlbumID=?', [AlbumID])
|
||||||
|
|
||||||
controlValueDict = {"AlbumID": AlbumID}
|
controlValueDict = {"AlbumID": AlbumID}
|
||||||
|
|
||||||
newValueDict = {"ArtistID": newalbumdata['ArtistID'],
|
newValueDict = {"ArtistID": newalbumdata['ArtistID'],
|
||||||
"ArtistName": newalbumdata['ArtistName'],
|
"ArtistName": newalbumdata['ArtistName'],
|
||||||
"AlbumTitle": newalbumdata['AlbumTitle'],
|
"AlbumTitle": newalbumdata['AlbumTitle'],
|
||||||
"ReleaseID": newalbumdata['ReleaseID'],
|
"ReleaseID": newalbumdata['ReleaseID'],
|
||||||
"AlbumASIN": newalbumdata['AlbumASIN'],
|
"AlbumASIN": newalbumdata['AlbumASIN'],
|
||||||
"ReleaseDate": newalbumdata['ReleaseDate'],
|
"ReleaseDate": newalbumdata['ReleaseDate'],
|
||||||
"Type": newalbumdata['Type'],
|
"Type": newalbumdata['Type'],
|
||||||
"ReleaseCountry": newalbumdata['ReleaseCountry'],
|
"ReleaseCountry": newalbumdata['ReleaseCountry'],
|
||||||
"ReleaseFormat": newalbumdata['ReleaseFormat']
|
"ReleaseFormat": newalbumdata['ReleaseFormat']
|
||||||
}
|
|
||||||
|
|
||||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
|
||||||
|
|
||||||
for track in newtrackdata:
|
|
||||||
|
|
||||||
controlValueDict = {"TrackID": track['TrackID'],
|
|
||||||
"AlbumID": AlbumID}
|
|
||||||
|
|
||||||
newValueDict = {"ArtistID": track['ArtistID'],
|
|
||||||
"ArtistName": track['ArtistName'],
|
|
||||||
"AlbumTitle": track['AlbumTitle'],
|
|
||||||
"AlbumASIN": track['AlbumASIN'],
|
|
||||||
"ReleaseID": track['ReleaseID'],
|
|
||||||
"TrackTitle": track['TrackTitle'],
|
|
||||||
"TrackDuration": track['TrackDuration'],
|
|
||||||
"TrackNumber": track['TrackNumber'],
|
|
||||||
"CleanName": track['CleanName'],
|
|
||||||
"Location": track['Location'],
|
|
||||||
"Format": track['Format'],
|
|
||||||
"BitRate": track['BitRate']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
myDB.upsert("tracks", newValueDict, controlValueDict)
|
|
||||||
|
|
||||||
# Mark albums as downloaded if they have at least 80% (by default, configurable) of the album
|
|
||||||
total_track_count = len(newtrackdata)
|
|
||||||
have_track_count = len(myDB.select('SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [AlbumID]))
|
|
||||||
|
|
||||||
if oldalbumdata['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)):
|
|
||||||
myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', AlbumID])
|
|
||||||
|
|
||||||
# Update have track counts on index
|
|
||||||
totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [newalbumdata['ArtistID']]))
|
|
||||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [newalbumdata['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [newalbumdata['ArtistID']]))
|
|
||||||
|
|
||||||
controlValueDict = {"ArtistID": newalbumdata['ArtistID']}
|
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||||
|
|
||||||
newValueDict = { "TotalTracks": totaltracks,
|
# Update cache
|
||||||
"HaveTracks": havetracks}
|
c = cache.Cache()
|
||||||
|
c.remove_from_cache(AlbumID=AlbumID)
|
||||||
|
c.get_artwork_from_cache(AlbumID=AlbumID)
|
||||||
|
|
||||||
|
for track in newtrackdata:
|
||||||
|
controlValueDict = {"TrackID": track['TrackID'],
|
||||||
|
"AlbumID": AlbumID}
|
||||||
|
|
||||||
|
newValueDict = {"ArtistID": track['ArtistID'],
|
||||||
|
"ArtistName": track['ArtistName'],
|
||||||
|
"AlbumTitle": track['AlbumTitle'],
|
||||||
|
"AlbumASIN": track['AlbumASIN'],
|
||||||
|
"ReleaseID": track['ReleaseID'],
|
||||||
|
"TrackTitle": track['TrackTitle'],
|
||||||
|
"TrackDuration": track['TrackDuration'],
|
||||||
|
"TrackNumber": track['TrackNumber'],
|
||||||
|
"CleanName": track['CleanName'],
|
||||||
|
"Location": track['Location'],
|
||||||
|
"Format": track['Format'],
|
||||||
|
"BitRate": track['BitRate']
|
||||||
|
}
|
||||||
|
|
||||||
|
myDB.upsert("tracks", newValueDict, controlValueDict)
|
||||||
|
|
||||||
|
# Mark albums as downloaded if they have at least 80% (by default,
|
||||||
|
# configurable) of the album
|
||||||
|
total_track_count = len(newtrackdata)
|
||||||
|
have_track_count = len(myDB.select(
|
||||||
|
'SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [AlbumID]))
|
||||||
|
|
||||||
|
if oldalbumdata['Status'] == 'Skipped' and ((have_track_count / float(total_track_count)) >= (
|
||||||
|
headphones.CONFIG.ALBUM_COMPLETION_PCT / 100.0)):
|
||||||
|
myDB.action(
|
||||||
|
'UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', AlbumID])
|
||||||
|
|
||||||
|
# Update have track counts on index
|
||||||
|
totaltracks = len(myDB.select(
|
||||||
|
'SELECT TrackTitle from tracks WHERE ArtistID=? AND AlbumID IN (SELECT AlbumID FROM albums WHERE Status != "Ignored")',
|
||||||
|
[newalbumdata['ArtistID']]))
|
||||||
|
havetracks = len(myDB.select(
|
||||||
|
'SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL',
|
||||||
|
[newalbumdata['ArtistID']]))
|
||||||
|
|
||||||
|
controlValueDict = {"ArtistID": newalbumdata['ArtistID']}
|
||||||
|
|
||||||
|
newValueDict = {"TotalTracks": totaltracks,
|
||||||
|
"HaveTracks": havetracks}
|
||||||
|
|
||||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||||
|
|||||||
@@ -13,156 +13,181 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from headphones import db, mb, updater, importer, searcher, cache, postprocessor, versioncheck, \
|
||||||
|
logger
|
||||||
import headphones
|
import headphones
|
||||||
|
|
||||||
from headphones import db, mb, importer, searcher, cache, postprocessor, versioncheck, logger
|
cmd_list = ['getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSnatched',
|
||||||
|
'getSimilar', 'getHistory', 'getLogs',
|
||||||
|
'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist',
|
||||||
|
'refreshArtist',
|
||||||
|
'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess',
|
||||||
|
'forceActiveArtistsUpdate',
|
||||||
|
'getVersion', 'checkGithub', 'shutdown', 'restart', 'update', 'getArtistArt',
|
||||||
|
'getAlbumArt',
|
||||||
|
'getArtistInfo', 'getAlbumInfo', 'getArtistThumb', 'getAlbumThumb', 'clearLogs',
|
||||||
|
'choose_specific_download', 'download_specific_release']
|
||||||
|
|
||||||
import lib.simplejson as simplejson
|
|
||||||
from xml.dom.minidom import Document
|
|
||||||
import copy
|
|
||||||
|
|
||||||
cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs',
|
|
||||||
'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist',
|
|
||||||
'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub',
|
|
||||||
'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo',
|
|
||||||
'getArtistThumb', 'getAlbumThumb', 'choose_specific_download', 'download_specific_release']
|
|
||||||
|
|
||||||
class Api(object):
|
class Api(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.apikey = None
|
self.apikey = None
|
||||||
self.cmd = None
|
self.cmd = None
|
||||||
self.id = None
|
self.id = None
|
||||||
|
|
||||||
self.kwargs = None
|
self.kwargs = None
|
||||||
|
|
||||||
self.data = None
|
self.data = None
|
||||||
|
|
||||||
self.callback = None
|
self.callback = None
|
||||||
|
|
||||||
|
def checkParams(self, *args, **kwargs):
|
||||||
def checkParams(self,*args,**kwargs):
|
|
||||||
|
if not headphones.CONFIG.API_ENABLED:
|
||||||
if not headphones.API_ENABLED:
|
|
||||||
self.data = 'API not enabled'
|
self.data = 'API not enabled'
|
||||||
return
|
return
|
||||||
if not headphones.API_KEY:
|
if not headphones.CONFIG.API_KEY:
|
||||||
self.data = 'API key not generated'
|
self.data = 'API key not generated'
|
||||||
return
|
return
|
||||||
if len(headphones.API_KEY) != 32:
|
if len(headphones.CONFIG.API_KEY) != 32:
|
||||||
self.data = 'API key not generated correctly'
|
self.data = 'API key not generated correctly'
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'apikey' not in kwargs:
|
if 'apikey' not in kwargs:
|
||||||
self.data = 'Missing api key'
|
self.data = 'Missing api key'
|
||||||
return
|
return
|
||||||
|
|
||||||
if kwargs['apikey'] != headphones.API_KEY:
|
if kwargs['apikey'] != headphones.CONFIG.API_KEY:
|
||||||
self.data = 'Incorrect API key'
|
self.data = 'Incorrect API key'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.apikey = kwargs.pop('apikey')
|
self.apikey = kwargs.pop('apikey')
|
||||||
|
|
||||||
if 'cmd' not in kwargs:
|
if 'cmd' not in kwargs:
|
||||||
self.data = 'Missing parameter: cmd'
|
self.data = 'Missing parameter: cmd'
|
||||||
return
|
return
|
||||||
|
|
||||||
if kwargs['cmd'] not in cmd_list:
|
if kwargs['cmd'] not in cmd_list:
|
||||||
self.data = 'Unknown command: %s' % kwargs['cmd']
|
self.data = 'Unknown command: %s' % kwargs['cmd']
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.cmd = kwargs.pop('cmd')
|
self.cmd = kwargs.pop('cmd')
|
||||||
|
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.data = 'OK'
|
self.data = 'OK'
|
||||||
|
|
||||||
def fetchData(self):
|
def fetchData(self):
|
||||||
|
|
||||||
if self.data == 'OK':
|
if self.data == 'OK':
|
||||||
logger.info('Recieved API command: %s', self.cmd)
|
logger.info('Recieved API command: %s', self.cmd)
|
||||||
methodToCall = getattr(self, "_" + self.cmd)
|
methodToCall = getattr(self, "_" + self.cmd)
|
||||||
result = methodToCall(**self.kwargs)
|
methodToCall(**self.kwargs)
|
||||||
if 'callback' not in self.kwargs:
|
if 'callback' not in self.kwargs:
|
||||||
if type(self.data) == type(''):
|
if isinstance(self.data, basestring):
|
||||||
return self.data
|
return self.data
|
||||||
else:
|
else:
|
||||||
return simplejson.dumps(self.data)
|
return json.dumps(self.data)
|
||||||
else:
|
else:
|
||||||
self.callback = self.kwargs['callback']
|
self.callback = self.kwargs['callback']
|
||||||
self.data = simplejson.dumps(self.data)
|
self.data = json.dumps(self.data)
|
||||||
self.data = self.callback + '(' + self.data + ');'
|
self.data = self.callback + '(' + self.data + ');'
|
||||||
return self.data
|
return self.data
|
||||||
else:
|
else:
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def _dic_from_query(self,query):
|
def _dic_from_query(self, query):
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
rows = myDB.select(query)
|
rows = myDB.select(query)
|
||||||
|
|
||||||
rows_as_dic = []
|
rows_as_dic = []
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
row_as_dic = dict(zip(row.keys(), row))
|
row_as_dic = dict(zip(row.keys(), row))
|
||||||
rows_as_dic.append(row_as_dic)
|
rows_as_dic.append(row_as_dic)
|
||||||
|
|
||||||
return rows_as_dic
|
return rows_as_dic
|
||||||
|
|
||||||
def _getIndex(self, **kwargs):
|
def _getIndex(self, **kwargs):
|
||||||
|
|
||||||
self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE')
|
self.data = self._dic_from_query(
|
||||||
return
|
'SELECT * from artists order by ArtistSortName COLLATE NOCASE')
|
||||||
|
return
|
||||||
|
|
||||||
def _getArtist(self, **kwargs):
|
def _getArtist(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"')
|
artist = self._dic_from_query(
|
||||||
albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC')
|
'SELECT * from artists WHERE ArtistID="' + self.id + '"')
|
||||||
description = self._dic_from_query('SELECT * from descriptions WHERE ArtistID="' + self.id + '"')
|
albums = self._dic_from_query(
|
||||||
|
'SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC')
|
||||||
self.data = { 'artist': artist, 'albums': albums, 'description' : description }
|
description = self._dic_from_query(
|
||||||
|
'SELECT * from descriptions WHERE ArtistID="' + self.id + '"')
|
||||||
|
|
||||||
|
self.data = {
|
||||||
|
'artist': artist, 'albums': albums, 'description': description}
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getAlbum(self, **kwargs):
|
def _getAlbum(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"')
|
album = self._dic_from_query(
|
||||||
tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"')
|
'SELECT * from albums WHERE AlbumID="' + self.id + '"')
|
||||||
description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"')
|
tracks = self._dic_from_query(
|
||||||
|
'SELECT * from tracks WHERE AlbumID="' + self.id + '"')
|
||||||
self.data = { 'album' : album, 'tracks' : tracks, 'description' : description }
|
description = self._dic_from_query(
|
||||||
|
'SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"')
|
||||||
|
|
||||||
|
self.data = {
|
||||||
|
'album': album, 'tracks': tracks, 'description': description}
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getHistory(self, **kwargs):
|
def _getHistory(self, **kwargs):
|
||||||
self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC')
|
self.data = self._dic_from_query(
|
||||||
|
'SELECT * from snatched WHERE status NOT LIKE "Seed%" order by DateAdded DESC')
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getUpcoming(self, **kwargs):
|
def _getUpcoming(self, **kwargs):
|
||||||
self.data = self._dic_from_query("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC")
|
self.data = self._dic_from_query(
|
||||||
|
"SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC")
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getWanted(self, **kwargs):
|
def _getWanted(self, **kwargs):
|
||||||
self.data = self._dic_from_query("SELECT * from albums WHERE Status='Wanted'")
|
self.data = self._dic_from_query(
|
||||||
|
"SELECT * from albums WHERE Status='Wanted'")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _getSnatched(self, **kwargs):
|
||||||
|
self.data = self._dic_from_query(
|
||||||
|
"SELECT * from albums WHERE Status='Snatched'")
|
||||||
|
return
|
||||||
|
|
||||||
def _getSimilar(self, **kwargs):
|
def _getSimilar(self, **kwargs):
|
||||||
self.data = self._dic_from_query('SELECT * from lastfmcloud')
|
self.data = self._dic_from_query('SELECT * from lastfmcloud')
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getLogs(self, **kwargs):
|
def _getLogs(self, **kwargs):
|
||||||
pass
|
self.data = headphones.LOG_LIST
|
||||||
|
return
|
||||||
|
|
||||||
|
def _clearLogs(self, **kwargs):
|
||||||
|
headphones.LOG_LIST = []
|
||||||
|
self.data = 'Cleared log'
|
||||||
|
return
|
||||||
|
|
||||||
def _findArtist(self, **kwargs):
|
def _findArtist(self, **kwargs):
|
||||||
if 'name' not in kwargs:
|
if 'name' not in kwargs:
|
||||||
self.data = 'Missing parameter: name'
|
self.data = 'Missing parameter: name'
|
||||||
@@ -170,8 +195,8 @@ class Api(object):
|
|||||||
if 'limit' in kwargs:
|
if 'limit' in kwargs:
|
||||||
limit = kwargs['limit']
|
limit = kwargs['limit']
|
||||||
else:
|
else:
|
||||||
limit=50
|
limit = 50
|
||||||
|
|
||||||
self.data = mb.findArtist(kwargs['name'], limit)
|
self.data = mb.findArtist(kwargs['name'], limit)
|
||||||
|
|
||||||
def _findAlbum(self, **kwargs):
|
def _findAlbum(self, **kwargs):
|
||||||
@@ -181,217 +206,225 @@ class Api(object):
|
|||||||
if 'limit' in kwargs:
|
if 'limit' in kwargs:
|
||||||
limit = kwargs['limit']
|
limit = kwargs['limit']
|
||||||
else:
|
else:
|
||||||
limit=50
|
limit = 50
|
||||||
|
|
||||||
self.data = mb.findRelease(kwargs['name'], limit)
|
self.data = mb.findRelease(kwargs['name'], limit)
|
||||||
|
|
||||||
def _addArtist(self, **kwargs):
|
def _addArtist(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
importer.addArtisttoDB(self.id)
|
importer.addArtisttoDB(self.id)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
self.data = e
|
self.data = e
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _delArtist(self, **kwargs):
|
def _delArtist(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"')
|
myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"')
|
||||||
myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"')
|
myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"')
|
||||||
myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"')
|
myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"')
|
||||||
|
|
||||||
def _pauseArtist(self, **kwargs):
|
def _pauseArtist(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
controlValueDict = {'ArtistID': self.id}
|
controlValueDict = {'ArtistID': self.id}
|
||||||
newValueDict = {'Status': 'Paused'}
|
newValueDict = {'Status': 'Paused'}
|
||||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||||
|
|
||||||
def _resumeArtist(self, **kwargs):
|
def _resumeArtist(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
controlValueDict = {'ArtistID': self.id}
|
controlValueDict = {'ArtistID': self.id}
|
||||||
newValueDict = {'Status': 'Active'}
|
newValueDict = {'Status': 'Active'}
|
||||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||||
|
|
||||||
def _refreshArtist(self, **kwargs):
|
def _refreshArtist(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
importer.addArtisttoDB(self.id)
|
importer.addArtisttoDB(self.id)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
self.data = e
|
self.data = e
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _addAlbum(self, **kwargs):
|
def _addAlbum(self, **kwargs):
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
importer.addReleaseById(self.id)
|
importer.addReleaseById(self.id)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
self.data = e
|
self.data = e
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _queueAlbum(self, **kwargs):
|
def _queueAlbum(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
if 'new' in kwargs:
|
if 'new' in kwargs:
|
||||||
new = kwargs['new']
|
new = kwargs['new']
|
||||||
else:
|
else:
|
||||||
new = False
|
new = False
|
||||||
|
|
||||||
if 'lossless' in kwargs:
|
if 'lossless' in kwargs:
|
||||||
lossless = kwargs['lossless']
|
lossless = kwargs['lossless']
|
||||||
else:
|
else:
|
||||||
lossless = False
|
lossless = False
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
controlValueDict = {'AlbumID': self.id}
|
controlValueDict = {'AlbumID': self.id}
|
||||||
if lossless:
|
if lossless:
|
||||||
newValueDict = {'Status': 'Wanted Lossless'}
|
newValueDict = {'Status': 'Wanted Lossless'}
|
||||||
else:
|
else:
|
||||||
newValueDict = {'Status': 'Wanted'}
|
newValueDict = {'Status': 'Wanted'}
|
||||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||||
searcher.searchforalbum(self.id, new)
|
searcher.searchforalbum(self.id, new)
|
||||||
|
|
||||||
def _unqueueAlbum(self, **kwargs):
|
def _unqueueAlbum(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
controlValueDict = {'AlbumID': self.id}
|
controlValueDict = {'AlbumID': self.id}
|
||||||
newValueDict = {'Status': 'Skipped'}
|
newValueDict = {'Status': 'Skipped'}
|
||||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||||
|
|
||||||
def _forceSearch(self, **kwargs):
|
def _forceSearch(self, **kwargs):
|
||||||
searcher.searchforalbum()
|
searcher.searchforalbum()
|
||||||
|
|
||||||
def _forceProcess(self, **kwargs):
|
def _forceProcess(self, **kwargs):
|
||||||
self.dir = None
|
if 'album_dir' in kwargs:
|
||||||
if 'dir' in kwargs:
|
album_dir = kwargs['album_dir']
|
||||||
|
dir = None
|
||||||
|
postprocessor.forcePostProcess(self, dir, album_dir)
|
||||||
|
elif 'dir' in kwargs:
|
||||||
self.dir = kwargs['dir']
|
self.dir = kwargs['dir']
|
||||||
postprocessor.forcePostProcess(self.dir)
|
postprocessor.forcePostProcess(self.dir)
|
||||||
|
else:
|
||||||
|
postprocessor.forcePostProcess()
|
||||||
|
|
||||||
|
def _forceActiveArtistsUpdate(self, **kwargs):
|
||||||
|
updater.dbUpdate()
|
||||||
|
|
||||||
def _getVersion(self, **kwargs):
|
def _getVersion(self, **kwargs):
|
||||||
self.data = {
|
self.data = {
|
||||||
'git_path' : headphones.GIT_PATH,
|
'git_path': headphones.CONFIG.GIT_PATH,
|
||||||
'install_type' : headphones.INSTALL_TYPE,
|
'install_type': headphones.INSTALL_TYPE,
|
||||||
'current_version' : headphones.CURRENT_VERSION,
|
'current_version': headphones.CURRENT_VERSION,
|
||||||
'latest_version' : headphones.LATEST_VERSION,
|
'latest_version': headphones.LATEST_VERSION,
|
||||||
'commits_behind' : headphones.COMMITS_BEHIND,
|
'commits_behind': headphones.COMMITS_BEHIND,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _checkGithub(self, **kwargs):
|
def _checkGithub(self, **kwargs):
|
||||||
versioncheck.checkGithub()
|
versioncheck.checkGithub()
|
||||||
self._getVersion()
|
self._getVersion()
|
||||||
|
|
||||||
def _shutdown(self, **kwargs):
|
def _shutdown(self, **kwargs):
|
||||||
headphones.SIGNAL = 'shutdown'
|
headphones.SIGNAL = 'shutdown'
|
||||||
|
|
||||||
def _restart(self, **kwargs):
|
def _restart(self, **kwargs):
|
||||||
headphones.SIGNAL = 'restart'
|
headphones.SIGNAL = 'restart'
|
||||||
|
|
||||||
def _update(self, **kwargs):
|
def _update(self, **kwargs):
|
||||||
headphones.SIGNAL = 'update'
|
headphones.SIGNAL = 'update'
|
||||||
|
|
||||||
def _getArtistArt(self, **kwargs):
|
def _getArtistArt(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getArtwork(ArtistID=self.id)
|
self.data = cache.getArtwork(ArtistID=self.id)
|
||||||
|
|
||||||
def _getAlbumArt(self, **kwargs):
|
def _getAlbumArt(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getArtwork(AlbumID=self.id)
|
self.data = cache.getArtwork(AlbumID=self.id)
|
||||||
|
|
||||||
def _getArtistInfo(self, **kwargs):
|
def _getArtistInfo(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getInfo(ArtistID=self.id)
|
self.data = cache.getInfo(ArtistID=self.id)
|
||||||
|
|
||||||
def _getAlbumInfo(self, **kwargs):
|
def _getAlbumInfo(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getInfo(AlbumID=self.id)
|
self.data = cache.getInfo(AlbumID=self.id)
|
||||||
|
|
||||||
def _getArtistThumb(self, **kwargs):
|
def _getArtistThumb(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getThumb(ArtistID=self.id)
|
self.data = cache.getThumb(ArtistID=self.id)
|
||||||
|
|
||||||
def _getAlbumThumb(self, **kwargs):
|
def _getAlbumThumb(self, **kwargs):
|
||||||
|
|
||||||
if 'id' not in kwargs:
|
if 'id' not in kwargs:
|
||||||
self.data = 'Missing parameter: id'
|
self.data = 'Missing parameter: id'
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
self.data = cache.getThumb(AlbumID=self.id)
|
self.data = cache.getThumb(AlbumID=self.id)
|
||||||
|
|
||||||
def _choose_specific_download(self, **kwargs):
|
def _choose_specific_download(self, **kwargs):
|
||||||
@@ -402,18 +435,18 @@ class Api(object):
|
|||||||
else:
|
else:
|
||||||
self.id = kwargs['id']
|
self.id = kwargs['id']
|
||||||
|
|
||||||
results = searcher.searchforalbum(self.id, choose_specific_download=True)
|
results = searcher.searchforalbum(
|
||||||
|
self.id, choose_specific_download=True)
|
||||||
results_as_dicts = []
|
|
||||||
|
|
||||||
for result in results:
|
|
||||||
|
|
||||||
|
results_as_dicts = []
|
||||||
|
|
||||||
|
for result in results:
|
||||||
result_dict = {
|
result_dict = {
|
||||||
'title':result[0],
|
'title': result[0],
|
||||||
'size':result[1],
|
'size': result[1],
|
||||||
'url':result[2],
|
'url': result[2],
|
||||||
'provider':result[3],
|
'provider': result[3],
|
||||||
'kind':result[4]
|
'kind': result[4]
|
||||||
}
|
}
|
||||||
results_as_dicts.append(result_dict)
|
results_as_dicts.append(result_dict)
|
||||||
|
|
||||||
@@ -421,7 +454,7 @@ class Api(object):
|
|||||||
|
|
||||||
def _download_specific_release(self, **kwargs):
|
def _download_specific_release(self, **kwargs):
|
||||||
|
|
||||||
expected_kwargs =['id', 'title','size','url','provider','kind']
|
expected_kwargs = ['id', 'title', 'size', 'url', 'provider', 'kind']
|
||||||
|
|
||||||
for kwarg in expected_kwargs:
|
for kwarg in expected_kwargs:
|
||||||
if kwarg not in kwargs:
|
if kwarg not in kwargs:
|
||||||
@@ -438,20 +471,24 @@ class Api(object):
|
|||||||
for kwarg in expected_kwargs:
|
for kwarg in expected_kwargs:
|
||||||
del kwargs[kwarg]
|
del kwargs[kwarg]
|
||||||
|
|
||||||
# Handle situations where the torrent url contains arguments that are parsed
|
# Handle situations where the torrent url contains arguments that are
|
||||||
|
# parsed
|
||||||
if kwargs:
|
if kwargs:
|
||||||
import urllib, urllib2
|
import urllib
|
||||||
url = urllib2.quote(url, safe=":?/=&") + '&' + urllib.urlencode(kwargs)
|
import urllib2
|
||||||
|
url = urllib2.quote(
|
||||||
|
url, safe=":?/=&") + '&' + urllib.urlencode(kwargs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = [(title,int(size),url,provider,kind)]
|
result = [(title, int(size), url, provider, kind)]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
result = [(title,float(size),url,provider,kind)]
|
result = [(title, float(size), url, provider, kind)]
|
||||||
|
|
||||||
logger.info(u"Making sure we can download the chosen result")
|
logger.info(u"Making sure we can download the chosen result")
|
||||||
(data, bestqual) = searcher.preprocess(result)
|
(data, bestqual) = searcher.preprocess(result)
|
||||||
|
|
||||||
if data and bestqual:
|
if data and bestqual:
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
album = myDB.action('SELECT * from albums WHERE AlbumID=?', [id]).fetchone()
|
album = myDB.action(
|
||||||
searcher.send_to_downloader(data, bestqual, album)
|
'SELECT * from albums WHERE AlbumID=?', [id]).fetchone()
|
||||||
|
searcher.send_to_downloader(data, bestqual, album)
|
||||||
|
|||||||
@@ -14,60 +14,58 @@
|
|||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import glob
|
|
||||||
import urllib
|
|
||||||
import headphones
|
|
||||||
|
|
||||||
|
import headphones
|
||||||
from headphones import db, helpers, logger, lastfm, request
|
from headphones import db, helpers, logger, lastfm, request
|
||||||
|
|
||||||
lastfm_apikey = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
|
LASTFM_API_KEY = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
|
||||||
|
|
||||||
|
|
||||||
class Cache(object):
|
class Cache(object):
|
||||||
"""
|
"""
|
||||||
This class deals with getting, storing and serving up artwork (album
|
This class deals with getting, storing and serving up artwork (album art,
|
||||||
art, artist images, etc) and info/descriptions (album info, artist descrptions)
|
artist images, etc) and info/descriptions (album info, artist descrptions)
|
||||||
to and from the cache folder. This can be called from within a web interface,
|
to and from the cache folder. This can be called from within a web
|
||||||
for example, using the helper functions getInfo(id) and getArtwork(id), to utilize the cached
|
interface. For example, using the helper functions `getInfo(id)` and
|
||||||
images rather than having to retrieve them every time the page is reloaded.
|
`getArtwork(id)`, to utilize the cached images rather than having to
|
||||||
|
retrieve them every time the page is reloaded.
|
||||||
|
|
||||||
So you can call cache.getArtwork(id) which will return an absolute path
|
You can call `getArtwork(id)` which will return an absolute path to the
|
||||||
to the image file on the local machine, or if the cache directory
|
image file on the local machine, or if the cache directory doesn't exist,
|
||||||
doesn't exist, or can not be written to, it will return a url to the image.
|
or can not be written to, it will return a url to the image.
|
||||||
|
|
||||||
Call cache.getInfo(id) to grab the artist/album info; will return the text description
|
Call `getInfo(id)` to grab the artist/album info. This will return the
|
||||||
|
text description.
|
||||||
|
|
||||||
The basic format for art in the cache is <musicbrainzid>.<date>.<ext>
|
The basic format for art in the cache is `<musicbrainzid>.<date>.<ext>`
|
||||||
and for info it is <musicbrainzid>.<date>.txt
|
and for info it is `<musicbrainzid>.<date>.txt`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
path_to_art_cache = os.path.join(headphones.CACHE_DIR, 'artwork')
|
path_to_art_cache = os.path.join(headphones.CONFIG.CACHE_DIR, 'artwork')
|
||||||
|
|
||||||
id = None
|
|
||||||
id_type = None # 'artist' or 'album' - set automatically depending on whether ArtistID or AlbumID is passed
|
|
||||||
query_type = None # 'artwork','thumb' or 'info' - set automatically
|
|
||||||
|
|
||||||
artwork_files = []
|
|
||||||
thumb_files = []
|
|
||||||
|
|
||||||
artwork_errors = False
|
|
||||||
artwork_url = None
|
|
||||||
|
|
||||||
thumb_errors = False
|
|
||||||
thumb_url = None
|
|
||||||
|
|
||||||
info_summary = None
|
|
||||||
info_content = None
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.id = None
|
||||||
|
self.id_type = None # 'artist' or 'album' - set automatically depending on whether ArtistID or AlbumID is passed
|
||||||
|
self.query_type = None # 'artwork','thumb' or 'info' - set automatically
|
||||||
|
|
||||||
pass
|
self.artwork_files = []
|
||||||
|
self.thumb_files = []
|
||||||
|
|
||||||
def _findfilesstartingwith(self,pattern,folder):
|
self.artwork_errors = False
|
||||||
|
self.artwork_url = None
|
||||||
|
|
||||||
|
self.thumb_errors = False
|
||||||
|
self.thumb_url = None
|
||||||
|
|
||||||
|
self.info_summary = None
|
||||||
|
self.info_content = None
|
||||||
|
|
||||||
|
def _findfilesstartingwith(self, pattern, folder):
|
||||||
files = []
|
files = []
|
||||||
if os.path.exists(folder):
|
if os.path.exists(folder):
|
||||||
for fname in os.listdir(folder):
|
for fname in os.listdir(folder):
|
||||||
if fname.startswith(pattern):
|
if fname.startswith(pattern):
|
||||||
files.append(os.path.join(folder,fname))
|
files.append(os.path.join(folder, fname))
|
||||||
return files
|
return files
|
||||||
|
|
||||||
def _exists(self, type):
|
def _exists(self, type):
|
||||||
@@ -75,14 +73,14 @@ class Cache(object):
|
|||||||
self.thumb_files = []
|
self.thumb_files = []
|
||||||
|
|
||||||
if type == 'artwork':
|
if type == 'artwork':
|
||||||
self.artwork_files = self._findfilesstartingwith(self.id,self.path_to_art_cache)
|
self.artwork_files = self._findfilesstartingwith(self.id, self.path_to_art_cache)
|
||||||
if self.artwork_files:
|
if self.artwork_files:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif type == 'thumb':
|
elif type == 'thumb':
|
||||||
self.thumb_files = self._findfilesstartingwith("T_" + self.id,self.path_to_art_cache)
|
self.thumb_files = self._findfilesstartingwith("T_" + self.id, self.path_to_art_cache)
|
||||||
if self.thumb_files:
|
if self.thumb_files:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -91,11 +89,10 @@ class Cache(object):
|
|||||||
def _get_age(self, date):
|
def _get_age(self, date):
|
||||||
# There's probably a better way to do this
|
# There's probably a better way to do this
|
||||||
split_date = date.split('-')
|
split_date = date.split('-')
|
||||||
days_old = int(split_date[0])*365 + int(split_date[1])*30 + int(split_date[2])
|
days_old = int(split_date[0]) * 365 + int(split_date[1]) * 30 + int(split_date[2])
|
||||||
|
|
||||||
return days_old
|
return days_old
|
||||||
|
|
||||||
|
|
||||||
def _is_current(self, filename=None, date=None):
|
def _is_current(self, filename=None, date=None):
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
@@ -119,16 +116,16 @@ class Cache(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
for image in images:
|
for image in images:
|
||||||
if image['size'] == 'medium':
|
if image['size'] == 'medium' and '#text' in image:
|
||||||
thumb_url = image['#text']
|
thumb_url = image['#text']
|
||||||
break
|
break
|
||||||
|
|
||||||
return thumb_url
|
return thumb_url
|
||||||
|
|
||||||
def get_artwork_from_cache(self, ArtistID=None, AlbumID=None):
|
def get_artwork_from_cache(self, ArtistID=None, AlbumID=None):
|
||||||
'''
|
"""
|
||||||
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.query_type = 'artwork'
|
self.query_type = 'artwork'
|
||||||
|
|
||||||
@@ -152,9 +149,9 @@ class Cache(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_thumb_from_cache(self, ArtistID=None, AlbumID=None):
|
def get_thumb_from_cache(self, ArtistID=None, AlbumID=None):
|
||||||
'''
|
"""
|
||||||
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.query_type = 'thumb'
|
self.query_type = 'thumb'
|
||||||
|
|
||||||
@@ -185,38 +182,43 @@ class Cache(object):
|
|||||||
if ArtistID:
|
if ArtistID:
|
||||||
self.id = ArtistID
|
self.id = ArtistID
|
||||||
self.id_type = 'artist'
|
self.id_type = 'artist'
|
||||||
db_info = myDB.action('SELECT Summary, Content, LastUpdated FROM descriptions WHERE ArtistID=?', [self.id]).fetchone()
|
db_info = myDB.action(
|
||||||
|
'SELECT Summary, Content, LastUpdated FROM descriptions WHERE ArtistID=?',
|
||||||
|
[self.id]).fetchone()
|
||||||
else:
|
else:
|
||||||
self.id = AlbumID
|
self.id = AlbumID
|
||||||
self.id_type = 'album'
|
self.id_type = 'album'
|
||||||
db_info = myDB.action('SELECT Summary, Content, LastUpdated FROM descriptions WHERE ReleaseGroupID=?', [self.id]).fetchone()
|
db_info = myDB.action(
|
||||||
|
'SELECT Summary, Content, LastUpdated FROM descriptions WHERE ReleaseGroupID=?',
|
||||||
|
[self.id]).fetchone()
|
||||||
|
|
||||||
if not db_info or not db_info['LastUpdated'] or not self._is_current(date=db_info['LastUpdated']):
|
if not db_info or not db_info['LastUpdated'] or not self._is_current(
|
||||||
|
date=db_info['LastUpdated']):
|
||||||
|
|
||||||
self._update_cache()
|
self._update_cache()
|
||||||
info_dict = { 'Summary' : self.info_summary, 'Content' : self.info_content }
|
info_dict = {'Summary': self.info_summary, 'Content': self.info_content}
|
||||||
return info_dict
|
return info_dict
|
||||||
|
|
||||||
else:
|
else:
|
||||||
info_dict = { 'Summary' : db_info['Summary'], 'Content' : db_info['Content'] }
|
info_dict = {'Summary': db_info['Summary'], 'Content': db_info['Content']}
|
||||||
return info_dict
|
return info_dict
|
||||||
|
|
||||||
def get_image_links(self, ArtistID=None, AlbumID=None):
|
def get_image_links(self, ArtistID=None, AlbumID=None):
|
||||||
'''
|
"""
|
||||||
Here we're just going to open up the last.fm url, grab the image links and return them
|
Here we're just going to open up the last.fm url, grab the image links and return them
|
||||||
Won't save any image urls, or save the artwork in the cache. Useful for search results, etc.
|
Won't save any image urls, or save the artwork in the cache. Useful for search results, etc.
|
||||||
'''
|
"""
|
||||||
if ArtistID:
|
if ArtistID:
|
||||||
|
|
||||||
self.id_type = 'artist'
|
self.id_type = 'artist'
|
||||||
data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=lastfm_apikey)
|
data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=LASTFM_API_KEY)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_url = data['artist']['image'][-1]['#text']
|
image_url = data['artist']['image'][-1]['#text']
|
||||||
except Exception:
|
except (KeyError, IndexError):
|
||||||
logger.debug('No artist image found')
|
logger.debug('No artist image found')
|
||||||
image_url = None
|
image_url = None
|
||||||
|
|
||||||
@@ -227,51 +229,83 @@ class Cache(object):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
self.id_type = 'album'
|
self.id_type = 'album'
|
||||||
data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=lastfm_apikey)
|
data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=LASTFM_API_KEY)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_url = data['artist']['image'][-1]['#text']
|
image_url = data['album']['image'][-1]['#text']
|
||||||
except Exception:
|
except (KeyError, IndexError):
|
||||||
logger.debug('No artist image found')
|
logger.debug('No album image found on last.fm')
|
||||||
image_url = None
|
image_url = None
|
||||||
|
|
||||||
thumb_url = self._get_thumb_url(data)
|
thumb_url = self._get_thumb_url(data)
|
||||||
|
|
||||||
if not thumb_url:
|
if not thumb_url:
|
||||||
logger.debug('No artist thumbnail image found')
|
logger.debug('No album thumbnail image found on last.fm')
|
||||||
|
|
||||||
return {'artwork' : image_url, 'thumbnail' : thumb_url }
|
return {'artwork': image_url, 'thumbnail': thumb_url}
|
||||||
|
|
||||||
|
def remove_from_cache(self, ArtistID=None, AlbumID=None):
|
||||||
|
"""
|
||||||
|
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if ArtistID:
|
||||||
|
self.id = ArtistID
|
||||||
|
self.id_type = 'artist'
|
||||||
|
else:
|
||||||
|
self.id = AlbumID
|
||||||
|
self.id_type = 'album'
|
||||||
|
|
||||||
|
self.query_type = 'artwork'
|
||||||
|
|
||||||
|
if self._exists('artwork'):
|
||||||
|
for artwork_file in self.artwork_files:
|
||||||
|
try:
|
||||||
|
os.remove(artwork_file)
|
||||||
|
except:
|
||||||
|
logger.warn('Error deleting file from the cache: %s', artwork_file)
|
||||||
|
|
||||||
|
self.query_type = 'thumb'
|
||||||
|
|
||||||
|
if self._exists('thumb'):
|
||||||
|
for thumb_file in self.thumb_files:
|
||||||
|
try:
|
||||||
|
os.remove(thumb_file)
|
||||||
|
except Exception:
|
||||||
|
logger.warn('Error deleting file from the cache: %s', thumb_file)
|
||||||
|
|
||||||
def _update_cache(self):
|
def _update_cache(self):
|
||||||
'''
|
"""
|
||||||
Since we call the same url for both info and artwork, we'll update both at the same time
|
Since we call the same url for both info and artwork, we'll update both at the same time
|
||||||
'''
|
"""
|
||||||
|
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
|
|
||||||
# Since lastfm uses release ids rather than release group ids for albums, we have to do a artist + album search for albums
|
# Since lastfm uses release ids rather than release group ids for albums, we have to do a artist + album search for albums
|
||||||
|
# Exception is when adding albums manually, then we should use release id
|
||||||
if self.id_type == 'artist':
|
if self.id_type == 'artist':
|
||||||
|
|
||||||
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=lastfm_apikey)
|
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=LASTFM_API_KEY)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.info_summary = data['artist']['bio']['summary']
|
self.info_summary = data['artist']['bio']['summary']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No artist bio summary found')
|
logger.debug('No artist bio summary found')
|
||||||
self.info_summary = None
|
self.info_summary = None
|
||||||
try:
|
try:
|
||||||
self.info_content = data['artist']['bio']['content']
|
self.info_content = data['artist']['bio']['content']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No artist bio found')
|
logger.debug('No artist bio found')
|
||||||
self.info_content = None
|
self.info_content = None
|
||||||
try:
|
try:
|
||||||
image_url = data['artist']['image'][-1]['#text']
|
image_url = data['artist']['image'][-1]['#text']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No artist image found')
|
logger.debug('No artist image found')
|
||||||
image_url = None
|
image_url = None
|
||||||
|
|
||||||
@@ -280,26 +314,36 @@ class Cache(object):
|
|||||||
logger.debug('No artist thumbnail image found')
|
logger.debug('No artist thumbnail image found')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
dbalbum = myDB.action(
|
||||||
dbartist = myDB.action('SELECT ArtistName, AlbumTitle FROM albums WHERE AlbumID=?', [self.id]).fetchone()
|
'SELECT ArtistName, AlbumTitle, ReleaseID FROM albums WHERE AlbumID=?',
|
||||||
data = lastfm.request_lastfm("album.getinfo", artist=dbartist['ArtistName'], album=dbartist['AlbumTitle'], api_key=lastfm_apikey)
|
[self.id]).fetchone()
|
||||||
|
if dbalbum['ReleaseID'] != self.id:
|
||||||
|
data = lastfm.request_lastfm("album.getinfo", mbid=dbalbum['ReleaseID'],
|
||||||
|
api_key=LASTFM_API_KEY)
|
||||||
|
if not data:
|
||||||
|
data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'],
|
||||||
|
album=dbalbum['AlbumTitle'],
|
||||||
|
api_key=LASTFM_API_KEY)
|
||||||
|
else:
|
||||||
|
data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'],
|
||||||
|
album=dbalbum['AlbumTitle'], api_key=LASTFM_API_KEY)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.info_summary = data['album']['wiki']['summary']
|
self.info_summary = data['album']['wiki']['summary']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No album summary found')
|
logger.debug('No album summary found')
|
||||||
self.info_summary = None
|
self.info_summary = None
|
||||||
try:
|
try:
|
||||||
self.info_content = data['album']['wiki']['content']
|
self.info_content = data['album']['wiki']['content']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No album infomation found')
|
logger.debug('No album infomation found')
|
||||||
self.info_content = None
|
self.info_content = None
|
||||||
try:
|
try:
|
||||||
image_url = data['album']['image'][-1]['#text']
|
image_url = data['album']['image'][-1]['#text']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logger.debug('No album image link found')
|
logger.debug('No album image link found')
|
||||||
image_url = None
|
image_url = None
|
||||||
|
|
||||||
@@ -308,22 +352,24 @@ class Cache(object):
|
|||||||
if not thumb_url:
|
if not thumb_url:
|
||||||
logger.debug('No album thumbnail image found')
|
logger.debug('No album thumbnail image found')
|
||||||
|
|
||||||
#Save the content & summary to the database no matter what if we've opened up the url
|
# Save the content & summary to the database no matter what if we've
|
||||||
|
# opened up the url
|
||||||
if self.id_type == 'artist':
|
if self.id_type == 'artist':
|
||||||
controlValueDict = {"ArtistID": self.id}
|
controlValueDict = {"ArtistID": self.id}
|
||||||
else:
|
else:
|
||||||
controlValueDict = {"ReleaseGroupID": self.id}
|
controlValueDict = {"ReleaseGroupID": self.id}
|
||||||
|
|
||||||
newValueDict = {"Summary": self.info_summary,
|
newValueDict = {"Summary": self.info_summary,
|
||||||
"Content": self.info_content,
|
"Content": self.info_content,
|
||||||
"LastUpdated": helpers.today()}
|
"LastUpdated": helpers.today()}
|
||||||
|
|
||||||
myDB.upsert("descriptions", newValueDict, controlValueDict)
|
myDB.upsert("descriptions", newValueDict, controlValueDict)
|
||||||
|
|
||||||
# Save the image URL to the database
|
# Save the image URL to the database
|
||||||
if image_url:
|
if image_url:
|
||||||
if self.id_type == 'artist':
|
if self.id_type == 'artist':
|
||||||
myDB.action('UPDATE artists SET ArtworkURL=? WHERE ArtistID=?', [image_url, self.id])
|
myDB.action('UPDATE artists SET ArtworkURL=? WHERE ArtistID=?',
|
||||||
|
[image_url, self.id])
|
||||||
else:
|
else:
|
||||||
myDB.action('UPDATE albums SET ArtworkURL=? WHERE AlbumID=?', [image_url, self.id])
|
myDB.action('UPDATE albums SET ArtworkURL=? WHERE AlbumID=?', [image_url, self.id])
|
||||||
|
|
||||||
@@ -334,7 +380,8 @@ class Cache(object):
|
|||||||
else:
|
else:
|
||||||
myDB.action('UPDATE albums SET ThumbURL=? WHERE AlbumID=?', [thumb_url, self.id])
|
myDB.action('UPDATE albums SET ThumbURL=? WHERE AlbumID=?', [thumb_url, self.id])
|
||||||
|
|
||||||
# Should we grab the artwork here if we're just grabbing thumbs or info?? Probably not since the files can be quite big
|
# Should we grab the artwork here if we're just grabbing thumbs or
|
||||||
|
# info? Probably not since the files can be quite big
|
||||||
if image_url and self.query_type == 'artwork':
|
if image_url and self.query_type == 'artwork':
|
||||||
artwork = request.request_content(image_url, timeout=20)
|
artwork = request.request_content(image_url, timeout=20)
|
||||||
|
|
||||||
@@ -343,12 +390,14 @@ class Cache(object):
|
|||||||
if not os.path.isdir(self.path_to_art_cache):
|
if not os.path.isdir(self.path_to_art_cache):
|
||||||
try:
|
try:
|
||||||
os.makedirs(self.path_to_art_cache)
|
os.makedirs(self.path_to_art_cache)
|
||||||
except Exception, e:
|
os.chmod(self.path_to_art_cache,
|
||||||
|
int(headphones.CONFIG.FOLDER_PERMISSIONS, 8))
|
||||||
|
except OSError as e:
|
||||||
logger.error('Unable to create artwork cache dir. Error: %s', e)
|
logger.error('Unable to create artwork cache dir. Error: %s', e)
|
||||||
self.artwork_errors = True
|
self.artwork_errors = True
|
||||||
self.artwork_url = image_url
|
self.artwork_url = image_url
|
||||||
|
|
||||||
#Delete the old stuff
|
# Delete the old stuff
|
||||||
for artwork_file in self.artwork_files:
|
for artwork_file in self.artwork_files:
|
||||||
try:
|
try:
|
||||||
os.remove(artwork_file)
|
os.remove(artwork_file)
|
||||||
@@ -357,18 +406,22 @@ class Cache(object):
|
|||||||
|
|
||||||
ext = os.path.splitext(image_url)[1]
|
ext = os.path.splitext(image_url)[1]
|
||||||
|
|
||||||
artwork_path = os.path.join(self.path_to_art_cache, self.id + '.' + helpers.today() + ext)
|
artwork_path = os.path.join(self.path_to_art_cache,
|
||||||
|
self.id + '.' + helpers.today() + ext)
|
||||||
try:
|
try:
|
||||||
f = open(artwork_path, 'wb')
|
with open(artwork_path, 'wb') as f:
|
||||||
f.write(artwork)
|
f.write(artwork)
|
||||||
f.close()
|
|
||||||
except Exception, e:
|
os.chmod(artwork_path, int(headphones.CONFIG.FILE_PERMISSIONS, 8))
|
||||||
|
except (OSError, IOError) as e:
|
||||||
logger.error('Unable to write to the cache dir: %s', e)
|
logger.error('Unable to write to the cache dir: %s', e)
|
||||||
self.artwork_errors = True
|
self.artwork_errors = True
|
||||||
self.artwork_url = image_url
|
self.artwork_url = image_url
|
||||||
|
|
||||||
# Grab the thumbnail as well if we're getting the full artwork (as long as it's missing/outdated
|
# Grab the thumbnail as well if we're getting the full artwork (as long
|
||||||
if thumb_url and self.query_type in ['thumb','artwork'] and not (self.thumb_files and self._is_current(self.thumb_files[0])):
|
# as it's missing/outdated.
|
||||||
|
if thumb_url and self.query_type in ['thumb', 'artwork'] and not (
|
||||||
|
self.thumb_files and self._is_current(self.thumb_files[0])):
|
||||||
artwork = request.request_content(thumb_url, timeout=20)
|
artwork = request.request_content(thumb_url, timeout=20)
|
||||||
|
|
||||||
if artwork:
|
if artwork:
|
||||||
@@ -376,32 +429,36 @@ class Cache(object):
|
|||||||
if not os.path.isdir(self.path_to_art_cache):
|
if not os.path.isdir(self.path_to_art_cache):
|
||||||
try:
|
try:
|
||||||
os.makedirs(self.path_to_art_cache)
|
os.makedirs(self.path_to_art_cache)
|
||||||
except Exception, e:
|
os.chmod(self.path_to_art_cache,
|
||||||
|
int(headphones.CONFIG.FOLDER_PERMISSIONS, 8))
|
||||||
|
except OSError as e:
|
||||||
logger.error('Unable to create artwork cache dir. Error: %s' + e)
|
logger.error('Unable to create artwork cache dir. Error: %s' + e)
|
||||||
self.thumb_errors = True
|
self.thumb_errors = True
|
||||||
self.thumb_url = thumb_url
|
self.thumb_url = thumb_url
|
||||||
|
|
||||||
#Delete the old stuff
|
# Delete the old stuff
|
||||||
for thumb_file in self.thumb_files:
|
for thumb_file in self.thumb_files:
|
||||||
try:
|
try:
|
||||||
os.remove(thumb_file)
|
os.remove(thumb_file)
|
||||||
except:
|
except OSError as e:
|
||||||
logger.error('Error deleting file from the cache: %s', thumb_file)
|
logger.error('Error deleting file from the cache: %s', thumb_file)
|
||||||
|
|
||||||
ext = os.path.splitext(image_url)[1]
|
ext = os.path.splitext(image_url)[1]
|
||||||
|
|
||||||
thumb_path = os.path.join(self.path_to_art_cache, 'T_' + self.id + '.' + helpers.today() + ext)
|
thumb_path = os.path.join(self.path_to_art_cache,
|
||||||
|
'T_' + self.id + '.' + helpers.today() + ext)
|
||||||
try:
|
try:
|
||||||
f = open(thumb_path, 'wb')
|
with open(thumb_path, 'wb') as f:
|
||||||
f.write(artwork)
|
f.write(artwork)
|
||||||
f.close()
|
|
||||||
except Exception, e:
|
os.chmod(thumb_path, int(headphones.CONFIG.FILE_PERMISSIONS, 8))
|
||||||
|
except (OSError, IOError) as e:
|
||||||
logger.error('Unable to write to the cache dir: %s', e)
|
logger.error('Unable to write to the cache dir: %s', e)
|
||||||
self.thumb_errors = True
|
self.thumb_errors = True
|
||||||
self.thumb_url = image_url
|
self.thumb_url = image_url
|
||||||
|
|
||||||
def getArtwork(ArtistID=None, AlbumID=None):
|
|
||||||
|
|
||||||
|
def getArtwork(ArtistID=None, AlbumID=None):
|
||||||
c = Cache()
|
c = Cache()
|
||||||
artwork_path = c.get_artwork_from_cache(ArtistID, AlbumID)
|
artwork_path = c.get_artwork_from_cache(ArtistID, AlbumID)
|
||||||
|
|
||||||
@@ -414,8 +471,8 @@ def getArtwork(ArtistID=None, AlbumID=None):
|
|||||||
artwork_file = os.path.basename(artwork_path)
|
artwork_file = os.path.basename(artwork_path)
|
||||||
return "cache/artwork/" + artwork_file
|
return "cache/artwork/" + artwork_file
|
||||||
|
|
||||||
def getThumb(ArtistID=None, AlbumID=None):
|
|
||||||
|
|
||||||
|
def getThumb(ArtistID=None, AlbumID=None):
|
||||||
c = Cache()
|
c = Cache()
|
||||||
artwork_path = c.get_thumb_from_cache(ArtistID, AlbumID)
|
artwork_path = c.get_thumb_from_cache(ArtistID, AlbumID)
|
||||||
|
|
||||||
@@ -428,16 +485,15 @@ def getThumb(ArtistID=None, AlbumID=None):
|
|||||||
thumbnail_file = os.path.basename(artwork_path)
|
thumbnail_file = os.path.basename(artwork_path)
|
||||||
return "cache/artwork/" + thumbnail_file
|
return "cache/artwork/" + thumbnail_file
|
||||||
|
|
||||||
|
|
||||||
def getInfo(ArtistID=None, AlbumID=None):
|
def getInfo(ArtistID=None, AlbumID=None):
|
||||||
|
|
||||||
c = Cache()
|
c = Cache()
|
||||||
|
|
||||||
info_dict = c.get_info_from_cache(ArtistID, AlbumID)
|
info_dict = c.get_info_from_cache(ArtistID, AlbumID)
|
||||||
|
|
||||||
return info_dict
|
return info_dict
|
||||||
|
|
||||||
def getImageLinks(ArtistID=None, AlbumID=None):
|
|
||||||
|
|
||||||
|
def getImageLinks(ArtistID=None, AlbumID=None):
|
||||||
c = Cache()
|
c = Cache()
|
||||||
image_links = c.get_image_links(ArtistID, AlbumID)
|
image_links = c.get_image_links(ArtistID, AlbumID)
|
||||||
|
|
||||||
|
|||||||
@@ -1,130 +1,137 @@
|
|||||||
# This file is part of Headphones.
|
# This file is part of Headphones.
|
||||||
#
|
#
|
||||||
# Headphones is free software: you can redistribute it and/or modify
|
# Headphones is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# Headphones is distributed in the hope that it will be useful,
|
# Headphones is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#########################################
|
#######################################
|
||||||
## Stolen from Sick-Beard's classes.py ##
|
# Stolen from Sick-Beard's classes.py #
|
||||||
#########################################
|
#######################################
|
||||||
|
|
||||||
import headphones
|
|
||||||
|
import urllib
|
||||||
import urllib
|
|
||||||
import datetime
|
from common import USER_AGENT
|
||||||
|
|
||||||
from common import USER_AGENT
|
|
||||||
|
class HeadphonesURLopener(urllib.FancyURLopener):
|
||||||
class HeadphonesURLopener(urllib.FancyURLopener):
|
version = USER_AGENT
|
||||||
version = USER_AGENT
|
|
||||||
|
|
||||||
class AuthURLOpener(HeadphonesURLopener):
|
class AuthURLOpener(HeadphonesURLopener):
|
||||||
"""
|
"""
|
||||||
URLOpener class that supports http auth without needing interactive password entry.
|
URLOpener class that supports http auth without needing interactive password entry.
|
||||||
If the provided username/password don't work it simply fails.
|
If the provided username/password don't work it simply fails.
|
||||||
|
|
||||||
user: username to use for HTTP auth
|
user: username to use for HTTP auth
|
||||||
pw: password to use for HTTP auth
|
pw: password to use for HTTP auth
|
||||||
"""
|
"""
|
||||||
def __init__(self, user, pw):
|
|
||||||
self.username = user
|
def __init__(self, user, pw):
|
||||||
self.password = pw
|
self.username = user
|
||||||
|
self.password = pw
|
||||||
# remember if we've tried the username/password before
|
|
||||||
self.numTries = 0
|
# remember if we've tried the username/password before
|
||||||
|
self.numTries = 0
|
||||||
# call the base class
|
|
||||||
urllib.FancyURLopener.__init__(self)
|
# call the base class
|
||||||
|
urllib.FancyURLopener.__init__(self)
|
||||||
def prompt_user_passwd(self, host, realm):
|
|
||||||
"""
|
def prompt_user_passwd(self, host, realm):
|
||||||
Override this function and instead of prompting just give the
|
"""
|
||||||
username/password that were provided when the class was instantiated.
|
Override this function and instead of prompting just give the
|
||||||
"""
|
username/password that were provided when the class was instantiated.
|
||||||
|
"""
|
||||||
# if this is the first try then provide a username/password
|
|
||||||
if self.numTries == 0:
|
# if this is the first try then provide a username/password
|
||||||
self.numTries = 1
|
if self.numTries == 0:
|
||||||
return (self.username, self.password)
|
self.numTries = 1
|
||||||
|
return (self.username, self.password)
|
||||||
# if we've tried before then return blank which cancels the request
|
|
||||||
else:
|
# if we've tried before then return blank which cancels the request
|
||||||
return ('', '')
|
else:
|
||||||
|
return ('', '')
|
||||||
# this is pretty much just a hack for convenience
|
|
||||||
def openit(self, url):
|
# this is pretty much just a hack for convenience
|
||||||
self.numTries = 0
|
def openit(self, url):
|
||||||
return HeadphonesURLopener.open(self, url)
|
self.numTries = 0
|
||||||
|
return HeadphonesURLopener.open(self, url)
|
||||||
class SearchResult:
|
|
||||||
"""
|
|
||||||
Represents a search result from an indexer.
|
class SearchResult:
|
||||||
"""
|
"""
|
||||||
|
Represents a search result from an indexer.
|
||||||
def __init__(self):
|
"""
|
||||||
self.provider = -1
|
|
||||||
|
def __init__(self):
|
||||||
# URL to the NZB/torrent file
|
self.provider = -1
|
||||||
self.url = ""
|
|
||||||
|
# URL to the NZB/torrent file
|
||||||
# used by some providers to store extra info associated with the result
|
self.url = ""
|
||||||
self.extraInfo = []
|
|
||||||
|
# used by some providers to store extra info associated with the result
|
||||||
# quality of the release
|
self.extraInfo = []
|
||||||
self.quality = -1
|
|
||||||
|
# quality of the release
|
||||||
# release name
|
self.quality = -1
|
||||||
self.name = ""
|
|
||||||
|
# release name
|
||||||
def __str__(self):
|
self.name = ""
|
||||||
|
|
||||||
if self.provider == None:
|
def __str__(self):
|
||||||
return "Invalid provider, unable to print self"
|
|
||||||
|
if self.provider is None:
|
||||||
myString = self.provider.name + " @ " + self.url + "\n"
|
return "Invalid provider, unable to print self"
|
||||||
myString += "Extra Info:\n"
|
|
||||||
for extra in self.extraInfo:
|
myString = self.provider.name + " @ " + self.url + "\n"
|
||||||
myString += " " + extra + "\n"
|
myString += "Extra Info:\n"
|
||||||
return myString
|
for extra in self.extraInfo:
|
||||||
|
myString += " " + extra + "\n"
|
||||||
class NZBSearchResult(SearchResult):
|
return myString
|
||||||
"""
|
|
||||||
Regular NZB result with an URL to the NZB
|
|
||||||
"""
|
class NZBSearchResult(SearchResult):
|
||||||
resultType = "nzb"
|
"""
|
||||||
|
Regular NZB result with an URL to the NZB
|
||||||
class NZBDataSearchResult(SearchResult):
|
"""
|
||||||
"""
|
resultType = "nzb"
|
||||||
NZB result where the actual NZB XML data is stored in the extraInfo
|
|
||||||
"""
|
|
||||||
resultType = "nzbdata"
|
class NZBDataSearchResult(SearchResult):
|
||||||
|
"""
|
||||||
class TorrentSearchResult(SearchResult):
|
NZB result where the actual NZB XML data is stored in the extraInfo
|
||||||
"""
|
"""
|
||||||
Torrent result with an URL to the torrent
|
resultType = "nzbdata"
|
||||||
"""
|
|
||||||
resultType = "torrent"
|
|
||||||
|
class TorrentSearchResult(SearchResult):
|
||||||
class Proper:
|
"""
|
||||||
def __init__(self, name, url, date):
|
Torrent result with an URL to the torrent
|
||||||
self.name = name
|
"""
|
||||||
self.url = url
|
resultType = "torrent"
|
||||||
self.date = date
|
|
||||||
self.provider = None
|
|
||||||
self.quality = -1
|
class Proper:
|
||||||
|
def __init__(self, name, url, date):
|
||||||
self.tvdbid = -1
|
self.name = name
|
||||||
self.season = -1
|
self.url = url
|
||||||
self.episode = -1
|
self.date = date
|
||||||
|
self.provider = None
|
||||||
def __str__(self):
|
self.quality = -1
|
||||||
return str(self.date)+" "+self.name+" "+str(self.season)+"x"+str(self.episode)+" of "+str(self.tvdbid)
|
|
||||||
|
self.tvdbid = -1
|
||||||
|
self.season = -1
|
||||||
|
self.episode = -1
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.date) + " " + self.name + " " + str(self.season) + "x" + str(
|
||||||
|
self.episode) + " of " + str(self.tvdbid)
|
||||||
|
|||||||
@@ -1,177 +1,185 @@
|
|||||||
# This file is part of Headphones.
|
# This file is part of Headphones.
|
||||||
#
|
#
|
||||||
# Headphones is free software: you can redistribute it and/or modify
|
# Headphones is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# Headphones is distributed in the hope that it will be useful,
|
# Headphones is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Created on Aug 1, 2011
|
Created on Aug 1, 2011
|
||||||
|
|
||||||
@author: Michael
|
@author: Michael
|
||||||
'''
|
'''
|
||||||
import platform, operator, os, re
|
import platform
|
||||||
|
import operator
|
||||||
from headphones import version
|
|
||||||
|
import os
|
||||||
#Identify Our Application
|
import re
|
||||||
USER_AGENT = 'Headphones/-'+version.HEADPHONES_VERSION+' ('+platform.system()+' '+platform.release()+')'
|
from headphones import version
|
||||||
|
|
||||||
### Notification Types
|
|
||||||
NOTIFY_SNATCH = 1
|
# Identify Our Application
|
||||||
NOTIFY_DOWNLOAD = 2
|
USER_AGENT = 'Headphones/-' + version.HEADPHONES_VERSION + ' (' + platform.system() + ' ' + platform.release() + ')'
|
||||||
|
|
||||||
notifyStrings = {}
|
# Notification Types
|
||||||
notifyStrings[NOTIFY_SNATCH] = "Started Download"
|
NOTIFY_SNATCH = 1
|
||||||
notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
|
NOTIFY_DOWNLOAD = 2
|
||||||
|
|
||||||
### Release statuses
|
notifyStrings = {}
|
||||||
UNKNOWN = -1 # should never happen
|
notifyStrings[NOTIFY_SNATCH] = "Started Download"
|
||||||
UNAIRED = 1 # releases that haven't dropped yet
|
notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
|
||||||
SNATCHED = 2 # qualified with quality
|
|
||||||
WANTED = 3 # releases we don't have but want to get
|
# Release statuses
|
||||||
DOWNLOADED = 4 # qualified with quality
|
UNKNOWN = -1 # should never happen
|
||||||
SKIPPED = 5 # releases we don't want
|
UNAIRED = 1 # releases that haven't dropped yet
|
||||||
ARCHIVED = 6 # releases that you don't have locally (counts toward download completion stats)
|
SNATCHED = 2 # qualified with quality
|
||||||
IGNORED = 7 # releases that you don't want included in your download stats
|
WANTED = 3 # releases we don't have but want to get
|
||||||
SNATCHED_PROPER = 9 # qualified with quality
|
DOWNLOADED = 4 # qualified with quality
|
||||||
|
SKIPPED = 5 # releases we don't want
|
||||||
class Quality:
|
ARCHIVED = 6 # releases that you don't have locally (counts toward download completion stats)
|
||||||
|
IGNORED = 7 # releases that you don't want included in your download stats
|
||||||
NONE = 0
|
SNATCHED_PROPER = 9 # qualified with quality
|
||||||
B192 = 1<<1 # 2
|
|
||||||
VBR = 1<<2 # 4
|
|
||||||
B256 = 1<<3 # 8
|
class Quality:
|
||||||
B320 = 1<<4 #16
|
NONE = 0
|
||||||
FLAC = 1<<5 #32
|
B192 = 1 << 1 # 2
|
||||||
|
VBR = 1 << 2 # 4
|
||||||
# put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
|
B256 = 1 << 3 # 8
|
||||||
UNKNOWN = 1<<15
|
B320 = 1 << 4 # 16
|
||||||
|
FLAC = 1 << 5 # 32
|
||||||
qualityStrings = {NONE: "N/A",
|
|
||||||
UNKNOWN: "Unknown",
|
# put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
|
||||||
B192: "MP3 192",
|
UNKNOWN = 1 << 15
|
||||||
VBR: "MP3 VBR",
|
|
||||||
B256: "MP3 256",
|
qualityStrings = {NONE: "N/A",
|
||||||
B320: "MP3 320",
|
UNKNOWN: "Unknown",
|
||||||
FLAC: "Flac"}
|
B192: "MP3 192",
|
||||||
|
VBR: "MP3 VBR",
|
||||||
statusPrefixes = {DOWNLOADED: "Downloaded",
|
B256: "MP3 256",
|
||||||
SNATCHED: "Snatched"}
|
B320: "MP3 320",
|
||||||
|
FLAC: "Flac"}
|
||||||
@staticmethod
|
|
||||||
def _getStatusStrings(status):
|
statusPrefixes = {DOWNLOADED: "Downloaded",
|
||||||
toReturn = {}
|
SNATCHED: "Snatched"}
|
||||||
for x in Quality.qualityStrings.keys():
|
|
||||||
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status]+" ("+Quality.qualityStrings[x]+")"
|
@staticmethod
|
||||||
return toReturn
|
def _getStatusStrings(status):
|
||||||
|
toReturn = {}
|
||||||
@staticmethod
|
for x in Quality.qualityStrings.keys():
|
||||||
def combineQualities(anyQualities, bestQualities):
|
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status] + " (" + \
|
||||||
anyQuality = 0
|
Quality.qualityStrings[x] + ")"
|
||||||
bestQuality = 0
|
return toReturn
|
||||||
if anyQualities:
|
|
||||||
anyQuality = reduce(operator.or_, anyQualities)
|
@staticmethod
|
||||||
if bestQualities:
|
def combineQualities(anyQualities, bestQualities):
|
||||||
bestQuality = reduce(operator.or_, bestQualities)
|
anyQuality = 0
|
||||||
return anyQuality | (bestQuality<<16)
|
bestQuality = 0
|
||||||
|
if anyQualities:
|
||||||
@staticmethod
|
anyQuality = reduce(operator.or_, anyQualities)
|
||||||
def splitQuality(quality):
|
if bestQualities:
|
||||||
anyQualities = []
|
bestQuality = reduce(operator.or_, bestQualities)
|
||||||
bestQualities = []
|
return anyQuality | (bestQuality << 16)
|
||||||
for curQual in Quality.qualityStrings.keys():
|
|
||||||
if curQual & quality:
|
@staticmethod
|
||||||
anyQualities.append(curQual)
|
def splitQuality(quality):
|
||||||
if curQual<<16 & quality:
|
anyQualities = []
|
||||||
bestQualities.append(curQual)
|
bestQualities = []
|
||||||
|
for curQual in Quality.qualityStrings.keys():
|
||||||
return (anyQualities, bestQualities)
|
if curQual & quality:
|
||||||
|
anyQualities.append(curQual)
|
||||||
@staticmethod
|
if curQual << 16 & quality:
|
||||||
def nameQuality(name):
|
bestQualities.append(curQual)
|
||||||
|
|
||||||
name = os.path.basename(name)
|
return (anyQualities, bestQualities)
|
||||||
|
|
||||||
# if we have our exact text then assume we put it there
|
@staticmethod
|
||||||
for x in Quality.qualityStrings:
|
def nameQuality(name):
|
||||||
if x == Quality.UNKNOWN:
|
|
||||||
continue
|
def checkName(list, func):
|
||||||
|
return func([re.search(x, name, re.I) for x in list])
|
||||||
regex = '\W'+Quality.qualityStrings[x].replace(' ','\W')+'\W'
|
|
||||||
regex_match = re.search(regex, name, re.I)
|
name = os.path.basename(name)
|
||||||
if regex_match:
|
|
||||||
return x
|
# if we have our exact text then assume we put it there
|
||||||
|
for x in Quality.qualityStrings:
|
||||||
checkName = lambda list, func: func([re.search(x, name, re.I) for x in list])
|
if x == Quality.UNKNOWN:
|
||||||
|
continue
|
||||||
#TODO: fix quality checking here
|
|
||||||
if checkName(["mp3", "192"], any) and not checkName(["flac"], all):
|
regex = '\W' + Quality.qualityStrings[x].replace(' ', '\W') + '\W'
|
||||||
return Quality.B192
|
regex_match = re.search(regex, name, re.I)
|
||||||
elif checkName(["mp3", "256"], any) and not checkName(["flac"], all):
|
if regex_match:
|
||||||
return Quality.B256
|
return x
|
||||||
elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all):
|
|
||||||
return Quality.VBR
|
# TODO: fix quality checking here
|
||||||
elif checkName(["mp3", "320"], any) and not checkName(["flac"], all):
|
if checkName(["mp3", "192"], any) and not checkName(["flac"], all):
|
||||||
return Quality.B320
|
return Quality.B192
|
||||||
else:
|
elif checkName(["mp3", "256"], any) and not checkName(["flac"], all):
|
||||||
return Quality.UNKNOWN
|
return Quality.B256
|
||||||
|
elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all):
|
||||||
@staticmethod
|
return Quality.VBR
|
||||||
def assumeQuality(name):
|
elif checkName(["mp3", "320"], any) and not checkName(["flac"], all):
|
||||||
|
return Quality.B320
|
||||||
if name.lower().endswith(".mp3"):
|
else:
|
||||||
return Quality.MP3
|
return Quality.UNKNOWN
|
||||||
elif name.lower().endswith(".flac"):
|
|
||||||
return Quality.LOSSLESS
|
@staticmethod
|
||||||
else:
|
def assumeQuality(name):
|
||||||
return Quality.UNKNOWN
|
if name.lower().endswith(".mp3"):
|
||||||
|
return Quality.MP3
|
||||||
@staticmethod
|
elif name.lower().endswith(".flac"):
|
||||||
def compositeStatus(status, quality):
|
return Quality.LOSSLESS
|
||||||
return status + 100 * quality
|
else:
|
||||||
|
return Quality.UNKNOWN
|
||||||
@staticmethod
|
|
||||||
def qualityDownloaded(status):
|
@staticmethod
|
||||||
return (status - DOWNLOADED) / 100
|
def compositeStatus(status, quality):
|
||||||
|
return status + 100 * quality
|
||||||
@staticmethod
|
|
||||||
def splitCompositeStatus(status):
|
@staticmethod
|
||||||
"""Returns a tuple containing (status, quality)"""
|
def qualityDownloaded(status):
|
||||||
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
|
return (status - DOWNLOADED) / 100
|
||||||
if status > x*100:
|
|
||||||
return (status-x*100, x)
|
@staticmethod
|
||||||
|
def splitCompositeStatus(status):
|
||||||
return (Quality.NONE, status)
|
"""Returns a tuple containing (status, quality)"""
|
||||||
|
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
|
||||||
@staticmethod
|
if status > x * 100:
|
||||||
def statusFromName(name, assume=True):
|
return (status - x * 100, x)
|
||||||
quality = Quality.nameQuality(name)
|
|
||||||
if assume and quality == Quality.UNKNOWN:
|
return (Quality.NONE, status)
|
||||||
quality = Quality.assumeQuality(name)
|
|
||||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
@staticmethod
|
||||||
|
def statusFromName(name, assume=True):
|
||||||
DOWNLOADED = None
|
quality = Quality.nameQuality(name)
|
||||||
SNATCHED = None
|
if assume and quality == Quality.UNKNOWN:
|
||||||
SNATCHED_PROPER = None
|
quality = Quality.assumeQuality(name)
|
||||||
|
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||||
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
|
|
||||||
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
|
DOWNLOADED = None
|
||||||
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
|
SNATCHED = None
|
||||||
|
SNATCHED_PROPER = None
|
||||||
MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], [])
|
|
||||||
LOSSLESS = Quality.combineQualities([Quality.FLAC], [])
|
|
||||||
ANY = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR, Quality.FLAC], [])
|
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
|
||||||
|
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
|
||||||
qualityPresets = (MP3, LOSSLESS, ANY)
|
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in
|
||||||
qualityPresetStrings = {MP3: "MP3 (All bitrates 192+)",
|
Quality.qualityStrings.keys()]
|
||||||
LOSSLESS: "Lossless (flac)",
|
|
||||||
ANY: "Any"}
|
MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], [])
|
||||||
|
LOSSLESS = Quality.combineQualities([Quality.FLAC], [])
|
||||||
|
ANY = Quality.combineQualities(
|
||||||
|
[Quality.B192, Quality.B256, Quality.B320, Quality.VBR, Quality.FLAC], [])
|
||||||
|
|
||||||
|
qualityPresets = (MP3, LOSSLESS, ANY)
|
||||||
|
qualityPresetStrings = {MP3: "MP3 (All bitrates 192+)",
|
||||||
|
LOSSLESS: "Lossless (flac)",
|
||||||
|
ANY: "Any"}
|
||||||
|
|||||||
@@ -0,0 +1,518 @@
|
|||||||
|
import itertools
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import headphones.logger
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
|
||||||
|
def bool_int(value):
|
||||||
|
"""
|
||||||
|
Casts a config value into a 0 or 1
|
||||||
|
"""
|
||||||
|
if isinstance(value, basestring):
|
||||||
|
if value.lower() in ('', '0', 'false', 'f', 'no', 'n', 'off'):
|
||||||
|
value = 0
|
||||||
|
return int(bool(value))
|
||||||
|
|
||||||
|
|
||||||
|
class path(str):
|
||||||
|
"""Internal 'marker' type for paths in config."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __call__(val):
|
||||||
|
return path(val)
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kw):
|
||||||
|
hstr = str.__new__(cls, *args, **kw)
|
||||||
|
return hstr
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'headphones.config.path(%s)' % self
|
||||||
|
|
||||||
|
_CONFIG_DEFINITIONS = {
|
||||||
|
'ADD_ALBUM_ART': (int, 'General', 0),
|
||||||
|
'ADVANCEDENCODER': (str, 'General', ''),
|
||||||
|
'ALBUM_ART_FORMAT': (str, 'General', 'folder'),
|
||||||
|
# This is used in importer.py to determine how complete an album needs to
|
||||||
|
# be - to be considered "downloaded". Percentage from 0-100
|
||||||
|
'ALBUM_COMPLETION_PCT': (int, 'Advanced', 80),
|
||||||
|
'API_ENABLED': (int, 'General', 0),
|
||||||
|
'API_KEY': (str, 'General', ''),
|
||||||
|
'AUTOWANT_ALL': (int, 'General', 0),
|
||||||
|
'AUTOWANT_MANUALLY_ADDED': (int, 'General', 1),
|
||||||
|
'AUTOWANT_UPCOMING': (int, 'General', 1),
|
||||||
|
'AUTO_ADD_ARTISTS': (int, 'General', 1),
|
||||||
|
'BITRATE': (int, 'General', 192),
|
||||||
|
'BLACKHOLE': (int, 'General', 0),
|
||||||
|
'BLACKHOLE_DIR': (path, 'General', ''),
|
||||||
|
'BOXCAR_ENABLED': (int, 'Boxcar', 0),
|
||||||
|
'BOXCAR_ONSNATCH': (int, 'Boxcar', 0),
|
||||||
|
'BOXCAR_TOKEN': (str, 'Boxcar', ''),
|
||||||
|
'CACHE_DIR': (path, 'General', ''),
|
||||||
|
'CACHE_SIZEMB': (int, 'Advanced', 32),
|
||||||
|
'CHECK_GITHUB': (int, 'General', 1),
|
||||||
|
'CHECK_GITHUB_INTERVAL': (int, 'General', 360),
|
||||||
|
'CHECK_GITHUB_ON_STARTUP': (int, 'General', 1),
|
||||||
|
'CLEANUP_FILES': (int, 'General', 0),
|
||||||
|
'CONFIG_VERSION': (str, 'General', '0'),
|
||||||
|
'CORRECT_METADATA': (int, 'General', 0),
|
||||||
|
'CUE_SPLIT': (int, 'General', 1),
|
||||||
|
'CUE_SPLIT_FLAC_PATH': (path, 'General', ''),
|
||||||
|
'CUE_SPLIT_SHNTOOL_PATH': (path, 'General', ''),
|
||||||
|
'CUSTOMAUTH': (int, 'General', 0),
|
||||||
|
'CUSTOMHOST': (str, 'General', 'localhost'),
|
||||||
|
'CUSTOMPASS': (str, 'General', ''),
|
||||||
|
'CUSTOMPORT': (int, 'General', 5000),
|
||||||
|
'CUSTOMSLEEP': (int, 'General', 1),
|
||||||
|
'CUSTOMUSER': (str, 'General', ''),
|
||||||
|
'DELETE_LOSSLESS_FILES': (int, 'General', 1),
|
||||||
|
'DELUGE_HOST': (str, 'Deluge', ''),
|
||||||
|
'DELUGE_PASSWORD': (str, 'Deluge', ''),
|
||||||
|
'DELUGE_LABEL': (str, 'Deluge', ''),
|
||||||
|
'DELUGE_DONE_DIRECTORY': (str, 'Deluge', ''),
|
||||||
|
'DELUGE_PAUSED': (int, 'Deluge', 0),
|
||||||
|
'DESTINATION_DIR': (str, 'General', ''),
|
||||||
|
'DETECT_BITRATE': (int, 'General', 0),
|
||||||
|
'DO_NOT_PROCESS_UNMATCHED': (int, 'General', 0),
|
||||||
|
'DOWNLOAD_DIR': (path, 'General', ''),
|
||||||
|
'DOWNLOAD_SCAN_INTERVAL': (int, 'General', 5),
|
||||||
|
'DOWNLOAD_TORRENT_DIR': (path, 'General', ''),
|
||||||
|
'DO_NOT_OVERRIDE_GIT_BRANCH': (int, 'General', 0),
|
||||||
|
'EMAIL_ENABLED': (int, 'Email', 0),
|
||||||
|
'EMAIL_FROM': (str, 'Email', ''),
|
||||||
|
'EMAIL_TO': (str, 'Email', ''),
|
||||||
|
'EMAIL_SMTP_SERVER': (str, 'Email', ''),
|
||||||
|
'EMAIL_SMTP_USER': (str, 'Email', ''),
|
||||||
|
'EMAIL_SMTP_PASSWORD': (str, 'Email', ''),
|
||||||
|
'EMAIL_SMTP_PORT': (int, 'Email', 25),
|
||||||
|
'EMAIL_SSL': (int, 'Email', 0),
|
||||||
|
'EMAIL_TLS': (int, 'Email', 0),
|
||||||
|
'EMAIL_ONSNATCH': (int, 'Email', 0),
|
||||||
|
'EMBED_ALBUM_ART': (int, 'General', 0),
|
||||||
|
'EMBED_LYRICS': (int, 'General', 0),
|
||||||
|
'ENABLE_HTTPS': (int, 'General', 0),
|
||||||
|
'ENCODER': (str, 'General', 'ffmpeg'),
|
||||||
|
'ENCODERFOLDER': (path, 'General', ''),
|
||||||
|
'ENCODERLOSSLESS': (int, 'General', 1),
|
||||||
|
'ENCODEROUTPUTFORMAT': (str, 'General', 'mp3'),
|
||||||
|
'ENCODERQUALITY': (int, 'General', 2),
|
||||||
|
'ENCODERVBRCBR': (str, 'General', 'cbr'),
|
||||||
|
'ENCODER_MULTICORE': (int, 'General', 0),
|
||||||
|
'ENCODER_MULTICORE_COUNT': (int, 'General', 0),
|
||||||
|
'ENCODER_PATH': (path, 'General', ''),
|
||||||
|
'EXTRAS': (str, 'General', ''),
|
||||||
|
'EXTRA_NEWZNABS': (list, 'Newznab', ''),
|
||||||
|
'EXTRA_TORZNABS': (list, 'Torznab', ''),
|
||||||
|
'FILE_FORMAT': (str, 'General', 'Track Artist - Album [Year] - Title'),
|
||||||
|
'FILE_PERMISSIONS': (str, 'General', '0644'),
|
||||||
|
'FILE_PERMISSIONS_ENABLED': (bool_int, 'General', True),
|
||||||
|
'FILE_UNDERSCORES': (int, 'General', 0),
|
||||||
|
'FOLDER_FORMAT': (str, 'General', 'Artist/Album [Year]'),
|
||||||
|
'FOLDER_PERMISSIONS_ENABLED': (bool_int, 'General', True),
|
||||||
|
'FOLDER_PERMISSIONS': (str, 'General', '0755'),
|
||||||
|
'FREEZE_DB': (int, 'General', 0),
|
||||||
|
'GIT_BRANCH': (str, 'General', 'master'),
|
||||||
|
'GIT_PATH': (path, 'General', ''),
|
||||||
|
'GIT_USER': (str, 'General', 'rembo10'),
|
||||||
|
'GROWL_ENABLED': (int, 'Growl', 0),
|
||||||
|
'GROWL_HOST': (str, 'Growl', ''),
|
||||||
|
'GROWL_ONSNATCH': (int, 'Growl', 0),
|
||||||
|
'GROWL_PASSWORD': (str, 'Growl', ''),
|
||||||
|
'HEADPHONES_INDEXER': (bool_int, 'General', False),
|
||||||
|
'HPPASS': (str, 'General', ''),
|
||||||
|
'HPUSER': (str, 'General', ''),
|
||||||
|
'HTTPS_CERT': (path, 'General', ''),
|
||||||
|
'HTTPS_KEY': (path, 'General', ''),
|
||||||
|
'HTTP_HOST': (str, 'General', 'localhost'),
|
||||||
|
'HTTP_PASSWORD': (str, 'General', ''),
|
||||||
|
'HTTP_PORT': (int, 'General', 8181),
|
||||||
|
'HTTP_PROXY': (int, 'General', 0),
|
||||||
|
'HTTP_ROOT': (str, 'General', '/'),
|
||||||
|
'HTTP_USERNAME': (str, 'General', ''),
|
||||||
|
'IDTAG': (int, 'Beets', 0),
|
||||||
|
'IGNORE_CLEAN_RELEASES': (int, 'General', 0),
|
||||||
|
'IGNORED_WORDS': (str, 'General', ''),
|
||||||
|
'IGNORED_FOLDERS': (list, 'Advanced', []), # path
|
||||||
|
'IGNORED_FILES': (list, 'Advanced', []), # path
|
||||||
|
'INCLUDE_EXTRAS': (int, 'General', 0),
|
||||||
|
'INTERFACE': (str, 'General', 'default'),
|
||||||
|
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
|
||||||
|
'KAT': (int, 'Kat', 0),
|
||||||
|
'KAT_PROXY_URL': (str, 'Kat', ''),
|
||||||
|
'KAT_RATIO': (str, 'Kat', ''),
|
||||||
|
'KEEP_NFO': (int, 'General', 0),
|
||||||
|
'KEEP_TORRENT_FILES': (int, 'General', 0),
|
||||||
|
'LASTFM_USERNAME': (str, 'General', ''),
|
||||||
|
'LAUNCH_BROWSER': (int, 'General', 1),
|
||||||
|
'LIBRARYSCAN': (int, 'General', 1),
|
||||||
|
'LIBRARYSCAN_INTERVAL': (int, 'General', 300),
|
||||||
|
'LMS_ENABLED': (int, 'LMS', 0),
|
||||||
|
'LMS_HOST': (str, 'LMS', ''),
|
||||||
|
'LOG_DIR': (path, 'General', ''),
|
||||||
|
'LOSSLESS_BITRATE_FROM': (int, 'General', 0),
|
||||||
|
'LOSSLESS_BITRATE_TO': (int, 'General', 0),
|
||||||
|
'LOSSLESS_DESTINATION_DIR': (path, 'General', ''),
|
||||||
|
'MB_IGNORE_AGE': (int, 'General', 365),
|
||||||
|
'MININOVA': (int, 'Mininova', 0),
|
||||||
|
'MININOVA_RATIO': (str, 'Mininova', ''),
|
||||||
|
'MIRROR': (str, 'General', 'musicbrainz.org'),
|
||||||
|
'MOVE_FILES': (int, 'General', 0),
|
||||||
|
'MPC_ENABLED': (bool_int, 'MPC', False),
|
||||||
|
'MUSIC_DIR': (path, 'General', ''),
|
||||||
|
'MUSIC_ENCODER': (int, 'General', 0),
|
||||||
|
'NEWZNAB': (int, 'Newznab', 0),
|
||||||
|
'NEWZNAB_APIKEY': (str, 'Newznab', ''),
|
||||||
|
'NEWZNAB_ENABLED': (int, 'Newznab', 1),
|
||||||
|
'NEWZNAB_HOST': (str, 'Newznab', ''),
|
||||||
|
'NMA_APIKEY': (str, 'NMA', ''),
|
||||||
|
'NMA_ENABLED': (int, 'NMA', 0),
|
||||||
|
'NMA_ONSNATCH': (int, 'NMA', 0),
|
||||||
|
'NMA_PRIORITY': (int, 'NMA', 0),
|
||||||
|
'NUMBEROFSEEDERS': (str, 'General', '10'),
|
||||||
|
'NZBGET_CATEGORY': (str, 'NZBget', ''),
|
||||||
|
'NZBGET_HOST': (str, 'NZBget', ''),
|
||||||
|
'NZBGET_PASSWORD': (str, 'NZBget', ''),
|
||||||
|
'NZBGET_PRIORITY': (int, 'NZBget', 0),
|
||||||
|
'NZBGET_USERNAME': (str, 'NZBget', 'nzbget'),
|
||||||
|
'NZBSORG': (int, 'NZBsorg', 0),
|
||||||
|
'NZBSORG_HASH': (str, 'NZBsorg', ''),
|
||||||
|
'NZBSORG_UID': (str, 'NZBsorg', ''),
|
||||||
|
'NZB_DOWNLOADER': (int, 'General', 0),
|
||||||
|
'OFFICIAL_RELEASES_ONLY': (int, 'General', 0),
|
||||||
|
'OMGWTFNZBS': (int, 'omgwtfnzbs', 0),
|
||||||
|
'OMGWTFNZBS_APIKEY': (str, 'omgwtfnzbs', ''),
|
||||||
|
'OMGWTFNZBS_UID': (str, 'omgwtfnzbs', ''),
|
||||||
|
'OPEN_MAGNET_LINKS': (int, 'General', 0), # 0: Ignore, 1: Open, 2: Convert, 3: Embed (rtorrent)
|
||||||
|
'MAGNET_LINKS': (int, 'General', 0),
|
||||||
|
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/Headphones'),
|
||||||
|
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
|
||||||
|
'OSX_NOTIFY_ONSNATCH': (int, 'OSX_Notify', 0),
|
||||||
|
'PIRATEBAY': (int, 'Piratebay', 0),
|
||||||
|
'PIRATEBAY_PROXY_URL': (str, 'Piratebay', ''),
|
||||||
|
'PIRATEBAY_RATIO': (str, 'Piratebay', ''),
|
||||||
|
'OLDPIRATEBAY': (int, 'Old Piratebay', 0),
|
||||||
|
'OLDPIRATEBAY_URL': (str, 'Old Piratebay', ''),
|
||||||
|
'OLDPIRATEBAY_RATIO': (str, 'Old Piratebay', ''),
|
||||||
|
'PLEX_CLIENT_HOST': (str, 'Plex', ''),
|
||||||
|
'PLEX_ENABLED': (int, 'Plex', 0),
|
||||||
|
'PLEX_NOTIFY': (int, 'Plex', 0),
|
||||||
|
'PLEX_PASSWORD': (str, 'Plex', ''),
|
||||||
|
'PLEX_SERVER_HOST': (str, 'Plex', ''),
|
||||||
|
'PLEX_UPDATE': (int, 'Plex', 0),
|
||||||
|
'PLEX_USERNAME': (str, 'Plex', ''),
|
||||||
|
'PLEX_TOKEN': (str, 'Plex', ''),
|
||||||
|
'PREFERRED_BITRATE': (str, 'General', ''),
|
||||||
|
'PREFERRED_BITRATE_ALLOW_LOSSLESS': (int, 'General', 0),
|
||||||
|
'PREFERRED_BITRATE_HIGH_BUFFER': (int, 'General', 0),
|
||||||
|
'PREFERRED_BITRATE_LOW_BUFFER': (int, 'General', 0),
|
||||||
|
'PREFERRED_QUALITY': (int, 'General', 0),
|
||||||
|
'PREFERRED_WORDS': (str, 'General', ''),
|
||||||
|
'PREFER_TORRENTS': (int, 'General', 0),
|
||||||
|
'PROWL_ENABLED': (int, 'Prowl', 0),
|
||||||
|
'PROWL_KEYS': (str, 'Prowl', ''),
|
||||||
|
'PROWL_ONSNATCH': (int, 'Prowl', 0),
|
||||||
|
'PROWL_PRIORITY': (int, 'Prowl', 0),
|
||||||
|
'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
|
||||||
|
'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
|
||||||
|
'PUSHALOT_ONSNATCH': (int, 'Pushalot', 0),
|
||||||
|
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
|
||||||
|
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
|
||||||
|
'PUSHBULLET_ENABLED': (int, 'PushBullet', 0),
|
||||||
|
'PUSHBULLET_ONSNATCH': (int, 'PushBullet', 0),
|
||||||
|
'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
|
||||||
|
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
|
||||||
|
'PUSHOVER_KEYS': (str, 'Pushover', ''),
|
||||||
|
'PUSHOVER_ONSNATCH': (int, 'Pushover', 0),
|
||||||
|
'PUSHOVER_PRIORITY': (int, 'Pushover', 0),
|
||||||
|
'RENAME_FILES': (int, 'General', 0),
|
||||||
|
'RENAME_UNPROCESSED': (bool_int, 'General', 1),
|
||||||
|
'RENAME_FROZEN': (bool_int, 'General', 1),
|
||||||
|
'REPLACE_EXISTING_FOLDERS': (int, 'General', 0),
|
||||||
|
'KEEP_ORIGINAL_FOLDER': (int, 'General', 0),
|
||||||
|
'REQUIRED_WORDS': (str, 'General', ''),
|
||||||
|
'RUTRACKER': (int, 'Rutracker', 0),
|
||||||
|
'RUTRACKER_PASSWORD': (str, 'Rutracker', ''),
|
||||||
|
'RUTRACKER_RATIO': (str, 'Rutracker', ''),
|
||||||
|
'RUTRACKER_USER': (str, 'Rutracker', ''),
|
||||||
|
'SAB_APIKEY': (str, 'SABnzbd', ''),
|
||||||
|
'SAB_CATEGORY': (str, 'SABnzbd', ''),
|
||||||
|
'SAB_HOST': (str, 'SABnzbd', ''),
|
||||||
|
'SAB_PASSWORD': (str, 'SABnzbd', ''),
|
||||||
|
'SAB_USERNAME': (str, 'SABnzbd', ''),
|
||||||
|
'SAMPLINGFREQUENCY': (int, 'General', 44100),
|
||||||
|
'SEARCH_INTERVAL': (int, 'General', 1440),
|
||||||
|
'SOFT_CHROOT': (path, 'General', ''),
|
||||||
|
'SONGKICK_APIKEY': (str, 'Songkick', 'nd1We7dFW2RqxPw8'),
|
||||||
|
'SONGKICK_ENABLED': (int, 'Songkick', 1),
|
||||||
|
'SONGKICK_FILTER_ENABLED': (int, 'Songkick', 0),
|
||||||
|
'SONGKICK_LOCATION': (str, 'Songkick', ''),
|
||||||
|
'STRIKE': (int, 'Strike', 0),
|
||||||
|
'STRIKE_RATIO': (str, 'Strike', ''),
|
||||||
|
'SUBSONIC_ENABLED': (int, 'Subsonic', 0),
|
||||||
|
'SUBSONIC_HOST': (str, 'Subsonic', ''),
|
||||||
|
'SUBSONIC_PASSWORD': (str, 'Subsonic', ''),
|
||||||
|
'SUBSONIC_USERNAME': (str, 'Subsonic', ''),
|
||||||
|
'SYNOINDEX_ENABLED': (int, 'Synoindex', 0),
|
||||||
|
'TELEGRAM_TOKEN': (str, 'Telegram', ''),
|
||||||
|
'TELEGRAM_USERID': (str, 'Telegram', ''),
|
||||||
|
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ONSNATCH': (int, 'Telegram', 0),
|
||||||
|
'TORRENTBLACKHOLE_DIR': (str, 'General', ''),
|
||||||
|
'TORRENT_DOWNLOADER': (int, 'General', 0),
|
||||||
|
'TORRENT_REMOVAL_INTERVAL': (int, 'General', 720),
|
||||||
|
'TORZNAB': (int, 'Torznab', 0),
|
||||||
|
'TORZNAB_APIKEY': (str, 'Torznab', ''),
|
||||||
|
'TORZNAB_ENABLED': (int, 'Torznab', 1),
|
||||||
|
'TORZNAB_HOST': (str, 'Torznab', ''),
|
||||||
|
'TRANSMISSION_HOST': (str, 'Transmission', ''),
|
||||||
|
'TRANSMISSION_PASSWORD': (str, 'Transmission', ''),
|
||||||
|
'TRANSMISSION_USERNAME': (str, 'Transmission', ''),
|
||||||
|
'TWITTER_ENABLED': (int, 'Twitter', 0),
|
||||||
|
'TWITTER_ONSNATCH': (int, 'Twitter', 0),
|
||||||
|
'TWITTER_PASSWORD': (str, 'Twitter', ''),
|
||||||
|
'TWITTER_PREFIX': (str, 'Twitter', 'Headphones'),
|
||||||
|
'TWITTER_USERNAME': (str, 'Twitter', ''),
|
||||||
|
'UPDATE_DB_INTERVAL': (int, 'General', 24),
|
||||||
|
'USENET_RETENTION': (int, 'General', '1500'),
|
||||||
|
'UTORRENT_HOST': (str, 'uTorrent', ''),
|
||||||
|
'UTORRENT_LABEL': (str, 'uTorrent', ''),
|
||||||
|
'UTORRENT_PASSWORD': (str, 'uTorrent', ''),
|
||||||
|
'UTORRENT_USERNAME': (str, 'uTorrent', ''),
|
||||||
|
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
|
||||||
|
'WAIT_UNTIL_RELEASE_DATE': (int, 'General', 0),
|
||||||
|
'WAFFLES': (int, 'Waffles', 0),
|
||||||
|
'WAFFLES_PASSKEY': (str, 'Waffles', ''),
|
||||||
|
'WAFFLES_RATIO': (str, 'Waffles', ''),
|
||||||
|
'WAFFLES_UID': (str, 'Waffles', ''),
|
||||||
|
'WHATCD': (int, 'What.cd', 0),
|
||||||
|
'WHATCD_PASSWORD': (str, 'What.cd', ''),
|
||||||
|
'WHATCD_RATIO': (str, 'What.cd', ''),
|
||||||
|
'WHATCD_USERNAME': (str, 'What.cd', ''),
|
||||||
|
'XBMC_ENABLED': (int, 'XBMC', 0),
|
||||||
|
'XBMC_HOST': (str, 'XBMC', ''),
|
||||||
|
'XBMC_NOTIFY': (int, 'XBMC', 0),
|
||||||
|
'XBMC_PASSWORD': (str, 'XBMC', ''),
|
||||||
|
'XBMC_UPDATE': (int, 'XBMC', 0),
|
||||||
|
'XBMC_USERNAME': (str, 'XBMC', ''),
|
||||||
|
'XLDPROFILE': (str, 'General', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# pylint:disable=R0902
|
||||||
|
# it might be nice to refactor for fewer instance variables
|
||||||
|
class Config(object):
|
||||||
|
""" Wraps access to particular values in a config file """
|
||||||
|
|
||||||
|
def __init__(self, config_file):
|
||||||
|
""" Initialize the config with values from a file """
|
||||||
|
self._config_file = config_file
|
||||||
|
self._config = ConfigObj(self._config_file, encoding='utf-8')
|
||||||
|
for key in _CONFIG_DEFINITIONS.keys():
|
||||||
|
self.check_setting(key)
|
||||||
|
self.ENCODER_MULTICORE_COUNT = max(0, self.ENCODER_MULTICORE_COUNT)
|
||||||
|
self._upgrade()
|
||||||
|
|
||||||
|
def _define(self, name):
|
||||||
|
key = name.upper()
|
||||||
|
ini_key = name.lower()
|
||||||
|
definition = _CONFIG_DEFINITIONS[key]
|
||||||
|
if len(definition) == 3:
|
||||||
|
definition_type, section, default = definition
|
||||||
|
elif len(definition) == 4:
|
||||||
|
definition_type, section, _, default = definition
|
||||||
|
return key, definition_type, section, ini_key, default
|
||||||
|
|
||||||
|
def check_section(self, section):
|
||||||
|
""" Check if INI section exists, if not create it """
|
||||||
|
if section not in self._config:
|
||||||
|
self._config[section] = {}
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_setting(self, key):
|
||||||
|
""" Cast any value in the config to the right type or use the default """
|
||||||
|
key, definition_type, section, ini_key, default = self._define(key)
|
||||||
|
self.check_section(section)
|
||||||
|
try:
|
||||||
|
my_val = definition_type(self._config[section][ini_key])
|
||||||
|
except Exception:
|
||||||
|
my_val = definition_type(default)
|
||||||
|
self._config[section][ini_key] = my_val
|
||||||
|
return my_val
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
""" Make a copy of the stored config and write it to the configured file """
|
||||||
|
new_config = ConfigObj(encoding="UTF-8")
|
||||||
|
new_config.filename = self._config_file
|
||||||
|
|
||||||
|
# first copy over everything from the old config, even if it is not
|
||||||
|
# correctly defined to keep from losing data
|
||||||
|
for key, subkeys in self._config.items():
|
||||||
|
if key not in new_config:
|
||||||
|
new_config[key] = {}
|
||||||
|
for subkey, value in subkeys.items():
|
||||||
|
new_config[key][subkey] = value
|
||||||
|
|
||||||
|
# next make sure that everything we expect to have defined is so
|
||||||
|
for key in _CONFIG_DEFINITIONS.keys():
|
||||||
|
key, definition_type, section, ini_key, default = self._define(key)
|
||||||
|
self.check_setting(key)
|
||||||
|
if section not in new_config:
|
||||||
|
new_config[section] = {}
|
||||||
|
new_config[section][ini_key] = self._config[section][ini_key]
|
||||||
|
|
||||||
|
# Write it to file
|
||||||
|
headphones.logger.info("Writing configuration to file")
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_config.write()
|
||||||
|
except IOError as e:
|
||||||
|
headphones.logger.error("Error writing configuration file: %s", e)
|
||||||
|
|
||||||
|
def get_extra_newznabs(self):
|
||||||
|
""" Return the extra newznab tuples """
|
||||||
|
extra_newznabs = list(
|
||||||
|
itertools.izip(*[itertools.islice(self.EXTRA_NEWZNABS, i, None, 3)
|
||||||
|
for i in range(3)])
|
||||||
|
)
|
||||||
|
return extra_newznabs
|
||||||
|
|
||||||
|
def clear_extra_newznabs(self):
|
||||||
|
""" Forget about the configured extra newznabs """
|
||||||
|
self.EXTRA_NEWZNABS = []
|
||||||
|
|
||||||
|
def add_extra_newznab(self, newznab):
|
||||||
|
""" Add a new extra newznab """
|
||||||
|
extra_newznabs = self.EXTRA_NEWZNABS
|
||||||
|
for item in newznab:
|
||||||
|
extra_newznabs.append(item)
|
||||||
|
self.EXTRA_NEWZNABS = extra_newznabs
|
||||||
|
|
||||||
|
def get_extra_torznabs(self):
|
||||||
|
""" Return the extra torznab tuples """
|
||||||
|
extra_torznabs = list(
|
||||||
|
itertools.izip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 3)
|
||||||
|
for i in range(3)])
|
||||||
|
)
|
||||||
|
return extra_torznabs
|
||||||
|
|
||||||
|
def clear_extra_torznabs(self):
|
||||||
|
""" Forget about the configured extra torznabs """
|
||||||
|
self.EXTRA_TORZNABS = []
|
||||||
|
|
||||||
|
def add_extra_torznab(self, torznab):
|
||||||
|
""" Add a new extra torznab """
|
||||||
|
extra_torznabs = self.EXTRA_TORZNABS
|
||||||
|
for item in torznab:
|
||||||
|
extra_torznabs.append(item)
|
||||||
|
self.EXTRA_TORZNABS = extra_torznabs
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
"""
|
||||||
|
Returns something from the ini unless it is a real property
|
||||||
|
of the configuration object or is not all caps.
|
||||||
|
"""
|
||||||
|
if not re.match(r'[A-Z_]+$', name):
|
||||||
|
return super(Config, self).__getattr__(name)
|
||||||
|
else:
|
||||||
|
return self.check_setting(name)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
"""
|
||||||
|
Maps all-caps properties to ini values unless they exist on the
|
||||||
|
configuration object.
|
||||||
|
"""
|
||||||
|
if not re.match(r'[A-Z_]+$', name):
|
||||||
|
super(Config, self).__setattr__(name, value)
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
key, definition_type, section, ini_key, default = self._define(name)
|
||||||
|
self._config[section][ini_key] = definition_type(value)
|
||||||
|
return self._config[section][ini_key]
|
||||||
|
|
||||||
|
def process_kwargs(self, kwargs):
|
||||||
|
"""
|
||||||
|
Given a big bunch of key value pairs, apply them to the ini.
|
||||||
|
"""
|
||||||
|
for name, value in kwargs.items():
|
||||||
|
key, definition_type, section, ini_key, default = self._define(name)
|
||||||
|
self._config[section][ini_key] = definition_type(value)
|
||||||
|
|
||||||
|
def _upgrade(self):
|
||||||
|
""" Update folder formats in the config & bump up config version """
|
||||||
|
if self.CONFIG_VERSION == '0':
|
||||||
|
from headphones.helpers import replace_all
|
||||||
|
file_values = {
|
||||||
|
'tracknumber': 'Track',
|
||||||
|
'title': 'Title',
|
||||||
|
'artist': 'Artist',
|
||||||
|
'album': 'Album',
|
||||||
|
'year': 'Year'
|
||||||
|
}
|
||||||
|
folder_values = {
|
||||||
|
'artist': 'Artist',
|
||||||
|
'album': 'Album',
|
||||||
|
'year': 'Year',
|
||||||
|
'releasetype': 'Type',
|
||||||
|
'first': 'First',
|
||||||
|
'lowerfirst': 'first'
|
||||||
|
}
|
||||||
|
self.FILE_FORMAT = replace_all(self.FILE_FORMAT, file_values)
|
||||||
|
self.FOLDER_FORMAT = replace_all(self.FOLDER_FORMAT, folder_values)
|
||||||
|
|
||||||
|
self.CONFIG_VERSION = '1'
|
||||||
|
|
||||||
|
if self.CONFIG_VERSION == '1':
|
||||||
|
from headphones.helpers import replace_all
|
||||||
|
file_values = {
|
||||||
|
'Track': '$Track',
|
||||||
|
'Title': '$Title',
|
||||||
|
'Artist': '$Artist',
|
||||||
|
'Album': '$Album',
|
||||||
|
'Year': '$Year',
|
||||||
|
'track': '$track',
|
||||||
|
'title': '$title',
|
||||||
|
'artist': '$artist',
|
||||||
|
'album': '$album',
|
||||||
|
'year': '$year'
|
||||||
|
}
|
||||||
|
folder_values = {
|
||||||
|
'Artist': '$Artist',
|
||||||
|
'Album': '$Album',
|
||||||
|
'Year': '$Year',
|
||||||
|
'Type': '$Type',
|
||||||
|
'First': '$First',
|
||||||
|
'artist': '$artist',
|
||||||
|
'album': '$album',
|
||||||
|
'year': '$year',
|
||||||
|
'type': '$type',
|
||||||
|
'first': '$first'
|
||||||
|
}
|
||||||
|
self.FILE_FORMAT = replace_all(self.FILE_FORMAT, file_values)
|
||||||
|
self.FOLDER_FORMAT = replace_all(self.FOLDER_FORMAT, folder_values)
|
||||||
|
self.CONFIG_VERSION = '2'
|
||||||
|
|
||||||
|
if self.CONFIG_VERSION == '2':
|
||||||
|
# Update the config to use direct path to the encoder rather than the encoder folder
|
||||||
|
if self.ENCODERFOLDER:
|
||||||
|
self.ENCODER_PATH = os.path.join(self.ENCODERFOLDER, self.ENCODER)
|
||||||
|
self.CONFIG_VERSION = '3'
|
||||||
|
|
||||||
|
if self.CONFIG_VERSION == '3':
|
||||||
|
# Update the BLACKHOLE option to the NZB_DOWNLOADER format
|
||||||
|
if self.BLACKHOLE:
|
||||||
|
self.NZB_DOWNLOADER = 2
|
||||||
|
self.CONFIG_VERSION = '4'
|
||||||
|
|
||||||
|
# Enable Headphones Indexer if they have a VIP account
|
||||||
|
if self.CONFIG_VERSION == '4':
|
||||||
|
if self.HPUSER and self.HPPASS:
|
||||||
|
self.HEADPHONES_INDEXER = True
|
||||||
|
self.CONFIG_VERSION = '5'
|
||||||
|
|
||||||
|
if self.CONFIG_VERSION == '5':
|
||||||
|
if self.OPEN_MAGNET_LINKS:
|
||||||
|
self.MAGNET_LINKS = 2
|
||||||
|
self.CONFIG_VERSION = '5'
|
||||||
@@ -0,0 +1,440 @@
|
|||||||
|
import mock
|
||||||
|
from mock import MagicMock
|
||||||
|
import headphones.config
|
||||||
|
import re
|
||||||
|
import unittestcompat
|
||||||
|
from unittestcompat import TestCase, TestArgs
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigApiTest(TestCase):
|
||||||
|
""" Common tests for headphones.Config
|
||||||
|
|
||||||
|
Common tests for headphones.Config This test suite guarantees, that external
|
||||||
|
API of the Config class conforms all expectations of other modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _setUpConfigMock(self, mock, sections):
|
||||||
|
# every constructor `xx = ConfigObj()` in headphones.config will return
|
||||||
|
# this mock:
|
||||||
|
self.config_mock = self.config_module_mock.return_value = mock
|
||||||
|
|
||||||
|
if sections:
|
||||||
|
mock.__contains__.side_effect = sections.__contains__
|
||||||
|
mock.__getitem__.side_effect = sections.__getitem__
|
||||||
|
mock.__setitem__.side_effect = sections.__setitem__
|
||||||
|
mock.items.side_effect = sections.items
|
||||||
|
|
||||||
|
return mock
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# patch for low-level ConfigObj for entire test class
|
||||||
|
# result - each test_* method will get one additional
|
||||||
|
# argument during testing
|
||||||
|
self.config_module_mock_patcher = mock.patch('headphones.config.ConfigObj', name='ConfigObjModuleMock')
|
||||||
|
self.config_module_mock = self.config_module_mock_patcher.start()
|
||||||
|
|
||||||
|
existing_sections = {'General': {}, 'Email': {}}
|
||||||
|
# every constructor `xx = ConfigObj()` in headphones.config will return
|
||||||
|
# this mock:
|
||||||
|
self._setUpConfigMock(MagicMock(), existing_sections)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.config_module_mock_patcher.stop()
|
||||||
|
|
||||||
|
def test_constructor(self):
|
||||||
|
""" Config : creating """
|
||||||
|
|
||||||
|
cf = headphones.config.Config('/tmp/notexist')
|
||||||
|
self.assertIsInstance(cf, headphones.config.Config)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
# this sections are explicitly added in the test body:
|
||||||
|
('General', False),
|
||||||
|
('Email', False),
|
||||||
|
|
||||||
|
# this sections will not be created nor in the test, either in the
|
||||||
|
# Config module
|
||||||
|
('some_new_section_never_defined', True),
|
||||||
|
('another_new_section_never_defined', True),
|
||||||
|
)
|
||||||
|
def test_check_section(self, section_name, expected_return):
|
||||||
|
""" Config : check_section """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.check_section(section_name)
|
||||||
|
res2 = c.check_section(section_name)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(res, expected_return)
|
||||||
|
self.assertFalse(res2)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
('api_enabled', 0, int),
|
||||||
|
('Api_Key', '', str),
|
||||||
|
)
|
||||||
|
def test_check_setting(self, setting_name, expected_return, expected_instance):
|
||||||
|
""" Config: check_setting , basic cases """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.check_setting(setting_name)
|
||||||
|
res2 = c.check_setting(setting_name)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertIsInstance(res, expected_instance)
|
||||||
|
self.assertEqual(res, expected_return)
|
||||||
|
self.assertEqual(res, res2)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
(''),
|
||||||
|
('This_IsNew_Name'),
|
||||||
|
)
|
||||||
|
def test_check_setting_raise_on_unknown_settings(self, setting_name):
|
||||||
|
""" Config: check_setting should raise on unknown """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
exc_regex = re.compile(setting_name, re.IGNORECASE)
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
# assertions:
|
||||||
|
with self.assertRaisesRegexp(KeyError, exc_regex):
|
||||||
|
c.check_setting(setting_name)
|
||||||
|
pass
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
(None)
|
||||||
|
)
|
||||||
|
def test_check_setting_raise_on_none(self, setting_name):
|
||||||
|
""" Config: check_setting shoud raise on None name """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
# assertions:
|
||||||
|
with self.assertRaises(AttributeError):
|
||||||
|
c.check_setting(setting_name)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_write(self):
|
||||||
|
""" Config : write """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# overload mocks, defined in setUp:
|
||||||
|
old_conf_mock = self._setUpConfigMock(MagicMock(), {'a': {}})
|
||||||
|
|
||||||
|
option_name_not_from_definitions = 'some_invalid_option_with_super_uniq1_name'
|
||||||
|
option_name_not_from_definitions_value = 1
|
||||||
|
old_conf_mock['asdf'] = {option_name_not_from_definitions: option_name_not_from_definitions_value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
cf = headphones.config.Config(path)
|
||||||
|
|
||||||
|
# overload mock-patching for NEW CONFIG
|
||||||
|
new_patcher = mock.patch('headphones.config.ConfigObj', name='NEW_ConfigObjModuleMock_FOR_WRITE')
|
||||||
|
|
||||||
|
new_conf_module_mock = new_patcher.start()
|
||||||
|
new_conf_mock = \
|
||||||
|
new_conf_module_mock.return_value = \
|
||||||
|
MagicMock()
|
||||||
|
cf.write()
|
||||||
|
new_patcher.stop()
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertFalse(old_conf_mock.write.called, 'write not called for old config')
|
||||||
|
self.assertTrue(new_conf_mock.write.called, 'write called for new config')
|
||||||
|
self.assertEqual(new_conf_mock.filename, path)
|
||||||
|
|
||||||
|
new_conf_mock['General'].__setitem__.assert_any_call('download_dir', '')
|
||||||
|
# from 3.5... new_conf_mock['asdf'].__setitem__.assert_not_called('download_dir', '')
|
||||||
|
new_conf_mock['asdf'].__setitem__.assert_any_call(option_name_not_from_definitions, option_name_not_from_definitions_value)
|
||||||
|
|
||||||
|
@unittestcompat.skip("process_kwargs should be removed")
|
||||||
|
def test_process_kwargs(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# GET ATTR
|
||||||
|
# ===========================================================
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
('ADD_ALBUM_ART', True),
|
||||||
|
('ALBUM_ART_FORMAT', 'shmolder'),
|
||||||
|
('API_ENABLED', 1),
|
||||||
|
('API_KEY', 'Hello'),
|
||||||
|
)
|
||||||
|
def test__getattr__ConfValues(self, name, value):
|
||||||
|
""" Config: __getattr__ with setting value explicit """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
self.config_mock["General"] = {name.lower(): value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
act = c.__getattr__(name)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(act, value)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
('ADD_ALBUM_ART', 0),
|
||||||
|
('ALBUM_ART_FORMAT', 'folder'),
|
||||||
|
('API_ENABLED', 0),
|
||||||
|
('API_KEY', ''),
|
||||||
|
)
|
||||||
|
def test__getattr__ConfValuesDefault(self, name, value):
|
||||||
|
""" Config: __getattr__ from config(by braces), default values """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.__getattr__(name)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(res, value)
|
||||||
|
|
||||||
|
def test__getattr__ConfValuesDefaultUsingDotNotation(self):
|
||||||
|
""" Config: __getattr__ from config (by dot), default values """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(c.ALBUM_ART_FORMAT, 'folder')
|
||||||
|
self.assertEqual(c.API_ENABLED, 0)
|
||||||
|
self.assertEqual(c.API_KEY, '')
|
||||||
|
|
||||||
|
def test__getattr__OwnAttributes(self):
|
||||||
|
""" Config: __getattr__ access own attrs """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertIsNotNone(c)
|
||||||
|
self.assertIn('<headphones.config.Config', c.__str__())
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# SET ATTR
|
||||||
|
# ===========================================================
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
('ADD_ALBUM_ART', True),
|
||||||
|
('ALBUM_ART_FORMAT', 'shmolder'),
|
||||||
|
('API_ENABLED', 1),
|
||||||
|
('API_KEY', 'Hello'),
|
||||||
|
)
|
||||||
|
def test__setattr__ConfValuesDefault(self, name, value):
|
||||||
|
""" Config: __setattr__ with setting value explicit """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
act = c.__setattr__(name, value)
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(self.config_mock["General"][name.lower()], value)
|
||||||
|
self.assertEqual(act, value)
|
||||||
|
|
||||||
|
def test__setattr__ExplicitSetUsingDotNotation(self):
|
||||||
|
""" Config: __setattr__ with setting values using dot notation """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
act1 = c.ALBUM_ART_FORMAT = 'Apple'
|
||||||
|
act2 = c.API_ENABLED = True
|
||||||
|
act3 = c.API_KEY = 123
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(self.config_mock["General"]['album_art_format'], 'Apple')
|
||||||
|
self.assertEqual(self.config_mock["General"]['api_enabled'], 1)
|
||||||
|
self.assertEqual(self.config_mock["General"]['api_key'], '123')
|
||||||
|
|
||||||
|
self.assertEqual(act1, 'Apple')
|
||||||
|
self.assertEqual(act2, 1)
|
||||||
|
|
||||||
|
# TODO : check this trange behaviour. I have expected to see here '123', not 123.
|
||||||
|
self.assertEqual(act3, 123)
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# NEWZNABS
|
||||||
|
#
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
('', []),
|
||||||
|
('ABCDEF', [('A', 'B', 'C'), ('D', 'E', 'F')]),
|
||||||
|
(['ABC', 'DEF'], []),
|
||||||
|
([1], []),
|
||||||
|
([1, 2], []),
|
||||||
|
([1, 2, 3], [(1, 2, 3)]),
|
||||||
|
|
||||||
|
([1, 2, 3, 'Aaa'], [(1, 2, 3)]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba'], [(1, 2, 3)]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba', 'Ccccc'], [(1, 2, 3), ('Aaa', 'Bbba', 'Ccccc')]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba', 'Ccccc', 'Ddddda'], [(1, 2, 3), ('Aaa', 'Bbba', 'Ccccc')]),
|
||||||
|
)
|
||||||
|
def test_get_extra_newznabs(self, conf_value, expected):
|
||||||
|
""" Config: get_extra_newznabs """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
#itertools.izip(*[itertools.islice('', i, None, 3) for i in range(3)])
|
||||||
|
# set up mocks:
|
||||||
|
# 'EXTRA_NEWZNABS': (list, 'Newznab', ''),
|
||||||
|
# 'EXTRA_TORZNABS': (list, 'Torznab', ''),
|
||||||
|
self.config_mock["Newznab"] = {"extra_newznabs": conf_value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.get_extra_newznabs()
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(res, expected)
|
||||||
|
|
||||||
|
def test_clear_extra_newznabs(self):
|
||||||
|
""" Config: clear_extra_newznabs """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
random_value = 1827746
|
||||||
|
self.config_mock["Newznab"] = {"extra_newznabs": [1, 2, 3]}
|
||||||
|
self.config_mock["Newznab"] = {"do_not_touch": random_value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.clear_extra_newznabs()
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertIsNone(res)
|
||||||
|
self.assertEqual(self.config_mock["Newznab"]["extra_newznabs"], [])
|
||||||
|
self.assertEqual(self.config_mock["Newznab"]["do_not_touch"], random_value)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
([], [''], ['']),
|
||||||
|
([], 'ABCDEF', ['A', 'B', 'C', 'D', 'E', 'F']),
|
||||||
|
|
||||||
|
([1, 2, [False, True]], ['3', [0, 0]], [1, 2, [False, True], '3', [0, 0]]),
|
||||||
|
)
|
||||||
|
def test_add_extra_newznab(self, initial, added, expected):
|
||||||
|
""" Config: add_extra_newznab """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
self.config_mock["Newznab"] = {"extra_newznabs": initial}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
c.add_extra_newznab(added)
|
||||||
|
act = self.config_mock["Newznab"]["extra_newznabs"]
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(act, expected)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
(None),
|
||||||
|
([]),
|
||||||
|
([1, 2, 3]),
|
||||||
|
([True]),
|
||||||
|
)
|
||||||
|
def test_add_extra_newznab_raise_on_none(self, initial):
|
||||||
|
""" Config: add_extra_newznab should raise on None adding"""
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
self.config_mock["Newznab"] = {"extra_newznabs": initial}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
c.add_extra_newznab(None)
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# TORZNABS
|
||||||
|
# TODO : here is copypaste from of NEZNABS tests. Make tests better, plz refactor them
|
||||||
|
#
|
||||||
|
@TestArgs(
|
||||||
|
('', []),
|
||||||
|
('ABCDEF', [('A', 'B', 'C'), ('D', 'E', 'F')]),
|
||||||
|
(['ABC', 'DEF'], []),
|
||||||
|
([1], []),
|
||||||
|
([1, 2], []),
|
||||||
|
([1, 2, 3], [(1, 2, 3)]),
|
||||||
|
|
||||||
|
([1, 2, 3, 'Aaa'], [(1, 2, 3)]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba'], [(1, 2, 3)]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba', 'Ccccc'], [(1, 2, 3), ('Aaa', 'Bbba', 'Ccccc')]),
|
||||||
|
([1, 2, 3, 'Aaa', 'Bbba', 'Ccccc', 'Ddddda'], [(1, 2, 3), ('Aaa', 'Bbba', 'Ccccc')]),
|
||||||
|
)
|
||||||
|
def test_get_extra_torznabs(self, conf_value, expected):
|
||||||
|
""" Config: get_extra_torznabs """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
#itertools.izip(*[itertools.islice('', i, None, 3) for i in range(3)])
|
||||||
|
# set up mocks:
|
||||||
|
# 'EXTRA_TORZNABS': (list, '', ''),
|
||||||
|
self.config_mock["Torznab"] = {"extra_torznabs": conf_value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.get_extra_torznabs()
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(res, expected)
|
||||||
|
|
||||||
|
def test_clear_extra_torznabs(self):
|
||||||
|
""" Config: clear_extra_torznabs """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
random_value = -1292721
|
||||||
|
self.config_mock["Torznab"] = {"extra_torznabs": [1, 2, 3]}
|
||||||
|
self.config_mock["Torznab"] = {"do_not_touch": random_value}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
res = c.clear_extra_torznabs()
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertIsNone(res)
|
||||||
|
self.assertEqual(self.config_mock["Torznab"]["extra_torznabs"], [])
|
||||||
|
self.assertEqual(self.config_mock["Torznab"]["do_not_touch"], random_value)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
([], [''], ['']),
|
||||||
|
([], 'ABCDEF', ['A', 'B', 'C', 'D', 'E', 'F']),
|
||||||
|
|
||||||
|
([1, 2, [False, True]], ['3', [0, 0]], [1, 2, [False, True], '3', [0, 0]]),
|
||||||
|
)
|
||||||
|
def test_add_extra_torznab(self, initial, added, expected):
|
||||||
|
""" Config: add_extra_torznab """
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
self.config_mock["Torznab"] = {"extra_torznabs": initial}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
c.add_extra_torznab(added)
|
||||||
|
act = self.config_mock["Torznab"]["extra_torznabs"]
|
||||||
|
|
||||||
|
# assertions:
|
||||||
|
self.assertEqual(act, expected)
|
||||||
|
|
||||||
|
@TestArgs(
|
||||||
|
(None),
|
||||||
|
([]),
|
||||||
|
([1, 2, 3]),
|
||||||
|
([True]),
|
||||||
|
)
|
||||||
|
def test_add_extra_torznab_raise_on_none(self, initial):
|
||||||
|
""" Config: add_extra_torznab should raise on None adding"""
|
||||||
|
path = '/tmp/notexist'
|
||||||
|
|
||||||
|
self.config_mock["Torznab"] = {"extra_torznabs": initial}
|
||||||
|
|
||||||
|
# call methods
|
||||||
|
c = headphones.config.Config(path)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
c.add_extra_torznab(None)
|
||||||
|
pass
|
||||||