Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b3b8d2795 | ||
|
|
eeff731858 | ||
|
|
63abd73942 | ||
|
|
c74bec2d29 | ||
|
|
769e9c1730 | ||
|
|
33d38f33e5 | ||
|
|
36107c11d5 | ||
|
|
6bc3adbd33 | ||
|
|
583c71eca3 | ||
|
|
1e3c3c8f6c | ||
|
|
0a36166317 | ||
|
|
661f01319c | ||
|
|
6f8941d7be | ||
|
|
f7116edc9a | ||
|
|
ea69d56ab4 | ||
|
|
223e129477 | ||
|
|
4a5674a679 | ||
|
|
91c3a81cb1 | ||
|
|
e6921730c0 | ||
|
|
89ef0636a8 | ||
|
|
769ef001ec | ||
|
|
44b8ac79da | ||
|
|
5ada65b2a0 | ||
|
|
e433e7eda1 | ||
|
|
7de7dea1da | ||
|
|
159474cf9b | ||
|
|
b143a942ae | ||
|
|
dba264cc5f | ||
|
|
7ff27a42ca | ||
|
|
8e1aa18b8f | ||
|
|
99cab7304d | ||
|
|
c575b861ed | ||
|
|
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 | ||
|
|
6c4da76056 | ||
|
|
bf918e6e56 | ||
|
|
0616aae684 | ||
|
|
2d137ddcd6 | ||
|
|
94a2be275b | ||
|
|
aba4bae39e | ||
|
|
541fe5a610 | ||
|
|
f7df469cc3 | ||
|
|
648fe27604 | ||
|
|
a6532d149c | ||
|
|
966c097f76 | ||
|
|
d3d5b9dda2 | ||
|
|
a693777433 | ||
|
|
b406c3cb68 | ||
|
|
432354dcd1 | ||
|
|
496e9a0fd5 | ||
|
|
d3727d1fef | ||
|
|
a37ce75038 | ||
|
|
69d1807e9a | ||
|
|
0c32fbe706 | ||
|
|
a1ed6901d4 | ||
|
|
1f4ca0ac48 | ||
|
|
ff16c84b8b | ||
|
|
d1d45c21d4 | ||
|
|
9c14a2699e | ||
|
|
66becf2642 | ||
|
|
7240db2567 | ||
|
|
6118fea823 | ||
|
|
49e0571cb9 | ||
|
|
de78d1e737 | ||
|
|
93d4df29a6 | ||
|
|
ae7dc456ca | ||
|
|
ea1e01d417 | ||
|
|
00c545b37e | ||
|
|
f51962f645 | ||
|
|
8e5b96059c | ||
|
|
aab4461ab6 | ||
|
|
001404d5c4 | ||
|
|
9416d185e7 | ||
|
|
a43df2b275 | ||
|
|
856bed9337 | ||
|
|
3a00c7a79a | ||
|
|
fa202e3eb3 | ||
|
|
d6e3ebf41a | ||
|
|
81f9614595 | ||
|
|
a464856216 | ||
|
|
150a49fb51 | ||
|
|
8cdc48f33f | ||
|
|
0325660abe | ||
|
|
b52acfdcd0 | ||
|
|
d34455af3f | ||
|
|
06f2f1c661 | ||
|
|
7c87c534f9 | ||
|
|
22151ff4b0 | ||
|
|
a9a2fb78ed | ||
|
|
21e1c100e7 | ||
|
|
8d267febcb | ||
|
|
cd3cc7c811 | ||
|
|
686ea47449 | ||
|
|
41cb84957e | ||
|
|
1de8ace080 | ||
|
|
7d3401ff9f | ||
|
|
a7d517fb09 | ||
|
|
3dfedc305a | ||
|
|
210c23dd5d | ||
|
|
9544b0e616 | ||
|
|
55796324b6 | ||
|
|
a717594950 | ||
|
|
084bf59b6a | ||
|
|
51c32e09bd | ||
|
|
cbb15a13dd | ||
|
|
f757ce0bde | ||
|
|
667f768749 | ||
|
|
ea6e3335a3 | ||
|
|
a59ed331a8 | ||
|
|
d3c48970ae | ||
|
|
65bd0ad229 | ||
|
|
1580f5c22e | ||
|
|
da05a44187 | ||
|
|
0f619a2d43 | ||
|
|
769982cc43 | ||
|
|
30ba21c616 | ||
|
|
91b596fb0f | ||
|
|
d79dc8f1ab | ||
|
|
ed63a08333 | ||
|
|
e136a6a4e4 | ||
|
|
b02889a451 | ||
|
|
1683e4da76 | ||
|
|
d7bc0625e3 | ||
|
|
20863084da | ||
|
|
75dc68f176 | ||
|
|
6ae655ad7a | ||
|
|
eb3a0ac217 | ||
|
|
42207516b9 | ||
|
|
f90a87f943 | ||
|
|
a5480dd124 | ||
|
|
1ac7ba0892 | ||
|
|
e409859c35 | ||
|
|
2add9fa264 | ||
|
|
f3d192c548 | ||
|
|
e4032106d6 | ||
|
|
300b47d6f8 | ||
|
|
89aff6e199 | ||
|
|
bfbfb4262b | ||
|
|
79c293f32b | ||
|
|
a364c435e4 | ||
|
|
799aa5c26a | ||
|
|
d7655ec95a | ||
|
|
7761015b15 | ||
|
|
6279a95b6c | ||
|
|
1cff59fdaf | ||
|
|
864ea22a70 | ||
|
|
acad25c534 | ||
|
|
269f346e1c | ||
|
|
f03a2518ba | ||
|
|
cc28e7b4c3 | ||
|
|
41119b1d77 | ||
|
|
5da4cb7d12 | ||
|
|
933dbd205c | ||
|
|
431e24ff37 | ||
|
|
d12b6cabc9 | ||
|
|
8ccb7f2f16 | ||
|
|
dd9c21fe3f | ||
|
|
a48babfa09 | ||
|
|
e13cc15edf | ||
|
|
073f1dad3f | ||
|
|
e721f3d302 | ||
|
|
b18487f512 | ||
|
|
c1b0dc4225 | ||
|
|
31ed56bcc9 | ||
|
|
0c714a78de | ||
|
|
708f5fcd70 | ||
|
|
feb86435d8 | ||
|
|
11b5c9ced1 | ||
|
|
ad1c4851b1 | ||
|
|
5a1a6d1b57 | ||
|
|
341e2c5edb | ||
|
|
4f5a2cd3d0 | ||
|
|
2ba476c6ef | ||
|
|
9872e9a1a3 | ||
|
|
c26e867859 | ||
|
|
4607c2f28b | ||
|
|
17f1bc5344 | ||
|
|
7bb7cf9551 | ||
|
|
b51a2eb4e3 | ||
|
|
1c3132b3aa | ||
|
|
88a2d364f6 | ||
|
|
3dc515507c | ||
|
|
a1fe8f2519 | ||
|
|
f0b4f2f3c8 | ||
|
|
d2e2008f1b | ||
|
|
2f10b9bbe8 | ||
|
|
d0ae36ac56 | ||
|
|
729a105377 | ||
|
|
0bdc707fdf | ||
|
|
ca8ab32eaf | ||
|
|
6e2338184e | ||
|
|
dbe644aee2 | ||
|
|
e293302bfe | ||
|
|
7c068ca4c5 |
@@ -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 #
|
||||
###################
|
||||
@@ -6,12 +16,13 @@
|
||||
*.pyproj
|
||||
*.sln
|
||||
|
||||
# Logs and databases #
|
||||
# Headphones files #
|
||||
######################
|
||||
*.log
|
||||
*.db*
|
||||
*.db-journal
|
||||
*.ini
|
||||
version.lock
|
||||
logs/*
|
||||
cache/*
|
||||
|
||||
@@ -19,6 +30,7 @@ cache/*
|
||||
##################
|
||||
*.crt
|
||||
*.key
|
||||
*.csr
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
@@ -28,8 +40,12 @@ ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
|
||||
#ignore thumbnails created by windows
|
||||
Thumbs.db
|
||||
#Ignore files generated by PyCharm
|
||||
.idea/*
|
||||
|
||||
#Ignore files generated by vi
|
||||
*.swp
|
||||
|
||||
#Ignore files build by Visual Studio
|
||||
*.obj
|
||||
*.exe
|
||||
@@ -55,8 +71,4 @@ Thumbs.db
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
_ReSharper*/
|
||||
[Tt]est[Rr]esult*
|
||||
/cache
|
||||
/logs
|
||||
.project
|
||||
.pydevproject
|
||||
.vscode
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[pep8]
|
||||
# E111 indentation is not a multiple of four
|
||||
# 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 '# '
|
||||
# E302 expected 2 blank lines, found 1
|
||||
# E501 line too long (312 > 160 characters)
|
||||
# E502 the backslash is redundant between brackets
|
||||
ignore = E111,E121,E122,E123,E124,E125,E126,E127,E128,E261,E262,E265,E302,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,67 +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)
|
||||
@@ -0,0 +1,277 @@
|
||||
# Changelog
|
||||
|
||||
## 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,26 +14,30 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, sys, locale
|
||||
import time
|
||||
import signal
|
||||
import os
|
||||
import sys
|
||||
|
||||
from lib.configobj import ConfigObj
|
||||
|
||||
import headphones
|
||||
# Ensure lib added to path, before any other imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
||||
|
||||
from headphones import webstart, logger
|
||||
|
||||
try:
|
||||
import argparse
|
||||
except ImportError:
|
||||
import lib.argparse as argparse
|
||||
import locale
|
||||
import time
|
||||
import signal
|
||||
import argparse
|
||||
import headphones
|
||||
|
||||
# Register signals, such as CTRL + C
|
||||
signal.signal(signal.SIGINT, headphones.sig_handler)
|
||||
signal.signal(signal.SIGTERM, headphones.sig_handler)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Headphones application entry point. Parses arguments, setups encoding and
|
||||
initializes the application.
|
||||
"""
|
||||
|
||||
# Fixed paths to Headphones
|
||||
if hasattr(sys, 'frozen'):
|
||||
@@ -59,112 +63,146 @@ def main():
|
||||
headphones.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
# 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('-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
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(
|
||||
'-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
||||
parser.add_argument(
|
||||
'-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
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('--nolaunch', action='store_true', help='Prevent browser from launching on startup')
|
||||
parser.add_argument('--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||
parser.add_argument('--nolaunch', action='store_true',
|
||||
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()
|
||||
|
||||
if args.verbose:
|
||||
headphones.VERBOSE = 2
|
||||
elif args.quiet:
|
||||
headphones.VERBOSE = 0
|
||||
headphones.VERBOSE = True
|
||||
if args.quiet:
|
||||
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 sys.platform == 'win32':
|
||||
print "Daemonize not supported under Windows, starting normally"
|
||||
sys.stderr.write(
|
||||
"Daemonizing not supported under Windows, starting normally\n")
|
||||
else:
|
||||
headphones.DAEMON=True
|
||||
headphones.VERBOSE = False
|
||||
headphones.DAEMON = True
|
||||
headphones.QUIET = True
|
||||
|
||||
if 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):
|
||||
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:
|
||||
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:
|
||||
headphones.DATA_DIR = args.datadir
|
||||
else:
|
||||
headphones.DATA_DIR = headphones.PROG_DIR
|
||||
|
||||
if args.config:
|
||||
headphones.CONFIG_FILE = args.config
|
||||
config_file = args.config
|
||||
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
|
||||
if not os.path.exists(headphones.DATA_DIR):
|
||||
try:
|
||||
os.makedirs(headphones.DATA_DIR)
|
||||
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
|
||||
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
|
||||
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
|
||||
|
||||
headphones.CFG = ConfigObj(headphones.CONFIG_FILE, encoding='utf-8')
|
||||
|
||||
# Read config & start logging
|
||||
headphones.initialize()
|
||||
# Read config and start logging
|
||||
try:
|
||||
headphones.initialize(config_file)
|
||||
except headphones.exceptions.SoftChrootError as e:
|
||||
raise SystemExit('FATAL ERROR')
|
||||
|
||||
if headphones.DAEMON:
|
||||
if sys.platform == "win32":
|
||||
print "Daemonize not supported under Windows, starting normally"
|
||||
else:
|
||||
headphones.daemonize()
|
||||
headphones.daemonize()
|
||||
|
||||
#configure the connection to the musicbrainz database
|
||||
# Configure the connection to the musicbrainz database
|
||||
headphones.mb.startmb()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if 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:
|
||||
http_port = int(headphones.HTTP_PORT)
|
||||
http_port = int(headphones.CONFIG.HTTP_PORT)
|
||||
|
||||
# Try to start the server.
|
||||
webstart.initialize({
|
||||
'http_port': http_port,
|
||||
'http_host': headphones.HTTP_HOST,
|
||||
'http_root': headphones.HTTP_ROOT,
|
||||
'http_proxy': headphones.HTTP_PROXY,
|
||||
'enable_https': headphones.ENABLE_HTTPS,
|
||||
'https_cert': headphones.HTTPS_CERT,
|
||||
'https_key': headphones.HTTPS_KEY,
|
||||
'http_username': headphones.HTTP_USERNAME,
|
||||
'http_password': headphones.HTTP_PASSWORD,
|
||||
})
|
||||
# Check if pyOpenSSL is installed. It is required for certificate generation
|
||||
# and for CherryPy.
|
||||
if headphones.CONFIG.ENABLE_HTTPS:
|
||||
try:
|
||||
import OpenSSL
|
||||
except ImportError:
|
||||
logger.warn("The pyOpenSSL module is missing. Install this " \
|
||||
"module to enable HTTPS. HTTPS will be disabled.")
|
||||
headphones.CONFIG.ENABLE_HTTPS = False
|
||||
|
||||
if headphones.LAUNCH_BROWSER and not args.nolaunch:
|
||||
headphones.launch_browser(headphones.HTTP_HOST, http_port, headphones.HTTP_ROOT)
|
||||
# Try to start the server. Will exit here is address is already in use.
|
||||
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
|
||||
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:
|
||||
if not headphones.SIGNAL:
|
||||
try:
|
||||
@@ -172,7 +210,8 @@ def main():
|
||||
except KeyboardInterrupt:
|
||||
headphones.SIGNAL = 'shutdown'
|
||||
else:
|
||||
logger.info('Received signal: ' + headphones.SIGNAL)
|
||||
logger.info('Received signal: %s', headphones.SIGNAL)
|
||||
|
||||
if headphones.SIGNAL == 'shutdown':
|
||||
headphones.shutdown()
|
||||
elif headphones.SIGNAL == 'restart':
|
||||
@@ -182,7 +221,6 @@ def main():
|
||||
|
||||
headphones.SIGNAL = None
|
||||
|
||||
return
|
||||
|
||||
# Call main()
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,44 +1,67 @@
|
||||
#Headphones
|
||||
|
||||
###Installation and Notes
|
||||
|
||||
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!
|
||||
|
||||
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'.
|
||||
|
||||
For additional startup options, type 'python Headphones.py -h' or 'python Headphones.py --help'
|
||||
|
||||
###Screenshots
|
||||
|
||||
Homepage (Artist Overview)
|
||||
|
||||

|
||||
|
||||
One of the many settings pages....
|
||||
|
||||

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

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

|
||||
|
||||
Artist Search Results (also search by album!):
|
||||
|
||||

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

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

|
||||
|
||||
If you run into any issues, use the GitHub issue tracker at: http://github.com/rembo10/headphones/issues.
|
||||
|
||||
This is free software under the GPL v3 open source license - so feel free to do with it what you wish.
|
||||
## Headphones
|
||||
|
||||
**Master Branch:** [](https://travis-ci.org/rembo10/headphones)
|
||||
**Develop Branch:** [](https://travis-ci.org/rembo10/headphones)
|
||||
|
||||
Headphones is an automated music downloader for NZB and Torrent, written in Python. It supports SABnzbd, NZBget, Transmission, µTorrent and Blackhole.
|
||||
|
||||
## 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.
|
||||
|
||||
1. Use any IRC client and connect to the Freenode server, `irc.freenode.net`.
|
||||
2. Join the `#headphones` channel.
|
||||
|
||||
## Installation and Notes
|
||||
|
||||
* [Installation page](../../wiki/Installation) shows you how to install Headphones.
|
||||
* [Usage guide](../../wiki/Usage-guide) introduces you to Headphones.
|
||||
* [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:
|
||||
|
||||
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.
|
||||
|
||||
**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'.
|
||||
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).
|
||||
|
||||
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
||||
|
||||
## Screenshots
|
||||
|
||||
Homepage (Artist Overview):
|
||||
|
||||

|
||||
|
||||
One of the many settings pages:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
##############################
|
||||
### TODO LIST (18/08/12) ###
|
||||
##############################
|
||||
|
||||
1. Add ability to fetch a specific extra, rather than pull down the whole category
|
||||
- This requires extra calls to MB right now, so modify 1st mb artist call to pull down the
|
||||
whole album list, then only send the desried ones to importer.py
|
||||
2. Update API with newstyle getExtras function (lacking entirely right now)
|
||||
@@ -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 |
@@ -5,19 +5,24 @@
|
||||
%>
|
||||
|
||||
<%def name="headerIncludes()">
|
||||
|
||||
<div id="subhead_container">
|
||||
<div id="back_to_previous_link">
|
||||
<a href="artistPage?ArtistID=${album['ArtistID']}" class="back">« Back to ${album['ArtistName']}</a>
|
||||
</div>
|
||||
<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>
|
||||
%if album['Status'] == 'Skipped':
|
||||
<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>
|
||||
%if album['Status'] == 'Skipped' or album['Status'] == 'Ignored':
|
||||
<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':
|
||||
<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_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_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="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:
|
||||
<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_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_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="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
|
||||
<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 class="links">
|
||||
<%
|
||||
@@ -25,11 +30,12 @@
|
||||
%>
|
||||
%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>
|
||||
<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:
|
||||
%for alternate_album in alternate_albums:
|
||||
<%
|
||||
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']]))
|
||||
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]"
|
||||
@@ -37,42 +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]"
|
||||
|
||||
%>
|
||||
<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
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<a class="menu_link_edit" id="edit_search_term" href="#"><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">
|
||||
<form action="editSearchTerm" method="GET" id="editSearchTerm">
|
||||
<input type="hidden" name="AlbumID" value="${album['AlbumID']}">
|
||||
<div class="row">
|
||||
<%
|
||||
if not album['SearchTerm']:
|
||||
search_term = ""
|
||||
else:
|
||||
search_term = album['SearchTerm']
|
||||
|
||||
%>
|
||||
<input type="text" value="${search_term}" name="SearchTerm" size="40" />
|
||||
</div>
|
||||
<input type="button" value="Save changes" onclick="doAjaxCall('editSearchTerm',$(this),'tabs',true);return false;" data-success="Search term updated"/>
|
||||
</form>
|
||||
</div>
|
||||
<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">
|
||||
<form action="editSearchTerm" method="GET" id="editSearchTerm">
|
||||
<input type="hidden" name="AlbumID" value="${album['AlbumID']}">
|
||||
<div class="row">
|
||||
<%
|
||||
if not album['SearchTerm']:
|
||||
search_term = ""
|
||||
else:
|
||||
search_term = album['SearchTerm']
|
||||
%>
|
||||
<input type="text" value="${search_term}" name="SearchTerm" size="40" />
|
||||
</div>
|
||||
<input type="button" value="Save changes" onclick="doAjaxCall('editSearchTerm',$(this),'tabs',true);return false;" data-success="Search term updated"/>
|
||||
</form>
|
||||
</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>
|
||||
<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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="title">Title</th>
|
||||
<th id="size">Size</th>
|
||||
<th id="provider">Provider</th>
|
||||
<th id="kind">Kind</th>
|
||||
<th id="matches" title="Matches quality and/or seed settings.">Matches</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="downloads_table_body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="artistPage?ArtistID=${album['ArtistID']}" class="back">« Back to ${album['ArtistName']}</a>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
<div class="table_wrapper">
|
||||
<div id="albumheader" class="clearfix">
|
||||
<div id="albumheader" class="clearfix">
|
||||
<div id="albumImg">
|
||||
<img height="200" alt="" class="albumArt" src="artwork/album/${album['AlbumID']}">
|
||||
</div>
|
||||
|
||||
<h1><a href="http://musicbrainz.org/release-group/${album['AlbumID']}">${album['AlbumTitle']}</a></h1>
|
||||
<h2><a href="http://musicbrainz.org/artist/${album['ArtistID']}">${album['ArtistName']}</a></h2>
|
||||
|
||||
<h1 id="albumname">
|
||||
<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]
|
||||
totaltracks = len(myDB.select("SELECT TrackTitle from tracks WHERE AlbumID=?", [album['AlbumID']]))
|
||||
@@ -89,7 +115,6 @@
|
||||
<li>Duration: <span>${albumduration}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="track_wrapper">
|
||||
<table class="display" id="track_table">
|
||||
@@ -173,14 +198,20 @@
|
||||
var elem = $("#albumInfo");
|
||||
getInfo(elem,id,'album');
|
||||
}
|
||||
|
||||
|
||||
function initThisPage() {
|
||||
$('#album_chooser').click(function() {
|
||||
$('#dialog').dialog({ width: "500px" });
|
||||
$('#dialog').dialog({
|
||||
width: 500,
|
||||
maxHeight: 500
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('#edit_search_term').click(function() {
|
||||
$('#dialog2').dialog({ width: "500px" });
|
||||
$('#dialog2').dialog({
|
||||
width: 500,
|
||||
maxHeight: 500
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('#refresh_artist').click(function() {
|
||||
@@ -189,20 +220,146 @@
|
||||
initActions();
|
||||
setTimeout(function(){
|
||||
initFancybox();
|
||||
},1000);
|
||||
|
||||
}, 1000);
|
||||
|
||||
$('#track_table').dataTable({
|
||||
"aaSorting": [],
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false,
|
||||
"bDestroy": true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function getAvailableDownloads() {
|
||||
ShowSpinner();
|
||||
$.getJSON("choose_specific_download?AlbumID=${album['AlbumID']}", function(data) {
|
||||
loader.remove();
|
||||
feedback.fadeOut();
|
||||
search_results = data
|
||||
for( var i = 0, len = data.length; i < len; i++ ) {
|
||||
$('#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({
|
||||
"aoColumns": [
|
||||
null,
|
||||
{"sType": "title-numeric"},
|
||||
null,
|
||||
null,
|
||||
{"sType": "string"}
|
||||
],
|
||||
"aaSorting": [[ 4, 'desc']],
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false,
|
||||
"bDestroy": true
|
||||
});
|
||||
$("#choose_specific_download_dialog").dialog({
|
||||
width: "80%",
|
||||
maxHeight: 500
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function downloadSpecificRelease(i){
|
||||
|
||||
title = search_results[i].title
|
||||
size = search_results[i].size
|
||||
url = search_results[i].url
|
||||
provider = search_results[i].provider
|
||||
kind = search_results[i].kind
|
||||
|
||||
ShowSpinner();
|
||||
$.getJSON("download_specific_release?AlbumID=${album['AlbumID']}&title="+title+"&size="+size+"&url="+url+"&provider="+provider+"&kind=" + kind, function(data) {
|
||||
loader.remove();
|
||||
feedback.fadeOut();
|
||||
refreshSubmenu();
|
||||
$("#choose_specific_download_dialog").dialog("close");
|
||||
});
|
||||
}
|
||||
|
||||
function ShowSpinner() {
|
||||
feedback = $("#ajaxMsg");
|
||||
update = $("#updatebar");
|
||||
if ( update.is(":visible") ) {
|
||||
var height = update.height() + 35;
|
||||
feedback.css("bottom",height + "px");
|
||||
} else {
|
||||
feedback.removeAttr("style");
|
||||
}
|
||||
loader = $("<i class='fa fa-refresh fa-spin'></i>");
|
||||
feedback.prepend(loader);
|
||||
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() {
|
||||
getAlbumInfo();
|
||||
initThisPage();
|
||||
checkAlbumStatus();
|
||||
refreshInterval = setInterval(function(){
|
||||
checkAlbumStatus();
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -8,30 +8,31 @@
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<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_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':
|
||||
<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:
|
||||
<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
|
||||
%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 class="menu_link_edit" id="menu_link_modifyextra" href="#"><i class="fa fa-pencil"></i> Modify 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="javascript:void(0)"><i class="fa fa-pencil"></i> Modify Extras</a>
|
||||
%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
|
||||
<div id="dialog" title="Choose Which Extras to Fetch" style="display:none" class="configtable">
|
||||
<form action="getExtras" method="get" class="form">
|
||||
<input type="hidden" name="ArtistID" value="${artist['ArtistID']}">
|
||||
<input type="hidden" name="newstyle" value="true">
|
||||
%for extra in extras:
|
||||
<input type="checkbox" id="${extra}" name="${extra}" value="1" ${extras[extra]} />${string.capwords(extra)}<br>
|
||||
%endfor
|
||||
<br>
|
||||
<input id="submit" type="submit" value="Fetch Extras">
|
||||
</form>
|
||||
</div>
|
||||
<div id="dialog" title="Choose Which Extras to Fetch" style="display:none" class="configtable">
|
||||
<form action="getExtras" method="get" class="form">
|
||||
<input type="hidden" name="ArtistID" value="${artist['ArtistID']}">
|
||||
<input type="hidden" name="newstyle" value="true">
|
||||
%for extra in extras:
|
||||
<input type="checkbox" id="${extra}" name="${extra}" value="1" ${extras[extra]} />${string.capwords(extra)}<br>
|
||||
%endfor
|
||||
<br>
|
||||
<input id="submit" type="submit" value="Fetch Extras">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="home" class="back">« Back to overview</a>
|
||||
@@ -42,14 +43,8 @@
|
||||
<div id="artistImg">
|
||||
<img id="artistImage" class="albumArt" alt="" src="artwork/artist/${artist['ArtistID']}"/>
|
||||
</div>
|
||||
<h1>
|
||||
%if artist['Status'] == 'Loading':
|
||||
<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 id="artistname">
|
||||
<a href="http://musicbrainz.org/artist/${artist['ArtistID']}" id="artistnamelink">${artist['ArtistName']}</a>
|
||||
</h1>
|
||||
<div id="artistBio"></div>
|
||||
</div>
|
||||
@@ -62,6 +57,7 @@
|
||||
<option value="Wanted">Wanted</option>
|
||||
<option value="WantedNew">Wanted (new only)</option>
|
||||
<option value="Skipped">Skipped</option>
|
||||
<option value="Ignored">Ignored</option>
|
||||
<option value="Downloaded">Downloaded</option>
|
||||
</select>
|
||||
<input type="hidden" value="Go">
|
||||
@@ -74,6 +70,7 @@
|
||||
<th id="albumname">Name</th>
|
||||
<th id="reldate">Date</th>
|
||||
<th id="type">Type</th>
|
||||
<th id="score">Metacritic</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="have">Have</th>
|
||||
<th id="bitrate">Bitrate</th>
|
||||
@@ -89,6 +86,8 @@
|
||||
grade = 'X'
|
||||
elif album['Status'] == 'Snatched':
|
||||
grade = 'C'
|
||||
elif album['Status'] == 'Ignored':
|
||||
grade = 'I'
|
||||
else:
|
||||
grade = 'A'
|
||||
|
||||
@@ -123,22 +122,23 @@
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<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="reldate">${album['ReleaseDate']}</td>
|
||||
<td id="type">${album['Type']}</td>
|
||||
<td id="score">${album['CriticScore']}/${album['UserScore']}</td>
|
||||
<td id="status">${album['Status']}
|
||||
%if album['Status'] == 'Skipped':
|
||||
[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' added to Wanted list">want</a>]
|
||||
%if album['Status'] == 'Skipped' or album['Status'] == 'Ignored':
|
||||
[<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'):
|
||||
[<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:
|
||||
[<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
|
||||
%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'):
|
||||
[<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
|
||||
</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()">
|
||||
<link rel="stylesheet" href="interfaces/default/css/data_table.css">
|
||||
%if artist['Status'] == 'Loading':
|
||||
<meta http-equiv="refresh" content="5">
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
@@ -168,23 +165,22 @@
|
||||
getInfo(elem,id,'artist');
|
||||
}
|
||||
|
||||
|
||||
<%
|
||||
if headphones.SONGKICK_FILTER_ENABLED:
|
||||
if headphones.CONFIG.SONGKICK_FILTER_ENABLED:
|
||||
songkick_filter_enabled = "true"
|
||||
else:
|
||||
songkick_filter_enabled = "false"
|
||||
|
||||
if not headphones.SONGKICK_LOCATION:
|
||||
|
||||
if not headphones.CONFIG.SONGKICK_LOCATION:
|
||||
songkick_location = "none"
|
||||
else:
|
||||
songkick_location = headphones.SONGKICK_LOCATION
|
||||
|
||||
if headphones.SONGKICK_ENABLED:
|
||||
songkick_location = headphones.CONFIG.SONGKICK_LOCATION
|
||||
|
||||
if headphones.CONFIG.SONGKICK_ENABLED:
|
||||
songkick_enabled = "true"
|
||||
else:
|
||||
songkick_enabled = "false"
|
||||
|
||||
|
||||
%>
|
||||
function getArtistsCalendar() {
|
||||
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>';
|
||||
|
||||
$.getJSON("http://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){
|
||||
if (data['resultsPage'].totalEntries >= 1) {
|
||||
|
||||
if( ${songkick_filter_enabled} ) {
|
||||
data.resultsPage.results.event = $.grep(data.resultsPage.results.event, function(element,index){
|
||||
return element.venue.metroArea.id == ${songkick_location};
|
||||
});
|
||||
}
|
||||
|
||||
var tourDate;
|
||||
if (data['resultsPage'].totalEntries >= 1) {
|
||||
if (${songkick_filter_enabled}) {
|
||||
data.resultsPage.results.event = $.grep(data.resultsPage.results.event, function(element,index){
|
||||
return element.venue.metroArea.id == ${songkick_location};
|
||||
});
|
||||
}
|
||||
|
||||
calendarDomNode.show();
|
||||
$("#artistImg").addClass('on-tour');
|
||||
if (data.resultsPage.results.event.length > 0) {
|
||||
var 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.show();
|
||||
$("#artistImg").addClass('on-tour');
|
||||
|
||||
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>');
|
||||
|
||||
$(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(tourDate);
|
||||
});
|
||||
|
||||
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() {
|
||||
$('#menu_link_getextra').click(function() {
|
||||
$('#menu_link_getextra').click(function(event) {
|
||||
$('#dialog').dialog();
|
||||
return false;
|
||||
event.preventDefault();
|
||||
});
|
||||
$('#menu_link_modifyextra').click(function() {
|
||||
$('#menu_link_modifyextra').click(function(event) {
|
||||
$('#dialog').dialog();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
%if artist['Status'] == 'Loading':
|
||||
showMsg("Getting artist information",true);
|
||||
%endif
|
||||
$('#album_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumns": [
|
||||
@@ -258,6 +285,7 @@
|
||||
{ "sType": "date" },
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{ "sType": "title-numeric"},
|
||||
null,
|
||||
null
|
||||
@@ -265,6 +293,7 @@
|
||||
"aoColumnDefs": [
|
||||
{ 'bSortable': false, 'aTargets': [ 0,1 ] }
|
||||
],
|
||||
"bStateSave": true,
|
||||
"oLanguage": {
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
"sEmptyTable": "No album information available",
|
||||
@@ -279,7 +308,7 @@
|
||||
resetFilters("albums");
|
||||
setTimeout(function(){
|
||||
initFancybox();
|
||||
},1500)
|
||||
},1500);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
@@ -287,8 +316,12 @@
|
||||
initThisPage();
|
||||
getArtistBio();
|
||||
if( ${songkick_enabled} ){
|
||||
getArtistsCalendar();
|
||||
}
|
||||
getArtistsCalendar();
|
||||
}
|
||||
checkArtistStatus();
|
||||
setInterval(function(){
|
||||
checkArtistStatus();
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from headphones import version
|
||||
%>
|
||||
<!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 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
||||
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
||||
@@ -20,26 +19,28 @@
|
||||
|
||||
<link rel="shortcut icon" href="images/favicon.ico">
|
||||
<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/jquery-ui.css">
|
||||
<link rel="stylesheet" href="interfaces/default/css/font-awesome.min.css">
|
||||
${next.headIncludes()}
|
||||
|
||||
<script src="js/libs/modernizr-1.7.min.js"></script>
|
||||
<script src="js/libs/modernizr-2.8.3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="ajaxMsg"></div>
|
||||
% if not headphones.CURRENT_VERSION:
|
||||
<div id="updatebar">
|
||||
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
||||
<a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||
</div>
|
||||
% elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win':
|
||||
<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>
|
||||
</div>
|
||||
% endif
|
||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||
% if headphones.CONFIG.CHECK_GITHUB and not headphones.CURRENT_VERSION:
|
||||
<div id="updatebar">
|
||||
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
||||
<a href="javascript:void(0)" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||
</div>
|
||||
% 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">
|
||||
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>
|
||||
% endif
|
||||
|
||||
<header>
|
||||
<div class="wrapper">
|
||||
@@ -51,20 +52,21 @@
|
||||
<li><a href="extras">extras</a></li>
|
||||
<li><a href="manage">manage</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>
|
||||
</ul>
|
||||
<div id="searchbar">
|
||||
<form action="search" method="get">
|
||||
<input type="text" value="" placeholder="Search" onfocus="if(this.value==this.defaultValue) this.value='';" name="name" />
|
||||
<i class='fa fa-search mini-icon'></i>
|
||||
<select name="type">
|
||||
<option value="artist">Artist</option>
|
||||
<option value="album">Album</option>
|
||||
<form action="search" method="get">
|
||||
<input type="text" value="" placeholder="Search" onfocus="if(this.value==this.defaultValue) this.value='';" name="name" />
|
||||
<i class='fa fa-search mini-icon'></i>
|
||||
<select name="type" id="search_type">
|
||||
<option value="artist">Artist</option>
|
||||
<option value="album">Album</option>
|
||||
<option value="series">Series</option>
|
||||
</select>
|
||||
<input type="submit" value="Add"/>
|
||||
</form>
|
||||
</div>
|
||||
<input type="submit" value="Search"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
@@ -77,11 +79,17 @@
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<small>
|
||||
<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="#" 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>
|
||||
</div>
|
||||
<div id="version">
|
||||
@@ -89,23 +97,44 @@
|
||||
%if version.HEADPHONES_VERSION != 'master':
|
||||
(${version.HEADPHONES_VERSION})
|
||||
%endif
|
||||
%if headphones.CONFIG.GIT_BRANCH != 'master':
|
||||
(${headphones.CONFIG.GIT_BRANCH})
|
||||
%endif
|
||||
</div>
|
||||
</footer>
|
||||
<a href="#main" id="toTop"><i class="fa fa-angle-double-up"></i> <span>Back to top</span></a>
|
||||
</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/common.js"></script>
|
||||
|
||||
${next.javascriptIncludes()}
|
||||
|
||||
<script src="js/plugins.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 -->
|
||||
|
||||
<!-- 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>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Variables */
|
||||
/* Variables */
|
||||
@base-font-face: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||
@alt-font-face: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
||||
@base-font-size: 12px;
|
||||
@@ -10,20 +10,20 @@
|
||||
@msg-bg: #FFF6A9;
|
||||
@msg-bg-success: #D3FFD7;
|
||||
@msg-bg-error: #FFD3D3;
|
||||
|
||||
/* Mixins */
|
||||
.rounded(@radius: 5px) {
|
||||
-moz-border-radius: @radius;
|
||||
-webkit-border-radius: @radius;
|
||||
border-radius: @radius;
|
||||
}
|
||||
.roundedTop(@radius: 5px) {
|
||||
-moz-border-radius-topleft: @radius;
|
||||
-moz-border-radius-topright: @radius;
|
||||
-webkit-border-top-right-radius: @radius;
|
||||
-webkit-border-top-left-radius: @radius;
|
||||
border-top-left-radius: @radius;
|
||||
border-top-right-radius: @radius;
|
||||
|
||||
/* Mixins */
|
||||
.rounded(@radius: 5px) {
|
||||
-moz-border-radius: @radius;
|
||||
-webkit-border-radius: @radius;
|
||||
border-radius: @radius;
|
||||
}
|
||||
.roundedTop(@radius: 5px) {
|
||||
-moz-border-radius-topleft: @radius;
|
||||
-moz-border-radius-topright: @radius;
|
||||
-webkit-border-top-right-radius: @radius;
|
||||
-webkit-border-top-left-radius: @radius;
|
||||
border-top-left-radius: @radius;
|
||||
border-top-right-radius: @radius;
|
||||
}
|
||||
.roundedLeftTop(@radius: 5px) {
|
||||
-moz-border-radius-topleft: @radius;
|
||||
@@ -34,14 +34,14 @@
|
||||
-moz-border-radius-topright: @radius;
|
||||
-webkit-border-top-right-radius: @radius;
|
||||
border-top-right-radius: @radius;
|
||||
}
|
||||
.roundedBottom(@radius: 5px) {
|
||||
-moz-border-radius-bottomleft: @radius;
|
||||
-moz-border-radius-bottomright: @radius;
|
||||
-webkit-border-bottom-right-radius: @radius;
|
||||
-webkit-border-bottom-left-radius: @radius;
|
||||
border-bottom-left-radius: @radius;
|
||||
border-bottom-right-radius: @radius;
|
||||
}
|
||||
.roundedBottom(@radius: 5px) {
|
||||
-moz-border-radius-bottomleft: @radius;
|
||||
-moz-border-radius-bottomright: @radius;
|
||||
-webkit-border-bottom-right-radius: @radius;
|
||||
-webkit-border-bottom-left-radius: @radius;
|
||||
border-bottom-left-radius: @radius;
|
||||
border-bottom-right-radius: @radius;
|
||||
}
|
||||
.roundedLeftBottom(@radius: 5px) {
|
||||
-moz-border-radius-bottomleft: @radius;
|
||||
@@ -52,7 +52,7 @@
|
||||
-moz-border-radius-bottomright: @radius;
|
||||
-webkit-border-bottom-right-radius: @radius;
|
||||
border-bottom-right-radius: @radius;
|
||||
}
|
||||
}
|
||||
.shadow(@shadow: 0 17px 11px -1px #ced8d9) {
|
||||
-moz-box-shadow: @shadow;
|
||||
-webkit-box-shadow: @shadow;
|
||||
@@ -74,4 +74,4 @@
|
||||
-o-opacity:@opacity_percent / 100 !important;
|
||||
opacity:@opacity_percent / 100 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -224,6 +224,14 @@ table.display tr.even.gradeA {
|
||||
background-color: #ddffdd;
|
||||
}
|
||||
|
||||
table.display tr.odd.gradeI {
|
||||
background-color: #bebebe;
|
||||
}
|
||||
|
||||
table.display tr.even.gradeI {
|
||||
background-color: #bebebe;
|
||||
}
|
||||
|
||||
table.display tr.odd.gradeC {
|
||||
background-color: #ebf5ff;
|
||||
}
|
||||
@@ -254,6 +262,13 @@ table.display tr.even.gradeU {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
table.display tr.odd.gradeW {
|
||||
background-color: #ffffaa;
|
||||
}
|
||||
|
||||
table.display tr.even.gradeW {
|
||||
background-color: #ffffaa;
|
||||
}
|
||||
|
||||
table.display tr.odd.gradeZ {
|
||||
background-color: #FAFAFA;
|
||||
@@ -269,6 +284,7 @@ table.display tr.gradeL #status {
|
||||
}
|
||||
table.display tr.gradeA td,
|
||||
table.display tr.gradeC td,
|
||||
table.display tr.gradeI td,
|
||||
table.display tr.gradeX td,
|
||||
table.display tr.gradeU td,
|
||||
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;
|
||||
position: relative;
|
||||
}
|
||||
img.albumArt-nostretch {
|
||||
float: left;
|
||||
min-height: 64px;
|
||||
min-width: 64px;
|
||||
max-width: 250px;
|
||||
max-height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
.title {
|
||||
margin-bottom: 20px;
|
||||
margin-top: 10px;
|
||||
@@ -221,6 +229,17 @@ textarea,
|
||||
button {
|
||||
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 {
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -317,7 +336,7 @@ form fieldset small.heading {
|
||||
margin-top: -15px;
|
||||
}
|
||||
form .row {
|
||||
font-family: Helvetica, Arial;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
form .row label {
|
||||
@@ -326,7 +345,11 @@ form .row label {
|
||||
font-size: 12px;
|
||||
line-height: normal;
|
||||
padding-top: 7px;
|
||||
width: 175px;
|
||||
width: 170px;
|
||||
}
|
||||
form .row label.inline {
|
||||
margin-right: 5px;
|
||||
width: auto;
|
||||
}
|
||||
form .row input {
|
||||
margin-right: 5px;
|
||||
@@ -356,7 +379,7 @@ form .row small {
|
||||
display: block;
|
||||
font-size: 9px;
|
||||
line-height: 12px;
|
||||
margin-left: 175px;
|
||||
margin-left: 170px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
form .left label {
|
||||
@@ -393,6 +416,21 @@ form .checkbox small {
|
||||
margin: 0 !important;
|
||||
width: auto;
|
||||
}
|
||||
form .indent input {
|
||||
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,
|
||||
ol {
|
||||
margin-left: 2em;
|
||||
@@ -492,7 +530,7 @@ header .wrapper {
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 960px;
|
||||
width: 990px;
|
||||
}
|
||||
header #logo {
|
||||
float: left;
|
||||
@@ -534,7 +572,7 @@ footer {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
#ajaxMsg {
|
||||
.ajaxMsg {
|
||||
border: 1px solid #cccccc;
|
||||
background-image: -moz-linear-gradient(#ffffff, #eeeeee) !important;
|
||||
background-image: linear-gradient(#ffffff, #eeeeee) !important;
|
||||
@@ -565,16 +603,16 @@ footer {
|
||||
-o-opacity: 0.8 !important;
|
||||
opacity: 0.8 !important;
|
||||
}
|
||||
#ajaxMsg .msg {
|
||||
.ajaxMsg .msg {
|
||||
font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
|
||||
line-height: normal;
|
||||
padding-left: 20px;
|
||||
}
|
||||
#ajaxMsg .loader {
|
||||
.ajaxMsg .loader {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
#ajaxMsg.success {
|
||||
.ajaxMsg .success {
|
||||
background-image: -moz-linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||
background-image: linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||
background-image: -webkit-linear-gradient(#d3ffd7, #c2edc6) !important;
|
||||
@@ -584,7 +622,7 @@ footer {
|
||||
padding: 15px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
#ajaxMsg.error {
|
||||
.ajaxMsg .error {
|
||||
background-image: -moz-linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||
background-image: linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||
background-image: -webkit-linear-gradient(#ffd3d3, #edc4c4) !important;
|
||||
@@ -594,7 +632,7 @@ footer {
|
||||
padding: 15px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
#ajaxMsg .ui-icon {
|
||||
.ajaxMsg .ui-icon {
|
||||
display: inline-block;
|
||||
margin-left: -20px;
|
||||
top: 2px;
|
||||
@@ -688,7 +726,7 @@ footer {
|
||||
}
|
||||
#subhead #subhead_container #subhead_menu {
|
||||
float: right;
|
||||
margin-top: 5px;
|
||||
margin-top: 0px;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
}
|
||||
@@ -713,6 +751,11 @@ footer {
|
||||
color: #FFF;
|
||||
border-color: #518CC6 #518CC6 #2A65A0;
|
||||
}
|
||||
#subhead #subhead_container #back_to_previous_link {
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
}
|
||||
div#searchbar {
|
||||
border-left: 1px solid #FAFAFA;
|
||||
-moz-box-shadow: -1px 0 0 #e0e0e0;
|
||||
@@ -742,7 +785,7 @@ div#searchbar input[type=text] {
|
||||
line-height: normal;
|
||||
margin-right: 5px;
|
||||
padding: 4px 5px 4px 25px;
|
||||
width: 150px;
|
||||
width: 185px;
|
||||
}
|
||||
div#searchbar .mini-icon {
|
||||
color: #999;
|
||||
@@ -769,7 +812,7 @@ div#searchbar .mini-icon {
|
||||
_height: 302px;
|
||||
background-color: #FFF;
|
||||
clear: both;
|
||||
margin: 30px auto 0;
|
||||
margin: 60px auto 0;
|
||||
min-height: 100px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@@ -1051,6 +1094,22 @@ div#artistheader h2 a {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#downloads_table th#title {
|
||||
text-align: center;
|
||||
min-width: 500px;
|
||||
}
|
||||
#downloads_table th#size {
|
||||
text-align: center;
|
||||
min-width: 80px;
|
||||
}
|
||||
#downloads_table th#provider {
|
||||
text-align: center;
|
||||
min-width: 100px;
|
||||
}
|
||||
#downloads_table th#kind {
|
||||
text-align: center;
|
||||
min-width: 80px;
|
||||
}
|
||||
#history_table {
|
||||
background-color: #FFF;
|
||||
font-size: 13px;
|
||||
@@ -1094,13 +1153,34 @@ div#artistheader h2 a {
|
||||
padding: 2px 10px;
|
||||
}
|
||||
#searchresults_table th#albumname {
|
||||
min-width: 225px;
|
||||
min-width: 250px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
#searchresults_table th#artistname {
|
||||
min-width: 325px;
|
||||
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 {
|
||||
background: url("../images/loader_black.gif") no-repeat scroll center center #ffffff;
|
||||
border: 3px solid #FFFFFF;
|
||||
@@ -1112,15 +1192,40 @@ div#artistheader h2 a {
|
||||
width: 50px;
|
||||
}
|
||||
#searchresults_table td#albumname {
|
||||
min-width: 200px;
|
||||
min-width: 250px;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
}
|
||||
#searchresults_table td#artistname {
|
||||
min-width: 300px;
|
||||
text-align: left;
|
||||
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 {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1183,6 +1288,13 @@ div#artistheader h2 a {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
#info {
|
||||
float: left;
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
margin-top: -5px;
|
||||
color: #cccccc;
|
||||
}
|
||||
#actions {
|
||||
float: right;
|
||||
text-align: right;
|
||||
@@ -1212,6 +1324,7 @@ div#artistheader h2 a {
|
||||
position: relative;
|
||||
top: 7px;
|
||||
}
|
||||
#trashcan {margin-top:15px;}
|
||||
.cloudtag {
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -1321,6 +1434,22 @@ div#artistheader h2 a {
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.override-float {
|
||||
float: none !important;
|
||||
margin-bottom: 0px !important;
|
||||
clear: none !important;
|
||||
display: inline !important;
|
||||
font-size: 10px;
|
||||
line-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,
|
||||
#upcoming_table th#artistname,
|
||||
#wanted_table th#artistname {
|
||||
@@ -1346,6 +1475,7 @@ div#artistheader h2 a {
|
||||
#album_table td#albumname,
|
||||
#album_table td#reldate,
|
||||
#album_table td#type,
|
||||
#album_table td#score,
|
||||
#track_table td#duration,
|
||||
#upcoming_table td#select,
|
||||
#upcoming_table td#status,
|
||||
@@ -1356,6 +1486,7 @@ div#artistheader h2 a {
|
||||
}
|
||||
#album_table td#status,
|
||||
#album_table td#bitrate,
|
||||
#album_table td#score,
|
||||
#album_table td#albumformat,
|
||||
#album_table td#wantlossless {
|
||||
font-size: 13px;
|
||||
@@ -1374,6 +1505,11 @@ div#artistheader h3 span {
|
||||
min-width: 75px;
|
||||
text-align: center;
|
||||
}
|
||||
#searchresults_table th#scoresmall {
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
#track_table td#bitrate,
|
||||
#track_table td#format {
|
||||
font-size: 12px;
|
||||
|
||||
@@ -124,7 +124,23 @@ table {
|
||||
|
||||
|
||||
// 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;}
|
||||
input { .rounded(3px);}
|
||||
input:invalid, textarea:invalid {
|
||||
@@ -190,7 +206,12 @@ form {
|
||||
font-size: 12px;
|
||||
line-height: normal;
|
||||
padding-top: 7px;
|
||||
width: 175px;
|
||||
width: 170px;
|
||||
|
||||
&.inline {
|
||||
margin-right: 5px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
input { margin-right: 5px; }
|
||||
input[type=text], input[type=password] {
|
||||
@@ -211,7 +232,7 @@ form {
|
||||
display: block;
|
||||
font-size: 9px;
|
||||
line-height: 12px;
|
||||
margin-left: 175px;
|
||||
margin-left: 170px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
@@ -938,6 +959,13 @@ div#artistheader {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
#info {
|
||||
float: left;
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
margin-top: -5px;
|
||||
color: @border-color;
|
||||
}
|
||||
#actions {
|
||||
float: right;
|
||||
text-align: right;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<%inherit file="base.html"/>
|
||||
<%!
|
||||
from headphones import helpers
|
||||
import cgi
|
||||
%>
|
||||
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<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="#" 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="#" 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=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=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=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=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>
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
@@ -26,34 +28,45 @@
|
||||
<th id="size">Size</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="action"></th>
|
||||
<th id="delete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for item in history:
|
||||
<%
|
||||
if item['Status'] == 'Processed':
|
||||
grade = 'A'
|
||||
elif item['Status'] == 'Snatched':
|
||||
grade = 'C'
|
||||
elif item['Status'] == 'Unprocessed':
|
||||
grade = 'X'
|
||||
else:
|
||||
grade = 'U'
|
||||
|
||||
fileid = 'unknown'
|
||||
if item['URL'].find('nzb') != -1:
|
||||
fileid = 'nzb'
|
||||
if item['URL'].find('torrent') != -1:
|
||||
fileid = 'torrent'
|
||||
if item['URL'].find('rutracker') != -1:
|
||||
fileid = 'torrent'
|
||||
%>
|
||||
%for item in history:
|
||||
<%
|
||||
if item['Status'] == 'Processed':
|
||||
grade = 'A'
|
||||
elif item['Status'] == 'Snatched':
|
||||
grade = 'C'
|
||||
elif item['Status'] == 'Unprocessed':
|
||||
grade = 'X'
|
||||
elif item['Status'] == 'Frozen':
|
||||
grade = 'X'
|
||||
else:
|
||||
grade = 'U'
|
||||
|
||||
fileid = 'unknown'
|
||||
if item['URL'].find('nzb') != -1:
|
||||
fileid = 'nzb'
|
||||
if item['URL'].find('torrent') != -1:
|
||||
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}">
|
||||
<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="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 title="${folder}" id="status">${item['Status']}</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="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>
|
||||
%endfor
|
||||
</tbody>
|
||||
@@ -78,8 +91,11 @@
|
||||
"sInfoFiltered":"(filtered from _MAX_ total items)"},
|
||||
"iDisplayLength": 25,
|
||||
"sPaginationType": "full_numbers",
|
||||
"aaSorting": []
|
||||
|
||||
"aaSorting": [],
|
||||
"fnDrawCallback": function (o) {
|
||||
// Jump to top of page
|
||||
$('html,body').scrollTop(0);
|
||||
}
|
||||
});
|
||||
resetFilters("history");
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.2 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="name">Artist Name</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="album">Latest Album</th>
|
||||
<th id="album">Latest Release</th>
|
||||
<th id="have">Have</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -24,129 +24,132 @@
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
function initThisPage() {
|
||||
$('#artist_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumnDefs": [
|
||||
{
|
||||
"bSortable": false,
|
||||
"aTargets": [ 0 ],
|
||||
"mData":"ArtistID",
|
||||
"mRender": function ( data, type, full ) {
|
||||
return '<div id="artistImg"><img class="albumArt" alt="" id="'+ 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>'
|
||||
function initThisPage() {
|
||||
$('#artist_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumnDefs": [
|
||||
{
|
||||
"bSortable": false,
|
||||
"aTargets": [0],
|
||||
"mData":"ArtistID",
|
||||
"mRender": function ( data, type, full ) {
|
||||
return '<div id="artistImg"><img class="albumArt" alt="" id="'+ data + '" data-src="artwork/thumbs/artist/' + data + '"/></div>';
|
||||
}
|
||||
},
|
||||
{"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,
|
||||
"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) } )
|
||||
{
|
||||
"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": " ",
|
||||
},
|
||||
"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();
|
||||
});
|
||||
$(window).load(function(){
|
||||
initFancybox();
|
||||
refreshLoadArtist();
|
||||
});
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
$(window).load(function(){
|
||||
initFancybox();
|
||||
refreshLoadArtist();
|
||||
});
|
||||
</script>
|
||||
|
||||
</%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' ) {
|
||||
var infoURL = "getImageLinks?ArtistID=" + id;
|
||||
} else {
|
||||
var infoURL = "getImageLinks?AlbumID=" + id;
|
||||
}
|
||||
// Get Data from the cache by ID
|
||||
|
||||
// Get Data from the cache by ID
|
||||
$.ajax({
|
||||
url: infoURL,
|
||||
cache: true,
|
||||
dataType: "json",
|
||||
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";
|
||||
}
|
||||
else {
|
||||
var thumbnail = data.thumbnail;
|
||||
}
|
||||
if ( data.artwork == "" || data.artwork == undefined ) {
|
||||
if (!data.artwork) {
|
||||
var artwork = "interfaces/default/images/no-cover-artist.png";
|
||||
}
|
||||
else {
|
||||
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
|
||||
var header = $("#container header");
|
||||
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) {
|
||||
// Set Message
|
||||
feedback = $("#ajaxMsg");
|
||||
@@ -316,6 +343,10 @@ function doAjaxCall(url,elem,reload,form) {
|
||||
}
|
||||
if ( reload == "tabs") refreshTab();
|
||||
if ( reload == "page") location.reload();
|
||||
if ( reload == "submenu&table") {
|
||||
refreshSubmenu();
|
||||
refreshTable();
|
||||
}
|
||||
if ( form ) {
|
||||
// Change the option to 'choose...'
|
||||
$(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){
|
||||
if ( $(".dataTables_filter").length > 0 ) {
|
||||
$(".dataTables_filter input").attr("placeholder","filter " + text + "");
|
||||
}
|
||||
}
|
||||
|
||||
function preventDefault(){
|
||||
$("a[href='#']").live('click', function(){
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function initFancybox() {
|
||||
if ( $("a[rel=dialog]").length > 0 ) {
|
||||
$.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(){
|
||||
init();
|
||||
initHeader();
|
||||
});
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
lossless<%inherit file="base.html"/>
|
||||
<%inherit file="base.html"/>
|
||||
<%!
|
||||
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()">
|
||||
<div class="title">
|
||||
<div id="paddingheader">
|
||||
<h1 class="clearfix"><i class="fa fa-flag"></i> Logs</h1>
|
||||
</div>
|
||||
<table class="display" id="log_table">
|
||||
@@ -19,7 +28,7 @@ lossless<%inherit file="base.html"/>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<div align="center">Refresh rate:
|
||||
<div align="center">Refresh rate:
|
||||
<select id="refreshrate" onchange="setRefresh()">
|
||||
<option value="0" selected="selected">No Refresh</option>
|
||||
<option value="5">5 Seconds</option>
|
||||
@@ -29,7 +38,6 @@ lossless<%inherit file="base.html"/>
|
||||
<option value="300">5 Minutes</option>
|
||||
<option value="600">10 Minutes</option>
|
||||
</select></div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
@@ -39,46 +47,50 @@ lossless<%inherit file="base.html"/>
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#log_table').dataTable( {
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
"sAjaxSource": 'getLog',
|
||||
"sPaginationType": "full_numbers",
|
||||
"aaSorting": [[0, 'desc']],
|
||||
"iDisplayLength": 25,
|
||||
"bStateSave": true,
|
||||
"oLanguage": {
|
||||
"sSearch":"",
|
||||
"sLengthMenu":"Show _MENU_ lines per page",
|
||||
"sEmptyTable": "No log information available",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ lines",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 lines",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total lines)"},
|
||||
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
||||
if (aData[1] === "WARNING" || aData[1] === "ERROR")
|
||||
{
|
||||
$('td', nRow).closest('tr').addClass("gradeX");
|
||||
}
|
||||
else
|
||||
{
|
||||
$('td', nRow).closest('tr').addClass("gradeZ");
|
||||
}
|
||||
|
||||
|
||||
return nRow;
|
||||
},
|
||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||
/* Add some extra data to the sender */
|
||||
$.getJSON( sSource, aoData, function (json) {
|
||||
fnCallback(json)
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
$(document).ready(function() {
|
||||
initActions();
|
||||
|
||||
$('#log_table').dataTable( {
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
"sAjaxSource": 'getLog',
|
||||
"sPaginationType": "full_numbers",
|
||||
"aaSorting": [[0, 'desc']],
|
||||
"iDisplayLength": 25,
|
||||
"bStateSave": true,
|
||||
"oLanguage": {
|
||||
"sSearch":"Filter:",
|
||||
"sLengthMenu":"Show _MENU_ lines per page",
|
||||
"sEmptyTable": "No log information available",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ lines",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 lines",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total lines)"},
|
||||
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
||||
if (aData[1] === "ERROR") {
|
||||
$('td', nRow).closest('tr').addClass("gradeX");
|
||||
} else if (aData[1] === "WARNING") {
|
||||
$('td', nRow).closest('tr').addClass("gradeW");
|
||||
} else {
|
||||
$('td', nRow).closest('tr').addClass("gradeZ");
|
||||
}
|
||||
|
||||
return nRow;
|
||||
},
|
||||
"fnDrawCallback": function (o) {
|
||||
// Jump to top of page
|
||||
$('html,body').scrollTop(0);
|
||||
},
|
||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||
/* Add some extra data to the sender */
|
||||
$.getJSON(sSource, aoData, function (json) {
|
||||
fnCallback(json)
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
var timer;
|
||||
var timer;
|
||||
function setRefresh()
|
||||
{
|
||||
refreshrate = document.getElementById('refreshrate');
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<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 class="links">
|
||||
<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=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=Ignored"><i class="fa fa-meh-o fa-fw"></i> </span>Manage Ignored Albums</a><br>
|
||||
<br><br>
|
||||
<a href="manageAlbums">Manage All Albums</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
%endif
|
||||
<a class="menu_link_edit" href="manageUnmatched"><i class="fa fa-pencil"></i> Manage Unmatched</a>
|
||||
@@ -52,18 +53,18 @@
|
||||
<br/>
|
||||
<div class="row">
|
||||
<label for="">Path to directory</label>
|
||||
%if headphones.MUSIC_DIR:
|
||||
<input type="text" value="${headphones.MUSIC_DIR}" name="path" size="70" />
|
||||
%if headphones.CONFIG.MUSIC_DIR:
|
||||
<input type="text" value="${headphones.CONFIG.MUSIC_DIR}" name="path" size="70" />
|
||||
%else:
|
||||
<input type="text" value="Enter a Music Directory to scan" onfocus="if
|
||||
(this.value==this.defaultValue) this.value='';" name="path" size="70" />
|
||||
%endif
|
||||
</div>
|
||||
<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 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>
|
||||
|
||||
</fieldset>
|
||||
@@ -82,14 +83,14 @@
|
||||
<div class="row">
|
||||
<label for="">Username</label>
|
||||
<%
|
||||
if headphones.LASTFM_USERNAME:
|
||||
lastfmvalue = headphones.LASTFM_USERNAME
|
||||
if headphones.CONFIG.LASTFM_USERNAME:
|
||||
lastfmvalue = headphones.CONFIG.LASTFM_USERNAME
|
||||
else:
|
||||
lastfmvalue = ''
|
||||
%>
|
||||
<input type="text" value="${lastfmvalue}" placeholder="Last.fm username" onfocus="if
|
||||
(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>
|
||||
</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"/>
|
||||
@@ -120,11 +121,10 @@
|
||||
<fieldset>
|
||||
<legend>Force Search</legend>
|
||||
<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="#" 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('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>
|
||||
<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="#" id="delete_empty_artists"><i class="fa fa-trash-o fa-fw"></i> Delete empty Artists</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="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="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="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">
|
||||
%if emptyArtists:
|
||||
<h3>The following artists will be deleted:</h3>
|
||||
@@ -137,9 +137,27 @@
|
||||
No empty artists found.
|
||||
%endif
|
||||
</div>
|
||||
<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>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="row" id="post_process_alternate">
|
||||
<label>Force Post-Process Albums in Alternate Folder</label>
|
||||
<input type="text" value="" name="dir" id="dir" size="50" />
|
||||
<input type="button" class="btnOpenDialog" value="Submit" />
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="row" id="post_process_single">
|
||||
<label>Post-Process Single Folder</label>
|
||||
<input type="text" value="" name="album_dir" id="album_dir" size="50" />
|
||||
<input type="button" class="btnOpenDialog" value="Submit" />
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -148,9 +166,9 @@
|
||||
<legend>Force Legacy</legend>
|
||||
<p>Please note that these functions will take a significant amount of time to complete.</p>
|
||||
<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>
|
||||
<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>
|
||||
<small>*Warning: If you choose [Force Re-scan Library], any manually ignored/matched artists/albums will be reset to "unmatched".</small>
|
||||
|
||||
@@ -158,8 +176,7 @@
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="dialog-confirm"></div>
|
||||
|
||||
</div>
|
||||
</%def>
|
||||
@@ -168,6 +185,55 @@
|
||||
function addScanAction() {
|
||||
$('#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() {
|
||||
$('#manage_albums').click(function() {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<option value="WantedNew">Wanted (new only)</option>
|
||||
<option value="WantedLossless">Wanted (lossless)</option>
|
||||
<option value="Skipped">Skipped</option>
|
||||
<option value="Ignored">Ignored</option>
|
||||
<option value="Downloaded">Downloaded</option>
|
||||
</select>
|
||||
<input type="hidden" value="Go">
|
||||
@@ -50,6 +51,8 @@
|
||||
grade = 'Z'
|
||||
elif album['Status'] == 'Wanted':
|
||||
grade = 'X'
|
||||
elif album['Status'] == 'Ignored':
|
||||
grade = 'I'
|
||||
elif album['Status'] == 'Snatched':
|
||||
grade = 'C'
|
||||
else:
|
||||
@@ -109,40 +112,43 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></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({
|
||||
"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']]
|
||||
|
||||
});
|
||||
resetFilters("albums");
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
resetFilters("albums");
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -86,31 +86,31 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
function initThisPage() {
|
||||
$('#artist_table').dataTable({
|
||||
"bDestroy":true,
|
||||
"aoColumns": [
|
||||
null,
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null
|
||||
],
|
||||
"oLanguage": {
|
||||
"sSearch" : "",
|
||||
"sEmptyTable": " "
|
||||
},
|
||||
"bStateSave": true,
|
||||
"bPaginate": false
|
||||
function initThisPage() {
|
||||
$('#artist_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumns": [
|
||||
null,
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null
|
||||
],
|
||||
"oLanguage": {
|
||||
"sSearch" : "",
|
||||
"sEmptyTable": " "
|
||||
},
|
||||
"bStateSave": true,
|
||||
"bPaginate": false
|
||||
});
|
||||
resetFilters("artists");
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
$(window).load(function(){
|
||||
initFancybox();
|
||||
});
|
||||
resetFilters("artists");
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
$(window).load(function(){
|
||||
initFancybox();
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<%def name="body()">
|
||||
<div class="table_wrapper">
|
||||
<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>
|
||||
|
||||
<table class="display" id="artist_table">
|
||||
@@ -37,30 +37,30 @@
|
||||
old_album_clean = album['AlbumTitle'].replace('&','%26').replace('+', '%2B').replace("'","%27")
|
||||
%>
|
||||
<td id="artist">${album['ArtistName']}<BR>
|
||||
<button id="restore_artist${count_albums}" onClick="restore_Artist(this.id)">(<-) Restore Artist</button>
|
||||
<div id="restore_artist_dialog${count_albums}" title="Restore Artist" style="display:none">
|
||||
<button id="reset_artist${count_albums}" onClick="reset_Artist(this.id)">(<-) Reset Artist</button>
|
||||
<div id="reset_artist_dialog${count_albums}" title="Reset Artist" style="display:none">
|
||||
<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>
|
||||
%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":
|
||||
<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
|
||||
</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td id="album">${album['AlbumTitle']}<BR>
|
||||
<button id="restore_album${count_albums}" onClick="restore_Album(this.id)">(<-) Restore Album</button>
|
||||
<div id="restore_album_dialog${count_albums}" title="Restore Album" style="display:none">
|
||||
<button id="reset_album${count_albums}" onClick="reset_Album(this.id)">(<-) Reset Album</button>
|
||||
<div id="reset_album_dialog${count_albums}" title="Reset Album" style="display:none">
|
||||
<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>
|
||||
%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":
|
||||
<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
|
||||
</td></tr>
|
||||
</table>
|
||||
@@ -85,35 +85,37 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function()
|
||||
{
|
||||
$('#artist_table').dataTable(
|
||||
{
|
||||
"bStateSave": true,
|
||||
"bPaginate": true,
|
||||
"oLanguage": {
|
||||
"sSearch": "",
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||
"sEmptyTable": " ",
|
||||
},
|
||||
"sPaginationType": "full_numbers",
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$('#artist_table').dataTable({
|
||||
"bStateSave": true,
|
||||
"bPaginate": true,
|
||||
"oLanguage": {
|
||||
"sSearch": "",
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||
"sEmptyTable": " ",
|
||||
},
|
||||
"sPaginationType": "full_numbers",
|
||||
"fnDrawCallback": function (o) {
|
||||
// Jump to top of page
|
||||
$('html,body').scrollTop(0);
|
||||
}
|
||||
});
|
||||
|
||||
initActions();
|
||||
initActions();
|
||||
});
|
||||
|
||||
function restore_Artist(clicked_id) {
|
||||
n=clicked_id.replace("restore_artist","");
|
||||
$("#restore_artist_dialog"+n).dialog();
|
||||
function reset_Artist(clicked_id) {
|
||||
n=clicked_id.replace("reset_artist","");
|
||||
$("#reset_artist_dialog"+n).dialog();
|
||||
return false;
|
||||
}
|
||||
|
||||
function restore_Album(clicked_id) {
|
||||
n=clicked_id.replace("restore_album","");
|
||||
$("#restore_album_dialog"+n).dialog();
|
||||
function reset_Album(clicked_id) {
|
||||
n=clicked_id.replace("reset_album","");
|
||||
$("#reset_album_dialog"+n).dialog();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<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>
|
||||
<a href="manage" class="back">« Back to manage overview</a>
|
||||
@@ -53,18 +53,16 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function()
|
||||
{
|
||||
$('#artist_table').dataTable(
|
||||
{
|
||||
"aaSorting": [[1, 'asc']],
|
||||
"bStateSave": false,
|
||||
"bPaginate": false,
|
||||
"oLanguage": {
|
||||
"sSearch" : ""},
|
||||
|
||||
});
|
||||
initActions();
|
||||
$(document).ready(function() {
|
||||
$('#artist_table').dataTable({
|
||||
"aaSorting": [[1, 'asc']],
|
||||
"bStateSave": false,
|
||||
"bPaginate": false,
|
||||
"oLanguage": {
|
||||
"sSearch" : ""},
|
||||
});
|
||||
|
||||
initActions();
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<table>
|
||||
<tr><td>Are you sure you want to ignore Local Artist: ${album['ArtistName']} from future matching?</td></tr>
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
@@ -66,7 +66,7 @@
|
||||
</select>
|
||||
</td></tr>
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
@@ -78,7 +78,7 @@
|
||||
<table>
|
||||
<tr><td>Are you sure you want to ignore Local Album: ${album['AlbumTitle']} from future matching?</td></tr>
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
@@ -97,7 +97,7 @@
|
||||
</select>
|
||||
</td></tr>
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
@@ -118,22 +118,24 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function()
|
||||
{
|
||||
$('#artist_table').dataTable(
|
||||
{
|
||||
"bStateSave": true,
|
||||
"bPaginate": true,
|
||||
"oLanguage": {
|
||||
"sSearch": "",
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||
"sEmptyTable": " ",
|
||||
},
|
||||
"sPaginationType": "full_numbers",
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$('#artist_table').dataTable({
|
||||
"bStateSave": true,
|
||||
"bPaginate": true,
|
||||
"oLanguage": {
|
||||
"sSearch": "",
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
|
||||
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
|
||||
"sInfoFiltered":"(filtered from _MAX_ total albums)",
|
||||
"sEmptyTable": " ",
|
||||
},
|
||||
"sPaginationType": "full_numbers",
|
||||
"fnDrawCallback": function (o) {
|
||||
// Jump to top of page
|
||||
$('html,body').scrollTop(0);
|
||||
}
|
||||
});
|
||||
|
||||
initActions();
|
||||
});
|
||||
|
||||
@@ -11,36 +11,62 @@
|
||||
<th id="albumart"></th>
|
||||
%if type == 'album':
|
||||
<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
|
||||
<th id="artistname">Artist Name</th>
|
||||
<th id="score">Score</th>
|
||||
<th id="add"></th>
|
||||
<th id="mb"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%if searchresults:
|
||||
%for result in searchresults:
|
||||
<%
|
||||
if result['score'] == 100:
|
||||
grade = 'A'
|
||||
else:
|
||||
grade = 'Z'
|
||||
%>
|
||||
%for result in searchresults:
|
||||
<%
|
||||
if result['score'] == 100:
|
||||
grade = 'A'
|
||||
else:
|
||||
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}">
|
||||
%if type == 'album':
|
||||
<td id="albumart"><div id="artistImg"><img title="${result['albumid']}" class="albumArt" height="50" width="50"></div></td>
|
||||
%else:
|
||||
<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>
|
||||
%elif type == 'artist':
|
||||
<td id="albumart"><div id="artistImg"><img title="${result['id']}" class="albumArt" height="50" width="50"></div></td>
|
||||
%else:
|
||||
<td id="albumart"></td>
|
||||
%endif
|
||||
%if type == 'album':
|
||||
<td id="albumname"><a href="${result['albumurl']}">${result['title']}</a></td>
|
||||
%endif
|
||||
<td id="artistname"><a href="${result['url']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
||||
<td id="score"><div class="bar"><div class="score" style="width: ${result['score']}px">${result['score']}</div></div></td>
|
||||
%if type == 'album':
|
||||
<td id="add"><a href="addReleaseById?rid=${result['albumid']}"><i class="fa fa-plus"></i> Add this album</a></td>
|
||||
%else:
|
||||
<td id="add"><a href="addArtist?artistid=${result['id']}"><i class="fa fa-plus"></i> Add this artist</a></td>
|
||||
<td id="albumname"><a href="addReleaseById?rid=${result['albumid']}&rgid=${result['rgid']}" title="${albuminfo}">${result['title']}</a></td>
|
||||
<td id="artistnamesmall"><a href="addArtist?artistid=${result['id']}" title="${result['uniquename']}">${result['uniquename']}</a></td>
|
||||
<td id="format">${result['formats']}</td>
|
||||
<td id="tracks">${result['tracks']}</td>
|
||||
<td id="reldate">${result['date']}</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>
|
||||
<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="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
|
||||
</tr>
|
||||
%endfor
|
||||
@@ -55,28 +81,34 @@
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
|
||||
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
|
||||
<script>
|
||||
function getArt() {
|
||||
$("table#searchresults_table tr td#albumart img").each(function(){
|
||||
var id = $(this).attr('title');
|
||||
var image = $(this);
|
||||
if ( !image.hasClass('done') ) {
|
||||
image.addClass('done');
|
||||
getImageLinks(image,id,"${type}");
|
||||
}
|
||||
});
|
||||
}
|
||||
function initThisPage() {
|
||||
getArt();
|
||||
$('#searchresults_table').dataTable(
|
||||
{
|
||||
|
||||
<script type="text/javascript">
|
||||
function tryCCA(element, url) {
|
||||
element.onerror = function() {
|
||||
element.onerror = null;
|
||||
element.src = "interfaces/default/images/no-cover-art.png";
|
||||
};
|
||||
element.src = url;
|
||||
}
|
||||
function getArt() {
|
||||
$("table#searchresults_table tr td#albumart img").each(function(){
|
||||
var id = $(this).attr('title');
|
||||
var image = $(this);
|
||||
|
||||
if (!image.hasClass('done')) {
|
||||
image.addClass('done');
|
||||
getImageLinks(image, id, "${type}", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
function initThisPage() {
|
||||
$('#searchresults_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumnDefs": [
|
||||
{ 'bSortable': false, 'aTargets': [ 0,3 ] }
|
||||
],
|
||||
{ 'bSortable': false, 'aTargets': [ 0 ] }
|
||||
],
|
||||
"oLanguage": {
|
||||
"sLengthMenu":"Show _MENU_ results per page",
|
||||
"sEmptyTable": "No results",
|
||||
@@ -86,15 +118,36 @@
|
||||
"sSearch" : ""},
|
||||
"iDisplayLength": 25,
|
||||
"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");
|
||||
}
|
||||
$(document).ready(function(){
|
||||
initThisPage();
|
||||
});
|
||||
$(window).load(function(){
|
||||
initFancybox();
|
||||
});
|
||||
}
|
||||
$(document).ready(function(){
|
||||
initFancybox();
|
||||
initThisPage();
|
||||
});
|
||||
</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>
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<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>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="body()">
|
||||
|
||||
<div class="title">
|
||||
<div id="paddingheader">
|
||||
<h1 class="clearfix"><i class="fa fa-heart"></i> Wanted Albums</h1>
|
||||
</div>
|
||||
<form action="markAlbums" method="get" id="markAlbums">
|
||||
<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">
|
||||
<option disabled="disabled" selected="selected">Choose...</option>
|
||||
<option value="Skipped">Skipped</option>
|
||||
<option value="Ignored">Ignored</option>
|
||||
<option value="Downloaded">Downloaded</option>
|
||||
</select>
|
||||
<input type="hidden" value="Go">
|
||||
@@ -40,7 +40,7 @@
|
||||
%for album in wanted:
|
||||
<tr class="gradeZ">
|
||||
<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="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
||||
<td id="reldate">${album['ReleaseDate']}</td>
|
||||
@@ -49,7 +49,7 @@
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="title">
|
||||
@@ -70,7 +70,7 @@
|
||||
<tbody>
|
||||
%for album in upcoming:
|
||||
<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="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
|
||||
<td id="reldate">${album['ReleaseDate']}</td>
|
||||
@@ -88,24 +88,25 @@
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.unveil.min.js"></script>
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
function initThisPage() {
|
||||
|
||||
$('#wanted_table').dataTable({
|
||||
"oLanguage": {
|
||||
"sEmptyTable": " "
|
||||
},
|
||||
"bDestroy":true,
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false
|
||||
function initThisPage() {
|
||||
$("img").unveil();
|
||||
$('#wanted_table').dataTable({
|
||||
"oLanguage": {
|
||||
"sEmptyTable": " "
|
||||
},
|
||||
"bDestroy": true,
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false
|
||||
});
|
||||
resetFilters("artists");
|
||||
initActions();
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
resetFilters("artists");
|
||||
initActions();
|
||||
}
|
||||
$(document).ready(function() {
|
||||
initThisPage();
|
||||
});
|
||||
</script>
|
||||
</%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,39 +13,33 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib2
|
||||
from headphones import db
|
||||
from headphones import request, db, logger
|
||||
|
||||
|
||||
def getAlbumArt(albumid):
|
||||
|
||||
myDB = db.DBConnection()
|
||||
asin = myDB.action('SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0]
|
||||
|
||||
if not asin:
|
||||
return None
|
||||
|
||||
url = 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin
|
||||
|
||||
return url
|
||||
|
||||
asin = myDB.action(
|
||||
'SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0]
|
||||
|
||||
if asin:
|
||||
return 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin
|
||||
|
||||
|
||||
def getCachedArt(albumid):
|
||||
|
||||
from headphones import cache
|
||||
|
||||
|
||||
c = cache.Cache()
|
||||
|
||||
artwork_path = c.get_artwork_from_cache(AlbumID=albumid)
|
||||
|
||||
|
||||
if not artwork_path:
|
||||
return None
|
||||
|
||||
return
|
||||
|
||||
if artwork_path.startswith('http://'):
|
||||
try:
|
||||
artwork = urllib2.urlopen(artwork_path, timeout=20).read()
|
||||
return artwork
|
||||
except:
|
||||
logger.warn("Unable to open url: " + artwork_path)
|
||||
return None
|
||||
artwork = request.request_content(artwork_path, timeout=20)
|
||||
|
||||
if not artwork:
|
||||
logger.warn("Unable to open url: %s", artwork_path)
|
||||
return
|
||||
else:
|
||||
artwork = open(artwork_path, "r").read()
|
||||
return artwork
|
||||
with open(artwork_path, "r") as fp:
|
||||
return fp.read()
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#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/>.
|
||||
|
||||
import headphones
|
||||
from headphones import db, logger
|
||||
from headphones import db, logger, cache
|
||||
|
||||
|
||||
def switch(AlbumID, ReleaseID):
|
||||
'''
|
||||
"""
|
||||
Takes the contents from allalbums & alltracks (based on ReleaseID) and switches them into
|
||||
the albums & tracks table.
|
||||
'''
|
||||
"""
|
||||
logger.debug('Switching allalbums and alltracks')
|
||||
myDB = db.DBConnection()
|
||||
oldalbumdata = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone()
|
||||
newalbumdata = myDB.action('SELECT * from allalbums WHERE ReleaseID=?', [ReleaseID]).fetchone()
|
||||
newtrackdata = myDB.action('SELECT * from alltracks WHERE ReleaseID=?', [ReleaseID]).fetchall()
|
||||
oldalbumdata = myDB.action(
|
||||
'SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone()
|
||||
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])
|
||||
|
||||
controlValueDict = {"AlbumID": AlbumID}
|
||||
controlValueDict = {"AlbumID": AlbumID}
|
||||
|
||||
newValueDict = {"ArtistID": newalbumdata['ArtistID'],
|
||||
"ArtistName": newalbumdata['ArtistName'],
|
||||
"AlbumTitle": newalbumdata['AlbumTitle'],
|
||||
"ReleaseID": newalbumdata['ReleaseID'],
|
||||
"AlbumASIN": newalbumdata['AlbumASIN'],
|
||||
"ReleaseDate": newalbumdata['ReleaseDate'],
|
||||
"Type": newalbumdata['Type'],
|
||||
"ReleaseCountry": newalbumdata['ReleaseCountry'],
|
||||
"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']
|
||||
newValueDict = {"ArtistID": newalbumdata['ArtistID'],
|
||||
"ArtistName": newalbumdata['ArtistName'],
|
||||
"AlbumTitle": newalbumdata['AlbumTitle'],
|
||||
"ReleaseID": newalbumdata['ReleaseID'],
|
||||
"AlbumASIN": newalbumdata['AlbumASIN'],
|
||||
"ReleaseDate": newalbumdata['ReleaseDate'],
|
||||
"Type": newalbumdata['Type'],
|
||||
"ReleaseCountry": newalbumdata['ReleaseCountry'],
|
||||
"ReleaseFormat": newalbumdata['ReleaseFormat']
|
||||
}
|
||||
|
||||
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']}
|
||||
|
||||
newValueDict = { "TotalTracks": totaltracks,
|
||||
"HaveTracks": havetracks}
|
||||
|
||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||
|
||||
# Update cache
|
||||
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)
|
||||
|
||||
@@ -13,155 +13,181 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# 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
|
||||
|
||||
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']
|
||||
|
||||
class Api(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
|
||||
self.apikey = None
|
||||
self.cmd = None
|
||||
self.id = None
|
||||
|
||||
|
||||
self.kwargs = None
|
||||
|
||||
self.data = None
|
||||
|
||||
self.callback = None
|
||||
|
||||
|
||||
def checkParams(self,*args,**kwargs):
|
||||
|
||||
if not headphones.API_ENABLED:
|
||||
def checkParams(self, *args, **kwargs):
|
||||
|
||||
if not headphones.CONFIG.API_ENABLED:
|
||||
self.data = 'API not enabled'
|
||||
return
|
||||
if not headphones.API_KEY:
|
||||
if not headphones.CONFIG.API_KEY:
|
||||
self.data = 'API key not generated'
|
||||
return
|
||||
if len(headphones.API_KEY) != 32:
|
||||
if len(headphones.CONFIG.API_KEY) != 32:
|
||||
self.data = 'API key not generated correctly'
|
||||
return
|
||||
|
||||
|
||||
if 'apikey' not in kwargs:
|
||||
self.data = 'Missing api key'
|
||||
return
|
||||
|
||||
if kwargs['apikey'] != headphones.API_KEY:
|
||||
|
||||
if kwargs['apikey'] != headphones.CONFIG.API_KEY:
|
||||
self.data = 'Incorrect API key'
|
||||
return
|
||||
else:
|
||||
self.apikey = kwargs.pop('apikey')
|
||||
|
||||
|
||||
if 'cmd' not in kwargs:
|
||||
self.data = 'Missing parameter: cmd'
|
||||
return
|
||||
|
||||
|
||||
if kwargs['cmd'] not in cmd_list:
|
||||
self.data = 'Unknown command: %s' % kwargs['cmd']
|
||||
return
|
||||
else:
|
||||
self.cmd = kwargs.pop('cmd')
|
||||
|
||||
|
||||
self.kwargs = kwargs
|
||||
self.data = 'OK'
|
||||
|
||||
def fetchData(self):
|
||||
|
||||
if self.data == 'OK':
|
||||
logger.info('Recieved API command: ' + self.cmd)
|
||||
|
||||
if self.data == 'OK':
|
||||
logger.info('Recieved API command: %s', self.cmd)
|
||||
methodToCall = getattr(self, "_" + self.cmd)
|
||||
result = methodToCall(**self.kwargs)
|
||||
methodToCall(**self.kwargs)
|
||||
if 'callback' not in self.kwargs:
|
||||
if type(self.data) == type(''):
|
||||
if isinstance(self.data, basestring):
|
||||
return self.data
|
||||
else:
|
||||
return simplejson.dumps(self.data)
|
||||
return json.dumps(self.data)
|
||||
else:
|
||||
self.callback = self.kwargs['callback']
|
||||
self.data = simplejson.dumps(self.data)
|
||||
self.data = json.dumps(self.data)
|
||||
self.data = self.callback + '(' + self.data + ');'
|
||||
return self.data
|
||||
else:
|
||||
return self.data
|
||||
|
||||
def _dic_from_query(self,query):
|
||||
|
||||
|
||||
def _dic_from_query(self, query):
|
||||
|
||||
myDB = db.DBConnection()
|
||||
rows = myDB.select(query)
|
||||
|
||||
|
||||
rows_as_dic = []
|
||||
|
||||
|
||||
for row in rows:
|
||||
row_as_dic = dict(zip(row.keys(), row))
|
||||
rows_as_dic.append(row_as_dic)
|
||||
|
||||
|
||||
return rows_as_dic
|
||||
|
||||
|
||||
def _getIndex(self, **kwargs):
|
||||
|
||||
self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE')
|
||||
return
|
||||
|
||||
|
||||
self.data = self._dic_from_query(
|
||||
'SELECT * from artists order by ArtistSortName COLLATE NOCASE')
|
||||
return
|
||||
|
||||
def _getArtist(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"')
|
||||
albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC')
|
||||
description = self._dic_from_query('SELECT * from descriptions WHERE ArtistID="' + self.id + '"')
|
||||
|
||||
self.data = { 'artist': artist, 'albums': albums, 'description' : description }
|
||||
|
||||
artist = self._dic_from_query(
|
||||
'SELECT * from artists WHERE ArtistID="' + self.id + '"')
|
||||
albums = self._dic_from_query(
|
||||
'SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC')
|
||||
description = self._dic_from_query(
|
||||
'SELECT * from descriptions WHERE ArtistID="' + self.id + '"')
|
||||
|
||||
self.data = {
|
||||
'artist': artist, 'albums': albums, 'description': description}
|
||||
return
|
||||
|
||||
|
||||
def _getAlbum(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"')
|
||||
tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"')
|
||||
description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"')
|
||||
|
||||
self.data = { 'album' : album, 'tracks' : tracks, 'description' : description }
|
||||
|
||||
album = self._dic_from_query(
|
||||
'SELECT * from albums WHERE AlbumID="' + self.id + '"')
|
||||
tracks = self._dic_from_query(
|
||||
'SELECT * from tracks WHERE AlbumID="' + self.id + '"')
|
||||
description = self._dic_from_query(
|
||||
'SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"')
|
||||
|
||||
self.data = {
|
||||
'album': album, 'tracks': tracks, 'description': description}
|
||||
return
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def _getSnatched(self, **kwargs):
|
||||
self.data = self._dic_from_query(
|
||||
"SELECT * from albums WHERE Status='Snatched'")
|
||||
return
|
||||
|
||||
def _getSimilar(self, **kwargs):
|
||||
self.data = self._dic_from_query('SELECT * from lastfmcloud')
|
||||
return
|
||||
|
||||
|
||||
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):
|
||||
if 'name' not in kwargs:
|
||||
self.data = 'Missing parameter: name'
|
||||
@@ -169,8 +195,8 @@ class Api(object):
|
||||
if 'limit' in kwargs:
|
||||
limit = kwargs['limit']
|
||||
else:
|
||||
limit=50
|
||||
|
||||
limit = 50
|
||||
|
||||
self.data = mb.findArtist(kwargs['name'], limit)
|
||||
|
||||
def _findAlbum(self, **kwargs):
|
||||
@@ -180,212 +206,289 @@ class Api(object):
|
||||
if 'limit' in kwargs:
|
||||
limit = kwargs['limit']
|
||||
else:
|
||||
limit=50
|
||||
|
||||
limit = 50
|
||||
|
||||
self.data = mb.findRelease(kwargs['name'], limit)
|
||||
|
||||
|
||||
def _addArtist(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
try:
|
||||
importer.addArtisttoDB(self.id)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.data = e
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
def _delArtist(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
myDB = db.DBConnection()
|
||||
myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"')
|
||||
myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"')
|
||||
myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"')
|
||||
|
||||
|
||||
def _pauseArtist(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
myDB = db.DBConnection()
|
||||
controlValueDict = {'ArtistID': self.id}
|
||||
newValueDict = {'Status': 'Paused'}
|
||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||
|
||||
|
||||
def _resumeArtist(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
myDB = db.DBConnection()
|
||||
controlValueDict = {'ArtistID': self.id}
|
||||
newValueDict = {'Status': 'Active'}
|
||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||
|
||||
|
||||
def _refreshArtist(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
try:
|
||||
importer.addArtisttoDB(self.id)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.data = e
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
def _addAlbum(self, **kwargs):
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
try:
|
||||
importer.addReleaseById(self.id)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.data = e
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
def _queueAlbum(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
if 'new' in kwargs:
|
||||
new = kwargs['new']
|
||||
else:
|
||||
new = False
|
||||
|
||||
|
||||
if 'lossless' in kwargs:
|
||||
lossless = kwargs['lossless']
|
||||
else:
|
||||
lossless = False
|
||||
|
||||
|
||||
myDB = db.DBConnection()
|
||||
controlValueDict = {'AlbumID': self.id}
|
||||
if lossless:
|
||||
newValueDict = {'Status': 'Wanted Lossless'}
|
||||
else:
|
||||
else:
|
||||
newValueDict = {'Status': 'Wanted'}
|
||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||
searcher.searchforalbum(self.id, new)
|
||||
|
||||
searcher.searchforalbum(self.id, new)
|
||||
|
||||
def _unqueueAlbum(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
myDB = db.DBConnection()
|
||||
controlValueDict = {'AlbumID': self.id}
|
||||
newValueDict = {'Status': 'Skipped'}
|
||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||
|
||||
|
||||
def _forceSearch(self, **kwargs):
|
||||
searcher.searchforalbum()
|
||||
|
||||
|
||||
def _forceProcess(self, **kwargs):
|
||||
postprocessor.forcePostProcess()
|
||||
|
||||
if 'album_dir' in kwargs:
|
||||
album_dir = kwargs['album_dir']
|
||||
dir = None
|
||||
postprocessor.forcePostProcess(self, dir, album_dir)
|
||||
elif 'dir' in kwargs:
|
||||
self.dir = kwargs['dir']
|
||||
postprocessor.forcePostProcess(self.dir)
|
||||
else:
|
||||
postprocessor.forcePostProcess()
|
||||
|
||||
def _forceActiveArtistsUpdate(self, **kwargs):
|
||||
updater.dbUpdate()
|
||||
|
||||
def _getVersion(self, **kwargs):
|
||||
self.data = {
|
||||
'git_path' : headphones.GIT_PATH,
|
||||
'install_type' : headphones.INSTALL_TYPE,
|
||||
'current_version' : headphones.CURRENT_VERSION,
|
||||
'latest_version' : headphones.LATEST_VERSION,
|
||||
'commits_behind' : headphones.COMMITS_BEHIND,
|
||||
self.data = {
|
||||
'git_path': headphones.CONFIG.GIT_PATH,
|
||||
'install_type': headphones.INSTALL_TYPE,
|
||||
'current_version': headphones.CURRENT_VERSION,
|
||||
'latest_version': headphones.LATEST_VERSION,
|
||||
'commits_behind': headphones.COMMITS_BEHIND,
|
||||
}
|
||||
|
||||
|
||||
def _checkGithub(self, **kwargs):
|
||||
versioncheck.checkGithub()
|
||||
self._getVersion()
|
||||
|
||||
|
||||
def _shutdown(self, **kwargs):
|
||||
headphones.SIGNAL = 'shutdown'
|
||||
|
||||
|
||||
def _restart(self, **kwargs):
|
||||
headphones.SIGNAL = 'restart'
|
||||
|
||||
|
||||
def _update(self, **kwargs):
|
||||
headphones.SIGNAL = 'update'
|
||||
|
||||
|
||||
def _getArtistArt(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getArtwork(ArtistID=self.id)
|
||||
|
||||
def _getAlbumArt(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getArtwork(AlbumID=self.id)
|
||||
|
||||
|
||||
def _getArtistInfo(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getInfo(ArtistID=self.id)
|
||||
|
||||
|
||||
def _getAlbumInfo(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getInfo(AlbumID=self.id)
|
||||
|
||||
|
||||
def _getArtistThumb(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getThumb(ArtistID=self.id)
|
||||
|
||||
def _getAlbumThumb(self, **kwargs):
|
||||
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
|
||||
self.data = cache.getThumb(AlbumID=self.id)
|
||||
|
||||
def _choose_specific_download(self, **kwargs):
|
||||
|
||||
if 'id' not in kwargs:
|
||||
self.data = 'Missing parameter: id'
|
||||
return
|
||||
else:
|
||||
self.id = kwargs['id']
|
||||
|
||||
results = searcher.searchforalbum(
|
||||
self.id, choose_specific_download=True)
|
||||
|
||||
results_as_dicts = []
|
||||
|
||||
for result in results:
|
||||
result_dict = {
|
||||
'title': result[0],
|
||||
'size': result[1],
|
||||
'url': result[2],
|
||||
'provider': result[3],
|
||||
'kind': result[4]
|
||||
}
|
||||
results_as_dicts.append(result_dict)
|
||||
|
||||
self.data = results_as_dicts
|
||||
|
||||
def _download_specific_release(self, **kwargs):
|
||||
|
||||
expected_kwargs = ['id', 'title', 'size', 'url', 'provider', 'kind']
|
||||
|
||||
for kwarg in expected_kwargs:
|
||||
if kwarg not in kwargs:
|
||||
self.data = 'Missing parameter: ' + kwarg
|
||||
return self.data
|
||||
|
||||
title = kwargs['title']
|
||||
size = kwargs['size']
|
||||
url = kwargs['url']
|
||||
provider = kwargs['provider']
|
||||
kind = kwargs['kind']
|
||||
id = kwargs['id']
|
||||
|
||||
for kwarg in expected_kwargs:
|
||||
del kwargs[kwarg]
|
||||
|
||||
# Handle situations where the torrent url contains arguments that are
|
||||
# parsed
|
||||
if kwargs:
|
||||
import urllib
|
||||
import urllib2
|
||||
url = urllib2.quote(
|
||||
url, safe=":?/=&") + '&' + urllib.urlencode(kwargs)
|
||||
|
||||
try:
|
||||
result = [(title, int(size), url, provider, kind)]
|
||||
except ValueError:
|
||||
result = [(title, float(size), url, provider, kind)]
|
||||
|
||||
logger.info(u"Making sure we can download the chosen result")
|
||||
(data, bestqual) = searcher.preprocess(result)
|
||||
|
||||
if data and bestqual:
|
||||
myDB = db.DBConnection()
|
||||
album = myDB.action(
|
||||
'SELECT * from albums WHERE AlbumID=?', [id]).fetchone()
|
||||
searcher.send_to_downloader(data, bestqual, album)
|
||||
|
||||
@@ -14,76 +14,73 @@
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import glob, urllib, urllib2
|
||||
|
||||
import lib.simplejson as simplejson
|
||||
|
||||
import headphones
|
||||
from headphones import db, helpers, logger
|
||||
from headphones import db, helpers, logger, lastfm, request
|
||||
|
||||
LASTFM_API_KEY = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
|
||||
|
||||
lastfm_apikey = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
|
||||
|
||||
class Cache(object):
|
||||
"""
|
||||
This class deals with getting, storing and serving up artwork (album
|
||||
art, 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,
|
||||
for example, using the helper functions getInfo(id) and 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
|
||||
to the image file on the local machine, or if the cache directory
|
||||
doesn't exist, 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
|
||||
|
||||
The basic format for art in the cache is <musicbrainzid>.<date>.<ext>
|
||||
and for info it is <musicbrainzid>.<date>.txt
|
||||
"""
|
||||
|
||||
path_to_art_cache = os.path.join(headphones.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):
|
||||
|
||||
pass
|
||||
This class deals with getting, storing and serving up artwork (album art,
|
||||
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. For example, using the helper functions `getInfo(id)` and
|
||||
`getArtwork(id)`, to utilize the cached images rather than having to
|
||||
retrieve them every time the page is reloaded.
|
||||
|
||||
def _findfilesstartingwith(self,pattern,folder):
|
||||
You can call `getArtwork(id)` which will return an absolute path to the
|
||||
image file on the local machine, or if the cache directory doesn't exist,
|
||||
or can not be written to, it will return a url to the image.
|
||||
|
||||
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>`
|
||||
and for info it is `<musicbrainzid>.<date>.txt`
|
||||
"""
|
||||
|
||||
path_to_art_cache = os.path.join(headphones.CONFIG.CACHE_DIR, 'artwork')
|
||||
|
||||
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
|
||||
|
||||
self.artwork_files = []
|
||||
self.thumb_files = []
|
||||
|
||||
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 = []
|
||||
if os.path.exists(folder):
|
||||
for fname in os.listdir(folder):
|
||||
if fname.startswith(pattern):
|
||||
files.append(os.path.join(folder,fname))
|
||||
files.append(os.path.join(folder, fname))
|
||||
return files
|
||||
|
||||
|
||||
def _exists(self, type):
|
||||
self.artwork_files = []
|
||||
self.thumb_files = []
|
||||
|
||||
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:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
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:
|
||||
return True
|
||||
else:
|
||||
@@ -92,54 +89,53 @@ class Cache(object):
|
||||
def _get_age(self, date):
|
||||
# There's probably a better way to do this
|
||||
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
|
||||
|
||||
|
||||
|
||||
def _is_current(self, filename=None, date=None):
|
||||
|
||||
|
||||
if filename:
|
||||
base_filename = os.path.basename(filename)
|
||||
date = base_filename.split('.')[1]
|
||||
|
||||
|
||||
# Calculate how old the cached file is based on todays date & file date stamp
|
||||
# helpers.today() returns todays date in yyyy-mm-dd format
|
||||
if self._get_age(helpers.today()) - self._get_age(date) < 30:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def _get_thumb_url(self, data):
|
||||
|
||||
|
||||
thumb_url = None
|
||||
|
||||
|
||||
try:
|
||||
images = data[self.id_type]['image']
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
for image in images:
|
||||
if image['size'] == 'medium':
|
||||
if image['size'] == 'medium' and '#text' in image:
|
||||
thumb_url = image['#text']
|
||||
break
|
||||
|
||||
|
||||
return thumb_url
|
||||
|
||||
|
||||
def get_artwork_from_cache(self, ArtistID=None, AlbumID=None):
|
||||
'''
|
||||
"""
|
||||
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
self.query_type = 'artwork'
|
||||
|
||||
|
||||
if ArtistID:
|
||||
self.id = ArtistID
|
||||
self.id_type = 'artist'
|
||||
else:
|
||||
self.id = AlbumID
|
||||
self.id_type = 'album'
|
||||
|
||||
|
||||
if self._exists('artwork') and self._is_current(filename=self.artwork_files[0]):
|
||||
return self.artwork_files[0]
|
||||
else:
|
||||
@@ -151,21 +147,21 @@ class Cache(object):
|
||||
return self.artwork_files[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_thumb_from_cache(self, ArtistID=None, AlbumID=None):
|
||||
'''
|
||||
"""
|
||||
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
self.query_type = 'thumb'
|
||||
|
||||
|
||||
if ArtistID:
|
||||
self.id = ArtistID
|
||||
self.id_type = 'artist'
|
||||
else:
|
||||
self.id = AlbumID
|
||||
self.id_type = 'album'
|
||||
|
||||
|
||||
if self._exists('thumb') and self._is_current(filename=self.thumb_files[0]):
|
||||
return self.thumb_files[0]
|
||||
else:
|
||||
@@ -177,354 +173,328 @@ class Cache(object):
|
||||
return self.thumb_files[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_info_from_cache(self, ArtistID=None, AlbumID=None):
|
||||
|
||||
|
||||
self.query_type = 'info'
|
||||
myDB = db.DBConnection()
|
||||
|
||||
if ArtistID:
|
||||
|
||||
if ArtistID:
|
||||
self.id = ArtistID
|
||||
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:
|
||||
self.id = AlbumID
|
||||
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()
|
||||
info_dict = { 'Summary' : self.info_summary, 'Content' : self.info_content }
|
||||
info_dict = {'Summary': self.info_summary, 'Content': self.info_content}
|
||||
return info_dict
|
||||
|
||||
else:
|
||||
info_dict = { 'Summary' : db_info['Summary'], 'Content' : db_info['Content'] }
|
||||
info_dict = {'Summary': db_info['Summary'], 'Content': db_info['Content']}
|
||||
return info_dict
|
||||
|
||||
|
||||
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
|
||||
Won't save any image urls, or save the artwork in the cache. Useful for search results, etc.
|
||||
'''
|
||||
"""
|
||||
if ArtistID:
|
||||
|
||||
|
||||
self.id_type = 'artist'
|
||||
|
||||
params = { "method": "artist.getInfo",
|
||||
"api_key": lastfm_apikey,
|
||||
"mbid": ArtistID,
|
||||
"format": "json"
|
||||
}
|
||||
|
||||
url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params)
|
||||
logger.debug('Retrieving artist information from: ' + url)
|
||||
|
||||
try:
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
except:
|
||||
logger.warn('Could not open url: ' + url)
|
||||
return
|
||||
|
||||
if result:
|
||||
|
||||
try:
|
||||
data = simplejson.JSONDecoder().decode(result)
|
||||
except:
|
||||
logger.warn('Could not parse data from url: ' + url)
|
||||
return
|
||||
data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=LASTFM_API_KEY)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
try:
|
||||
image_url = data['artist']['image'][-1]['#text']
|
||||
except (KeyError, IndexError):
|
||||
logger.debug('No artist image found')
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
if not thumb_url:
|
||||
logger.debug('No artist thumbnail image found')
|
||||
|
||||
try:
|
||||
image_url = data['artist']['image'][-1]['#text']
|
||||
except Exception:
|
||||
logger.debug('No artist image found on url: ' + url)
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
if not thumb_url:
|
||||
logger.debug('No artist thumbnail image found on url: ' + url)
|
||||
|
||||
else:
|
||||
|
||||
|
||||
self.id_type = 'album'
|
||||
|
||||
params = { "method": "album.getInfo",
|
||||
"api_key": lastfm_apikey,
|
||||
"mbid": AlbumID,
|
||||
"format": "json"
|
||||
}
|
||||
|
||||
url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params)
|
||||
logger.debug('Retrieving album information from: ' + url)
|
||||
|
||||
try:
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
except:
|
||||
logger.warn('Could not open url: ' + url)
|
||||
data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=LASTFM_API_KEY)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
if result:
|
||||
|
||||
|
||||
try:
|
||||
image_url = data['album']['image'][-1]['#text']
|
||||
except (KeyError, IndexError):
|
||||
logger.debug('No album image found on last.fm')
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
|
||||
if not thumb_url:
|
||||
logger.debug('No album thumbnail image found on last.fm')
|
||||
|
||||
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:
|
||||
data = simplejson.JSONDecoder().decode(result)
|
||||
os.remove(artwork_file)
|
||||
except:
|
||||
logger.warn('Could not parse data from url: ' + url)
|
||||
return
|
||||
|
||||
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:
|
||||
image_url = data['artist']['image'][-1]['#text']
|
||||
os.remove(thumb_file)
|
||||
except Exception:
|
||||
logger.debug('No artist image found on url: ' + url)
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
|
||||
if not thumb_url:
|
||||
logger.debug('No artist thumbnail image found on url: ' + url)
|
||||
|
||||
image_dict = {'artwork' : image_url, 'thumbnail' : thumb_url }
|
||||
return image_dict
|
||||
|
||||
logger.warn('Error deleting file from the cache: %s', thumb_file)
|
||||
|
||||
def _update_cache(self):
|
||||
'''
|
||||
"""
|
||||
Since we call the same url for both info and artwork, we'll update both at the same time
|
||||
'''
|
||||
"""
|
||||
|
||||
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
|
||||
# Exception is when adding albums manually, then we should use release id
|
||||
if self.id_type == 'artist':
|
||||
|
||||
params = { "method": "artist.getInfo",
|
||||
"api_key": lastfm_apikey,
|
||||
"mbid": self.id,
|
||||
"format": "json"
|
||||
}
|
||||
|
||||
url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params)
|
||||
logger.debug('Retrieving artist information from: ' + url)
|
||||
|
||||
try:
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
except:
|
||||
logger.warn('Could not open url: ' + url)
|
||||
|
||||
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=LASTFM_API_KEY)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
if result:
|
||||
|
||||
try:
|
||||
data = simplejson.JSONDecoder().decode(result)
|
||||
except:
|
||||
logger.warn('Could not parse data from url: ' + url)
|
||||
return
|
||||
try:
|
||||
self.info_summary = data['artist']['bio']['summary']
|
||||
except Exception:
|
||||
logger.debug('No artist bio summary found on url: ' + url)
|
||||
self.info_summary = None
|
||||
try:
|
||||
self.info_content = data['artist']['bio']['content']
|
||||
except Exception:
|
||||
logger.debug('No artist bio found on url: ' + url)
|
||||
self.info_content = None
|
||||
try:
|
||||
image_url = data['artist']['image'][-1]['#text']
|
||||
except Exception:
|
||||
logger.debug('No artist image found on url: ' + url)
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
if not thumb_url:
|
||||
logger.debug('No artist thumbnail image found on url: ' + url)
|
||||
|
||||
|
||||
try:
|
||||
self.info_summary = data['artist']['bio']['summary']
|
||||
except KeyError:
|
||||
logger.debug('No artist bio summary found')
|
||||
self.info_summary = None
|
||||
try:
|
||||
self.info_content = data['artist']['bio']['content']
|
||||
except KeyError:
|
||||
logger.debug('No artist bio found')
|
||||
self.info_content = None
|
||||
try:
|
||||
image_url = data['artist']['image'][-1]['#text']
|
||||
except KeyError:
|
||||
logger.debug('No artist image found')
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
if not thumb_url:
|
||||
logger.debug('No artist thumbnail image found')
|
||||
|
||||
else:
|
||||
dbalbum = myDB.action(
|
||||
'SELECT ArtistName, AlbumTitle, ReleaseID FROM albums WHERE AlbumID=?',
|
||||
[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)
|
||||
|
||||
dbartist = myDB.action('SELECT ArtistName, AlbumTitle FROM albums WHERE AlbumID=?', [self.id]).fetchone()
|
||||
|
||||
params = { "method": "album.getInfo",
|
||||
"api_key": lastfm_apikey,
|
||||
"artist": dbartist['ArtistName'].encode('utf-8'),
|
||||
"album": dbartist['AlbumTitle'].encode('utf-8'),
|
||||
"format": "json"
|
||||
}
|
||||
|
||||
url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params)
|
||||
|
||||
logger.debug('Retrieving artist information from: ' + url)
|
||||
try:
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
except:
|
||||
logger.warn('Could not open url: ' + url)
|
||||
if not data:
|
||||
return
|
||||
|
||||
if result:
|
||||
try:
|
||||
data = simplejson.JSONDecoder().decode(result)
|
||||
except:
|
||||
logger.warn('Could not parse data from url: ' + url)
|
||||
return
|
||||
try:
|
||||
self.info_summary = data['album']['wiki']['summary']
|
||||
except Exception:
|
||||
logger.debug('No album summary found from: ' + url)
|
||||
self.info_summary = None
|
||||
try:
|
||||
self.info_content = data['album']['wiki']['content']
|
||||
except Exception:
|
||||
logger.debug('No album infomation found from: ' + url)
|
||||
self.info_content = None
|
||||
try:
|
||||
image_url = data['album']['image'][-1]['#text']
|
||||
except Exception:
|
||||
logger.debug('No album image link found on url: ' + url)
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
|
||||
if not thumb_url:
|
||||
logger.debug('No album thumbnail image found on url: ' + url)
|
||||
|
||||
#Save the content & summary to the database no matter what if we've opened up the url
|
||||
try:
|
||||
self.info_summary = data['album']['wiki']['summary']
|
||||
except KeyError:
|
||||
logger.debug('No album summary found')
|
||||
self.info_summary = None
|
||||
try:
|
||||
self.info_content = data['album']['wiki']['content']
|
||||
except KeyError:
|
||||
logger.debug('No album infomation found')
|
||||
self.info_content = None
|
||||
try:
|
||||
image_url = data['album']['image'][-1]['#text']
|
||||
except KeyError:
|
||||
logger.debug('No album image link found')
|
||||
image_url = None
|
||||
|
||||
thumb_url = self._get_thumb_url(data)
|
||||
|
||||
if not thumb_url:
|
||||
logger.debug('No album thumbnail image found')
|
||||
|
||||
# Save the content & summary to the database no matter what if we've
|
||||
# opened up the url
|
||||
if self.id_type == 'artist':
|
||||
controlValueDict = {"ArtistID": self.id}
|
||||
controlValueDict = {"ArtistID": self.id}
|
||||
else:
|
||||
controlValueDict = {"ReleaseGroupID": self.id}
|
||||
controlValueDict = {"ReleaseGroupID": self.id}
|
||||
|
||||
newValueDict = {"Summary": self.info_summary,
|
||||
"Content": self.info_content,
|
||||
"LastUpdated": helpers.today()}
|
||||
|
||||
newValueDict = {"Summary": self.info_summary,
|
||||
"Content": self.info_content,
|
||||
"LastUpdated": helpers.today()}
|
||||
|
||||
myDB.upsert("descriptions", newValueDict, controlValueDict)
|
||||
|
||||
|
||||
# Save the image URL to the database
|
||||
if image_url:
|
||||
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:
|
||||
myDB.action('UPDATE albums SET ArtworkURL=? WHERE AlbumID=?', [image_url, self.id])
|
||||
|
||||
|
||||
# Save the thumb URL to the database
|
||||
if thumb_url:
|
||||
if self.id_type == 'artist':
|
||||
myDB.action('UPDATE artists SET ThumbURL=? WHERE ArtistID=?', [thumb_url, self.id])
|
||||
else:
|
||||
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':
|
||||
try:
|
||||
artwork = urllib2.urlopen(image_url, timeout=20).read()
|
||||
except Exception, e:
|
||||
logger.error('Unable to open url "' + image_url + '". Error: ' + str(e))
|
||||
artwork = None
|
||||
|
||||
artwork = request.request_content(image_url, timeout=20)
|
||||
|
||||
if artwork:
|
||||
|
||||
# Make sure the artwork dir exists:
|
||||
if not os.path.isdir(self.path_to_art_cache):
|
||||
try:
|
||||
os.makedirs(self.path_to_art_cache)
|
||||
except Exception, e:
|
||||
logger.error('Unable to create artwork cache dir. Error: ' + str(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)
|
||||
self.artwork_errors = True
|
||||
self.artwork_url = image_url
|
||||
|
||||
#Delete the old stuff
|
||||
|
||||
# Delete the old stuff
|
||||
for artwork_file in self.artwork_files:
|
||||
try:
|
||||
os.remove(artwork_file)
|
||||
except:
|
||||
logger.error('Error deleting file from the cache: ' + artwork_file)
|
||||
|
||||
logger.error('Error deleting file from the cache: %s', artwork_file)
|
||||
|
||||
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:
|
||||
f = open(artwork_path, 'wb')
|
||||
f.write(artwork)
|
||||
f.close()
|
||||
except Exception, e:
|
||||
logger.error('Unable to write to the cache dir: ' + str(e))
|
||||
with open(artwork_path, 'wb') as f:
|
||||
f.write(artwork)
|
||||
|
||||
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)
|
||||
self.artwork_errors = True
|
||||
self.artwork_url = image_url
|
||||
|
||||
# Grab the thumbnail as well if we're getting the full artwork (as long 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])):
|
||||
|
||||
try:
|
||||
artwork = urllib2.urlopen(thumb_url, timeout=20).read()
|
||||
except Exception, e:
|
||||
logger.error('Unable to open url "' + thumb_url + '". Error: ' + str(e))
|
||||
artwork = None
|
||||
|
||||
|
||||
# Grab the thumbnail as well if we're getting the full artwork (as long
|
||||
# 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)
|
||||
|
||||
if artwork:
|
||||
|
||||
# Make sure the artwork dir exists:
|
||||
if not os.path.isdir(self.path_to_art_cache):
|
||||
try:
|
||||
os.makedirs(self.path_to_art_cache)
|
||||
except Exception, e:
|
||||
logger.error('Unable to create artwork cache dir. Error: ' + str(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)
|
||||
self.thumb_errors = True
|
||||
self.thumb_url = thumb_url
|
||||
|
||||
#Delete the old stuff
|
||||
|
||||
# Delete the old stuff
|
||||
for thumb_file in self.thumb_files:
|
||||
try:
|
||||
os.remove(thumb_file)
|
||||
except:
|
||||
logger.error('Error deleting file from the cache: ' + thumb_file)
|
||||
|
||||
except OSError as e:
|
||||
logger.error('Error deleting file from the cache: %s', thumb_file)
|
||||
|
||||
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:
|
||||
f = open(thumb_path, 'wb')
|
||||
f.write(artwork)
|
||||
f.close()
|
||||
except Exception, e:
|
||||
logger.error('Unable to write to the cache dir: ' + str(e))
|
||||
with open(thumb_path, 'wb') as f:
|
||||
f.write(artwork)
|
||||
|
||||
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)
|
||||
self.thumb_errors = True
|
||||
self.thumb_url = image_url
|
||||
|
||||
|
||||
def getArtwork(ArtistID=None, AlbumID=None):
|
||||
|
||||
c = Cache()
|
||||
artwork_path = c.get_artwork_from_cache(ArtistID, AlbumID)
|
||||
|
||||
|
||||
if not artwork_path:
|
||||
return None
|
||||
|
||||
|
||||
if artwork_path.startswith('http://'):
|
||||
return artwork_path
|
||||
else:
|
||||
artwork_file = os.path.basename(artwork_path)
|
||||
return "cache/artwork/" + artwork_file
|
||||
|
||||
|
||||
|
||||
def getThumb(ArtistID=None, AlbumID=None):
|
||||
|
||||
c = Cache()
|
||||
artwork_path = c.get_thumb_from_cache(ArtistID, AlbumID)
|
||||
|
||||
|
||||
if not artwork_path:
|
||||
return None
|
||||
|
||||
|
||||
if artwork_path.startswith('http://'):
|
||||
return artwork_path
|
||||
else:
|
||||
thumbnail_file = os.path.basename(artwork_path)
|
||||
return "cache/artwork/" + thumbnail_file
|
||||
|
||||
|
||||
|
||||
def getInfo(ArtistID=None, AlbumID=None):
|
||||
|
||||
c = Cache()
|
||||
|
||||
info_dict = c.get_info_from_cache(ArtistID, AlbumID)
|
||||
|
||||
|
||||
return info_dict
|
||||
|
||||
|
||||
|
||||
def getImageLinks(ArtistID=None, AlbumID=None):
|
||||
|
||||
c = Cache()
|
||||
image_links = c.get_image_links(ArtistID, AlbumID)
|
||||
|
||||
|
||||
return image_links
|
||||
|
||||
@@ -1,130 +1,137 @@
|
||||
# This file is part of Headphones.
|
||||
#
|
||||
# Headphones is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Headphones is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#########################################
|
||||
## Stolen from Sick-Beard's classes.py ##
|
||||
#########################################
|
||||
|
||||
import headphones
|
||||
|
||||
import urllib
|
||||
import datetime
|
||||
|
||||
from common import USER_AGENT
|
||||
|
||||
class HeadphonesURLopener(urllib.FancyURLopener):
|
||||
version = USER_AGENT
|
||||
|
||||
class AuthURLOpener(HeadphonesURLopener):
|
||||
"""
|
||||
URLOpener class that supports http auth without needing interactive password entry.
|
||||
If the provided username/password don't work it simply fails.
|
||||
|
||||
user: username to use for HTTP auth
|
||||
pw: password to use for HTTP auth
|
||||
"""
|
||||
def __init__(self, user, pw):
|
||||
self.username = user
|
||||
self.password = pw
|
||||
|
||||
# remember if we've tried the username/password before
|
||||
self.numTries = 0
|
||||
|
||||
# call the base class
|
||||
urllib.FancyURLopener.__init__(self)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
# if this is the first try then provide a username/password
|
||||
if self.numTries == 0:
|
||||
self.numTries = 1
|
||||
return (self.username, self.password)
|
||||
|
||||
# if we've tried before then return blank which cancels the request
|
||||
else:
|
||||
return ('', '')
|
||||
|
||||
# this is pretty much just a hack for convenience
|
||||
def openit(self, url):
|
||||
self.numTries = 0
|
||||
return HeadphonesURLopener.open(self, url)
|
||||
|
||||
class SearchResult:
|
||||
"""
|
||||
Represents a search result from an indexer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.provider = -1
|
||||
|
||||
# URL to the NZB/torrent file
|
||||
self.url = ""
|
||||
|
||||
# used by some providers to store extra info associated with the result
|
||||
self.extraInfo = []
|
||||
|
||||
# quality of the release
|
||||
self.quality = -1
|
||||
|
||||
# release name
|
||||
self.name = ""
|
||||
|
||||
def __str__(self):
|
||||
|
||||
if self.provider == None:
|
||||
return "Invalid provider, unable to print self"
|
||||
|
||||
myString = self.provider.name + " @ " + self.url + "\n"
|
||||
myString += "Extra Info:\n"
|
||||
for extra in self.extraInfo:
|
||||
myString += " " + extra + "\n"
|
||||
return myString
|
||||
|
||||
class NZBSearchResult(SearchResult):
|
||||
"""
|
||||
Regular NZB result with an URL to the NZB
|
||||
"""
|
||||
resultType = "nzb"
|
||||
|
||||
class NZBDataSearchResult(SearchResult):
|
||||
"""
|
||||
NZB result where the actual NZB XML data is stored in the extraInfo
|
||||
"""
|
||||
resultType = "nzbdata"
|
||||
|
||||
class TorrentSearchResult(SearchResult):
|
||||
"""
|
||||
Torrent result with an URL to the torrent
|
||||
"""
|
||||
resultType = "torrent"
|
||||
|
||||
class Proper:
|
||||
def __init__(self, name, url, date):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.date = date
|
||||
self.provider = None
|
||||
self.quality = -1
|
||||
|
||||
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)
|
||||
# This file is part of Headphones.
|
||||
#
|
||||
# Headphones is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Headphones is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#######################################
|
||||
# Stolen from Sick-Beard's classes.py #
|
||||
#######################################
|
||||
|
||||
|
||||
import urllib
|
||||
|
||||
from common import USER_AGENT
|
||||
|
||||
|
||||
class HeadphonesURLopener(urllib.FancyURLopener):
|
||||
version = USER_AGENT
|
||||
|
||||
|
||||
class AuthURLOpener(HeadphonesURLopener):
|
||||
"""
|
||||
URLOpener class that supports http auth without needing interactive password entry.
|
||||
If the provided username/password don't work it simply fails.
|
||||
|
||||
user: username to use for HTTP auth
|
||||
pw: password to use for HTTP auth
|
||||
"""
|
||||
|
||||
def __init__(self, user, pw):
|
||||
self.username = user
|
||||
self.password = pw
|
||||
|
||||
# remember if we've tried the username/password before
|
||||
self.numTries = 0
|
||||
|
||||
# call the base class
|
||||
urllib.FancyURLopener.__init__(self)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
# if this is the first try then provide a username/password
|
||||
if self.numTries == 0:
|
||||
self.numTries = 1
|
||||
return (self.username, self.password)
|
||||
|
||||
# if we've tried before then return blank which cancels the request
|
||||
else:
|
||||
return ('', '')
|
||||
|
||||
# this is pretty much just a hack for convenience
|
||||
def openit(self, url):
|
||||
self.numTries = 0
|
||||
return HeadphonesURLopener.open(self, url)
|
||||
|
||||
|
||||
class SearchResult:
|
||||
"""
|
||||
Represents a search result from an indexer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.provider = -1
|
||||
|
||||
# URL to the NZB/torrent file
|
||||
self.url = ""
|
||||
|
||||
# used by some providers to store extra info associated with the result
|
||||
self.extraInfo = []
|
||||
|
||||
# quality of the release
|
||||
self.quality = -1
|
||||
|
||||
# release name
|
||||
self.name = ""
|
||||
|
||||
def __str__(self):
|
||||
|
||||
if self.provider is None:
|
||||
return "Invalid provider, unable to print self"
|
||||
|
||||
myString = self.provider.name + " @ " + self.url + "\n"
|
||||
myString += "Extra Info:\n"
|
||||
for extra in self.extraInfo:
|
||||
myString += " " + extra + "\n"
|
||||
return myString
|
||||
|
||||
|
||||
class NZBSearchResult(SearchResult):
|
||||
"""
|
||||
Regular NZB result with an URL to the NZB
|
||||
"""
|
||||
resultType = "nzb"
|
||||
|
||||
|
||||
class NZBDataSearchResult(SearchResult):
|
||||
"""
|
||||
NZB result where the actual NZB XML data is stored in the extraInfo
|
||||
"""
|
||||
resultType = "nzbdata"
|
||||
|
||||
|
||||
class TorrentSearchResult(SearchResult):
|
||||
"""
|
||||
Torrent result with an URL to the torrent
|
||||
"""
|
||||
resultType = "torrent"
|
||||
|
||||
|
||||
class Proper:
|
||||
def __init__(self, name, url, date):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.date = date
|
||||
self.provider = None
|
||||
self.quality = -1
|
||||
|
||||
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.
|
||||
#
|
||||
# Headphones is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Headphones is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
'''
|
||||
Created on Aug 1, 2011
|
||||
|
||||
@author: Michael
|
||||
'''
|
||||
import platform, operator, os, re
|
||||
|
||||
from headphones import version
|
||||
|
||||
#Identify Our Application
|
||||
USER_AGENT = 'Headphones/-'+version.HEADPHONES_VERSION+' ('+platform.system()+' '+platform.release()+')'
|
||||
|
||||
### Notification Types
|
||||
NOTIFY_SNATCH = 1
|
||||
NOTIFY_DOWNLOAD = 2
|
||||
|
||||
notifyStrings = {}
|
||||
notifyStrings[NOTIFY_SNATCH] = "Started Download"
|
||||
notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
|
||||
|
||||
### Release statuses
|
||||
UNKNOWN = -1 # should never happen
|
||||
UNAIRED = 1 # releases that haven't dropped yet
|
||||
SNATCHED = 2 # qualified with quality
|
||||
WANTED = 3 # releases we don't have but want to get
|
||||
DOWNLOADED = 4 # qualified with quality
|
||||
SKIPPED = 5 # releases we don't want
|
||||
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
|
||||
SNATCHED_PROPER = 9 # qualified with quality
|
||||
|
||||
class Quality:
|
||||
|
||||
NONE = 0
|
||||
B192 = 1<<1 # 2
|
||||
VBR = 1<<2 # 4
|
||||
B256 = 1<<3 # 8
|
||||
B320 = 1<<4 #16
|
||||
FLAC = 1<<5 #32
|
||||
|
||||
# put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
|
||||
UNKNOWN = 1<<15
|
||||
|
||||
qualityStrings = {NONE: "N/A",
|
||||
UNKNOWN: "Unknown",
|
||||
B192: "MP3 192",
|
||||
VBR: "MP3 VBR",
|
||||
B256: "MP3 256",
|
||||
B320: "MP3 320",
|
||||
FLAC: "Flac"}
|
||||
|
||||
statusPrefixes = {DOWNLOADED: "Downloaded",
|
||||
SNATCHED: "Snatched"}
|
||||
|
||||
@staticmethod
|
||||
def _getStatusStrings(status):
|
||||
toReturn = {}
|
||||
for x in Quality.qualityStrings.keys():
|
||||
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status]+" ("+Quality.qualityStrings[x]+")"
|
||||
return toReturn
|
||||
|
||||
@staticmethod
|
||||
def combineQualities(anyQualities, bestQualities):
|
||||
anyQuality = 0
|
||||
bestQuality = 0
|
||||
if anyQualities:
|
||||
anyQuality = reduce(operator.or_, anyQualities)
|
||||
if bestQualities:
|
||||
bestQuality = reduce(operator.or_, bestQualities)
|
||||
return anyQuality | (bestQuality<<16)
|
||||
|
||||
@staticmethod
|
||||
def splitQuality(quality):
|
||||
anyQualities = []
|
||||
bestQualities = []
|
||||
for curQual in Quality.qualityStrings.keys():
|
||||
if curQual & quality:
|
||||
anyQualities.append(curQual)
|
||||
if curQual<<16 & quality:
|
||||
bestQualities.append(curQual)
|
||||
|
||||
return (anyQualities, bestQualities)
|
||||
|
||||
@staticmethod
|
||||
def nameQuality(name):
|
||||
|
||||
name = os.path.basename(name)
|
||||
|
||||
# if we have our exact text then assume we put it there
|
||||
for x in Quality.qualityStrings:
|
||||
if x == Quality.UNKNOWN:
|
||||
continue
|
||||
|
||||
regex = '\W'+Quality.qualityStrings[x].replace(' ','\W')+'\W'
|
||||
regex_match = re.search(regex, name, re.I)
|
||||
if regex_match:
|
||||
return x
|
||||
|
||||
checkName = lambda list, func: func([re.search(x, name, re.I) for x in list])
|
||||
|
||||
#TODO: fix quality checking here
|
||||
if checkName(["mp3", "192"], any) and not checkName(["flac"], all):
|
||||
return Quality.B192
|
||||
elif checkName(["mp3", "256"], any) and not checkName(["flac"], all):
|
||||
return Quality.B256
|
||||
elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all):
|
||||
return Quality.VBR
|
||||
elif checkName(["mp3", "320"], any) and not checkName(["flac"], all):
|
||||
return Quality.B320
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def assumeQuality(name):
|
||||
|
||||
if name.lower().endswith(".mp3"):
|
||||
return Quality.MP3
|
||||
elif name.lower().endswith(".flac"):
|
||||
return Quality.LOSSLESS
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def compositeStatus(status, quality):
|
||||
return status + 100 * quality
|
||||
|
||||
@staticmethod
|
||||
def qualityDownloaded(status):
|
||||
return (status - DOWNLOADED) / 100
|
||||
|
||||
@staticmethod
|
||||
def splitCompositeStatus(status):
|
||||
"""Returns a tuple containing (status, quality)"""
|
||||
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
|
||||
if status > x*100:
|
||||
return (status-x*100, x)
|
||||
|
||||
return (Quality.NONE, status)
|
||||
|
||||
@staticmethod
|
||||
def statusFromName(name, assume=True):
|
||||
quality = Quality.nameQuality(name)
|
||||
if assume and quality == Quality.UNKNOWN:
|
||||
quality = Quality.assumeQuality(name)
|
||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||
|
||||
DOWNLOADED = None
|
||||
SNATCHED = None
|
||||
SNATCHED_PROPER = None
|
||||
|
||||
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
|
||||
|
||||
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"}
|
||||
# This file is part of Headphones.
|
||||
#
|
||||
# Headphones is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Headphones is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
'''
|
||||
Created on Aug 1, 2011
|
||||
|
||||
@author: Michael
|
||||
'''
|
||||
import platform
|
||||
import operator
|
||||
|
||||
import os
|
||||
import re
|
||||
from headphones import version
|
||||
|
||||
|
||||
# Identify Our Application
|
||||
USER_AGENT = 'Headphones/-' + version.HEADPHONES_VERSION + ' (' + platform.system() + ' ' + platform.release() + ')'
|
||||
|
||||
# Notification Types
|
||||
NOTIFY_SNATCH = 1
|
||||
NOTIFY_DOWNLOAD = 2
|
||||
|
||||
notifyStrings = {}
|
||||
notifyStrings[NOTIFY_SNATCH] = "Started Download"
|
||||
notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
|
||||
|
||||
# Release statuses
|
||||
UNKNOWN = -1 # should never happen
|
||||
UNAIRED = 1 # releases that haven't dropped yet
|
||||
SNATCHED = 2 # qualified with quality
|
||||
WANTED = 3 # releases we don't have but want to get
|
||||
DOWNLOADED = 4 # qualified with quality
|
||||
SKIPPED = 5 # releases we don't want
|
||||
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
|
||||
SNATCHED_PROPER = 9 # qualified with quality
|
||||
|
||||
|
||||
class Quality:
|
||||
NONE = 0
|
||||
B192 = 1 << 1 # 2
|
||||
VBR = 1 << 2 # 4
|
||||
B256 = 1 << 3 # 8
|
||||
B320 = 1 << 4 # 16
|
||||
FLAC = 1 << 5 # 32
|
||||
|
||||
# put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
|
||||
UNKNOWN = 1 << 15
|
||||
|
||||
qualityStrings = {NONE: "N/A",
|
||||
UNKNOWN: "Unknown",
|
||||
B192: "MP3 192",
|
||||
VBR: "MP3 VBR",
|
||||
B256: "MP3 256",
|
||||
B320: "MP3 320",
|
||||
FLAC: "Flac"}
|
||||
|
||||
statusPrefixes = {DOWNLOADED: "Downloaded",
|
||||
SNATCHED: "Snatched"}
|
||||
|
||||
@staticmethod
|
||||
def _getStatusStrings(status):
|
||||
toReturn = {}
|
||||
for x in Quality.qualityStrings.keys():
|
||||
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status] + " (" + \
|
||||
Quality.qualityStrings[x] + ")"
|
||||
return toReturn
|
||||
|
||||
@staticmethod
|
||||
def combineQualities(anyQualities, bestQualities):
|
||||
anyQuality = 0
|
||||
bestQuality = 0
|
||||
if anyQualities:
|
||||
anyQuality = reduce(operator.or_, anyQualities)
|
||||
if bestQualities:
|
||||
bestQuality = reduce(operator.or_, bestQualities)
|
||||
return anyQuality | (bestQuality << 16)
|
||||
|
||||
@staticmethod
|
||||
def splitQuality(quality):
|
||||
anyQualities = []
|
||||
bestQualities = []
|
||||
for curQual in Quality.qualityStrings.keys():
|
||||
if curQual & quality:
|
||||
anyQualities.append(curQual)
|
||||
if curQual << 16 & quality:
|
||||
bestQualities.append(curQual)
|
||||
|
||||
return (anyQualities, bestQualities)
|
||||
|
||||
@staticmethod
|
||||
def nameQuality(name):
|
||||
|
||||
def checkName(list, func):
|
||||
return func([re.search(x, name, re.I) for x in list])
|
||||
|
||||
name = os.path.basename(name)
|
||||
|
||||
# if we have our exact text then assume we put it there
|
||||
for x in Quality.qualityStrings:
|
||||
if x == Quality.UNKNOWN:
|
||||
continue
|
||||
|
||||
regex = '\W' + Quality.qualityStrings[x].replace(' ', '\W') + '\W'
|
||||
regex_match = re.search(regex, name, re.I)
|
||||
if regex_match:
|
||||
return x
|
||||
|
||||
# TODO: fix quality checking here
|
||||
if checkName(["mp3", "192"], any) and not checkName(["flac"], all):
|
||||
return Quality.B192
|
||||
elif checkName(["mp3", "256"], any) and not checkName(["flac"], all):
|
||||
return Quality.B256
|
||||
elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all):
|
||||
return Quality.VBR
|
||||
elif checkName(["mp3", "320"], any) and not checkName(["flac"], all):
|
||||
return Quality.B320
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def assumeQuality(name):
|
||||
if name.lower().endswith(".mp3"):
|
||||
return Quality.MP3
|
||||
elif name.lower().endswith(".flac"):
|
||||
return Quality.LOSSLESS
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def compositeStatus(status, quality):
|
||||
return status + 100 * quality
|
||||
|
||||
@staticmethod
|
||||
def qualityDownloaded(status):
|
||||
return (status - DOWNLOADED) / 100
|
||||
|
||||
@staticmethod
|
||||
def splitCompositeStatus(status):
|
||||
"""Returns a tuple containing (status, quality)"""
|
||||
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
|
||||
if status > x * 100:
|
||||
return (status - x * 100, x)
|
||||
|
||||
return (Quality.NONE, status)
|
||||
|
||||
@staticmethod
|
||||
def statusFromName(name, assume=True):
|
||||
quality = Quality.nameQuality(name)
|
||||
if assume and quality == Quality.UNKNOWN:
|
||||
quality = Quality.assumeQuality(name)
|
||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||
|
||||
DOWNLOADED = None
|
||||
SNATCHED = None
|
||||
SNATCHED_PROPER = None
|
||||
|
||||
|
||||
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in
|
||||
Quality.qualityStrings.keys()]
|
||||
|
||||
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"}
|
||||
|
||||