Compare commits
763 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1a2e205c1f | ||
![]() |
5dd04cb8ab | ||
![]() |
62d05e5e08 | ||
![]() |
1c087ec856 | ||
![]() |
010c12da67 | ||
![]() |
9bdac38561 | ||
![]() |
790ca9c90a | ||
![]() |
58f72d2d9c | ||
![]() |
285e6513ed | ||
![]() |
412bc8cf2d | ||
![]() |
45cd8b8a00 | ||
![]() |
ae2227959e | ||
![]() |
b50c92f919 | ||
![]() |
93a1d9c164 | ||
![]() |
0b10e68c60 | ||
![]() |
73ac4076ac | ||
![]() |
5968b82a0b | ||
![]() |
ce1d2a0fd9 | ||
![]() |
de3f813b46 | ||
![]() |
4797b1a3b7 | ||
![]() |
3e996d284d | ||
![]() |
420c5a0836 | ||
![]() |
c6b953055a | ||
![]() |
1cd0c112a6 | ||
![]() |
492d28ea37 | ||
![]() |
4eb7e03b67 | ||
![]() |
e029f329eb | ||
![]() |
47de9a752c | ||
![]() |
51c9aa2887 | ||
![]() |
82499a53d4 | ||
![]() |
df15302f2c | ||
![]() |
039b51262d | ||
![]() |
465add46d4 | ||
![]() |
bce965b402 | ||
![]() |
95ce293169 | ||
![]() |
5d604c2cad | ||
![]() |
ed2d3ca277 | ||
![]() |
0478f40d02 | ||
![]() |
a4be73da3b | ||
![]() |
762192518f | ||
![]() |
fa51df192d | ||
![]() |
1a5cc02097 | ||
![]() |
a07f54ca33 | ||
![]() |
6a8cbe92a9 | ||
![]() |
16d9376ec9 | ||
![]() |
4356f5c72a | ||
![]() |
076dc94292 | ||
![]() |
fbc527010a | ||
![]() |
5b4a22276d | ||
![]() |
b55a563fce | ||
![]() |
2a701a6dfe | ||
![]() |
8931fb4758 | ||
![]() |
2124165319 | ||
![]() |
0d701129a0 | ||
![]() |
ebd8625e1e | ||
![]() |
b68ca67386 | ||
![]() |
17a7019c60 | ||
![]() |
54af92251c | ||
![]() |
d9edeb747d | ||
![]() |
b69b722a37 | ||
![]() |
669c23ea09 | ||
![]() |
2b3ba8e7fa | ||
![]() |
9a761e7d30 | ||
![]() |
9d00e052f0 | ||
![]() |
7c159e97de | ||
![]() |
ba8e4ff33c | ||
![]() |
9b067a437c | ||
![]() |
aba39d06bf | ||
![]() |
469d22a833 | ||
![]() |
43bd49ce5b | ||
![]() |
79dc190ccc | ||
![]() |
495659e9cd | ||
![]() |
2fec2c9e4c | ||
![]() |
9cba66634d | ||
![]() |
b2f63bf231 | ||
![]() |
9c9ef22730 | ||
![]() |
5d84ec3be2 | ||
![]() |
2150961d27 | ||
![]() |
53bca5a3d3 | ||
![]() |
cd3938eb33 | ||
![]() |
eb0b88bfcf | ||
![]() |
b9bbf8bbca | ||
![]() |
65b3d0c0de | ||
![]() |
93b8f32f68 | ||
![]() |
28bb164e8e | ||
![]() |
4911cc76a3 | ||
![]() |
26ac539bc4 | ||
![]() |
75ae6b16a4 | ||
![]() |
2835b1d28f | ||
![]() |
748aad16d7 | ||
![]() |
2c2fbb8583 | ||
![]() |
20edcbf7fa | ||
![]() |
db81dc39ba | ||
![]() |
c3b0aef1ef | ||
![]() |
50e29efdfe | ||
![]() |
285e41bc88 | ||
![]() |
ea9d0fc449 | ||
![]() |
9cdd2eef81 | ||
![]() |
2f8833236a | ||
![]() |
2b680eeb6d | ||
![]() |
809f120db0 | ||
![]() |
6d9ef8bbc3 | ||
![]() |
a26d6ec6bb | ||
![]() |
2d26ced3fc | ||
![]() |
d74cd4bf24 | ||
![]() |
f040d897a7 | ||
![]() |
ed2f87f57b | ||
![]() |
9b9e31f54c | ||
![]() |
b3cfcf660e | ||
![]() |
e5bcd1f94e | ||
![]() |
2b6fa769f7 | ||
![]() |
3ccc82f343 | ||
![]() |
f4273cafb6 | ||
![]() |
59d63f61d9 | ||
![]() |
9d9103a83b | ||
![]() |
0b085b6d03 | ||
![]() |
f77538f179 | ||
![]() |
f7810f7f95 | ||
![]() |
4d28e4603f | ||
![]() |
8b787e4ae0 | ||
![]() |
f5ba168172 | ||
![]() |
1df6dadbdd | ||
![]() |
3dc29144a3 | ||
![]() |
951167ce17 | ||
![]() |
906e4055d8 | ||
![]() |
2f5526388a | ||
![]() |
82341642f4 | ||
![]() |
c96b1eb09d | ||
![]() |
f5bfa67c69 | ||
![]() |
47797ffcd4 | ||
![]() |
a73053e380 | ||
![]() |
bc042fead7 | ||
![]() |
ed6779e937 | ||
![]() |
ee7ca68f87 | ||
![]() |
32693b6378 | ||
![]() |
984e5588c8 | ||
![]() |
a42a1af867 | ||
![]() |
03de680915 | ||
![]() |
8c6e142314 | ||
![]() |
b12bde4f79 | ||
![]() |
1120aa3841 | ||
![]() |
652ca73126 | ||
![]() |
8706e72f6a | ||
![]() |
319d521773 | ||
![]() |
d9474cdcc5 | ||
![]() |
e49a34177a | ||
![]() |
67d203e011 | ||
![]() |
0d38b3de16 | ||
![]() |
38116a14f3 | ||
![]() |
b28f0b65f0 | ||
![]() |
13ab4a9363 | ||
![]() |
7cb7783a34 | ||
![]() |
d1a13dad38 | ||
![]() |
b4e06dea99 | ||
![]() |
0f92dc0fdf | ||
![]() |
6a58895d37 | ||
![]() |
1709a2b7df | ||
![]() |
febb3da0c1 | ||
![]() |
552a428985 | ||
![]() |
38e04bd42a | ||
![]() |
8f0ba5ba4f | ||
![]() |
c67aedceb1 | ||
![]() |
b3a7fbd9b5 | ||
![]() |
29522428de | ||
![]() |
77bd52b2ae | ||
![]() |
d8112e7628 | ||
![]() |
ffa208e73f | ||
![]() |
61ead15c38 | ||
![]() |
407e2ae481 | ||
![]() |
5fb16edf43 | ||
![]() |
8eb5c475bb | ||
![]() |
84090310f7 | ||
![]() |
fc98e2f052 | ||
![]() |
bedcfa9520 | ||
![]() |
bb152b590b | ||
![]() |
3623732cf7 | ||
![]() |
05ba89f164 | ||
![]() |
cb5053476d | ||
![]() |
cee656a053 | ||
![]() |
cfc7d529e1 | ||
![]() |
a93dc68e6c | ||
![]() |
2d91cfd3db | ||
![]() |
36e81f44cb | ||
![]() |
cb0e65337f | ||
![]() |
1c627f4649 | ||
![]() |
16cbfed20b | ||
![]() |
f6a3bc57e2 | ||
![]() |
594443d1dc | ||
![]() |
c3378e1653 | ||
![]() |
bc57dd650c | ||
![]() |
311a8c6fa3 | ||
![]() |
bdb43c0e9e | ||
![]() |
8033b47596 | ||
![]() |
9d5052cc68 | ||
![]() |
f4c9dc8a5f | ||
![]() |
a3f0a78df0 | ||
![]() |
b70363e005 | ||
![]() |
65eab801e8 | ||
![]() |
9e764248d3 | ||
![]() |
a660a1c44b | ||
![]() |
33458c1bdb | ||
![]() |
e5530182cd | ||
![]() |
9ecabc3faf | ||
![]() |
8b58f6b861 | ||
![]() |
9e41bf529d | ||
![]() |
36398fe958 | ||
![]() |
69cfbea5f3 | ||
![]() |
1e1e3beca6 | ||
![]() |
a726154b1d | ||
![]() |
c2ccb51ef5 | ||
![]() |
a1d7062b1f | ||
![]() |
2e1e3f8409 | ||
![]() |
885604ef06 | ||
![]() |
cb8a5504f6 | ||
![]() |
325fa19e46 | ||
![]() |
06b684c899 | ||
![]() |
363d1b07ca | ||
![]() |
663b9a610a | ||
![]() |
f598d5046e | ||
![]() |
ae381f7762 | ||
![]() |
acc18b8d68 | ||
![]() |
6f33d29a51 | ||
![]() |
be82e64add | ||
![]() |
3ee000ed7d | ||
![]() |
0f338edacd | ||
![]() |
085d937946 | ||
![]() |
0cbc4b9546 | ||
![]() |
9e1c4b1a88 | ||
![]() |
b47f542df7 | ||
![]() |
baed101ef1 | ||
![]() |
c9ee6e3af9 | ||
![]() |
8ed7688277 | ||
![]() |
27716d080f | ||
![]() |
4311d12603 | ||
![]() |
6aa786698e | ||
![]() |
b0eb98c667 | ||
![]() |
955b69a9bf | ||
![]() |
5f5bfa864d | ||
![]() |
6ec6c69dba | ||
![]() |
19f3286a82 | ||
![]() |
36a3cae9c2 | ||
![]() |
86215c34be | ||
![]() |
4ad421d4d0 | ||
![]() |
e79f6d5617 | ||
![]() |
baf44a97b4 | ||
![]() |
c14053a199 | ||
![]() |
1e5153d69e | ||
![]() |
fed38bd046 | ||
![]() |
89d298ea65 | ||
![]() |
cd35fa1802 | ||
![]() |
150453bff3 | ||
![]() |
49833b3c51 | ||
![]() |
4a0f0238b0 | ||
![]() |
83b97111a0 | ||
![]() |
43bbf32098 | ||
![]() |
a70817f421 | ||
![]() |
9ae441b75a | ||
![]() |
21fcbd85d8 | ||
![]() |
e1b61214b7 | ||
![]() |
2d10b0748c | ||
![]() |
f4e719749a | ||
![]() |
600bca7e8b | ||
![]() |
a3a62b1d94 | ||
![]() |
dbe783d31d | ||
![]() |
f0d8492b66 | ||
![]() |
7587eb9ac2 | ||
![]() |
fe2fdafbb1 | ||
![]() |
e50c77d8c6 | ||
![]() |
81f9f52353 | ||
![]() |
6e5b02d326 | ||
![]() |
d09c7b13b3 | ||
![]() |
ff532a5c6c | ||
![]() |
698275633f | ||
![]() |
c5dff312e1 | ||
![]() |
972412e712 | ||
![]() |
453c46df00 | ||
![]() |
f001e19728 | ||
![]() |
3da8cc1e7f | ||
![]() |
68d124ff04 | ||
![]() |
4921458782 | ||
![]() |
86aa21a8bb | ||
![]() |
65de742f96 | ||
![]() |
4043398e01 | ||
![]() |
7be651f5cf | ||
![]() |
5ddd4d045e | ||
![]() |
1cc7e8725d | ||
![]() |
6fe115fd0d | ||
![]() |
d58dfec5ea | ||
![]() |
3e6f5ac70e | ||
![]() |
a1821fabf9 | ||
![]() |
b6461f4f9e | ||
![]() |
0781018e4e | ||
![]() |
2f8e768c5c | ||
![]() |
1622b0fa29 | ||
![]() |
e147ce9039 | ||
![]() |
2aa059a170 | ||
![]() |
ae60b21375 | ||
![]() |
03faebe776 | ||
![]() |
f66afc4cae | ||
![]() |
b327413bfa | ||
![]() |
54776e2712 | ||
![]() |
4b8d4488d7 | ||
![]() |
a2a1b66fc3 | ||
![]() |
d2bdb597f6 | ||
![]() |
85a7819469 | ||
![]() |
5689dfd3e3 | ||
![]() |
3c76ead1ab | ||
![]() |
be7fbdf5d8 | ||
![]() |
fc21f043ae | ||
![]() |
11659df89d | ||
![]() |
2bfa770f60 | ||
![]() |
4679121115 | ||
![]() |
3ecc90d21a | ||
![]() |
fd587fe108 | ||
![]() |
0f851ec2a3 | ||
![]() |
4d057a1c5e | ||
![]() |
c8b13ff5e1 | ||
![]() |
545dd08535 | ||
![]() |
c0a5a8d775 | ||
![]() |
d462ebe8e5 | ||
![]() |
5d7ba8cf14 | ||
![]() |
af9786f149 | ||
![]() |
6b990ee78a | ||
![]() |
f87102ccc7 | ||
![]() |
63398089cd | ||
![]() |
89694b5069 | ||
![]() |
4f8a5211f8 | ||
![]() |
b45df26fdc | ||
![]() |
c0b0181475 | ||
![]() |
62600a450a | ||
![]() |
4be41336b3 | ||
![]() |
3abea4ad3c | ||
![]() |
b2304992e5 | ||
![]() |
f9825410dc | ||
![]() |
24205dc86e | ||
![]() |
9fcd0da83d | ||
![]() |
e99bc73e46 | ||
![]() |
d8ad9adabd | ||
![]() |
11aa7d0140 | ||
![]() |
6f97173b00 | ||
![]() |
54a7367fb6 | ||
![]() |
51a12099e4 | ||
![]() |
00c0c96b1b | ||
![]() |
0aa2537d1e | ||
![]() |
14489e1db9 | ||
![]() |
bff4eaba56 | ||
![]() |
4a5d2f8502 | ||
![]() |
72af2fa281 | ||
![]() |
cf70b3e989 | ||
![]() |
7ebd74a54a | ||
![]() |
9a650b5cd6 | ||
![]() |
ffa1331802 | ||
![]() |
dacb4ea7d6 | ||
![]() |
ae5889dbac | ||
![]() |
7b169e9439 | ||
![]() |
541d2904d3 | ||
![]() |
2080bbcbca | ||
![]() |
f4c38008a1 | ||
![]() |
2d38b15f1b | ||
![]() |
13257b9f86 | ||
![]() |
22f106b357 | ||
![]() |
352b5aadba | ||
![]() |
f86c9ea947 | ||
![]() |
2f5f0ba1e1 | ||
![]() |
6a72923182 | ||
![]() |
d62f7b2a5f | ||
![]() |
8ca6255ff3 | ||
![]() |
d3b3afd593 | ||
![]() |
57cb5ff6cf | ||
![]() |
39f3da6cde | ||
![]() |
5a236c9357 | ||
![]() |
4b0eab57a8 | ||
![]() |
74a232630a | ||
![]() |
71d023ab77 | ||
![]() |
786a374233 | ||
![]() |
41899872cd | ||
![]() |
076659db52 | ||
![]() |
8f665622d6 | ||
![]() |
5cc6e0b172 | ||
![]() |
bff22900cb | ||
![]() |
5e79c9fd62 | ||
![]() |
92f55c254c | ||
![]() |
39034e38f6 | ||
![]() |
3c7b9558fe | ||
![]() |
c8f7f40b46 | ||
![]() |
2a764cf190 | ||
![]() |
ba6ef4d629 | ||
![]() |
67d3505733 | ||
![]() |
252145cf58 | ||
![]() |
dbc62542ef | ||
![]() |
005829ab72 | ||
![]() |
448c8b0e8a | ||
![]() |
ff0e724ee5 | ||
![]() |
568e4a5ee8 | ||
![]() |
fbf4a524c1 | ||
![]() |
29db2e958f | ||
![]() |
cc7bcbf9d5 | ||
![]() |
98d4484e6c | ||
![]() |
0b126278f9 | ||
![]() |
cc919415bb | ||
![]() |
a3f398390c | ||
![]() |
5ae89368f1 | ||
![]() |
c8f1cb0a0a | ||
![]() |
f783b08b78 | ||
![]() |
85e0c6d3cd | ||
![]() |
2259a96058 | ||
![]() |
afed5841e7 | ||
![]() |
52361cd505 | ||
![]() |
b743cca7bc | ||
![]() |
1d01d0bff1 | ||
![]() |
c45a488962 | ||
![]() |
6731c44541 | ||
![]() |
b04ed83963 | ||
![]() |
c35b79e642 | ||
![]() |
ed8c7c1052 | ||
![]() |
498a074222 | ||
![]() |
3fe6db4d42 | ||
![]() |
6e5cd82dfb | ||
![]() |
cbf3488de9 | ||
![]() |
c72314fb71 | ||
![]() |
c4af6feb92 | ||
![]() |
08537c1d69 | ||
![]() |
425da82f5f | ||
![]() |
2cfbf7c39a | ||
![]() |
d4eed9f8fd | ||
![]() |
fe10170826 | ||
![]() |
8dc3b0b250 | ||
![]() |
75da1220af | ||
![]() |
37a2c3c631 | ||
![]() |
5c5722714d | ||
![]() |
2ba529f9e3 | ||
![]() |
fd760ff015 | ||
![]() |
daab1d917b | ||
![]() |
4c3a63a7e1 | ||
![]() |
7cc58b84da | ||
![]() |
2bac4ac1a7 | ||
![]() |
c5b2b86786 | ||
![]() |
5652a2b6c2 | ||
![]() |
bd19f543a2 | ||
![]() |
cc1e888227 | ||
![]() |
d73e379dcf | ||
![]() |
0569abd00d | ||
![]() |
7f5d9bec87 | ||
![]() |
b39e7bbb6d | ||
![]() |
70270a8e3b | ||
![]() |
efdc050a28 | ||
![]() |
e8a65df7f0 | ||
![]() |
59628a72fb | ||
![]() |
a4d6c6c0d8 | ||
![]() |
bea82c6640 | ||
![]() |
1ba3bdfbda | ||
![]() |
98b4000bc0 | ||
![]() |
14f6824931 | ||
![]() |
795d7d0a93 | ||
![]() |
673fa2b556 | ||
![]() |
0e2504fc78 | ||
![]() |
2afca9f2b4 | ||
![]() |
464d2a541d | ||
![]() |
fa8c5e0982 | ||
![]() |
5e15884d8f | ||
![]() |
b5e9ff3b4e | ||
![]() |
fed7d4cc34 | ||
![]() |
d7ab066ff8 | ||
![]() |
4100917016 | ||
![]() |
5d2c1ffb88 | ||
![]() |
ddb0f198a9 | ||
![]() |
13438e3e25 | ||
![]() |
05a410b327 | ||
![]() |
23fa64d289 | ||
![]() |
1920c9b7e3 | ||
![]() |
eedd0d9c07 | ||
![]() |
9ef389d335 | ||
![]() |
6542997520 | ||
![]() |
a58b2e2038 | ||
![]() |
6860e348dc | ||
![]() |
5e094e7597 | ||
![]() |
2f2cb8386b | ||
![]() |
965fd170bd | ||
![]() |
2610d29b60 | ||
![]() |
064131c842 | ||
![]() |
00b6bf8394 | ||
![]() |
2a885d709d | ||
![]() |
5bed46c0aa | ||
![]() |
8b27c7e01a | ||
![]() |
177902a286 | ||
![]() |
48b0f7dc27 | ||
![]() |
d5f4a1a48a | ||
![]() |
de9f60aa7f | ||
![]() |
c93b65b299 | ||
![]() |
3c6a6cdc5b | ||
![]() |
b669f3d715 | ||
![]() |
f663fac220 | ||
![]() |
bc42e79bb5 | ||
![]() |
ca29333cd0 | ||
![]() |
f9f478e100 | ||
![]() |
97c414d1ad | ||
![]() |
7afbd98d17 | ||
![]() |
1f5c60588e | ||
![]() |
284ab45a17 | ||
![]() |
eab6365af9 | ||
![]() |
de86516a0a | ||
![]() |
3e50e11933 | ||
![]() |
e2ac8be451 | ||
![]() |
0e53252a27 | ||
![]() |
b1ecff3d10 | ||
![]() |
0fee4fee2a | ||
![]() |
66282d817c | ||
![]() |
932c93e573 | ||
![]() |
71d30af582 | ||
![]() |
1c8428c3ea | ||
![]() |
e38e98d9e7 | ||
![]() |
85b3f081bf | ||
![]() |
3926d97fc6 | ||
![]() |
13ac8f2ea4 | ||
![]() |
d94f991ab5 | ||
![]() |
d476d2e96a | ||
![]() |
635bf364ac | ||
![]() |
e1c7a37f62 | ||
![]() |
9d780701f5 | ||
![]() |
0bd40405b5 | ||
![]() |
25c2f95e48 | ||
![]() |
5d738e58eb | ||
![]() |
70325f9247 | ||
![]() |
38c9c5a6ea | ||
![]() |
c90dd147bb | ||
![]() |
322f106e75 | ||
![]() |
91a5529438 | ||
![]() |
8f7dd2df6a | ||
![]() |
2fcd55eb60 | ||
![]() |
9359567a8a | ||
![]() |
42bfacfb19 | ||
![]() |
71131c699e | ||
![]() |
6ebfc516a6 | ||
![]() |
5c952b1d86 | ||
![]() |
1d9a4e0b99 | ||
![]() |
ebae628d8d | ||
![]() |
9865460fe5 | ||
![]() |
39884b71fe | ||
![]() |
82b7128c04 | ||
![]() |
16756ddb8c | ||
![]() |
877002961f | ||
![]() |
7e9e68ecd8 | ||
![]() |
6419190272 | ||
![]() |
ac42563c5e | ||
![]() |
98c1063e07 | ||
![]() |
a4dfc57cbe | ||
![]() |
db543b8912 | ||
![]() |
49fb4540a2 | ||
![]() |
e2120393a2 | ||
![]() |
0b301fff3f | ||
![]() |
eeb351e991 | ||
![]() |
1095e29b4d | ||
![]() |
be058eaff7 | ||
![]() |
f409dda2ef | ||
![]() |
f409cdda8f | ||
![]() |
9cd6396c35 | ||
![]() |
ee754ea533 | ||
![]() |
36de20dd75 | ||
![]() |
a957e8eb4f | ||
![]() |
14a90d84ec | ||
![]() |
fae9bc618a | ||
![]() |
3248e6500e | ||
![]() |
c17bf79d79 | ||
![]() |
1ff1270bfa | ||
![]() |
b1a2cf33d8 | ||
![]() |
b2292e98c1 | ||
![]() |
4d156a8911 | ||
![]() |
7193b6518b | ||
![]() |
cff6b44109 | ||
![]() |
fb7ad9438e | ||
![]() |
afc265a188 | ||
![]() |
01fe7bf612 | ||
![]() |
1cb75bd053 | ||
![]() |
0eaea4d011 | ||
![]() |
67377a2561 | ||
![]() |
a8aae9f1f5 | ||
![]() |
a9ce92decb | ||
![]() |
c19162295a | ||
![]() |
58796c45ed | ||
![]() |
d94b348780 | ||
![]() |
95f92bd292 | ||
![]() |
bc52ac3559 | ||
![]() |
8bbc6a6611 | ||
![]() |
8902b93a26 | ||
![]() |
ae36af807d | ||
![]() |
fd256625c6 | ||
![]() |
bee543a25a | ||
![]() |
55eb79cb52 | ||
![]() |
35965a8320 | ||
![]() |
8a902ae3e6 | ||
![]() |
52bed5bf98 | ||
![]() |
9e83f6d779 | ||
![]() |
0873beaed2 | ||
![]() |
0ba5012464 | ||
![]() |
73ff28465d | ||
![]() |
7484d65dbb | ||
![]() |
4a120e7a54 | ||
![]() |
8d63d85821 | ||
![]() |
5cec84a802 | ||
![]() |
48da41690d | ||
![]() |
1c82241f30 | ||
![]() |
b1ea3bcd4e | ||
![]() |
05e485b55e | ||
![]() |
c62e0e4e99 | ||
![]() |
3c6a1a02b8 | ||
![]() |
cc287607cd | ||
![]() |
b24e9a2185 | ||
![]() |
01791eac52 | ||
![]() |
651b57a93f | ||
![]() |
cc857364f4 | ||
![]() |
f29d7c8cfb | ||
![]() |
af4d0248d9 | ||
![]() |
2990664b2b | ||
![]() |
ed52038bc4 | ||
![]() |
ee125dfadc | ||
![]() |
196048cf38 | ||
![]() |
5faf357045 | ||
![]() |
4e0f06f24d | ||
![]() |
717530fff7 | ||
![]() |
4a5e38dc1e | ||
![]() |
419f8dadad | ||
![]() |
2dcae5e219 | ||
![]() |
36a99f70a3 | ||
![]() |
c98cf858c1 | ||
![]() |
dd463f00a7 | ||
![]() |
2459340c6f | ||
![]() |
76db5ffa3a | ||
![]() |
2db0e9c280 | ||
![]() |
db6dbe6c19 | ||
![]() |
ecedd4d231 | ||
![]() |
1809b95e2d | ||
![]() |
0d7e261bd1 | ||
![]() |
908941fb82 | ||
![]() |
fbacc4f789 | ||
![]() |
3c1290e8fd | ||
![]() |
35528ef602 | ||
![]() |
c0f0cb0d9e | ||
![]() |
4a65dc1d6e | ||
![]() |
b4a25e33bb | ||
![]() |
7f1a08dd04 | ||
![]() |
6152a1e913 | ||
![]() |
002cb93187 | ||
![]() |
381c3da31c | ||
![]() |
10e4d562ab | ||
![]() |
2a85e11ad9 | ||
![]() |
95b55760ad | ||
![]() |
636f898da8 | ||
![]() |
5fedac691d | ||
![]() |
979d68957e | ||
![]() |
a5b0837cf5 | ||
![]() |
8ba68dcfcf | ||
![]() |
8f367d140f | ||
![]() |
771885f27f | ||
![]() |
09aac22909 | ||
![]() |
1de3c0d559 | ||
![]() |
0988b68c8c | ||
![]() |
325ad4094e | ||
![]() |
16a09407e4 | ||
![]() |
84256f42c6 | ||
![]() |
7befbef6ec | ||
![]() |
754df5bea7 | ||
![]() |
78ee646558 | ||
![]() |
3d6f89d309 | ||
![]() |
e321479712 | ||
![]() |
9328b7e586 | ||
![]() |
50ace54cd0 | ||
![]() |
ad365c7dd0 | ||
![]() |
11427dbecd | ||
![]() |
b490831a50 | ||
![]() |
af76017a79 | ||
![]() |
43409b3089 | ||
![]() |
bfad769f93 | ||
![]() |
7e5dce1c14 | ||
![]() |
8ba4bebe01 | ||
![]() |
78a87db017 | ||
![]() |
b73a259f68 | ||
![]() |
65f27ee605 | ||
![]() |
6d5b5e15d5 | ||
![]() |
f31c4dcccd | ||
![]() |
1e616fa585 | ||
![]() |
0d2666f7d3 | ||
![]() |
0dd8970668 | ||
![]() |
89cda3dcff | ||
![]() |
1d48688518 | ||
![]() |
0b59f5e29c | ||
![]() |
5aebc8d191 | ||
![]() |
6e00c5da04 | ||
![]() |
50b06e041c | ||
![]() |
1c539f00dd | ||
![]() |
87ca432ec8 | ||
![]() |
ca33f4a2a5 | ||
![]() |
9409303f24 | ||
![]() |
94a3d35c90 | ||
![]() |
851de4934b | ||
![]() |
2942640eb9 | ||
![]() |
1cef037db5 | ||
![]() |
a00d43092d | ||
![]() |
45c2f50018 | ||
![]() |
ef8c6e82e6 | ||
![]() |
3eebb58da5 | ||
![]() |
447c50fd03 | ||
![]() |
0620ebebcf | ||
![]() |
cf081ee291 | ||
![]() |
3c29b8e9c5 | ||
![]() |
6143da5a6a | ||
![]() |
664f71575c | ||
![]() |
9ae111b8a1 | ||
![]() |
18682c7a2e | ||
![]() |
b21c50dfcf | ||
![]() |
49b6965e8e | ||
![]() |
c6cc2b8831 | ||
![]() |
f9f65eae53 | ||
![]() |
b51d442673 | ||
![]() |
5863b62ccf | ||
![]() |
66bb922012 | ||
![]() |
53876e8f0d | ||
![]() |
cb7ba7fdde | ||
![]() |
9cf6793b24 | ||
![]() |
6e62ffdd22 | ||
![]() |
c042d9e39a | ||
![]() |
1262de2ae2 | ||
![]() |
307230cec8 | ||
![]() |
4fa2711e78 | ||
![]() |
5c9c2f9ab8 | ||
![]() |
604155b41b | ||
![]() |
6e4198e7be | ||
![]() |
14d4940d05 | ||
![]() |
a6b0cdef97 | ||
![]() |
6265943607 | ||
![]() |
de39f7691c | ||
![]() |
921a219beb | ||
![]() |
b9c95d49a6 | ||
![]() |
fc0be6bce2 | ||
![]() |
8db891cfe6 | ||
![]() |
f6e77cc578 | ||
![]() |
a3782f9150 | ||
![]() |
7546c7ef42 | ||
![]() |
53de8cda30 | ||
![]() |
1fb7473dc5 | ||
![]() |
cc9d09bd54 | ||
![]() |
42ff4a2f62 | ||
![]() |
3fa5f80fc4 | ||
![]() |
9b5b7ef8db | ||
![]() |
560acf62fe | ||
![]() |
27d12922da | ||
![]() |
37b92f3d88 | ||
![]() |
79d5c0c92e | ||
![]() |
0bdaedd486 | ||
![]() |
018a201688 | ||
![]() |
a5bd7e6563 | ||
![]() |
a055feccd5 | ||
![]() |
ba68a9b52b | ||
![]() |
49669dc7e0 | ||
![]() |
5bdf79606e | ||
![]() |
ff3a9e47df | ||
![]() |
a18ba24f4a | ||
![]() |
baeb744a7c | ||
![]() |
d07add383f | ||
![]() |
a1f18bc133 | ||
![]() |
e00c23bc49 | ||
![]() |
0228a356e4 | ||
![]() |
b0fa0d534e | ||
![]() |
1157fda96c |
7
.gitignore
vendored
@@ -15,12 +15,17 @@
|
|||||||
version.lock
|
version.lock
|
||||||
logs/*
|
logs/*
|
||||||
cache/*
|
cache/*
|
||||||
|
*.mmdb
|
||||||
|
|
||||||
# HTTPS Cert/Key #
|
# HTTPS Cert/Key #
|
||||||
##################
|
##################
|
||||||
*.crt
|
*.crt
|
||||||
*.key
|
*.key
|
||||||
*.csr
|
*.csr
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Mergetool
|
||||||
|
*.orgin
|
||||||
|
|
||||||
# OS generated files #
|
# OS generated files #
|
||||||
######################
|
######################
|
||||||
@@ -31,7 +36,7 @@ Icon?
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
#Ignore files generated by PyCharm
|
#Ignore files generated by PyCharm
|
||||||
.idea/*
|
*.idea/*
|
||||||
|
|
||||||
#Ignore files generated by vi
|
#Ignore files generated by vi
|
||||||
*.swp
|
*.swp
|
||||||
|
486
CHANGELOG.md
@@ -1,5 +1,491 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.4.13 (2016-10-08)
|
||||||
|
|
||||||
|
* New: Option to set the number of days to keep PlexPy backups.
|
||||||
|
* New: Option to add a supplementary url to Pushover notifications.
|
||||||
|
* New: Option to set a timeout duration for script notifications.
|
||||||
|
* New: Added flush temporary sessions button to extra settings for emergency use.
|
||||||
|
* New: Added pms_image_proxy to the API.
|
||||||
|
* Fix: Insanely long play durations being recorded when connection to the Plex server is lost.
|
||||||
|
* Fix: Script notification output not being sent to the logger.
|
||||||
|
* Fix: New libraries not being added to homepage automatically.
|
||||||
|
* Fix: Success message shown incorrectly when sending a test notification.
|
||||||
|
* Fix: PlexPy log level filter not working.
|
||||||
|
* Fix: Admin username not shown in login logs.
|
||||||
|
* Fix: FeatHub link in readme document.
|
||||||
|
* Change: Posters disabled by default for all notification agents.
|
||||||
|
* Change: Disable manual changing of the PlexPy API key.
|
||||||
|
* Change: Force refresh the Plex.tv token when fetching a new token.
|
||||||
|
* Change: Script notifications run in a new thread with the timeout setting.
|
||||||
|
* Change: Watched percent moved to general settings.
|
||||||
|
* Change: Use human readable file sizes to the media info tables. (Thanks @logaritmisk)
|
||||||
|
* Change: Update pytz library.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.12 (2016-09-18)
|
||||||
|
|
||||||
|
* Fix: PMS update check not working for MacOSX.
|
||||||
|
* Fix: Square covers for music stats on homepage.
|
||||||
|
* Fix: Card width on the homepage for iPhone 6/7 Plus. (Thanks @XusBadia)
|
||||||
|
* Fix: Check for running PID when starting PlexPy. (Thanks @spolyack)
|
||||||
|
* Fix: FreeBSD service script not stopping PlexPy properly.
|
||||||
|
* Fix: Some web UI cleanup.
|
||||||
|
* Change: GitHub repostitory moved.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.11 (2016-09-02)
|
||||||
|
|
||||||
|
* Fix: PlexWatch and Plexivity import errors.
|
||||||
|
* Fix: Searching in history datatables.
|
||||||
|
* Fix: Notifications not sending for Local user.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.10 (2016-08-15)
|
||||||
|
|
||||||
|
* Fix: Missing python ipaddress module preventing PlexPy from starting.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.9 (2016-08-14)
|
||||||
|
|
||||||
|
* New: Option to include current activity in the history tables.
|
||||||
|
* New: ISP lookup info in the IP address modal.
|
||||||
|
* New: Option to disable web page previews for Telegram notifications.
|
||||||
|
* Fix: Send correct JSON header for Slack/Mattermost notifications.
|
||||||
|
* Fix: Twitter and Facebook test notifications incorrectly showing as "failed".
|
||||||
|
* Fix: Current activity progress bars extending past 100%.
|
||||||
|
* Fix: Typo in the setup wizard. (Thanks @wopian)
|
||||||
|
* Fix: Update PMS server version before checking for a new update.
|
||||||
|
* Change: Compare distro and build when checking for server updates.
|
||||||
|
* Change: Nicer y-axis intervals when viewing "Play Duration" graphs.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.8 (2016-07-16)
|
||||||
|
|
||||||
|
* New: Setting to specify PlexPy backup interval.
|
||||||
|
* Fix: User Concurrent Streams Notifications by IP Address checkbox not working.
|
||||||
|
* Fix: Substitute {update_version} in fallback PMS update notification text.
|
||||||
|
* Fix: Check version for automatic IP logging setting.
|
||||||
|
* Fix: Use library refresh interval.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.7 (2016-07-14)
|
||||||
|
|
||||||
|
* New: Use MaxMind GeoLite2 for IP address lookup.
|
||||||
|
* Note: The GeoLite2 database must be installed from the settings page.
|
||||||
|
* New: Check for Plex updates using plex.tv downloads instead of the server API.
|
||||||
|
* Note: Check for Plex updates has been disabled and must be re-enabled in the settings.
|
||||||
|
* New: More notification options for Plex updates.
|
||||||
|
* New: Notifications for concurrent streams by a single user.
|
||||||
|
* New: Notifications for user streaming from a new device.
|
||||||
|
* New: HipChat notification agent. (Thanks @aboron)
|
||||||
|
* Fix: Username showing as blank when friendly name is blank.
|
||||||
|
* Fix: Direct stream count wrong in the current activity header.
|
||||||
|
* Fix: Current activity reporting direct stream when reducing the stream quality switches to transcoding.
|
||||||
|
* Fix: Apostophe in an Arnold quote causing the shutdown/restart page to crash.
|
||||||
|
* Fix: Disable refreshing posters in guest mode.
|
||||||
|
* Fix: PlexWatch/Plexivity import unable to select the "grouped" database table.
|
||||||
|
* Change: Updated Facebook notification instructions.
|
||||||
|
* Change: Subject line optional for Join notifications.
|
||||||
|
* Change: Line break between subject and body text instead of a colon for Facebook, Slack, Twitter, and Telegram.
|
||||||
|
* Change: Allow Mattermost notifications using the Slack config.
|
||||||
|
* Change: Better formatting for Slack poster notifications.
|
||||||
|
* Change: Telegram only notifies once instead of twice when posters are enabled.
|
||||||
|
* Change: Host Open Sans font locally instead of querying Google Fonts.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.6 (2016-06-11)
|
||||||
|
|
||||||
|
* New: Added User and Library statistics to the API.
|
||||||
|
* New: Ability to refresh individual poster images without clearing the entire cache. (Thanks @Hellowlol)
|
||||||
|
* New: Added {added_date}, {updated_date}, and {last_viewed_date} to metadata notification options.
|
||||||
|
* New: Log level filter for Plex logs. (Thanks @sanderploegsma)
|
||||||
|
* New: Log level filter for PlexPy logs.
|
||||||
|
* New: Button to download Plex logs directly from the web interface.
|
||||||
|
* New: Advanced setting in the config file to change the number of Plex log lines retrieved.
|
||||||
|
* Fix: FreeBSD and FreeNAS init scripts to reflect the path in the installation guide. (Thanks @nortron)
|
||||||
|
* Fix: Monitoring crashing when failed to retrieve current activity.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.5 (2016-05-25)
|
||||||
|
|
||||||
|
* Fix: PlexPy unable to start if failed to get shared libraries for a user.
|
||||||
|
* Fix: Matching port number when retrieving the PMS url.
|
||||||
|
* Fix: Extract mapped IPv4 address in Plexivity import.
|
||||||
|
* Change: Revert back to internal url when retrieving PMS images.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.4 (2016-05-24)
|
||||||
|
|
||||||
|
* Fix: Image queries crashing the PMS when playing clips from channels.
|
||||||
|
* Fix: Plexivity import if IP address is missing.
|
||||||
|
* Fix: Tooltips shown behind the datatable headers.
|
||||||
|
* Fix: Current activity instances rendered in a random order causing them to jump around.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.3 (2016-05-22)
|
||||||
|
|
||||||
|
* Fix: PlexPy not starting without any authentication method.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.2 (2016-05-22)
|
||||||
|
|
||||||
|
* New: Option to use HTTP basic authentication instead of the HTML login form.
|
||||||
|
* Fix: Unable to save settings when enabling the HTTP proxy setting.
|
||||||
|
* Change: Match the PMS port when retrieving the PMS url.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.1 (2016-05-20)
|
||||||
|
|
||||||
|
* New: HTTP Proxy checkbox in the settings. Enable this if using an SSL enabled reverse proxy in front of PlexPy.
|
||||||
|
* Fix: Check for blank username/password on login.
|
||||||
|
* Fix: Persist current activity artwork blur across refreshes when transcoding details are visible.
|
||||||
|
* Fix: Send notifications to multiple XBMC/Plex Home Theater devices.
|
||||||
|
* Fix: Reset PMS identifier when clicking verify server button in settings.
|
||||||
|
* Fix: Crash when trying to group current activity session in database.
|
||||||
|
* Fix: Check current activity returns sessions when refreshing.
|
||||||
|
* Fix: Logs sorted out of order.
|
||||||
|
* Fix: Resolution reported incorrectly in the stream info modal.
|
||||||
|
* Fix: PlexPy crashing when hashing password in the config file.
|
||||||
|
* Fix: CherryPy doubling the port number when accessing PlexPy locally with http_proxy enabled.
|
||||||
|
* Change: Sort by most recent for ties in watch statistics.
|
||||||
|
* Change: Refresh Join devices when changing the API key.
|
||||||
|
* Change: Format the Join device IDs.
|
||||||
|
* Change: Join notifications now sent with Python Requests module.
|
||||||
|
* Change: Add paging for recently added in the API.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.0 (2016-05-15)
|
||||||
|
|
||||||
|
* New: An HTML form login page with sessions support.
|
||||||
|
* New: Guest access control for shared users using Plex.tv authentication.
|
||||||
|
* Enable the option in the settings and toggle guest access per user from Users > Edit mode.
|
||||||
|
* Guests can only view their own user data. Other user info is removed/masked.
|
||||||
|
* Guests can only view media from libraries that are shared with them (content rating and label filters are respected). Other libraries are removed/masked.
|
||||||
|
* All settings and admin controls are restricted from guests.
|
||||||
|
* All current activity on the server is shown, but with masked user/metadata info.
|
||||||
|
* New: Login logs table on the User and Logs pages.
|
||||||
|
* New: Filter the history table by user.
|
||||||
|
* New: Filter the graphs by user. (Thanks @Otger)
|
||||||
|
* New: Option to hash the admin passowrd in the config file.
|
||||||
|
* New: Options to enable/disable/rearrange each section on the homepage
|
||||||
|
* New: Toggle media types for recently added items on the homepage.
|
||||||
|
* New: Option to enter an Imgur API client ID for uploading posters.
|
||||||
|
* Note: The shared Imgur client id will be removed in a future PlexPy update! Please enter your own client id in the settings to continue uploading posters!
|
||||||
|
* New: HTML support for Email.
|
||||||
|
* New: Posters and HTML support for Telegram.
|
||||||
|
* New: Poster support for Slack.
|
||||||
|
* New: Poster support for Twitter.
|
||||||
|
* New: Re-added Plex Home Theater notification agent.
|
||||||
|
* New: Browser notification agent (experimental).
|
||||||
|
* New: Added {plex_url} as a notification option.
|
||||||
|
* New: Added transcode decision to the activity header.
|
||||||
|
* New: Documentation for APIv2 (see API.md for details).
|
||||||
|
* New: Import a Plexivity database into PlexPy.
|
||||||
|
* New: Prettier fallback image for art/episodes.
|
||||||
|
* New: Prettier confirm modal dialogues.
|
||||||
|
* New: Cache images to reduce Plex API calls. This can be disabled in the under Settings > Extra Settings. (Thanks @Hellowlol)
|
||||||
|
* New: Scheduled backups of the config file.
|
||||||
|
* New: Button to clear the PlexPy cache/images in the settings.
|
||||||
|
* New: Button to manually backup the PlexPy database/config in the settings.
|
||||||
|
* New: Button to clear the PlexPy logs in the settings.
|
||||||
|
* New: Button to download PlexPy log file on the Logs tab.
|
||||||
|
* New: Advanced setting in config file to change the Plex API timeout value.
|
||||||
|
* Fix: Mixed content HTTP request in settings (for reverse proxies with SSL).
|
||||||
|
* Fix: Rename recently "watched" music to "played".
|
||||||
|
* Change: Current activity details now persists across refreshes.
|
||||||
|
* Change: Smoother transitions between preview thumbnails in current activity.
|
||||||
|
* Change: Datatables now display all columns and scroll horizontally on smaller screens.
|
||||||
|
* Change: Ability to change the base URL for reverse proxies in the web interface.
|
||||||
|
* Change: Added a "Verify Server" button in the settings.
|
||||||
|
* Change: Added request status code in the logs for notifer errors.
|
||||||
|
* Change: Remove in-memory logs and read lines from log file instead. (Thanks @Hellowlol)
|
||||||
|
* Change: Limit number of failed attempts to write sessions to history. Default is 5 attempts.
|
||||||
|
* Change: A bunch of UI updates.
|
||||||
|
* Change: A bunch of backend code cleanup.
|
||||||
|
* Removed: All unused Python packages.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.16 (2016-05-01)
|
||||||
|
|
||||||
|
* Fix: Viewing photos crashing PlexPy.
|
||||||
|
* Fix: Persist Users > Edit mode on datatable page change.
|
||||||
|
* Fix: PMS update notifications broken.
|
||||||
|
* Change: Cache notifications poster with thread ID to avoid overwritting images.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.15 (2016-04-18)
|
||||||
|
|
||||||
|
* Fix: Slack notifications failing when using and icon URL.
|
||||||
|
* Fix: 127.0.0.1 showing as an external IP address on the history tables.
|
||||||
|
* Fix: Regression file sizes not shown in the media info table footer.
|
||||||
|
* Fix: Retrieving proper PMS URL when multiple connections are published to plex.tv.
|
||||||
|
* Fix: Some typos in the logger.
|
||||||
|
* Fix: Some other typos in the WebUI. (Thanks @xtjoeytx)
|
||||||
|
* Change: Optimized mobile web app icons and spash screens. (Thanks @alotufo)
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.14 (2016-03-29)
|
||||||
|
|
||||||
|
* Fix: Regression for missing notify_action for script notifications.
|
||||||
|
* Fix: Typo for home stats cards in the settings.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.13 (2016-03-27)
|
||||||
|
|
||||||
|
* Fix: Only mask strings longer than 5 characters in logs.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.12 (2016-03-27)
|
||||||
|
|
||||||
|
* Fix: "Check GitHub for updates" not rescheduling when toggling setting.
|
||||||
|
* Fix: Bug where notifications would fail if metadata is not found.
|
||||||
|
* Fix: Bug where notifications would fail if unable to upload poster to Imgur.
|
||||||
|
* Fix: PlexPy will now start properly for different Python environment variables.
|
||||||
|
* New: Feature requests moved to FeatHub.
|
||||||
|
* New: Ability to specify a GitHub API token for updates (optional).
|
||||||
|
* New: Mask out sensitive information from the logs.
|
||||||
|
* New: New and updated Arnold quotes. (Thanks @Vilsol & @Chrisophogus)
|
||||||
|
* New: "First" and "Last" page buttons to datatables.
|
||||||
|
* New: Access log file from the "Help & Info" page.
|
||||||
|
* New: CherryPy environment options (for development). (Thanks @codedecay)
|
||||||
|
* New: PlexPy development environment (for development only).
|
||||||
|
* Change: Facebook posts with a posters now include a summary.
|
||||||
|
* Change: Facebook posts now use a default poster if the poster is not found or unable to upload to Imgur.
|
||||||
|
* Change: IFTTT events can be fromatted with the {action} name.
|
||||||
|
* Change: Logs now use ISO date format to avoid locale encoding errors. (Thanks @alshain)
|
||||||
|
* Remove: Non-functioning Plex notification agent.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.11 (2016-03-15)
|
||||||
|
|
||||||
|
* Fix: Typo preventing history logging for websockets.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.10 (2016-03-12)
|
||||||
|
|
||||||
|
* Fix: Actually allow HTML tags for Pushover.
|
||||||
|
* Fix: PlexPy not restarting on Windows if there is a space in the folder path.
|
||||||
|
* Fix: Reconnect websocket when changing PMS SSL setting.
|
||||||
|
* Fix: Datatables not loading when view_offset or duration is blank.
|
||||||
|
* Fix: Bug when checking the PMS version in the settings.
|
||||||
|
* Fix: Auto-refreshing of log tables.
|
||||||
|
* Fix: Logging of IPv6 addresses. (PMS version >0.9.14 only.)
|
||||||
|
* Fix: Hide days selection from the Play Totals graph page.
|
||||||
|
* Fix: PlexPy overwriting user's own SSL certificate/key.
|
||||||
|
* Fix: Multiple watched notifications when using websocket.
|
||||||
|
* Fix: Some missing python library imports.
|
||||||
|
* Fix: Some typos in settings and PlexWatch importer.
|
||||||
|
* New: Ability to get notified of PMS updates.
|
||||||
|
* New: Ability to disable the link to Plex Web with Facebook notifications and use IMDB, TVDB, TMDb, or Last.fm instead.
|
||||||
|
* New: Ability to reset Imgur poster url from the info page if the poster is changed.
|
||||||
|
* New: Tooltips on the current activity progress bars.
|
||||||
|
* New: Side scrolling of Recently Added/Recently Played items.
|
||||||
|
* New: Document all date/time format options.
|
||||||
|
* New: Button to clear notification logs.
|
||||||
|
* New: Customizable backup, cache, and log directories.
|
||||||
|
* Change: Retry writing sessions to history if it fails, so sessions don't get lost. (Activity pinger only, not availble for websocket.)
|
||||||
|
* Change: Save any unknown sessions to the "Local" user.
|
||||||
|
* Change: History table modal is filtered depending on which graph series is clicked.
|
||||||
|
* Change: Revert back to saving the state of datatables (search, sorting, entries per page, etc.).
|
||||||
|
* Change: Newlines are not longer stripped from notification text which allows for finer control of how notifications look.
|
||||||
|
* Change: Updated FreeNAS/FreeBSD init scripts. (Must have updated jails.) (Thanks @chiviak)
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.9 (2016-02-21)
|
||||||
|
|
||||||
|
* Fix: Recently added notification not sent to all notification agents.
|
||||||
|
* New: Pushover HTML support. (Thanks @elseym)
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.8 (2016-02-21)
|
||||||
|
|
||||||
|
* Fix: Regression unable to clear HTTP password.
|
||||||
|
* Fix: Remove media tags from script arguments for server notifications.
|
||||||
|
* Fix: Encode poster titles to UTF-8 for Imgur upload.
|
||||||
|
* Fix: Allow notifications to send without poster if Imgur upload fails.
|
||||||
|
* New: Notification Logs table in the Logs tab.
|
||||||
|
* New: Toggle in settings to enable posters in notifications. (Disabled by default.)
|
||||||
|
* Change: Save Imgur poster URL to database so upload is not needed every time.
|
||||||
|
* Change: Notify log in database to log each event as a separate entry.
|
||||||
|
* Change: Monitor remote access is unchecked if remote access is disabled on server.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.7 (2016-02-20)
|
||||||
|
|
||||||
|
* Fix: Verifying server with SSL enabled.
|
||||||
|
* Fix: Regression where {stream_duration} reported as 0.
|
||||||
|
* Fix: Video metadata flags showing up for track info.
|
||||||
|
* Fix: Custom library icons not applied to Library Statistics.
|
||||||
|
* Fix: Typos in the Web UI.
|
||||||
|
* New: ETA to Current Activity overlay.
|
||||||
|
* New: Total duration to Libraries and Users tables.
|
||||||
|
* New: {machine_id} to notification options.
|
||||||
|
* New: IMDB, TVDB, TMDb, Last.fm, and Trackt IDs/URLs to notification options.
|
||||||
|
* New: {poster_url} to notification options using Imgur.
|
||||||
|
* New: Poster and link for Facebook notifications.
|
||||||
|
* New: Log javascript errors from the Web UI.
|
||||||
|
* New: Configuration and Scheduler info to the settings page.
|
||||||
|
* New: Schedule background task to backup the PlexPy database.
|
||||||
|
* New: URL anonymizer for external links.
|
||||||
|
* New: Plex Media Scanner log file to Log viewer.
|
||||||
|
* New: API v2 (sill very experimental). (Thanks @Hellowlol)
|
||||||
|
* Change: Allow secure websocket connections.
|
||||||
|
* Change: History grouping now accounts for the view offset.
|
||||||
|
* Change: Subject line can be toggled off for Facebook, Slack, Telegram, and Twitter.
|
||||||
|
* Change: Create self-signed SSL certificates when enabling HTTPS.
|
||||||
|
* Change: Revert homepage "Last Played" to "Last Watched".
|
||||||
|
* Change: Disable monitor remote access checkbox if remote access is not enabled on the PMS.
|
||||||
|
* Change: Disable IP logging checkbox if PMS version is 0.9.14 or greater.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.6 (2016-02-03)
|
||||||
|
|
||||||
|
* Fix: Regression where {duration} not reported in minutes.
|
||||||
|
* Fix: Proper daemonizing in FreeBSD and FreeNAS init scripts.
|
||||||
|
* Change: Update readme documentation.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.5 (2016-02-02)
|
||||||
|
|
||||||
|
* Fix: Removing unique constraints from database.
|
||||||
|
* Fix: Unable to expand media info table when missing "Added At" date.
|
||||||
|
* Fix: Server verification for unpublished servers.
|
||||||
|
* Fix: Updating PMS identifier for server change.
|
||||||
|
* New: {stream_time}, {remaining_time}, and {progress_time} to notification options.
|
||||||
|
* New: Powershell script support. (Thanks @Hellowlol)
|
||||||
|
* New: Method to delete duplicate libraries.
|
||||||
|
* Change: Daemonize before running start up tasks.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.4 (2016-01-29)
|
||||||
|
|
||||||
|
* Fix: Activity checker not starting with library update (history not logging).
|
||||||
|
* Fix: Libraries duplicated in database.
|
||||||
|
* Fix: Buffer notifications even when disabled when using websockets.
|
||||||
|
* Fix: Libraries and Users lists not refreshing.
|
||||||
|
* Fix: Server verification in settings.
|
||||||
|
* Fix: Empty libraries not added to database.
|
||||||
|
* New: Unique identifiers to notification options.
|
||||||
|
* Remove: Requirement of media type toggles for recently added notifications.
|
||||||
|
* Remove: Built in Twitter key and secret.
|
||||||
|
* Change: Unnecessary quoting of script arguments.
|
||||||
|
* Change: Facebook notification instructions.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.3 (2016-01-26)
|
||||||
|
|
||||||
|
* Fix: Plays by Month graph not loading.
|
||||||
|
* Change: Disable caching for datatables.
|
||||||
|
* Change: Improved updating library data in the database again.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.2 (2016-01-24)
|
||||||
|
|
||||||
|
* Fix: 'datestamp' and 'timestamp' for server notifications.
|
||||||
|
* Change: New method for updating library data in database.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.1 (2016-01-23)
|
||||||
|
|
||||||
|
* Fix: Notifiers authorization popups for reverse proxies.
|
||||||
|
* Fix: Empty brackets in titles on tables.
|
||||||
|
* Fix: Star rating overlapping text.
|
||||||
|
* Fix: Unable to startup when library refresh fails.
|
||||||
|
* Fix: Unable to parse 'datestamp' and 'timestamp' format.
|
||||||
|
* Change: Rename "Last Watched" to "Last Played".
|
||||||
|
* Change: More descriptive libraries updating message.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.3.0 (2016-01-23)
|
||||||
|
|
||||||
|
* New: Brand new Libraries section.
|
||||||
|
* New: Lots of new library statistics.
|
||||||
|
* New: Media info table for libraries.
|
||||||
|
* New: Web app for Android and iOS. (Thanks @zobe123)
|
||||||
|
* New: Slack notification agent. (Thanks @richipargo)
|
||||||
|
* New: Facebook notification agent.
|
||||||
|
* New: Custom script notification agent. (Thanks @Hellowlol)
|
||||||
|
* New: Custom "From Name" to email notification agent.
|
||||||
|
* New: Ability to test notifications / send custom one-off notifications.
|
||||||
|
* New: 'datestamp' and 'timestamp' notification options.
|
||||||
|
* New: More concurrent stream statistics.
|
||||||
|
* New: Media info flags on the info pages.
|
||||||
|
* New: Ability to fix broken metadata if the item has been moved in Plex.
|
||||||
|
* New: Ability to rearrange the homepage statistics cards.
|
||||||
|
* New: CentOS startup script (Thanks @PHoSawyer)
|
||||||
|
* Fix: Server name blank after first run wizard.
|
||||||
|
* Fix: Incorrect duration for grouped home stats.
|
||||||
|
* Fix: Allow SSL when verifying server in settings.
|
||||||
|
* Fix: Metadata for grouped recently added notifications.
|
||||||
|
* Fix: Unable to access settings with missing changelog file.
|
||||||
|
* Fix: Month name localization on play totals graphs.
|
||||||
|
* Fix: Get new PMS identifier when changing servers.
|
||||||
|
* Fix: Websocket log spam when there is no active session.
|
||||||
|
* Fix: Logs and cache folder not created in the data directory.
|
||||||
|
* Fix: Title links on sync table.
|
||||||
|
* Fix: Other various minor bugs and graphical glitches.
|
||||||
|
* Change: Prettier thumbnail popovers on tables.
|
||||||
|
* Change: Star ratings to use css/font-awesome.
|
||||||
|
* Change: More detailed logging info to warnings and errors.
|
||||||
|
* Change: Better PlexPy process restart handling (Thanks @jackwilsdon)
|
||||||
|
* Change: Massive behind the scenes code cleanup.
|
||||||
|
* Remove: Built in Pushover API token (User's own API token is now required).
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.16 (2015-12-22)
|
||||||
|
|
||||||
|
* Fix Most Concurrent stream stat for emtpy databases
|
||||||
|
* Change logs to 50 lines by default
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.15 (2015-12-20)
|
||||||
|
|
||||||
|
* Fix navbar covering current activity on smaller screens.
|
||||||
|
* Fix metadata for grouped recently added notifications.
|
||||||
|
* Fix Growl notification agent not working.
|
||||||
|
* Change graph days selection.
|
||||||
|
* Change watch statistics to match table history grouping.
|
||||||
|
* Add automatic discovery of Pushbullet devices.
|
||||||
|
* Add Most Concurrent Streams watch statistic.
|
||||||
|
* Add precentage to current activity progress bars.
|
||||||
|
* Add a bunch of stream details to notification options.
|
||||||
|
* Add notification for Plex Remote Access/Plex Media Server back up.
|
||||||
|
* Add CC/BCC and multiple recipients to email notification agent.
|
||||||
|
* Add total watch time to history table footer.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.14 (2015-12-07)
|
||||||
|
|
||||||
|
* Fix regression with PlexWatch db importer and buffer warnings.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.13 (2015-12-06)
|
||||||
|
|
||||||
|
* Fix match newlines between tags in notification text.
|
||||||
|
* Fix current activity not showing on PMS 0.9.12.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.12 (2015-12-06)
|
||||||
|
|
||||||
|
* Fix for "too many open files" error.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.11 (2015-12-06)
|
||||||
|
|
||||||
|
* Fix more regressions (sorry).
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.10 (2015-12-06)
|
||||||
|
|
||||||
|
* Fix broken count graphs regression.
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.9 (2015-12-06)
|
||||||
|
|
||||||
|
* Fix and improve text sanitization.
|
||||||
|
|
||||||
|
|
||||||
## v1.2.8 (2015-12-06)
|
## v1.2.8 (2015-12-06)
|
||||||
|
|
||||||
* Fix sanitize player names
|
* Fix sanitize player names
|
||||||
|
@@ -1,12 +1,45 @@
|
|||||||
# Contributing to PlexPy
|
# Contributing to PlexPy
|
||||||
|
|
||||||
## Issues
|
## 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 PlexPy, but don't expect step-by-step answers.
|
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. However, we are glad to help you, and make sure the problem is not caused by PlexPy, 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.
|
##### Many issues can simply be solved by:
|
||||||
* Visit the [Troubleshooting](../../wiki/TroubleShooting) wiki first.
|
|
||||||
* Use [proper formatting](https://help.github.com/articles/github-flavored-markdown/). Paste your logs in code blocks.
|
- Making sure you update to the latest version.
|
||||||
* Close your issue if you resolved it.
|
- Turning your device off and on again.
|
||||||
|
- Analyzing your logs, you just might find the solution yourself!
|
||||||
|
- Using the **search** function to see if this issue has already been reported/solved.
|
||||||
|
- Checking the [Wiki](https://github.com/JonnyWong16/plexpy/wiki) for
|
||||||
|
[ [Installation] ](https://github.com/JonnyWong16/plexpy/wiki/Installation) and
|
||||||
|
[ [FAQs] ](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
||||||
|
- For basic questions try asking on [Gitter](https://gitter.im/plexpy/general) or the [Plex Forums](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program) first before opening an issue.
|
||||||
|
|
||||||
|
##### If nothing has worked:
|
||||||
|
|
||||||
|
1. Open a new issue on the GitHub [issue tracker](http://github.com/JonnyWong16/plexpy/issues).
|
||||||
|
2. Provide a clear title to easily help identify your problem.
|
||||||
|
3. Use proper [markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your post (i.e. code/log in code blocks).
|
||||||
|
4. Make sure you provide the following information:
|
||||||
|
- [ ] Version
|
||||||
|
- [ ] Branch
|
||||||
|
- [ ] Commit hash
|
||||||
|
- [ ] Operating system
|
||||||
|
- [ ] Python version
|
||||||
|
- [ ] What you did?
|
||||||
|
- [ ] What happened?
|
||||||
|
- [ ] What you expected?
|
||||||
|
- [ ] How can we reproduce your issue?
|
||||||
|
- [ ] What are your (relevant) settings?
|
||||||
|
- [ ] Include a link to your **FULL** (not just a few lines!) log file that has the error. Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||||
|
5. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||||
|
|
||||||
|
## Feature Requests
|
||||||
|
|
||||||
|
Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
|
||||||
|
|
||||||
|
1. Search the existing requests to see if your suggestion has already been submitted.
|
||||||
|
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.
|
||||||
|
3. If no similar requests exist, you can create a new one. Make sure to provide a clear title to easily identify the feature request.
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.
|
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.
|
||||||
|
41
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!---
|
||||||
|
Reporting Issues:
|
||||||
|
* To ensure that a developer has enough information to work with please include all of the information below.
|
||||||
|
Please provide as much detail as possible. Screenshots can be very useful to see the problem.
|
||||||
|
* Use proper markdown syntax to structure your post (i.e. code/log in code blocks).
|
||||||
|
See: https://help.github.com/articles/basic-writing-and-formatting-syntax/
|
||||||
|
* Include a link to your **FULL** log file that has the error(not just a few lines!).
|
||||||
|
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||||
|
|
||||||
|
Feature Requests:
|
||||||
|
* Feature requests are handled on FeatHub: http://feathub.com/JonnyWong16/plexpy
|
||||||
|
* Do not post them on the GitHub issues tracker.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Version:**
|
||||||
|
|
||||||
|
**Branch:**
|
||||||
|
|
||||||
|
**Commit hash:**
|
||||||
|
|
||||||
|
**Operating system:**
|
||||||
|
|
||||||
|
**Python version:**
|
||||||
|
|
||||||
|
**What you did?**
|
||||||
|
|
||||||
|
**What happened?**
|
||||||
|
|
||||||
|
**What you expected?**
|
||||||
|
|
||||||
|
**How can we reproduce your issue?**
|
||||||
|
<!-- Provide a detailed step-by-step. -->
|
||||||
|
|
||||||
|
**What are your (relevant) settings?**
|
||||||
|
|
||||||
|
**Link to logs:**
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||||
|
-->
|
86
PlexPy.py
@@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/bin/sh
|
||||||
|
''''which python >/dev/null 2>&1 && exec python "$0" "$@" # '''
|
||||||
|
''''which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" # '''
|
||||||
|
''''which python2.7 >/dev/null 2>&1 && exec python2.7 "$0" "$@" # '''
|
||||||
|
''''exec echo "Error: Python not found!" # '''
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# This file is part of PlexPy.
|
# This file is part of PlexPy.
|
||||||
@@ -22,13 +27,14 @@ import sys
|
|||||||
# Ensure lib added to path, before any other imports
|
# Ensure lib added to path, before any other imports
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
||||||
|
|
||||||
from plexpy import webstart, logger, web_socket
|
|
||||||
|
|
||||||
import locale
|
|
||||||
import time
|
|
||||||
import signal
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import locale
|
||||||
|
import signal
|
||||||
|
import time
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
|
from plexpy import config, database, logger, web_socket, webstart
|
||||||
|
|
||||||
|
|
||||||
# Register signals, such as CTRL + C
|
# Register signals, such as CTRL + C
|
||||||
signal.signal(signal.SIGINT, plexpy.sig_handler)
|
signal.signal(signal.SIGINT, plexpy.sig_handler)
|
||||||
@@ -76,11 +82,14 @@ def main():
|
|||||||
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-p', '--port', type=int, help='Force PlexPy to run on a specified port')
|
'-p', '--port', type=int, help='Force PlexPy to run on a specified port')
|
||||||
|
parser.add_argument(
|
||||||
|
'--dev', action='store_true', help='Start PlexPy in the development environment')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--datadir', help='Specify a directory where to store your data files')
|
'--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(
|
||||||
parser.add_argument('--nolaunch', action='store_true',
|
'--config', help='Specify a config file to use')
|
||||||
help='Prevent browser from launching on startup')
|
parser.add_argument(
|
||||||
|
'--nolaunch', action='store_true', help='Prevent browser from launching on startup')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||||
|
|
||||||
@@ -95,6 +104,10 @@ def main():
|
|||||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
||||||
verbose=plexpy.VERBOSE)
|
verbose=plexpy.VERBOSE)
|
||||||
|
|
||||||
|
if args.dev:
|
||||||
|
plexpy.DEV = True
|
||||||
|
logger.debug(u"PlexPy is running in the dev environment.")
|
||||||
|
|
||||||
if args.daemon:
|
if args.daemon:
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
@@ -109,8 +122,21 @@ def main():
|
|||||||
# If the pidfile already exists, plexpy may still be running, so
|
# If the pidfile already exists, plexpy may still be running, so
|
||||||
# exit
|
# exit
|
||||||
if os.path.exists(plexpy.PIDFILE):
|
if os.path.exists(plexpy.PIDFILE):
|
||||||
raise SystemExit("PID file '%s' already exists. Exiting." %
|
try:
|
||||||
plexpy.PIDFILE)
|
with open(plexpy.PIDFILE, 'r') as fp:
|
||||||
|
pid = int(fp.read())
|
||||||
|
os.kill(pid, 0)
|
||||||
|
except IOError as e:
|
||||||
|
raise SystemExit("Unable to read PID file: %s", e)
|
||||||
|
except OSError:
|
||||||
|
logger.warn("PID file '%s' already exists, but PID %d is " \
|
||||||
|
"not running. Ignoring PID file." %
|
||||||
|
(plexpy.PIDFILE, pid))
|
||||||
|
else:
|
||||||
|
# The pidfile exists and points to a live PID. plexpy may
|
||||||
|
# still be running, so exit.
|
||||||
|
raise SystemExit("PID file '%s' already exists. Exiting." %
|
||||||
|
plexpy.PIDFILE)
|
||||||
|
|
||||||
# The pidfile is only useful in daemon mode, make sure we can write the
|
# The pidfile is only useful in daemon mode, make sure we can write the
|
||||||
# file properly
|
# file properly
|
||||||
@@ -135,7 +161,7 @@ def main():
|
|||||||
if args.config:
|
if args.config:
|
||||||
config_file = args.config
|
config_file = args.config
|
||||||
else:
|
else:
|
||||||
config_file = os.path.join(plexpy.DATA_DIR, 'config.ini')
|
config_file = os.path.join(plexpy.DATA_DIR, config.FILENAME)
|
||||||
|
|
||||||
# Try to create the DATA_DIR if it doesn't exist
|
# Try to create the DATA_DIR if it doesn't exist
|
||||||
if not os.path.exists(plexpy.DATA_DIR):
|
if not os.path.exists(plexpy.DATA_DIR):
|
||||||
@@ -151,13 +177,26 @@ def main():
|
|||||||
'Cannot write to the data directory: ' + plexpy.DATA_DIR + '. Exiting...')
|
'Cannot write to the data directory: ' + plexpy.DATA_DIR + '. Exiting...')
|
||||||
|
|
||||||
# Put the database in the DATA_DIR
|
# Put the database in the DATA_DIR
|
||||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, 'plexpy.db')
|
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, database.FILENAME)
|
||||||
|
|
||||||
|
if plexpy.DAEMON:
|
||||||
|
plexpy.daemonize()
|
||||||
|
|
||||||
# Read config and start logging
|
# Read config and start logging
|
||||||
plexpy.initialize(config_file)
|
plexpy.initialize(config_file)
|
||||||
|
|
||||||
if plexpy.DAEMON:
|
# Start the background threads
|
||||||
plexpy.daemonize()
|
plexpy.start()
|
||||||
|
|
||||||
|
# Open connection for websocket
|
||||||
|
if plexpy.CONFIG.MONITORING_USE_WEBSOCKET:
|
||||||
|
try:
|
||||||
|
web_socket.start_thread()
|
||||||
|
except:
|
||||||
|
logger.warn(u"Websocket :: Unable to open connection.")
|
||||||
|
# Fallback to polling
|
||||||
|
plexpy.POLLING_FAILOVER = True
|
||||||
|
plexpy.initialize_scheduler()
|
||||||
|
|
||||||
# Force the http port if neccessary
|
# Force the http port if neccessary
|
||||||
if args.port:
|
if args.port:
|
||||||
@@ -181,30 +220,19 @@ def main():
|
|||||||
'http_port': http_port,
|
'http_port': http_port,
|
||||||
'http_host': plexpy.CONFIG.HTTP_HOST,
|
'http_host': plexpy.CONFIG.HTTP_HOST,
|
||||||
'http_root': plexpy.CONFIG.HTTP_ROOT,
|
'http_root': plexpy.CONFIG.HTTP_ROOT,
|
||||||
|
'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
|
||||||
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
|
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
|
||||||
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
|
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
|
||||||
'https_cert': plexpy.CONFIG.HTTPS_CERT,
|
'https_cert': plexpy.CONFIG.HTTPS_CERT,
|
||||||
'https_key': plexpy.CONFIG.HTTPS_KEY,
|
'https_key': plexpy.CONFIG.HTTPS_KEY,
|
||||||
'http_username': plexpy.CONFIG.HTTP_USERNAME,
|
'http_username': plexpy.CONFIG.HTTP_USERNAME,
|
||||||
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
|
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
|
||||||
|
'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
|
||||||
}
|
}
|
||||||
webstart.initialize(web_config)
|
webstart.initialize(web_config)
|
||||||
|
|
||||||
# Start the background threads
|
|
||||||
plexpy.start()
|
|
||||||
|
|
||||||
# Open connection for websocket
|
|
||||||
if plexpy.CONFIG.MONITORING_USE_WEBSOCKET:
|
|
||||||
try:
|
|
||||||
web_socket.start_thread()
|
|
||||||
except:
|
|
||||||
logger.warn(u"Websocket :: Unable to open connection.")
|
|
||||||
# Fallback to polling
|
|
||||||
plexpy.POLLING_FAILOVER = True
|
|
||||||
plexpy.initialize_scheduler()
|
|
||||||
|
|
||||||
# Open webbrowser
|
# Open webbrowser
|
||||||
if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch:
|
if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch and not plexpy.DEV:
|
||||||
plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, http_port,
|
plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, http_port,
|
||||||
plexpy.CONFIG.HTTP_ROOT)
|
plexpy.CONFIG.HTTP_ROOT)
|
||||||
|
|
||||||
|
174
README.md
@@ -1,121 +1,87 @@
|
|||||||
#PlexPy
|
# PlexPy
|
||||||
|
|
||||||
[](https://gitter.im/drzoidberg33/plexpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/plexpy/general?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
A python based web application for monitoring, analytics and notifications for Plex Media Server (www.plex.tv).
|
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
|
||||||
|
|
||||||
This project is based on code from Headphones (https://github.com/rembo10/headphones) and PlexWatchWeb (https://github.com/ecleese/plexWatchWeb).
|
This project is based on code from [Headphones](https://github.com/rembo10/headphones) and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb).
|
||||||
|
|
||||||
* PlexPy forum thread: https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program
|
* [Plex forum thread](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
|
||||||
|
* [Gitter chat](https://gitter.im/plexpy/general)
|
||||||
|
* [/r/Plex Discord server](https://discord.gg/011TFFWSuNFI02EKr) | [PlexPy Discord server](https://discord.gg/36ggawe)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
###Support
|
* Responsive web design viewable on desktop, tablet and mobile web browsers.
|
||||||
-----------
|
* Themed to complement Plex/Web.
|
||||||
* PlexPy Wiki: https://github.com/drzoidberg33/plexpy/wiki
|
* Easy configuration setup (no separate web server required).
|
||||||
|
* Monitor current Plex Media Server activity.
|
||||||
|
* Fully customizable notifications for stream activity and recently added media.
|
||||||
|
* Top statistics on home page with configurable duration and measurement metric.
|
||||||
|
* Global watching history with search/filtering & dynamic column sorting.
|
||||||
|
* Full user list with general information and comparison stats.
|
||||||
|
* Individual user information including devices IP addresses.
|
||||||
|
* Complete library statistics and media file information.
|
||||||
|
* Rich analytics presented using Highcharts graphing.
|
||||||
|
* Beautiful content information pages.
|
||||||
|
* Full sync list data on all users syncing items from your library.
|
||||||
|
* And many more!!
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
|
||||||
###Features
|
* [Full preview gallery on Imgur](https://imgur.com/a/RwQPM)
|
||||||
-----------
|
|
||||||
* Responsive web design viewable on desktop, tablet and mobile web browsers
|
|
||||||
|
|
||||||
* Themed to complement Plex/Web
|

|
||||||
|
|
||||||
* Easy configuration setup via html form
|
## Installation and Support
|
||||||
|
|
||||||
* Current Plex Media Server viewing activity including:
|
* [Installation Guides](https://github.com/JonnyWong16/plexpy/wiki/Installation) shows you how to install PlexPy.
|
||||||
* number of current users
|
* [FAQs](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)) in the wiki can help you with common problems.
|
||||||
* title
|
|
||||||
* progress
|
|
||||||
* platform
|
|
||||||
* user
|
|
||||||
* state (playing, paused, buffering, etc)
|
|
||||||
* stream type (direct, transcoded)
|
|
||||||
* video type & resolution
|
|
||||||
* audio type & channel count.
|
|
||||||
|
|
||||||
* Top statistics on home page with configurable duration and measurement metric:
|
|
||||||
* Most watched TV
|
|
||||||
* Most popular TV
|
|
||||||
* Most watched Movie
|
|
||||||
* Most popular Movie
|
|
||||||
* Most active user
|
|
||||||
* Most active platform
|
|
||||||
|
|
||||||
* Recently added media and how long ago it was added
|
|
||||||
|
|
||||||
* Global watching history with search/filtering & dynamic column sorting
|
|
||||||
* date
|
|
||||||
* user
|
|
||||||
* platform
|
|
||||||
* ip address
|
|
||||||
* title
|
|
||||||
* stream information details
|
|
||||||
* start time
|
|
||||||
* paused duration length
|
|
||||||
* stop time
|
|
||||||
* duration length
|
|
||||||
* watched progress
|
|
||||||
* show/hide columns
|
|
||||||
* delete mode - allows deletion of specific history items
|
|
||||||
|
|
||||||
* Full user list with general information and comparison stats
|
|
||||||
|
|
||||||
* Individual user information
|
|
||||||
* username and gravatar (if available)
|
|
||||||
* daily, weekly, monthly, all time stats for play count and duration length
|
|
||||||
* individual platform stats for each user
|
|
||||||
* public ip address history with last seen date and geo tag location
|
|
||||||
* recently watched content
|
|
||||||
* watching history
|
|
||||||
* synced items
|
|
||||||
* assign users custom friendly names within PlexPy
|
|
||||||
* assign users custom avatar URL within PlexPy
|
|
||||||
* disable history logging per user
|
|
||||||
* disable notifications per user
|
|
||||||
* option to purge all history per user.
|
|
||||||
|
|
||||||
* Rich analytics presented using Highcharts graphing
|
|
||||||
* user-selectable time periods of 30, 90 or 365 days
|
|
||||||
* daily watch count and duration
|
|
||||||
* totals by day of week and hours of the day
|
|
||||||
* totals by top 10 platform
|
|
||||||
* totals by top 10 users
|
|
||||||
* detailed breakdown by transcode decision
|
|
||||||
* source and stream resolutions
|
|
||||||
* transcode decision counts by user and platform
|
|
||||||
* total monthly counts
|
|
||||||
|
|
||||||
* Content information pages
|
|
||||||
* movies (includes watching history)
|
|
||||||
* tv shows (includes watching history)
|
|
||||||
* tv seasons
|
|
||||||
* tv episodes (includes watching history)
|
|
||||||
|
|
||||||
* Full sync list data on all users syncing items from your library
|
|
||||||
|
|
||||||
## Installation and Notes
|
|
||||||
|
|
||||||
* [Installation page](../../wiki/Installation) shows you how to install PlexPy.
|
|
||||||
* [Usage guide](../../wiki/Usage-guide) introduces you to PlexPy.
|
|
||||||
* [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/drzoidberg33/plexpy/issues).
|
|
||||||
|
|
||||||
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
##### Many issues can simply be solved by:
|
||||||
|
|
||||||
|
- Making sure you update to the latest version.
|
||||||
|
- Turning your device off and on again.
|
||||||
|
- Analyzing your logs, you just might find the solution yourself!
|
||||||
|
- Using the **search** function to see if this issue has already been reported/solved.
|
||||||
|
- Checking the [Wiki](https://github.com/JonnyWong16/plexpy/wiki) for
|
||||||
|
[ [Installation] ](https://github.com/JonnyWong16/plexpy/wiki/Installation) and
|
||||||
|
[ [FAQs] ](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
||||||
|
- For basic questions try asking on [Gitter](https://gitter.im/plexpy/general) or the [Plex Forums](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program) first before opening an issue.
|
||||||
|
|
||||||
|
##### If nothing has worked:
|
||||||
|
|
||||||
|
1. Open a new issue on the GitHub [issue tracker](http://github.com/JonnyWong16/plexpy/issues).
|
||||||
|
2. Provide a clear title to easily help identify your problem.
|
||||||
|
3. Use proper [markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your post (i.e. code/log in code blocks).
|
||||||
|
4. Make sure you provide the following information:
|
||||||
|
- [ ] Version
|
||||||
|
- [ ] Branch
|
||||||
|
- [ ] Commit hash
|
||||||
|
- [ ] Operating system
|
||||||
|
- [ ] Python version
|
||||||
|
- [ ] What you did?
|
||||||
|
- [ ] What happened?
|
||||||
|
- [ ] What you expected?
|
||||||
|
- [ ] How can we reproduce your issue?
|
||||||
|
- [ ] What are your (relevant) settings?
|
||||||
|
- [ ] Include a link to your **FULL** (not just a few lines!) log file that has the error. Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||||
|
5. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||||
|
|
||||||
|
## Feature Requests
|
||||||
|
|
||||||
|
Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
|
||||||
|
|
||||||
|
1. Search the existing requests to see if your suggestion has already been submitted.
|
||||||
|
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.
|
||||||
|
3. If no similar requests exist, you can create a new one. Make sure to provide a clear title to easily identify the feature request.
|
||||||
|
|
||||||
## License
|
## 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.
|
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.
|
||||||
|
|
||||||
This software includes Highsoft software libraries which you may freely distribute for non-commercial use. Commerical users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.
|
This software includes Highsoft software libraries which you may freely distribute for non-commercial use. Commerical users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.
|
@@ -2,11 +2,18 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
<h4 class="modal-title">Import PlexWatch Database</h4>
|
<h4 class="modal-title">Import ${app} Database</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="modal-text">
|
<div class="modal-body" id="modal-text">
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Please ensure your PlexWatch database is at version 0.3.2 or higher.
|
<%
|
||||||
|
v = ''
|
||||||
|
if app == 'PlexWatch':
|
||||||
|
v = '0.3.2'
|
||||||
|
elif app == 'Plexivity':
|
||||||
|
v = '0.9.8'
|
||||||
|
%>
|
||||||
|
<strong>Please ensure your ${app} database is at version ${v} or higher.</strong>
|
||||||
</p>
|
</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="db_location">Database Location</label>
|
<label for="db_location">Database Location</label>
|
||||||
@@ -15,7 +22,7 @@
|
|||||||
<input type="text" class="form-control" id="db_location" name="db_location" value="" required>
|
<input type="text" class="form-control" id="db_location" name="db_location" value="" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">Enter the path and file name for the PlexWatch database you wish to import.</p>
|
<p class="help-block">Enter the path and file name for the ${app} database you wish to import.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="table_name">Table Name</label>
|
<label for="table_name">Table Name</label>
|
||||||
@@ -23,7 +30,7 @@
|
|||||||
<div class="col-xs-4">
|
<div class="col-xs-4">
|
||||||
<select id="table_name" class="form-control" name="table_name">
|
<select id="table_name" class="form-control" name="table_name">
|
||||||
<option value="processed">processed</option>
|
<option value="processed">processed</option>
|
||||||
<option value="processed">grouped</option>
|
<option value="grouped">grouped</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<div>
|
<div>
|
||||||
<span id="status-message"></span>
|
<span id="status-message" style="padding-right: 25px;"></span>
|
||||||
<input type="button" id="import_db" class="btn btn-bright" value="Import">
|
<input type="button" id="import_db" class="btn btn-bright" value="Import">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,8 +61,13 @@
|
|||||||
var table_name = $("#table_name").val();
|
var table_name = $("#table_name").val();
|
||||||
var import_ignore_interval = $("#import_ignore_interval").val();
|
var import_ignore_interval = $("#import_ignore_interval").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_plexwatch_export_data',
|
url: 'import_database',
|
||||||
data: {database_path: database_path, table_name:table_name, import_ignore_interval:import_ignore_interval},
|
data: {
|
||||||
|
app: "${app}",
|
||||||
|
database_path: database_path,
|
||||||
|
table_name: table_name,
|
||||||
|
import_ignore_interval: import_ignore_interval
|
||||||
|
},
|
||||||
cache: false,
|
cache: false,
|
||||||
async: true,
|
async: true,
|
||||||
success: function(data) {
|
success: function(data) {
|
@@ -1,6 +1,7 @@
|
|||||||
<%
|
<%
|
||||||
import plexpy
|
import plexpy
|
||||||
from plexpy import version
|
from plexpy import version
|
||||||
|
from plexpy.helpers import anon_url
|
||||||
%>
|
%>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
||||||
@@ -11,37 +12,145 @@ from plexpy import version
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
<link href="interfaces/default/css/bootstrap3/bootstrap.css" rel="stylesheet">
|
<link href="${http_root}css/bootstrap3/bootstrap.css" rel="stylesheet">
|
||||||
<link href="interfaces/default/css/plexpy.css" rel="stylesheet">
|
<link href="${http_root}css/pnotify.custom.min.css" rel="stylesheet" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
|
<link href="${http_root}css/plexpy.css" rel="stylesheet">
|
||||||
<link href="interfaces/default/css/font-awesome.min.css" rel="stylesheet">
|
<link href="${http_root}css/opensans.min.css" rel="stylesheet">
|
||||||
|
<link href="${http_root}css/font-awesome.min.css" rel="stylesheet">
|
||||||
${next.headIncludes()}
|
${next.headIncludes()}
|
||||||
|
|
||||||
<link rel="icon" type="image/x-icon" href="interfaces/default/images/favicon.ico"/>
|
<link rel="icon" type="image/x-icon" href="${http_root}images/favicon.ico"/>
|
||||||
<!-- touch icons -->
|
<link rel="shortcut icon" href="${http_root}images/favicon.png">
|
||||||
<link rel="shortcut icon" href="interfaces/default/images/favicon.png">
|
|
||||||
<link rel="apple-touch-icon" href="interfaces/default/images/icon_iphone.png">
|
<!-- Allow web app to be run in full-screen mode. -->
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="interfaces/default/images/icon_ipad.png">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="interfaces/default/images/icon_iphone@2x.png">
|
<!-- Configure the status bar. -->
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="interfaces/default/images/icon_ipad@2x.png">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<!-- Set the viewport. -->
|
||||||
|
<meta name="viewport" content="initial-scale=1">
|
||||||
|
<!-- Disable automatic phone number detection. -->
|
||||||
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
|
||||||
|
<!-- ICONS -->
|
||||||
|
<!-- IE10 icon -->
|
||||||
|
<meta name="application-name" content="PlexPy" />
|
||||||
|
<meta name="msapplication-config" content="${http_root}xml/IEconfig.xml"/>
|
||||||
|
<!-- Android >M39 icon -->
|
||||||
|
<link rel="manifest" href="${http_root}json/Android-manifest.json">
|
||||||
|
<!-- iPad retina icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-76@2x.png" sizes="152x152" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPad retina icon (iOS < 7) -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-72@2x.png" sizes="144x144" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPad non-retina icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-76.png" sizes="76x76" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPad non-retina icon (iOS < 7) -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-72.png" sizes="72x72" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPhone 6 Plus icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-60@2x.png" sizes="120x120" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPhone retina icon (iOS < 7) -->
|
||||||
|
<link href="${http_root}images/res/ios/icon@2x.png" sizes="114x114" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPhone non-retina icon (iOS < 7) -->
|
||||||
|
<link href="${http_root}images/res/ios/icon.png" sizes="57x57" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPhone / iPod Touch -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-60@3x.png" sizes="180x180" rel="apple-touch-icon-precomposed">
|
||||||
|
<link href="${http_root}images/res/ios/icon-60.png" sizes="60x60" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- Spotlight Icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-40.png" sizes="40x40" rel="apple-touch-icon-precomposed">
|
||||||
|
<link href="${http_root}images/res/ios/icon-40@2x.png" sizes="80x80" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPhone Spotlight and Settings Icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-small.png" sizes="29x29" rel="apple-touch-icon-precomposed">
|
||||||
|
<link href="${http_root}images/res/ios/icon-small@2x.png" sizes="58x58" rel="apple-touch-icon-precomposed">
|
||||||
|
<!-- iPad Spotlight and Settings Icon -->
|
||||||
|
<link href="${http_root}images/res/ios/icon-50.png" sizes="50x50" rel="apple-touch-icon-precomposed">
|
||||||
|
<link href="${http_root}images/res/ios/icon-50@2x.png" sizes="100x100" rel="apple-touch-icon-precomposed">
|
||||||
|
|
||||||
|
<!-- STARTUP IMAGES -->
|
||||||
|
<!-- iPad retina portrait startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-Portrait@2x~ipad.png"
|
||||||
|
media="(device-width: 768px) and (device-height: 1024px)
|
||||||
|
and (-webkit-device-pixel-ratio: 2)
|
||||||
|
and (orientation: portrait)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPad retina landscape startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-Landscape@2x~ipad.png"
|
||||||
|
media="(device-width: 768px) and (device-height: 1024px)
|
||||||
|
and (-webkit-device-pixel-ratio: 2)
|
||||||
|
and (orientation: landscape)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPad non-retina portrait startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-Portrait~ipad.png"
|
||||||
|
media="(device-width: 768px) and (device-height: 1024px)
|
||||||
|
and (-webkit-device-pixel-ratio: 1)
|
||||||
|
and (orientation: portrait)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPad non-retina landscape startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-Landscape~ipad.png"
|
||||||
|
media="(device-width: 768px) and (device-height: 1024px)
|
||||||
|
and (-webkit-device-pixel-ratio: 1)
|
||||||
|
and (orientation: landscape)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone 6 Plus portrait startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-736h.png"
|
||||||
|
media="(device-width: 414px) and (device-height: 736px)
|
||||||
|
and (-webkit-device-pixel-ratio: 3)
|
||||||
|
and (orientation: portrait)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone 6 Plus landscape startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-Landscape-736h.png"
|
||||||
|
media="(device-width: 414px) and (device-height: 736px)
|
||||||
|
and (-webkit-device-pixel-ratio: 3)
|
||||||
|
and (orientation: landscape)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone 6 startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-667h.png"
|
||||||
|
media="(device-width: 375px) and (device-height: 667px)
|
||||||
|
and (-webkit-device-pixel-ratio: 2)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone 5 startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default-568h@2x~iphone5.jpg"
|
||||||
|
media="(device-width: 320px) and (device-height: 568px)
|
||||||
|
and (-webkit-device-pixel-ratio: 2)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone < 5 retina startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default@2x~iphone.png"
|
||||||
|
media="(device-width: 320px) and (device-height: 480px)
|
||||||
|
and (-webkit-device-pixel-ratio: 2)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
<!-- iPhone < 5 non-retina startup image -->
|
||||||
|
<link href="${http_root}images/res/screen/ios/Default~iphone.png"
|
||||||
|
media="(device-width: 320px) and (device-height: 480px)
|
||||||
|
and (-webkit-device-pixel-ratio: 1)"
|
||||||
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="content">
|
<body class="content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
% if plexpy.CONFIG.CHECK_GITHUB and not plexpy.CURRENT_VERSION:
|
% if plexpy.CONFIG.CHECK_GITHUB and not plexpy.CURRENT_VERSION:
|
||||||
<div id="updatebar" style="display: none;">
|
<div id="updatebar" style="display: none;">
|
||||||
You're running an unknown version of PlexPy. <a href="update">Update</a> or
|
You're running an unknown version of PlexPy.<br />
|
||||||
<a href="#" id="updateDismiss">Close</a>
|
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||||
</div>
|
</div>
|
||||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
|
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
|
||||||
<div id="updatebar" style="display: none;">
|
<div id="updatebar" style="display: none;">
|
||||||
A <a
|
A <a href="${anon_url('https://github.com/%s/plexpy/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.CURRENT_VERSION, plexpy.LATEST_VERSION))}" target="_blank">
|
||||||
href="https://github.com/${plexpy.CONFIG.GIT_USER}/plexpy/compare/${plexpy.CURRENT_VERSION}...${plexpy.LATEST_VERSION}" target="_blank">
|
newer version</a> is available.<br />
|
||||||
newer version</a> is available. You're ${plexpy.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or
|
You're ${plexpy.COMMITS_BEHIND} commits behind.<br />
|
||||||
<a href="#" id="updateDismiss">Close</a>
|
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
% endif
|
||||||
<nav class="navbar navbar-fixed-top">
|
<nav class="navbar navbar-fixed-top">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
@@ -52,16 +161,16 @@ from plexpy import version
|
|||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="home">
|
<a class="navbar-brand" href="home">
|
||||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40">
|
<img alt="PlexPy" src="${http_root}images/logo-plexpy@2x.png" height="40">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
|
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li>
|
<li class="hidden-sm hidden-xs">
|
||||||
<form action="search" method="post" class="form" id="search_form">
|
<form action="search" method="post" class="form" id="search_form">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-textbox">
|
<span class="input-textbox">
|
||||||
<input type="text" class="form-control" name="query" id="query" aria-label="Search" placeholder="Search..."/>
|
<input type="text" class="form-control" name="query" id="query" aria-label="Search" placeholder="Search Plex library..."/>
|
||||||
</span>
|
</span>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
|
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
|
||||||
@@ -69,56 +178,150 @@ from plexpy import version
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
% if title=="Home":
|
% if title == "Home":
|
||||||
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||||
% else:
|
% else:
|
||||||
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||||
% endif
|
% endif
|
||||||
% if title=="Users" or title=="User":
|
% if title == "Libraries" or title == "Library" or title == "Info":
|
||||||
<li class="active"><a href="users">Users</a></li>
|
<li class="active"><a href="libraries">Libraries</a></li>
|
||||||
% else:
|
% else:
|
||||||
<li><a href="users">Users</a></li>
|
<li><a href="libraries">Libraries</a></li>
|
||||||
% endif
|
% endif
|
||||||
% if title=="History":
|
% if title == "Users" or title == "User":
|
||||||
|
<li class="active"><a href="users">Users</a></li>
|
||||||
|
% else:
|
||||||
|
<li><a href="users">Users</a></li>
|
||||||
|
% endif
|
||||||
|
% if title == "History":
|
||||||
<li class="active"><a href="history">History</a></li>
|
<li class="active"><a href="history">History</a></li>
|
||||||
% else:
|
% else:
|
||||||
<li><a href="history">History</a></li>
|
<li><a href="history">History</a></li>
|
||||||
% endif
|
% endif
|
||||||
% if title=="Graphs":
|
% if title == "Graphs":
|
||||||
<li class="active"><a href="graphs">Graphs</a></li>
|
<li class="active"><a href="graphs">Graphs</a></li>
|
||||||
% else:
|
% else:
|
||||||
<li><a href="graphs">Graphs</a></li>
|
<li><a href="graphs">Graphs</a></li>
|
||||||
% endif
|
% endif
|
||||||
% if title=="Synced Items":
|
% if title == "Synced Items":
|
||||||
<li class="active"><a href="sync">Synced Items</a></li>
|
<li class="active"><a href="sync">Synced Items</a></li>
|
||||||
% else:
|
% else:
|
||||||
<li><a href="sync">Synced Items</a></li>
|
<li><a href="sync">Synced Items</a></li>
|
||||||
% endif
|
% endif
|
||||||
% if title=="Log":
|
% if title == "Settings":
|
||||||
<li class="active"><a href="logs">Logs</a></li>
|
<li class="dropdown active">
|
||||||
% else:
|
% else:
|
||||||
<li><a href="logs">Logs</a></li>
|
<li class="dropdown">
|
||||||
% endif
|
|
||||||
% if title=="Settings":
|
|
||||||
<li class="active"><a href="settings">Settings</a></li>
|
|
||||||
% else:
|
|
||||||
<li><a href="settings">Settings</a></li>
|
|
||||||
% endif
|
% endif
|
||||||
|
<% href = 'settings' if _session['user_group'] == 'admin' else '#' %>
|
||||||
|
<a href="#" class="dropdown-toggle" aria-haspopup="true" data-toggle="dropdown" data-hover="dropdown" data-href="${href}"><i class="fa fa-lg fa-cogs"></i> <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu" id="settings-dropdown-menu">
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<li><a href="settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a href="logs"><i class="fa fa-fw fa-list-alt"></i> View Logs</a></li>
|
||||||
|
<li><a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li>
|
||||||
|
<li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DG783BMSCU3V4')}" target="_blank"><i class="fa fa-fw fa-paypal"></i> Paypal</a></li>
|
||||||
|
<li><a href="${anon_url('http://swiftpanda16.tip.me/')}" target="_blank"><i class="fa fa-fw fa-btc"></i> Bitcoin</a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
% if plexpy.CONFIG.CHECK_GITHUB:
|
||||||
|
<li><a href="#" id="nav-update"><i class="fa fa-fw fa-arrow-circle-up"></i> Check for Updates</a></li>
|
||||||
|
% endif
|
||||||
|
<li><a href="#" id="nav-restart"><i class="fa fa-fw fa-refresh"></i> Restart</a></li>
|
||||||
|
<li><a href="#" id="nav-shutdown"><i class="fa fa-fw fa-power-off"></i> Shutdown</a></li>
|
||||||
|
% else:
|
||||||
|
<li><a href="#" data-target="#admin-login-modal" data-toggle="modal"><i class="fa fa-fw fa-lock"></i> Admin Login</a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
% endif
|
||||||
|
% if _session['expiry']:
|
||||||
|
<li><a href="${http_root}auth/logout"><i class="fa fa-fw fa-sign-out"></i> Sign Out</a></li>
|
||||||
|
% endif
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">Confirm</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="confirm-message" style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-button">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% if _session['user_group'] != 'admin':
|
||||||
|
<div id="admin-login-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="admin-login-modal">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form action="${http_root}auth/login" method="post">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">Admin Login</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="col-md-6" style="margin: auto;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" class="control-label">
|
||||||
|
Username
|
||||||
|
</label>
|
||||||
|
<input type="text" id="username" name="username" class="form-control" autocorrect="off" autocapitalize="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="control-label">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="form-footer">
|
||||||
|
<div class="remember-group">
|
||||||
|
<label class="control-label">
|
||||||
|
<input type="checkbox" id="remember_me" name="remember_me" title="for 30 days" value="1" checked="checked" /> Remember me
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-bright login-button"><i class="fa fa-sign-in"></i> Sign In</button>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="admin_login" name="admin_login" value="1" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
|
||||||
${next.headerIncludes()}
|
${next.headerIncludes()}
|
||||||
<div class="body-container">
|
<div class="body-container">
|
||||||
${next.body()}
|
${next.body()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="interfaces/default/js/jquery-2.1.4.min.js"></script>
|
<script src="${http_root}js/jquery-2.1.4.min.js"></script>
|
||||||
<script src="interfaces/default/js/bootstrap3/bootstrap.min.js"></script>
|
<script src="${http_root}js/bootstrap.min.js"></script>
|
||||||
<script src="interfaces/default/js/script.js"></script>
|
<script src="${http_root}js/bootstrap-hover-dropdown.min.js"></script>
|
||||||
|
<script src="${http_root}js/pnotify.custom.min.js"></script>
|
||||||
|
<script src="${http_root}js/script.js"></script>
|
||||||
|
% if _session['user_group'] == 'admin' and plexpy.CONFIG.BROWSER_ENABLED:
|
||||||
|
<script src="${http_root}js/ajaxNotifications.js"></script>
|
||||||
|
% endif
|
||||||
<script>
|
<script>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
$('#updateDismiss').click(function() {
|
$('#updateDismiss').click(function() {
|
||||||
$('#updatebar').slideUp('slow');
|
$('#updatebar').slideUp('slow');
|
||||||
// Set cookie to remember dismiss decision for 1 hour.
|
// Set cookie to remember dismiss decision for 1 hour.
|
||||||
@@ -128,28 +331,77 @@ ${next.headerIncludes()}
|
|||||||
if (!getCookie('updateDismiss')) {
|
if (!getCookie('updateDismiss')) {
|
||||||
$('#updatebar').show();
|
$('#updatebar').show();
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
<script>
|
$("#nav-shutdown").click(function() {
|
||||||
|
$("#confirm-message").text("Are you sure you want to shutdown PlexPy?");
|
||||||
|
$('#confirm-modal').modal();
|
||||||
|
$('#confirm-modal').one('click', '#confirm-button', function () {
|
||||||
|
window.location.href = "shutdown";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#nav-restart").click(function() {
|
||||||
|
$("#confirm-message").text("Are you sure you want to restart PlexPy?");
|
||||||
|
$('#confirm-modal').modal();
|
||||||
|
$('#confirm-modal').one('click', '#confirm-button', function () {
|
||||||
|
window.location.href = "restart";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#nav-update").first().one("click", function () {
|
||||||
|
// Allow the update bar to show again if previously dismissed.
|
||||||
|
setCookie('updateDismiss', 'true', 0);
|
||||||
|
$(this).html('<i class="fa fa-spin fa-refresh"></i> Checking');
|
||||||
|
window.location.href = "checkGithub";
|
||||||
|
});
|
||||||
|
% endif
|
||||||
|
|
||||||
|
$('.dropdown-toggle').click(function (e) {
|
||||||
|
if (!(('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))) {
|
||||||
|
window.location.href = $(this).data('href');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#search_form').submit(function (e) {
|
$('#search_form').submit(function (e) {
|
||||||
if ($('#query').hasClass('active') && $('#query').val().trim() != '') {
|
if ($('#query').hasClass('active') && $('#query').val().trim() != '') {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: 'search',
|
url: 'search',
|
||||||
data: { 'query': $('#query').val() }
|
data: { query: $('#query').val() }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#search_button').removeClass('btn-inactive');
|
$('#search_button').removeClass('btn-inactive');
|
||||||
$('#query').clearQueue().val('').animate({ right: '0', width: '250px' }).addClass('active').focus();
|
$('#query').clearQueue().val('').animate({ right: '0', width: '200px' }).addClass('active').focus();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$('#query').on('blur', function (e) {
|
$('#query').on('blur', function (e) {
|
||||||
if ($(this).val().trim() == '') {
|
if ($(this).val().trim() == '') {
|
||||||
$(this).delay(200).animate({ right: '-250px', width: '0' }, function () {
|
$(this).delay(200).animate({ right: '-200px', width: '0' }, function () {
|
||||||
$('#search_button').addClass('btn-inactive');
|
$('#search_button').addClass('btn-inactive');
|
||||||
}).removeClass('active');
|
}).removeClass('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
// Work around for iOS web app links opening in Safari
|
||||||
|
if (("standalone" in window.navigator) && window.navigator.standalone) {
|
||||||
|
// For iOS Apps
|
||||||
|
$('a').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var new_location = $(this).attr('href');
|
||||||
|
if (new_location != undefined && new_location.substr(0, 1) != '#' && $(this).attr('data-method') == undefined) {
|
||||||
|
window.location = new_location;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
% if _session['user_group'] != 'admin':
|
||||||
|
$('#admin-login-modal').on('shown.bs.modal', function () {
|
||||||
|
$('#admin-login-modal #username').focus()
|
||||||
|
})
|
||||||
|
% endif
|
||||||
</script>
|
</script>
|
||||||
${next.javascriptIncludes()}
|
${next.javascriptIncludes()}
|
||||||
</body>
|
</body>
|
||||||
|
172
data/interfaces/default/configuration_table.html
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<%doc>
|
||||||
|
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||||
|
|
||||||
|
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||||
|
|
||||||
|
Filename: configuration_table.html
|
||||||
|
Version: 0.1
|
||||||
|
|
||||||
|
DOCUMENTATION :: END
|
||||||
|
</%doc>
|
||||||
|
|
||||||
|
<%!
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import plexpy
|
||||||
|
from plexpy import common, logger
|
||||||
|
from plexpy.helpers import anon_url
|
||||||
|
%>
|
||||||
|
|
||||||
|
<table class="config-info-table small-muted">
|
||||||
|
<tbody>
|
||||||
|
% if plexpy.CURRENT_VERSION:
|
||||||
|
<tr>
|
||||||
|
<td>Git Branch:</td>
|
||||||
|
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/tree/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CONFIG.GIT_BRANCH}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Git Commit Hash:</td>
|
||||||
|
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/commit/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CURRENT_VERSION}</a></td>
|
||||||
|
</tr>
|
||||||
|
% endif
|
||||||
|
<tr>
|
||||||
|
<td>Configuration File:</td>
|
||||||
|
<td>${plexpy.CONFIG_FILE}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Database File:</td>
|
||||||
|
<td>${plexpy.DB_FILE}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Log File:</td>
|
||||||
|
<td><a class="no-highlight" href="logFile" target="_blank">${os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Backup Directory:</td>
|
||||||
|
<td>${plexpy.CONFIG.BACKUP_DIR}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Cache Directory:</td>
|
||||||
|
<td>${plexpy.CONFIG.CACHE_DIR}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>GeoLite2 Database:</td>
|
||||||
|
% if plexpy.CONFIG.GEOIP_DB:
|
||||||
|
<td>${plexpy.CONFIG.GEOIP_DB} | <a class="no-highlight" href="#" id="reinstall_geoip_db">Reinstall / Update</a> | <a class="no-highlight" href="#" id="uninstall_geoip_db">Uninstall</a></td>
|
||||||
|
% else:
|
||||||
|
<td><a class="no-highlight" href="#" id="install_geoip_db">Click here to install the GeoLite2 database.</a></td>
|
||||||
|
% endif
|
||||||
|
</tr>
|
||||||
|
% if plexpy.ARGS:
|
||||||
|
<tr>
|
||||||
|
<td>Arguments:</td>
|
||||||
|
<td>${plexpy.ARGS}</td>
|
||||||
|
</tr>
|
||||||
|
% endif
|
||||||
|
<tr>
|
||||||
|
<td>Platform:</td>
|
||||||
|
<td>${common.PLATFORM} ${common.PLATFORM_VERSION}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Python Version:</td>
|
||||||
|
<td>${sys.version}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="top-line">Resources:</td>
|
||||||
|
<td class="top-line">
|
||||||
|
<a id="source-link" class="no-highlight" href="${anon_url('https://github.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" target="_blank">GitHub Source</a> |
|
||||||
|
<a class="no-highlight guidelines-modal-link" href="${anon_url('https://github.com/%s/plexpy/issues' % plexpy.CONFIG.GIT_USER)}" data-id="issue">GitHub Issues</a> |
|
||||||
|
<a class="no-highlight guidelines-modal-link" href="${anon_url('http://feathub.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" data-id="feature request">FeatHub Feature Requests</a> |
|
||||||
|
<a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki' % plexpy.CONFIG.GIT_USER)}" target="_blank">PlexPy Wiki</a> |
|
||||||
|
<a id="faq-source-link" class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank">PlexPy FAQ</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Support:</td>
|
||||||
|
<td>
|
||||||
|
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program')}" target="_blank">Plex Forums</a> |
|
||||||
|
<a class="no-highlight support-modal-link" href="${anon_url('https://gitter.im/plexpy/general')}" target="_blank">PlexPy Gitter Chat</a> |
|
||||||
|
<a id="best-support-link" class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/011TFFWSuNFI02EKr')}" target="_blank">/r/Plex Discord Server</a> |
|
||||||
|
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/36ggawe')}" target="_blank">PlexPy Discord Server</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="guidelines-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="guidelines-modal">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">Guidelines</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||||
|
<strong>Please read the <a href="#" target="_blank" id="guidelines-link">guidelines</a> in the README document <br />before submitting a new <span id="guidelines-type"></span>!</strong>
|
||||||
|
<br /><br />
|
||||||
|
Your post may be removed for failure to follow the guidelines.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#" target="_blank" id="guidelines-continue" class="btn btn-bright">Continue</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="support-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="support-modal">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">Support</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||||
|
<strong>Please read the <a href="#" target="_blank" id="faq-link">FAQ</a> before asking for help!</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#" target="_blank" id="support-continue" class="btn btn-bright">Continue</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$("#install_geoip_db, #reinstall_geoip_db").click(function () {
|
||||||
|
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
|
||||||
|
'The database is used to lookup IP address geolocation info.<br />' +
|
||||||
|
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
|
||||||
|
'and requires <strong>100MB</strong> of free space to install in your PlexPy directory.<br />'
|
||||||
|
var url = 'install_geoip_db';
|
||||||
|
confirmAjaxCall(url, msg, 'Installing GeoLite2 database.', getConfigurationTable);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#uninstall_geoip_db").click(function () {
|
||||||
|
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
|
||||||
|
'You will not be able to lookup IP address geolocation info.';
|
||||||
|
var url = 'uninstall_geoip_db';
|
||||||
|
confirmAjaxCall(url, msg, 'Uninstalling GeoLite2 database.', getConfigurationTable);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.guidelines-modal-link').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#guidelines-link').attr('href', $('#source-link').attr('href'));
|
||||||
|
$('#guidelines-type').text($(this).data('id'))
|
||||||
|
$('#guidelines-modal').modal();
|
||||||
|
$('#guidelines-continue').attr('href', $(this).attr('href')).on('click', function () {
|
||||||
|
$('#guidelines-modal').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('.support-modal-link').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#faq-link').attr('href', $('#faq-source-link').attr('href'));
|
||||||
|
$('#support-modal').modal();
|
||||||
|
$('#support-continue').attr('href', $(this).attr('href')).on('click', function () {
|
||||||
|
$('#support-modal').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
1
data/interfaces/default/css/opensans.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@font-face{font-family:'Open Sans';font-weight:400;font-style:normal;src:url(../fonts/Open-Sans-regular/Open-Sans-regular.eot);src:url(../fonts/Open-Sans-regular/Open-Sans-regular.eot?#iefix) format('embedded-opentype'),local('Open Sans'),local('Open-Sans-regular'),url(../fonts/Open-Sans-regular/Open-Sans-regular.woff2) format('woff2'),url(../fonts/Open-Sans-regular/Open-Sans-regular.woff) format('woff'),url(../fonts/Open-Sans-regular/Open-Sans-regular.ttf) format('truetype'),url(../fonts/Open-Sans-regular/Open-Sans-regular.svg#OpenSans) format('svg')}@font-face{font-family:'Open Sans';font-weight:600;font-style:normal;src:url(../fonts/Open-Sans-600/Open-Sans-600.eot);src:url(../fonts/Open-Sans-600/Open-Sans-600.eot?#iefix) format('embedded-opentype'),local('Open Sans Semibold'),local('Open-Sans-600'),url(../fonts/Open-Sans-600/Open-Sans-600.woff2) format('woff2'),url(../fonts/Open-Sans-600/Open-Sans-600.woff) format('woff'),url(../fonts/Open-Sans-600/Open-Sans-600.ttf) format('truetype'),url(../fonts/Open-Sans-600/Open-Sans-600.svg#OpenSans) format('svg')}
|
1
data/interfaces/default/css/pnotify.custom.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.ui-pnotify{top:36px;right:36px;position:absolute;height:auto;z-index:2}body>.ui-pnotify{position:fixed;z-index:100040}.ui-pnotify-modal-overlay{background-color:rgba(0,0,0,.4);top:0;left:0;position:absolute;height:100%;width:100%;z-index:1}body>.ui-pnotify-modal-overlay{position:fixed;z-index:100039}.ui-pnotify.ui-pnotify-in{display:block!important}.ui-pnotify.ui-pnotify-move{transition:left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-slow{transition:opacity .6s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-slow.ui-pnotify.ui-pnotify-move{transition:opacity .6s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-normal{transition:opacity .4s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-normal.ui-pnotify.ui-pnotify-move{transition:opacity .4s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-fast{transition:opacity .2s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-fast.ui-pnotify.ui-pnotify-move{transition:opacity .2s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-in{opacity:1}.ui-pnotify .ui-pnotify-shadow{-webkit-box-shadow:0 6px 28px 0 rgba(0,0,0,.1);-moz-box-shadow:0 6px 28px 0 rgba(0,0,0,.1);box-shadow:0 6px 28px 0 rgba(0,0,0,.1)}.ui-pnotify-container{background-position:0 0;padding:.8em;height:100%;margin:0}.ui-pnotify-container:after{content:" ";visibility:hidden;display:block;height:0;clear:both}.ui-pnotify-container.ui-pnotify-sharp{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.ui-pnotify-title{display:block;margin-bottom:.4em;margin-top:0}.ui-pnotify-text{display:block}.ui-pnotify-icon,.ui-pnotify-icon span{display:block;float:left;margin-right:.2em}.ui-pnotify.stack-bottomleft,.ui-pnotify.stack-topleft{left:25px;right:auto}.ui-pnotify.stack-bottomleft,.ui-pnotify.stack-bottomright{bottom:25px;top:auto}.ui-pnotify.stack-modal{left:50%;right:auto;margin-left:-150px}.ui-pnotify-closer,.ui-pnotify-sticker{float:right;margin-left:.2em}.ui-pnotify-container{position:relative;left:0}@media (max-width:480px){.ui-pnotify-mobile-able.ui-pnotify{position:fixed;top:0;right:0;left:0;width:auto!important;font-size:1.2em;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;-ms-font-smoothing:antialiased;font-smoothing:antialiased}.ui-pnotify-mobile-able.ui-pnotify .ui-pnotify-shadow{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-bottom-width:5px}.ui-pnotify-mobile-able .ui-pnotify-container{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft,.ui-pnotify-mobile-able.ui-pnotify.stack-topleft{left:0;right:0}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft,.ui-pnotify-mobile-able.ui-pnotify.stack-bottomright{left:0;right:0;bottom:0;top:auto}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft .ui-pnotify-shadow,.ui-pnotify-mobile-able.ui-pnotify.stack-bottomright .ui-pnotify-shadow{border-top-width:5px;border-bottom-width:1px}}
|
@@ -68,20 +68,23 @@ DOCUMENTATION :: END
|
|||||||
% if data['stream_count'] != '0':
|
% if data['stream_count'] != '0':
|
||||||
% for a in data['sessions']:
|
% for a in data['sessions']:
|
||||||
<div class="dashboard-instance" id="instance-${a['session_key']}">
|
<div class="dashboard-instance" id="instance-${a['session_key']}">
|
||||||
% if a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track':
|
% if (a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track') and a['rating_key']:
|
||||||
<a href="info?item_id=${a['rating_key']}">
|
<a href="info?rating_key=${a['rating_key']}">
|
||||||
|
% else:
|
||||||
|
<a href="#">
|
||||||
% endif
|
% endif
|
||||||
<div class="dashboard-activity-poster">
|
<div class="dashboard-activity-poster">
|
||||||
% if a['media_type'] == 'movie' and not a['indexes']:
|
% if not a['art'].startswith('interfaces') or not a['thumb'].startswith('interfaces'):
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
% if (a['media_type'] == 'movie' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
|
||||||
% elif a['media_type'] == 'episode' and not a['indexes']:
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
% elif (a['media_type'] == 'episode' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
% elif a['indexes']:
|
% elif a['indexes']:
|
||||||
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280); display: none;"></div>
|
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280&fallback=art); display: none;"></div>
|
||||||
% else:
|
% else:
|
||||||
% if a['media_type'] == 'track':
|
% if a['media_type'] == 'track':
|
||||||
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300);"></div>
|
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300);"></div>
|
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
% elif a['media_type'] == 'clip':
|
% elif a['media_type'] == 'clip':
|
||||||
% if a['art'][:4] == 'http':
|
% if a['art'][:4] == 'http':
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
|
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
|
||||||
@@ -89,17 +92,23 @@ DOCUMENTATION :: END
|
|||||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['thumb']});"></div>
|
<div class="dashboard-activity-poster-face" style="background-image: url(${a['thumb']});"></div>
|
||||||
% else:
|
% else:
|
||||||
% if a['art']:
|
% if a['art']:
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
% else:
|
% else:
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280);"></div>
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280&fallback=art);"></div>
|
||||||
% endif
|
% endif
|
||||||
% endif
|
% endif
|
||||||
% elif a['media_type'] == 'photo':
|
% elif a['media_type'] == 'photo':
|
||||||
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=500);"></div>
|
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=500&fallback=cover);"></div>
|
||||||
% else:
|
% else:
|
||||||
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
|
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
% endif
|
% endif
|
||||||
% endif
|
% endif
|
||||||
|
% else:
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
|
||||||
|
% endif
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
<div class="dashboard-activity-button-info">
|
<div class="dashboard-activity-button-info">
|
||||||
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${a['session_key']}">
|
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${a['session_key']}">
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
@@ -198,6 +207,13 @@ DOCUMENTATION :: END
|
|||||||
% else:
|
% else:
|
||||||
<span>IP: N/A</span>
|
<span>IP: N/A</span>
|
||||||
% endif
|
% endif
|
||||||
|
<br />
|
||||||
|
ETA:
|
||||||
|
<span id="stream-eta-${a['session_key']}">
|
||||||
|
<script>
|
||||||
|
$("#stream-eta-${a['session_key']}").html(moment().add(parseInt(${a['duration']}) - parseInt(${a['view_offset']}), 'milliseconds').format(time_format));
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-activity-poster-info-time">
|
<div class="dashboard-activity-poster-info-time">
|
||||||
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||||
@@ -205,19 +221,25 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% if a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track':
|
% if (a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track') and a['rating_key']:
|
||||||
|
</a>
|
||||||
|
% else:
|
||||||
</a>
|
</a>
|
||||||
% endif
|
% endif
|
||||||
<div class="dashboard-activity-progress">
|
<div class="dashboard-activity-progress">
|
||||||
<div class="dashboard-activity-progress-bar">
|
<div class="dashboard-activity-progress-bar">
|
||||||
<div class="bufferbar" style="width: ${a['transcode_progress']}%">${a['transcode_progress']}%</div>
|
<div class="bufferbar" style="width: ${a['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress">${a['transcode_progress']}%</div>
|
||||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
<div class="bar" style="width: ${a['progress_percent']}%" data-toggle="tooltip" title="Stream Progress">${a['progress_percent']}%</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-activity-metadata-wrapper">
|
<div class="dashboard-activity-metadata-wrapper">
|
||||||
|
% if a['user_id']:
|
||||||
<a href="user?user_id=${a['user_id']}">
|
<a href="user?user_id=${a['user_id']}">
|
||||||
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
|
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
|
||||||
|
% endif
|
||||||
<div class="dashboard-activity-metadata-title">
|
<div class="dashboard-activity-metadata-title">
|
||||||
% if a['state'] == 'playing':
|
% if a['state'] == 'playing':
|
||||||
<i class="fa fa-play"></i>
|
<i class="fa fa-play"></i>
|
||||||
@@ -226,38 +248,44 @@ DOCUMENTATION :: END
|
|||||||
% elif a['state'] == 'buffering':
|
% elif a['state'] == 'buffering':
|
||||||
<i class="fa fa-spinner"></i>
|
<i class="fa fa-spinner"></i>
|
||||||
% endif
|
% endif
|
||||||
|
% if a['rating_key']:
|
||||||
% if a['media_type'] == 'episode':
|
% if a['media_type'] == 'episode':
|
||||||
<a href="info?item_id=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
|
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||||
% elif a['media_type'] == 'movie':
|
% elif a['media_type'] == 'movie':
|
||||||
<a href="info?item_id=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
|
<a href="info?rating_key=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
|
||||||
% elif a['media_type'] == 'clip':
|
% elif a['media_type'] == 'clip':
|
||||||
<span title="${a['title']}">${a['title']}</span>
|
<span title="${a['title']}">${a['title']}</span>
|
||||||
% elif a['media_type'] == 'track':
|
% elif a['media_type'] == 'track':
|
||||||
<a href="info?item_id=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
|
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||||
% elif a['media_type'] == 'photo':
|
% elif a['media_type'] == 'photo':
|
||||||
<span title="${a['parent_title']}">${a['parent_title']}</span>
|
<span title="${a['parent_title']}">${a['parent_title']}</span>
|
||||||
% else:
|
% else:
|
||||||
<span title="${a['title']}">${a['title']}</span>
|
<span title="${a['title']}">${a['title']}</span>
|
||||||
% endif
|
% endif
|
||||||
|
% else:
|
||||||
|
${a['title']}
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-activity-metadata-subtitle">
|
<div class="dashboard-activity-metadata-subtitle">
|
||||||
|
% if a['rating_key']:
|
||||||
% if a['media_type'] == 'episode':
|
% if a['media_type'] == 'episode':
|
||||||
<span title="S${a['parent_media_index']} · E${a['media_index']}">S${a['parent_media_index']} · E${a['media_index']}</span>
|
<span title="S${a['parent_media_index']} · E${a['media_index']}">S${a['parent_media_index']} · E${a['media_index']}</span>
|
||||||
% elif a['media_type'] == 'movie':
|
% elif a['media_type'] == 'movie':
|
||||||
<span title="${a['year']}">${a['year']}</span>
|
<span title="${a['year']}">${a['year']}</span>
|
||||||
% elif a['media_type'] == 'track':
|
% elif a['media_type'] == 'track':
|
||||||
<a href="info?item_id=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
|
<a href="info?rating_key=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
|
||||||
% elif a['media_type'] == 'photo':
|
% elif a['media_type'] == 'photo':
|
||||||
<span title="${a['title']}">${a['title']}</span>
|
<span title="${a['title']}">${a['title']}</span>
|
||||||
% else:
|
% else:
|
||||||
<span title="${a['year']}">${a['year']}</span>
|
<span title="${a['year']}">${a['year']}</span>
|
||||||
% endif
|
% endif
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-activity-metadata-user">
|
<div class="dashboard-activity-metadata-user">
|
||||||
% if a['user_id']:
|
% if a['user_id']:
|
||||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||||
% else:
|
% else:
|
||||||
<a href="user?user=${a['user']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
${a['friendly_name']}
|
||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,6 +308,15 @@ DOCUMENTATION :: END
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$($(this).attr('data-target')).toggle();
|
$($(this).attr('data-target')).toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add hover class to dashboard-instance
|
||||||
|
$('.dashboard-activity-poster, .dashboard-activity-progress-bar').hover(function() {
|
||||||
|
$(this).closest('.dashboard-instance').addClass('hover');
|
||||||
|
}, function() {
|
||||||
|
$(this).closest('.dashboard-instance').removeClass('hover');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.bar, .bufferbar').tooltip({container: 'body', placement: 'right', delay: 50});
|
||||||
</script>
|
</script>
|
||||||
% else:
|
% else:
|
||||||
<div class="text-muted">Nothing is currently being watched.</div><br>
|
<div class="text-muted">Nothing is currently being watched.</div><br>
|
||||||
|
@@ -15,11 +15,24 @@ DOCUMENTATION :: END
|
|||||||
</%doc>
|
</%doc>
|
||||||
|
|
||||||
% if data != None:
|
% if data != None:
|
||||||
% if data == '0':
|
<%
|
||||||
<h3>Activity</h3>
|
s = '('
|
||||||
|
if data['direct_play']:
|
||||||
|
s += str(data['direct_play']) + ' direct play' + ('s' if data['direct_play'] > 1 else '') + ', '
|
||||||
|
if data['direct_stream']:
|
||||||
|
s += str(data['direct_stream']) + ' direct stream' + ('s' if data['direct_stream'] > 1 else '') + ', '
|
||||||
|
if data['transcode']:
|
||||||
|
s += str(data['transcode']) + ' transcode' + ('s' if data['transcode'] > 1 else '') + ', '
|
||||||
|
s = s.rstrip(', ')
|
||||||
|
s += ')'
|
||||||
|
%>
|
||||||
|
% if data['stream_count'] == '0':
|
||||||
|
<h3>Activity</h3>
|
||||||
|
% elif data['stream_count'] == '1':
|
||||||
|
<h3>Activity <small>${data['stream_count']} stream ${s}</small></h3>
|
||||||
|
% else:
|
||||||
|
<h3>Activity <small>${data['stream_count']} streams ${s}</small></h3>
|
||||||
|
% endif
|
||||||
% else:
|
% else:
|
||||||
<h3>Activity <small>${data} stream(s)</small></h3>
|
<h3>Activity</h3>
|
||||||
% endif
|
% endif
|
||||||
% else:
|
|
||||||
<h3>Activity</h3>
|
|
||||||
% endif
|
|
278
data/interfaces/default/current_activity_instance.html
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
<%doc>
|
||||||
|
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||||
|
|
||||||
|
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||||
|
|
||||||
|
Filename: current_activity_instance.html
|
||||||
|
Version: 0.1
|
||||||
|
Variable names: data {dict}
|
||||||
|
|
||||||
|
data :: Usable parameters
|
||||||
|
|
||||||
|
== Global keys ==
|
||||||
|
session_key Returns a unique session id for the active stream
|
||||||
|
rating_key Returns the unique identifier for the media item.
|
||||||
|
media_index Returns the index of the media item.
|
||||||
|
parent_media_index Returns the index of the media item's parent.
|
||||||
|
media_type Returns the type of session. Either 'track', 'episode' or 'movie'.
|
||||||
|
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
|
||||||
|
bif_thumb Returns the location of the item's bif thumbnail. Use with pms_image_proxy.
|
||||||
|
art Returns the location of the item's artwork
|
||||||
|
progress_percent Returns the current progress of the item. 0 to 100.
|
||||||
|
user Returns the name of the user owning the session.
|
||||||
|
user_id Returns the Plex user id if available.
|
||||||
|
machine_id Returns the machine id of the players being used.
|
||||||
|
friendly_name Returns the friendlly name of the user owning the session.
|
||||||
|
user_thumb Returns the profile picture of the user owning the session.
|
||||||
|
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||||
|
title Returns the name of the episode, movie or music track.
|
||||||
|
year Returns the year of the episode, movie, or clip.
|
||||||
|
ip_address Returns the ip address of the stream.
|
||||||
|
player Returns the name of the platform used to play the stream.
|
||||||
|
platform Returns the type of platform used to play the stream.
|
||||||
|
throttled Returns true if the transcode session is throttled.
|
||||||
|
transcode_progress Returns the current transcode progress of the item. 0 to 100.
|
||||||
|
transcode_speed Returns the current transcode speed of the item.
|
||||||
|
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||||
|
audio_codec Returns the name of the audio codec.
|
||||||
|
audio_channels Returns the number of audio channels.
|
||||||
|
grandparent_title Returns the title of the item's grandparent.
|
||||||
|
parent_title Returns the title of the item's parent.
|
||||||
|
video_decision Returns the video transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||||
|
video_codec Returns the name of the video codec.
|
||||||
|
height Returns the value of the video height.
|
||||||
|
width Returns the value of the video width.
|
||||||
|
container Returns the value of the media container.
|
||||||
|
bitrate Returns the value of the media bitrate.
|
||||||
|
video_resolution Returns the value of the video resolution.
|
||||||
|
video_framerate Returns the value of the video framerate.
|
||||||
|
video_aspect_ratio Returns the value of the video aspect ratio.
|
||||||
|
transcode_audio_channels Returns the amount of audio channels if there is a transcode session.
|
||||||
|
transcode_audio_codec Returns the name of the audio codec if there is a transcode session.
|
||||||
|
transcode_video_codec Returns the name of the video codec if there is a transcode session.
|
||||||
|
transcode_width Returns the video width if there is a transcode session.
|
||||||
|
transcode_height Returns the video height if there is a transcode session.
|
||||||
|
transcode_container Returns the value of media container if there is a transcode session.
|
||||||
|
transcode_protocol Returns the value of media protocol if there is a transcode session.
|
||||||
|
indexes Returns true if the media has media indexes and are enabled
|
||||||
|
|
||||||
|
DOCUMENTATION :: END
|
||||||
|
</%doc>
|
||||||
|
|
||||||
|
% if data is not None:
|
||||||
|
<%
|
||||||
|
from urllib import quote
|
||||||
|
|
||||||
|
from plexpy import helpers
|
||||||
|
data['indexes'] = helpers.cast_to_int(data['indexes'])
|
||||||
|
%>
|
||||||
|
<div class="dashboard-instance" id="instance-${data['session_key']}" data-id="${data['session_key']}">
|
||||||
|
<div class="dashboard-hover-container">
|
||||||
|
% if (data['media_type'] == 'movie' or data['media_type'] == 'episode' or data['media_type'] == 'track') and data['rating_key']:
|
||||||
|
<a href="info?rating_key=${data['rating_key']}">
|
||||||
|
% else:
|
||||||
|
<a href="#">
|
||||||
|
% endif
|
||||||
|
<div class="dashboard-activity-poster" id="poster-${data['session_key']}">
|
||||||
|
% if not data['art'].startswith('interfaces') or not data['thumb'].startswith('interfaces'):
|
||||||
|
% if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
|
||||||
|
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
|
% elif (data['media_type'] == 'episode' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
|
||||||
|
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
|
% elif data['indexes']:
|
||||||
|
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['bif_thumb']}&width=500&height=280&fallback=art); display: none;"></div>
|
||||||
|
% else:
|
||||||
|
% if data['media_type'] == 'track':
|
||||||
|
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% elif data['media_type'] == 'clip':
|
||||||
|
% if data['art'].startswith('http'):
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
|
||||||
|
% elif data['thumb'].startswith('http'):
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(${data['thumb']});"></div>
|
||||||
|
% else:
|
||||||
|
% if data['art']:
|
||||||
|
<!--Hacky solution to escape the image url until I come up with something better-->
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${quote(data['art'])}&width=500&height=280&fallback=art);"></div>
|
||||||
|
% else:
|
||||||
|
<!--Hacky solution to escape the image url until I come up with something better-->
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${quote(data['thumb'])}&width=500&height=280&fallback=art);"></div>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
% elif data['media_type'] == 'photo':
|
||||||
|
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=cover);"></div>
|
||||||
|
% else:
|
||||||
|
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
% else:
|
||||||
|
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
|
||||||
|
% endif
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image left" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
|
<div class="dashboard-activity-button-info">
|
||||||
|
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${data['session_key']}" data-id="${data['session_key']}">
|
||||||
|
<i class="fa fa-info-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="stream-${data['session_key']}" class="dashboard-activity-info-details-overlay">
|
||||||
|
<div class="dashboard-activity-info-details-content">
|
||||||
|
<div id="platform-${data['session_key']}" title="${data['platform']}">
|
||||||
|
<script>
|
||||||
|
$("#platform-${data['session_key']}").html("<div class='dashboard-activity-info-platform-box' style='background-image: url(" + getPlatformImagePath('${data['platform']}') + ");'>");
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-activity-info-platform">
|
||||||
|
<strong>${data['player']}</strong><br />
|
||||||
|
<span id="overlay-play-state-${data['session_key']}">
|
||||||
|
% if data['state'] == 'playing':
|
||||||
|
State <strong>Playing</strong>
|
||||||
|
% elif data['state'] == 'paused':
|
||||||
|
State <strong>Paused</strong>
|
||||||
|
% elif data['state'] == 'buffering':
|
||||||
|
State <strong>Buffering</strong>
|
||||||
|
% endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span id="transcode-state-${data['session_key']}">
|
||||||
|
% if data['video_decision'] == 'transcode' or data['audio_decision'] == 'transcode':
|
||||||
|
Stream <strong>Transcode (Speed: ${data['transcode_speed']})
|
||||||
|
% if data['throttled'] == '1':
|
||||||
|
(Throttled)
|
||||||
|
% endif
|
||||||
|
</strong>
|
||||||
|
% elif data['video_decision'] == 'copy' or data['audio_decision'] == 'copy':
|
||||||
|
Stream <strong>Direct Stream</strong>
|
||||||
|
% else:
|
||||||
|
Stream <strong>Direct Play</strong>
|
||||||
|
% endif
|
||||||
|
<br />
|
||||||
|
% if data['video_decision'] and data['media_type'] != 'photo':
|
||||||
|
% if data['video_decision'] == 'transcode':
|
||||||
|
Video <strong>Transcode (${data['transcode_video_codec']}) (${data['transcode_width']}x${data['transcode_height']})</strong>
|
||||||
|
% elif data['video_decision'] == 'copy':
|
||||||
|
Video <strong>Direct Stream (${data['transcode_video_codec']}) (${data['width']}x${data['height']})</strong>
|
||||||
|
% else:
|
||||||
|
Video <strong>Direct Play (${data['video_codec']}) (${data['width']}x${data['height']})</strong>
|
||||||
|
% endif
|
||||||
|
<br />
|
||||||
|
% endif
|
||||||
|
% if data['audio_decision']:
|
||||||
|
% if data['audio_decision'] == 'transcode':
|
||||||
|
Audio <strong>Transcode (${data['transcode_audio_codec']}) (${data['transcode_audio_channels']}ch)</strong>
|
||||||
|
% elif data['audio_decision'] == 'copy':
|
||||||
|
Audio <strong>Direct Stream (${data['transcode_audio_codec']}) (${data['transcode_audio_channels']}ch)</strong>
|
||||||
|
% else:
|
||||||
|
Audio <strong>Direct Play (${data['audio_codec']}) (${data['audio_channels']}ch)</strong>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% if data['media_type'] != 'photo':
|
||||||
|
<div class="dashboard-activity-poster-info-bar">
|
||||||
|
<div class="dashboard-activity-poster-info-ip-address">
|
||||||
|
% if data['ip_address']:
|
||||||
|
<span>IP: ${data['ip_address']}</span>
|
||||||
|
% else:
|
||||||
|
<span>IP: N/A</span>
|
||||||
|
% endif
|
||||||
|
<br />
|
||||||
|
ETA:
|
||||||
|
<span id="stream-eta-${data['session_key']}">
|
||||||
|
<script>
|
||||||
|
$("#stream-eta-${data['session_key']}").html(moment().add(parseInt("${data['duration']}") - parseInt("${data['view_offset']}"), 'milliseconds').format(time_format));
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-activity-poster-info-time">
|
||||||
|
<span class="progress_time" id="stream-view-offset-${data['session_key']}">
|
||||||
|
<script>
|
||||||
|
$("#stream-view-offset-${data['session_key']}").html(millisecondsToMinutes(parseInt("${data['view_offset']}"), false));
|
||||||
|
</script>
|
||||||
|
</span>/<span class="progress_time" id="stream-duration-${data['session_key']}">
|
||||||
|
<script>
|
||||||
|
$("#stream-duration-${data['session_key']}").html(millisecondsToMinutes(parseInt("${data['duration']}"), false));
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
% if (data['media_type'] == 'movie' or data['media_type'] == 'episode' or data['media_type'] == 'track') and data['rating_key']:
|
||||||
|
</a>
|
||||||
|
% else:
|
||||||
|
</a>
|
||||||
|
% endif
|
||||||
|
<div class="dashboard-activity-progress">
|
||||||
|
<div class="dashboard-activity-progress-bar">
|
||||||
|
<div id="bufferbar-${data['session_key']}" class="bufferbar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress">${data['transcode_progress']}%</div>
|
||||||
|
<div id="bar-${data['session_key']}" class="bar" style="width: ${data['progress_percent']}%" data-toggle="tooltip" title="Stream Progress">${data['progress_percent']}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-activity-metadata-wrapper">
|
||||||
|
% if data['user_id']:
|
||||||
|
<a href="user?user_id=${data['user_id']}">
|
||||||
|
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${data['user_thumb']});"></div>
|
||||||
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${data['user_thumb']});"></div>
|
||||||
|
% endif
|
||||||
|
<div class="dashboard-activity-metadata-title">
|
||||||
|
<span id="play-state-${data['session_key']}">
|
||||||
|
% if data['state'] == 'playing':
|
||||||
|
<i class="fa fa-fw fa-play"></i>
|
||||||
|
% elif data['state'] == 'paused':
|
||||||
|
<i class="fa fa-fw fa-pause"></i>
|
||||||
|
% elif data['state'] == 'buffering':
|
||||||
|
<i class="fa fa-fw fa-spinner"></i>
|
||||||
|
% endif
|
||||||
|
</span>
|
||||||
|
% if data['rating_key']:
|
||||||
|
% if data['media_type'] == 'episode':
|
||||||
|
<a href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
|
||||||
|
- <a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
|
||||||
|
% elif data['media_type'] == 'movie':
|
||||||
|
<a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
|
||||||
|
% elif data['media_type'] == 'clip':
|
||||||
|
<span title="${data['title']}">${data['title']}</span>
|
||||||
|
% elif data['media_type'] == 'track':
|
||||||
|
<a href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
|
||||||
|
- <a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
|
||||||
|
% elif data['media_type'] == 'photo':
|
||||||
|
<span title="${data['parent_title']}">${data['parent_title']}</span>
|
||||||
|
% else:
|
||||||
|
<span title="${data['title']}">${data['title']}</span>
|
||||||
|
% endif
|
||||||
|
% else:
|
||||||
|
${data['title']}
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-activity-metadata-subtitle">
|
||||||
|
% if data['rating_key']:
|
||||||
|
% if data['media_type'] == 'episode':
|
||||||
|
<a href="info?rating_key=${data['parent_rating_key']}" title="Season ${data['parent_media_index']}" class="text-muted">S${data['parent_media_index']}</a>
|
||||||
|
· <a href="info?rating_key=${data['rating_key']}" title="Episode ${data['media_index']}" class="text-muted">E${data['media_index']}</a>
|
||||||
|
% elif data['media_type'] == 'movie':
|
||||||
|
<span title="${data['year']}">${data['year']}</span>
|
||||||
|
% elif data['media_type'] == 'track':
|
||||||
|
<a href="info?rating_key=${data['parent_rating_key']}" title="${data['parent_title']}">${data['parent_title']}</a>
|
||||||
|
% elif data['media_type'] == 'photo':
|
||||||
|
<span title="${data['title']}">${data['title']}</span>
|
||||||
|
% else:
|
||||||
|
<span title="${data['year']}">${data['year']}</span>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-activity-metadata-user">
|
||||||
|
% if data['user_id']:
|
||||||
|
<a href="user?user_id=${data['user_id']}" title="${data['friendly_name']}">${data['friendly_name']}</a>
|
||||||
|
% else:
|
||||||
|
${data['friendly_name']}
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
179
data/interfaces/default/edit_library.html
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<%doc>
|
||||||
|
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||||
|
|
||||||
|
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||||
|
|
||||||
|
Filename: edit_library.html
|
||||||
|
Version: 0.1
|
||||||
|
Variable names: data [list]
|
||||||
|
|
||||||
|
data :: Usable parameters
|
||||||
|
|
||||||
|
== Global keys ==
|
||||||
|
section_id Returns the library id of the library.
|
||||||
|
section_name Returns the name of the library.
|
||||||
|
section_type Returns the type of the library.
|
||||||
|
library_thumb Returns the thumbnail for the library.
|
||||||
|
custom_thumb Returns the custom thumbnail for the library.
|
||||||
|
library_art Returns the artwork for the library.
|
||||||
|
count Returns the item count for the library.
|
||||||
|
parent_count Returns the parent item count for the library.
|
||||||
|
child_count Returns the child item count for the library.
|
||||||
|
do_notify Returns bool value for whether to send notifications for the library.
|
||||||
|
keep_history Returns bool value for whether to keep history for the library.
|
||||||
|
|
||||||
|
DOCUMENTATION :: END
|
||||||
|
</%doc>
|
||||||
|
|
||||||
|
<%!
|
||||||
|
from plexpy import helpers
|
||||||
|
%>
|
||||||
|
|
||||||
|
% if data != None:
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">Edit library <strong>${data['section_name']}</strong></h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="modal-text">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="profile_url">Library Picture URL</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['library_thumb']}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Change the library's picture in PlexPy. To reset to default, leave this field empty and save.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Uncheck this if you do not want to receive notifications for this library's activity.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Uncheck this if you do not want to keep any history on this library's activity.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="do_notify_created" name="do_notify_created" value="1" ${helpers.checked(data['do_notify_created'])}> Enable recently added notifications
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Uncheck this if you do not want to receive recently added notifications for this library.</p>
|
||||||
|
</div>
|
||||||
|
% if data['section_id']:
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-danger" id="delete-all-history">Purge</button>
|
||||||
|
<p class="help-block">DANGER ZONE! Click the purge button to remove all history logged for this library. This is permanent!</p>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div>
|
||||||
|
<span id="edit-library-status-message"></span>
|
||||||
|
<input type="button" id="save_library" class="btn btn-bright" value="Save">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="confirm-modal-purge" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-purge">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title" id="myModalLabel">Confirm Purge</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" style="text-align: center;">
|
||||||
|
<p>Are you REALLY sure you want to purge all history for this library?</p>
|
||||||
|
<p>This is permanent and cannot be undone!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-purge">Purge</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// Save library options
|
||||||
|
$("#save_library").on('click', function () {
|
||||||
|
var custom_thumb = $("#custom_thumb_url").val();
|
||||||
|
var do_notify = 0;
|
||||||
|
var do_notify_created = 0;
|
||||||
|
var keep_history = 0;
|
||||||
|
if ($("#do_notify").is(":checked")) {
|
||||||
|
do_notify = 1;
|
||||||
|
}
|
||||||
|
if ($("#do_notify_created").is(":checked")) {
|
||||||
|
do_notify_created = 1;
|
||||||
|
}
|
||||||
|
if ($("#keep_history").is(":checked")) {
|
||||||
|
keep_history = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: 'edit_library',
|
||||||
|
data: {
|
||||||
|
section_id: '${data["section_id"]}',
|
||||||
|
custom_thumb: custom_thumb,
|
||||||
|
do_notify: do_notify,
|
||||||
|
do_notify_created: do_notify_created,
|
||||||
|
keep_history: keep_history
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
success: function (data) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#delete-all-history").on('click', function() {
|
||||||
|
$('#confirm-modal-purge').modal();
|
||||||
|
$('#confirm-modal-purge').one('click', '#confirm-purge', function () {
|
||||||
|
$.ajax({
|
||||||
|
url: 'delete_all_library_history',
|
||||||
|
data: { section_id: '${data["section_id"]}' },
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
success: function(data) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Move #confirm-modal to parent container
|
||||||
|
if (!($('#edit-library-modal').next().is('#confirm-modal-purge'))) {
|
||||||
|
$('#confirm-modal-purge').appendTo($('#edit-library-modal').parent());
|
||||||
|
}
|
||||||
|
$('#edit-library-modal > #confirm-modal-purge').remove();
|
||||||
|
|
||||||
|
$('#edit-library-modal').css('z-index', '1050');
|
||||||
|
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
|
||||||
|
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||||
|
|
||||||
|
$('#confirm-modal-purge').on('show.bs.modal', function () {
|
||||||
|
// Fix position to match parent modal
|
||||||
|
var currentPadding = parseInt($('body').css('padding-right'));
|
||||||
|
$(this).children('.modal-dialog').css('left', -currentPadding/2);
|
||||||
|
$('#edit-library-modal').css('overflow-y', 'hidden');
|
||||||
|
});
|
||||||
|
$('#confirm-modal-purge').on('shown.bs.modal', function () {
|
||||||
|
$(this).css('z-index', '1060');
|
||||||
|
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
||||||
|
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||||
|
});
|
||||||
|
$('#confirm-modal-purge').on('hidden.bs.modal', function () {
|
||||||
|
$('body').addClass('modal-open');
|
||||||
|
$('#edit-library-modal').css('overflow-y', 'auto');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
% endif
|
@@ -10,21 +10,31 @@ Variable names: data [list]
|
|||||||
data :: Usable parameters
|
data :: Usable parameters
|
||||||
|
|
||||||
== Global keys ==
|
== Global keys ==
|
||||||
user Return the real Plex username
|
user_id Returns the user id of the user.
|
||||||
user_id Return the Plex user_id
|
username Returns the user's username.
|
||||||
friendly_name Returns the friendly edited Plex username
|
friendly_name Returns the friendly name of the user.
|
||||||
do_notify Returns bool value for whether the user should trigger notifications
|
email Returns the user's email address.
|
||||||
keep_history Returns bool value for whether the user's activity should be logged
|
user_thumb Returns the thumbnail for the user.
|
||||||
|
is_home_user Returns bool value for whether the user is part of a Plex Home.
|
||||||
|
is_allow_sync Returns bool value for whether the user has sync rights.
|
||||||
|
is_restricted Returns bool value for whether the user account is restricted.
|
||||||
|
do_notify Returns bool value for whether to send notifications for the user.
|
||||||
|
keep_history Returns bool value for whether to keep history for the user.
|
||||||
|
allow_guest Returns bool value for whether to allow guest access for the user.
|
||||||
|
|
||||||
DOCUMENTATION :: END
|
DOCUMENTATION :: END
|
||||||
</%doc>
|
</%doc>
|
||||||
|
|
||||||
% if data is not None:
|
<%!
|
||||||
|
from plexpy import helpers
|
||||||
|
%>
|
||||||
|
|
||||||
|
% if data != None:
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
<h4 class="modal-title">Edit user <strong>${data['user']}</strong></h4>
|
<h4 class="modal-title">Edit user <strong>${data['username']}</strong></h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="modal-text">
|
<div class="modal-body" id="modal-text">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -41,22 +51,28 @@ DOCUMENTATION :: END
|
|||||||
<label for="profile_url">Profile Picture URL</label>
|
<label for="profile_url">Profile Picture URL</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<input type="text" class="form-control" id="profile_url" name="profile_url" value="${data['thumb']}">
|
<input type="text" class="form-control" id="custom_avatar_url" name="custom_avatar_url" value="${data['user_thumb']}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">Change the users profile picture in PlexPy. To reset to default, leave this field empty and save then perform a user refresh.</p>
|
<p class="help-block">Change the users profile picture in PlexPy. To reset to default, leave this field empty and save.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${data['do_notify']}> Enable notifications
|
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Uncheck this if you do not want to receive notifications for this user's activity.</p>
|
<p class="help-block">Uncheck this if you do not want to receive notifications for this user's activity.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${data['keep_history']}> Keep history
|
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Uncheck this if you do not want this keep any history on this user's activity.</p>
|
<p class="help-block">Uncheck this if you do not want to keep any history on this user's activity.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="allow_guest" name="allow_guest" value="1" ${helpers.checked(data['allow_guest'])}> Allow Guest Access
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Uncheck this if you do not want to allow this user to login to PlexPy.</p>
|
||||||
</div>
|
</div>
|
||||||
% if data['user_id']:
|
% if data['user_id']:
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -69,12 +85,12 @@ DOCUMENTATION :: END
|
|||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<div>
|
<div>
|
||||||
<span id="edit-user-status-message"></span>
|
<span id="edit-user-status-message"></span>
|
||||||
<input type="button" id="save_user_name" class="btn btn-bright" value="Save">
|
<input type="button" id="save_user" class="btn btn-bright" value="Save">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
<div id="confirm-modal-purge" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-purge">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -93,56 +109,47 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
// Set new friendly name
|
// Set user options
|
||||||
$("#save_user_name").click(function() {
|
$("#save_user").on('click', function () {
|
||||||
var friendly_name = $("#friendly_name").val();
|
var friendly_name = $("#friendly_name").val();
|
||||||
var thumb = $("#profile_url").val();
|
var custom_thumb = $("#custom_avatar_url").val();
|
||||||
var do_notify = 0;
|
var do_notify = 0;
|
||||||
var keep_history = 0;
|
var keep_history = 0;
|
||||||
|
var allow_guest = 0;
|
||||||
if ($("#do_notify").is(":checked")) {
|
if ($("#do_notify").is(":checked")) {
|
||||||
do_notify = 1;
|
do_notify = 1;
|
||||||
}
|
}
|
||||||
if ($("#keep_history").is(":checked")) {
|
if ($("#keep_history").is(":checked")) {
|
||||||
keep_history = 1;
|
keep_history = 1;
|
||||||
}
|
}
|
||||||
|
if ($("#allow_guest").is(":checked")) {
|
||||||
|
allow_guest = 1;
|
||||||
|
}
|
||||||
|
|
||||||
% if data['user_id']:
|
$.ajax({
|
||||||
$.ajax({
|
url: 'edit_user',
|
||||||
url: 'edit_user',
|
data: {
|
||||||
data: {user_id: '${data['user_id']}', friendly_name: friendly_name, do_notify: do_notify, keep_history: keep_history, thumb: thumb},
|
user_id: '${data["user_id"]}',
|
||||||
cache: false,
|
friendly_name: friendly_name,
|
||||||
async: true,
|
custom_thumb: custom_thumb,
|
||||||
success: function(data) {
|
do_notify: do_notify,
|
||||||
$("#edit-user-status-message").html(data);
|
keep_history: keep_history,
|
||||||
if ($.trim(friendly_name) !== '') {
|
allow_guest: allow_guest
|
||||||
$(".set-username").html(friendly_name);
|
},
|
||||||
}
|
cache: false,
|
||||||
$("#user-profile-thumb").attr('src', thumb);
|
async: true,
|
||||||
}
|
success: function(data) {
|
||||||
});
|
location.reload();
|
||||||
% else:
|
}
|
||||||
$.ajax({
|
});
|
||||||
url: 'edit_user',
|
|
||||||
data: {user: '${data['user']}', friendly_name: friendly_name, do_notify: do_notify, keep_history: keep_history, thumb: thumb},
|
|
||||||
cache: false,
|
|
||||||
async: true,
|
|
||||||
success: function(data) {
|
|
||||||
$("#edit-user-status-message").html(data);
|
|
||||||
if ($.trim(friendly_name) !== '') {
|
|
||||||
$(".set-username").html(friendly_name);
|
|
||||||
}
|
|
||||||
$("#user-profile-thumb").attr('src', thumb);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
% endif
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#delete-all-history").on('click', function() {
|
$("#delete-all-history").on('click', function() {
|
||||||
$('#confirm-modal').modal();
|
$('#confirm-modal-purge').modal();
|
||||||
$('#confirm-modal').one('click', '#confirm-purge', function () {
|
$('#confirm-modal-purge').one('click', '#confirm-purge', function () {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'delete_all_user_history',
|
url: 'delete_all_user_history',
|
||||||
data: {user_id: '${data['user_id']}'},
|
data: { user_id: '${data["user_id"]}' },
|
||||||
cache: false,
|
cache: false,
|
||||||
async: true,
|
async: true,
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
@@ -153,31 +160,31 @@ DOCUMENTATION :: END
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Move #confirm-modal to parent container
|
// Move #confirm-modal-purge to parent container
|
||||||
if(!($('#edit-user-modal').next().is('#confirm-modal'))) {
|
if (!($('#edit-user-modal').next().is('#confirm-modal-purge'))) {
|
||||||
$('#confirm-modal').appendTo($('#edit-user-modal').parent()); }
|
$('#confirm-modal-purge').appendTo($('#edit-user-modal').parent());
|
||||||
$('#edit-user-modal > #confirm-modal').remove();
|
}
|
||||||
|
$('#edit-user-modal > #confirm-modal-purge').remove();
|
||||||
|
|
||||||
$('#edit-user-modal').css('z-index', '1050');
|
$('#edit-user-modal').css('z-index', '1050');
|
||||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
|
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
|
||||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||||
|
|
||||||
$('#confirm-modal').on('show.bs.modal', function () {
|
$('#confirm-modal-purge').on('show.bs.modal', function () {
|
||||||
// Fix position to match parent modal
|
// Fix position to match parent modal
|
||||||
var currentPadding = parseInt($('body').css('padding-right'));
|
var currentPadding = parseInt($('body').css('padding-right'));
|
||||||
$(this).children('.modal-dialog').css('left', -currentPadding/2);
|
$(this).children('.modal-dialog').css('left', -currentPadding/2);
|
||||||
$('#edit-user-modal').css('overflow-y', 'hidden');
|
$('#edit-user-modal').css('overflow-y', 'hidden');
|
||||||
});
|
});
|
||||||
$('#confirm-modal').on('shown.bs.modal', function () {
|
$('#confirm-modal-purge').on('shown.bs.modal', function () {
|
||||||
$(this).css('z-index', '1060');
|
$(this).css('z-index', '1060');
|
||||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
||||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||||
});
|
});
|
||||||
$('#confirm-modal').on('hidden.bs.modal', function() {
|
$('#confirm-modal-purge').on('hidden.bs.modal', function () {
|
||||||
$('body').addClass('modal-open');
|
$('body').addClass('modal-open');
|
||||||
$('#edit-user-modal').css('overflow-y', 'auto');
|
$('#edit-user-modal').css('overflow-y', 'auto');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
% endif
|
% endif
|
202
data/interfaces/default/fonts/Open-Sans-600/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.eot
Normal file
1637
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.svg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.ttf
Normal file
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.woff
Normal file
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.woff2
Normal file
202
data/interfaces/default/fonts/Open-Sans-regular/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
After Width: | Height: | Size: 105 KiB |
@@ -1,8 +1,8 @@
|
|||||||
<%inherit file="base.html"/>
|
<%inherit file="base.html"/>
|
||||||
|
|
||||||
<%def name="headIncludes()">
|
<%def name="headIncludes()">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
|
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.css">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
|
<link rel="stylesheet" href="${http_root}css/plexpy-dataTables.css">
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
@@ -12,111 +12,141 @@
|
|||||||
<span><i class="fa fa-bar-chart"></i> Graphs</span>
|
<span><i class="fa fa-bar-chart"></i> Graphs</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-bar hidden-xs">
|
<div class="button-bar hidden-xs">
|
||||||
|
<div class="btn-group" id="user-selection">
|
||||||
|
<label>
|
||||||
|
<select name="graph-user" id="graph-user" class="btn" style="color: inherit;">
|
||||||
|
<option value="">All Users</option>
|
||||||
|
<option disabled>────────────</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="btn-group" data-toggle="buttons" id="yaxis-selection">
|
<div class="btn-group" data-toggle="buttons" id="yaxis-selection">
|
||||||
|
% if config['graph_type'] == 'duration':
|
||||||
|
<label class="btn btn-dark">
|
||||||
|
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off"> Play Count
|
||||||
|
</label>
|
||||||
|
<label class="btn btn-dark active">
|
||||||
|
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off" checked> Play Duration
|
||||||
|
</label>
|
||||||
|
% else:
|
||||||
<label class="btn btn-dark active">
|
<label class="btn btn-dark active">
|
||||||
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off" checked> Play Count
|
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off" checked> Play Count
|
||||||
</label>
|
</label>
|
||||||
<label class="btn btn-dark">
|
<label class="btn btn-dark">
|
||||||
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration
|
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration
|
||||||
</label>
|
</label>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group" data-toggle="buttons" id="days-selection">
|
<div class="btn-group" id="days-selection">
|
||||||
<label class="btn btn-dark">
|
<label>
|
||||||
<input type="radio" name="date-options" id="graph-7" value="7" autocomplete="off"> 7 days
|
<input type="number" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" /> days
|
||||||
</label>
|
|
||||||
<label class="btn btn-dark active">
|
|
||||||
<input type="radio" name="date-options" id="graph-30" value="30" autocomplete="off" checked> 30 days
|
|
||||||
</label>
|
|
||||||
<label class="btn btn-dark">
|
|
||||||
<input type="radio" name="date-options" id="graph-90" value="90" autocomplete="off"> 90 days
|
|
||||||
</label>
|
|
||||||
<label class="btn btn-dark">
|
|
||||||
<input type="radio" name="date-options" id="graph-365" value="365" autocomplete="off"> 1 year
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='table-card-back'>
|
<div class='table-card-back'>
|
||||||
<ul class="nav nav-pills" role="tablist" id="graph-tabs">
|
<ul class="nav nav-pills" role="tablist" id="graph-tabs">
|
||||||
|
% if config['graph_tab'] == 'tabs-3':
|
||||||
|
<li role="presentation"><a href="#tabs-1" aria-controls="tabs-1" data-toggle="tab" role="tab">Plays by period</a></li>
|
||||||
|
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" data-toggle="tab" role="tab">Stream Info</a></li>
|
||||||
|
<li role="presentation" class="active"><a href="#tabs-3" aria-controls="tabs-3" data-toggle="tab" role="tab">Play Totals</a></li>
|
||||||
|
% elif config['graph_tab'] == 'tabs-2':
|
||||||
|
<li role="presentation"><a href="#tabs-1" aria-controls="tabs-1" data-toggle="tab" role="tab">Plays by period</a></li>
|
||||||
|
<li role="presentation" class="active"><a href="#tabs-2" aria-controls="tabs-2" data-toggle="tab" role="tab">Stream Info</a></li>
|
||||||
|
<li role="presentation"><a href="#tabs-3" aria-controls="tabs-3" data-toggle="tab" role="tab">Play Totals</a></li>
|
||||||
|
% else:
|
||||||
<li role="presentation" class="active"><a href="#tabs-1" aria-controls="tabs-1" data-toggle="tab" role="tab">Plays by period</a></li>
|
<li role="presentation" class="active"><a href="#tabs-1" aria-controls="tabs-1" data-toggle="tab" role="tab">Plays by period</a></li>
|
||||||
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" data-toggle="tab" role="tab">Stream Info</a></li>
|
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" data-toggle="tab" role="tab">Stream Info</a></li>
|
||||||
<li role="presentation"><a href="#tabs-3" aria-controls="tabs-3" data-toggle="tab" role="tab">Play Totals</a></li>
|
<li role="presentation"><a href="#tabs-3" aria-controls="tabs-3" data-toggle="tab" role="tab">Play Totals</a></li>
|
||||||
|
% endif
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
% if config['graph_tab'] != 'tabs-2' and config['graph_tab'] != 'tabs-3':
|
||||||
<div role="tabpanel" class="tab-pane active" id="tabs-1">
|
<div role="tabpanel" class="tab-pane active" id="tabs-1">
|
||||||
<div class="row">
|
% else:
|
||||||
<div class="col-md-12">
|
<div role="tabpanel" class="tab-pane" id="tabs-1">
|
||||||
<h4><i class="fa fa-history"></i> Daily <span class="yaxis-text">Play count</span> <small>Last <span class="days">30</span> days</small></h4>
|
% endif
|
||||||
<p class="help-block">
|
<div class="row">
|
||||||
The total play count or duration of tv, movies, and music played per day. Click a graph point to open up a list of items played for that specific date.
|
<div class="col-md-12">
|
||||||
</p>
|
<h4><i class="fa fa-history"></i> Daily <span class="yaxis-text">Play count</span> <small>Last <span class="days">30</span> days</small></h4>
|
||||||
<div class="graphs-instance">
|
<p class="help-block">
|
||||||
<div class="watch-chart" id="chart_div_plays_by_day">
|
The total play count or duration of tv, movies, and music played per day. Click a graph point to open up a list of items played for that specific date.
|
||||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...</div>
|
</p>
|
||||||
<br>
|
<div class="graphs-instance">
|
||||||
|
<div class="watch-chart" id="chart_div_plays_by_day">
|
||||||
|
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4><i class="fa fa-calendar"></i> <span class="yaxis-text">Play count</span> by day of week <small>Last <span class="days">30</span> days</small></h4>
|
||||||
|
<p class="help-block">
|
||||||
|
The combined total of tv, movies, and music played per day of the week.
|
||||||
|
</p>
|
||||||
|
<div class="graphs-instance">
|
||||||
|
<div class="watch-chart" id="chart_div_plays_by_dayofweek" style="float: left;">
|
||||||
|
<div class="graphs-load">
|
||||||
|
<i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4><i class="fa fa-clock-o"></i> <span class="yaxis-text">Play count</span> by hour of day <small>Last <span class="days">30</span> days</small></h4>
|
||||||
|
<p class="help-block">
|
||||||
|
The combined total of tv, movies, and music played per hour of the day.
|
||||||
|
</p>
|
||||||
|
<div class="graphs-instance">
|
||||||
|
<div class="watch-chart" id="chart_div_plays_by_hourofday">
|
||||||
|
<div class="graphs-load">
|
||||||
|
<i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by top 10 platforms <small>Last <span class="days">30</span> days</small></h4>
|
||||||
|
<p class="help-block">
|
||||||
|
The combined total of tv, movies, and music played by top 10 most active platforms.
|
||||||
|
</p>
|
||||||
|
<div class="graphs-instance">
|
||||||
|
<div class="watch-chart" id="chart_div_plays_by_platform" style="float: left;">
|
||||||
|
<div class="graphs-load">
|
||||||
|
<i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by top 10 users <small>Last <span class="days">30</span> days</small></h4>
|
||||||
|
<p class="help-block">
|
||||||
|
The combined total of tv, movies, and music played by top 10 most active users.
|
||||||
|
</p>
|
||||||
|
<div class="graphs-instance">
|
||||||
|
<div class="watch-chart" id="chart_div_plays_by_user">
|
||||||
|
<div class="graphs-load">
|
||||||
|
<i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4><i class="fa fa-calendar"></i> <span class="yaxis-text">Play count</span> by day of week <small>Last <span class="days">30</span> days</small></h4>
|
|
||||||
<p class="help-block">
|
|
||||||
The combined total of tv, movies, and music played per day of the week.
|
|
||||||
</p>
|
|
||||||
<div class="graphs-instance">
|
|
||||||
<div class="watch-chart" id="chart_div_plays_by_dayofweek" style="float: left;">
|
|
||||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4><i class="fa fa-clock-o"></i> <span class="yaxis-text">Play count</span> by hour of day <small>Last <span class="days">30</span> days</small></h4>
|
|
||||||
<p class="help-block">
|
|
||||||
The combined total of tv, movies, and music played per hour of the day.
|
|
||||||
</p>
|
|
||||||
<div class="graphs-instance">
|
|
||||||
<div class="watch-chart" id="chart_div_plays_by_hourofday">
|
|
||||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by top 10 platforms <small>Last <span class="days">30</span> days</small></h4>
|
|
||||||
<p class="help-block">
|
|
||||||
The combined total of tv, movies, and music played by top 10 most active platforms.
|
|
||||||
</p>
|
|
||||||
<div class="graphs-instance">
|
|
||||||
<div class="watch-chart" id="chart_div_plays_by_platform" style="float: left;">
|
|
||||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by top 10 users <small>Last <span class="days">30</span> days</small></h4>
|
|
||||||
<p class="help-block">
|
|
||||||
The combined total of tv, movies, and music played by top 10 most active users.
|
|
||||||
</p>
|
|
||||||
<div class="graphs-instance">
|
|
||||||
<div class="watch-chart" id="chart_div_plays_by_user">
|
|
||||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
% if config['graph_tab'] == 'tabs-2':
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="tabs-2">
|
||||||
|
% else:
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||||
|
% endif
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h4><i class="fa fa-video-camera"></i> Daily Stream type breakdown <small>Last <span class="days">30</span> days</small></h4>
|
<h4><i class="fa fa-video-camera"></i> Daily Stream type breakdown <small>Last <span class="days">30</span> days</small></h4>
|
||||||
@@ -189,7 +219,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
% if config['graph_tab'] == 'tabs-3':
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="tabs-3">
|
||||||
|
% else:
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-3">
|
<div role="tabpanel" class="tab-pane" id="tabs-3">
|
||||||
|
% endif
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h4><i class="fa fa-calendar"></i> Plays by Month <small>Last 12 months</small></h4>
|
<h4><i class="fa fa-calendar"></i> Plays by Month <small>Last 12 months</small></h4>
|
||||||
@@ -214,16 +248,18 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||||
<script src="interfaces/default/js/moment-duration-format.js"></script>
|
<script src="${http_root}js/moment-duration-format.js"></script>
|
||||||
<script src="interfaces/default/js/highcharts/js/highcharts.js"></script>
|
<script src="${http_root}js/highcharts/js/highcharts.js"></script>
|
||||||
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
|
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||||
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
|
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||||
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
|
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
var selected_user_id = null
|
||||||
|
|
||||||
// Modal popup dialog
|
// Modal popup dialog
|
||||||
function selectHandler(selectedDate) {
|
function selectHandler(selectedDate, selectedSeries) {
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -233,10 +269,26 @@
|
|||||||
var y = dateValue.getFullYear();
|
var y = dateValue.getFullYear();
|
||||||
var dateString = '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
|
var dateString = '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
|
||||||
|
|
||||||
|
var media_type = null;
|
||||||
|
var transcode_decision = null;
|
||||||
|
switch(selectedSeries) {
|
||||||
|
case "TV": media_type = 'episode'; break;
|
||||||
|
case "Movies": media_type = 'movie'; break;
|
||||||
|
case "Music": media_type = 'track'; break;
|
||||||
|
case "Direct Play": transcode_decision = 'direct play'; break;
|
||||||
|
case "Direct Stream": transcode_decision = 'copy'; break;
|
||||||
|
case "Transcode": transcode_decision = 'transcode'; break;
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
"url": "history_table_modal",
|
url: "history_table_modal",
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: { 'start_date': dateString },
|
data: {
|
||||||
|
user_id: selected_user_id,
|
||||||
|
start_date: dateString,
|
||||||
|
media_type: media_type,
|
||||||
|
transcode_decision: transcode_decision
|
||||||
|
},
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
$('#history-modal').modal('show');
|
$('#history-modal').modal('show');
|
||||||
$("#history-modal").html(xhr.responseText);
|
$("#history-modal").html(xhr.responseText);
|
||||||
@@ -245,79 +297,84 @@
|
|||||||
}
|
}
|
||||||
catch(err)
|
catch(err)
|
||||||
{
|
{
|
||||||
console.log("Failed to retrieve data");
|
console.log("Failed to retrieve history modal data.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_day.js"></script>
|
<script src="${http_root}js/graphs/plays_by_day.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_dayofweek.js"></script>
|
<script src="${http_root}js/graphs/plays_by_dayofweek.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_hourofday.js"></script>
|
<script src="${http_root}js/graphs/plays_by_hourofday.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_platform.js"></script>
|
<script src="${http_root}js/graphs/plays_by_platform.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_user.js"></script>
|
<script src="${http_root}js/graphs/plays_by_user.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_stream_type.js"></script>
|
<script src="${http_root}js/graphs/plays_by_stream_type.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_source_resolution.js"></script>
|
<script src="${http_root}js/graphs/plays_by_source_resolution.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_stream_resolution.js"></script>
|
<script src="${http_root}js/graphs/plays_by_stream_resolution.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_platform_by_stream_type.js"></script>
|
<script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_user_by_stream_type.js"></script>
|
<script src="${http_root}js/graphs/plays_by_user_by_stream_type.js"></script>
|
||||||
<script src="interfaces/default/js/graphs/plays_by_month.js"></script>
|
<script src="${http_root}js/graphs/plays_by_month.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
// Save graph state to cookies
|
// Initial values for graph from config
|
||||||
$('input[name=yaxis-options]').change(function() {
|
var yaxis = "${config['graph_type']}";
|
||||||
setCookie('graphType', $(this).val(), 365, '/');
|
var current_range = ${config['graph_days']};
|
||||||
});
|
var current_tab = "${'#' + config['graph_tab']}";
|
||||||
$('input[name=date-options]').change(function() {
|
|
||||||
setCookie('graphDate', $(this).val(), 365, '/');
|
|
||||||
});
|
|
||||||
$('a[data-toggle=tab]').click(function() {
|
|
||||||
setCookie('graphTab', $(this).attr('href'), 365, '/');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial values for graph if no saved state
|
$('.days').html(current_range);
|
||||||
var yaxis = 'plays';
|
|
||||||
var current_range = 30;
|
// Load user ids and names (for the selector)
|
||||||
var current_tab = '#tabs-1';
|
$.ajax({
|
||||||
|
url: 'get_user_names',
|
||||||
// Read saved graph state from cookies and set initial values
|
type: 'get',
|
||||||
if(getCookie('graphType')) {
|
dataType: "json",
|
||||||
var yaxis = getCookie('graphType');
|
success: function (data) {
|
||||||
$('input[name=yaxis-options][value=' + yaxis + ']').prop('checked', true).trigger('click');
|
var select = $('#graph-user');
|
||||||
}
|
data.sort(function(a, b) {
|
||||||
if(getCookie('graphDate')) {
|
return a.friendly_name.localeCompare(b.friendly_name);
|
||||||
var current_range = getCookie('graphDate');
|
});
|
||||||
$('input[name=date-options][value=' + current_range + ']').prop('checked', true).trigger('click');
|
data.forEach(function(item) {
|
||||||
$('.days').html(current_range);
|
select.append('<option value="' + item.user_id + '">' +
|
||||||
}
|
item.friendly_name + '</option>');
|
||||||
if(getCookie('graphTab')) {
|
});
|
||||||
var current_tab = getCookie('graphTab');
|
}
|
||||||
$('a[data-toggle=tab][href=' + current_tab + ']').trigger('click');
|
});
|
||||||
}
|
|
||||||
|
|
||||||
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
||||||
|
|
||||||
|
function dataSecondsToHours(data) {
|
||||||
|
$.each(data.series, function (i, series) {
|
||||||
|
series.data = $.map(series.data, function (value) {
|
||||||
|
return value / 60 / 60;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadGraphsTab1(time_range, yaxis) {
|
function loadGraphsTab1(time_range, yaxis) {
|
||||||
|
$('#days-selection').show();
|
||||||
|
|
||||||
setGraphFormat(yaxis);
|
setGraphFormat(yaxis);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_date",
|
url: "get_plays_by_date",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
var dateArray = [];
|
var dateArray = [];
|
||||||
for (var i = 0; i < data.categories.length; i++) {
|
$.each(data.categories, function (i, day) {
|
||||||
dateArray.push(moment(data.categories[i], 'YYYY-MM-DD').valueOf());
|
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
|
||||||
// Highlight the weekend
|
// Highlight the weekend
|
||||||
if ((moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||||
(moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||||
hc_plays_by_day_options.xAxis.plotBands.push({
|
hc_plays_by_day_options.xAxis.plotBands.push({
|
||||||
from: i-0.5,
|
from: i-0.5,
|
||||||
to: i+0.5,
|
to: i+0.5,
|
||||||
color: 'rgba(80,80,80,0.3)'
|
color: 'rgba(80,80,80,0.3)'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_day_options.yAxis.min = 0;
|
hc_plays_by_day_options.yAxis.min = 0;
|
||||||
hc_plays_by_day_options.xAxis.categories = dateArray;
|
hc_plays_by_day_options.xAxis.categories = dateArray;
|
||||||
hc_plays_by_day_options.series = data.series;
|
hc_plays_by_day_options.series = data.series;
|
||||||
@@ -329,9 +386,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_dayofweek",
|
url: "get_plays_by_dayofweek",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_dayofweek_options.xAxis.categories = data.categories;
|
hc_plays_by_dayofweek_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_dayofweek_options.series = data.series;
|
hc_plays_by_dayofweek_options.series = data.series;
|
||||||
hc_plays_by_dayofweek_options.series[2].visible = music_visible;
|
hc_plays_by_dayofweek_options.series[2].visible = music_visible;
|
||||||
@@ -342,9 +400,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_hourofday",
|
url: "get_plays_by_hourofday",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_hourofday_options.xAxis.categories = data.categories;
|
hc_plays_by_hourofday_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_hourofday_options.series = data.series;
|
hc_plays_by_hourofday_options.series = data.series;
|
||||||
hc_plays_by_hourofday_options.series[2].visible = music_visible;
|
hc_plays_by_hourofday_options.series[2].visible = music_visible;
|
||||||
@@ -355,9 +414,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_top_10_platforms",
|
url: "get_plays_by_top_10_platforms",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_platform_options.xAxis.categories = data.categories;
|
hc_plays_by_platform_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_platform_options.series = data.series;
|
hc_plays_by_platform_options.series = data.series;
|
||||||
hc_plays_by_platform_options.series[2].visible = music_visible;
|
hc_plays_by_platform_options.series[2].visible = music_visible;
|
||||||
@@ -368,9 +428,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_top_10_users",
|
url: "get_plays_by_top_10_users",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_user_options.xAxis.categories = data.categories;
|
hc_plays_by_user_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_user_options.series = data.series;
|
hc_plays_by_user_options.series = data.series;
|
||||||
hc_plays_by_user_options.series[2].visible = music_visible;
|
hc_plays_by_user_options.series[2].visible = music_visible;
|
||||||
@@ -380,27 +441,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadGraphsTab2(time_range, yaxis) {
|
function loadGraphsTab2(time_range, yaxis) {
|
||||||
|
$('#days-selection').show();
|
||||||
|
|
||||||
setGraphFormat(yaxis);
|
setGraphFormat(yaxis);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_stream_type",
|
url: "get_plays_by_stream_type",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
var dateArray = [];
|
var dateArray = [];
|
||||||
for (var i = 0; i < data.categories.length; i++) {
|
$.each(data.categories, function (i, day) {
|
||||||
dateArray.push(moment(data.categories[i], 'YYYY-MM-DD').valueOf());
|
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
|
||||||
// Highlight the weekend
|
// Highlight the weekend
|
||||||
if ((moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||||
(moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||||
hc_plays_by_stream_type_options.xAxis.plotBands.push({
|
hc_plays_by_day_options.xAxis.plotBands.push({
|
||||||
from: i-0.5,
|
from: i-0.5,
|
||||||
to: i+0.5,
|
to: i+0.5,
|
||||||
color: 'rgba(80,80,80,0.3)'
|
color: 'rgba(80,80,80,0.3)'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_stream_type_options.yAxis.min = 0;
|
hc_plays_by_stream_type_options.yAxis.min = 0;
|
||||||
hc_plays_by_stream_type_options.xAxis.categories = dateArray;
|
hc_plays_by_stream_type_options.xAxis.categories = dateArray;
|
||||||
hc_plays_by_stream_type_options.series = data.series;
|
hc_plays_by_stream_type_options.series = data.series;
|
||||||
@@ -411,9 +475,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_source_resolution",
|
url: "get_plays_by_source_resolution",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_source_resolution_options.xAxis.categories = data.categories;
|
hc_plays_by_source_resolution_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_source_resolution_options.series = data.series;
|
hc_plays_by_source_resolution_options.series = data.series;
|
||||||
var hc_plays_by_source_resolution = new Highcharts.Chart(hc_plays_by_source_resolution_options);
|
var hc_plays_by_source_resolution = new Highcharts.Chart(hc_plays_by_source_resolution_options);
|
||||||
@@ -423,9 +488,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_by_stream_resolution",
|
url: "get_plays_by_stream_resolution",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_stream_resolution_options.xAxis.categories = data.categories;
|
hc_plays_by_stream_resolution_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_stream_resolution_options.series = data.series;
|
hc_plays_by_stream_resolution_options.series = data.series;
|
||||||
var hc_plays_by_stream_resolution = new Highcharts.Chart(hc_plays_by_stream_resolution_options);
|
var hc_plays_by_stream_resolution = new Highcharts.Chart(hc_plays_by_stream_resolution_options);
|
||||||
@@ -435,9 +501,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_stream_type_by_top_10_platforms",
|
url: "get_stream_type_by_top_10_platforms",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_platform_by_stream_type_options.xAxis.categories = data.categories;
|
hc_plays_by_platform_by_stream_type_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_platform_by_stream_type_options.series = data.series;
|
hc_plays_by_platform_by_stream_type_options.series = data.series;
|
||||||
var hc_plays_by_platform_by_stream_type = new Highcharts.Chart(hc_plays_by_platform_by_stream_type_options);
|
var hc_plays_by_platform_by_stream_type = new Highcharts.Chart(hc_plays_by_platform_by_stream_type_options);
|
||||||
@@ -447,9 +514,10 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_stream_type_by_top_10_users",
|
url: "get_stream_type_by_top_10_users",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { time_range: time_range, y_axis: yaxis },
|
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_user_by_stream_type_options.xAxis.categories = data.categories;
|
hc_plays_by_user_by_stream_type_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_user_by_stream_type_options.series = data.series;
|
hc_plays_by_user_by_stream_type_options.series = data.series;
|
||||||
var hc_plays_by_user_by_stream_type = new Highcharts.Chart(hc_plays_by_user_by_stream_type_options);
|
var hc_plays_by_user_by_stream_type = new Highcharts.Chart(hc_plays_by_user_by_stream_type_options);
|
||||||
@@ -458,14 +526,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadGraphsTab3(yaxis) {
|
function loadGraphsTab3(yaxis) {
|
||||||
|
$('#days-selection').hide();
|
||||||
|
|
||||||
setGraphFormat(yaxis);
|
setGraphFormat(yaxis);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_plays_per_month",
|
url: "get_plays_per_month",
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: { y_axis: yaxis },
|
data: { y_axis: yaxis, user_id: selected_user_id },
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||||
hc_plays_by_month_options.yAxis.min = 0;
|
hc_plays_by_month_options.yAxis.min = 0;
|
||||||
hc_plays_by_month_options.xAxis.categories = data.categories;
|
hc_plays_by_month_options.xAxis.categories = data.categories;
|
||||||
hc_plays_by_month_options.series = data.series;
|
hc_plays_by_month_options.series = data.series;
|
||||||
@@ -476,104 +547,137 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set initial state
|
// Set initial state
|
||||||
loadGraphsTab1(current_range, yaxis);
|
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
||||||
|
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
||||||
|
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); }
|
||||||
|
|
||||||
// Tab1 opened
|
// Tab1 opened
|
||||||
$('#graph-tabs a[href="#tabs-1"]').on('shown.bs.tab', function (e) {
|
$('#graph-tabs a[href="#tabs-1"]').on('shown.bs.tab', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
current_tab = $(this).attr('href');
|
current_tab = $(this).attr('href');
|
||||||
$('#days-selection').show();
|
|
||||||
loadGraphsTab1(current_range, yaxis);
|
loadGraphsTab1(current_range, yaxis);
|
||||||
|
$.ajax({
|
||||||
|
url: 'set_graph_config',
|
||||||
|
data: { graph_tab: current_tab.replace('#','') },
|
||||||
|
async: true
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
// Tab2 opened
|
// Tab2 opened
|
||||||
$('#graph-tabs a[href="#tabs-2"]').on('shown.bs.tab', function (e) {
|
$('#graph-tabs a[href="#tabs-2"]').on('shown.bs.tab', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
current_tab = $(this).attr('href');
|
current_tab = $(this).attr('href');
|
||||||
$('#days-selection').show();
|
|
||||||
loadGraphsTab2(current_range, yaxis);
|
loadGraphsTab2(current_range, yaxis);
|
||||||
|
$.ajax({
|
||||||
|
url: 'set_graph_config',
|
||||||
|
data: { graph_tab: current_tab.replace('#','') },
|
||||||
|
async: true
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
// Tab3 opened
|
// Tab3 opened
|
||||||
$('#graph-tabs a[href="#tabs-3"]').on('shown.bs.tab', function (e) {
|
$('#graph-tabs a[href="#tabs-3"]').on('shown.bs.tab', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
current_tab = $(this).attr('href');
|
current_tab = $(this).attr('href');
|
||||||
$('#days-selection').hide();
|
|
||||||
console.log('loading....');
|
|
||||||
loadGraphsTab3(yaxis);
|
loadGraphsTab3(yaxis);
|
||||||
|
$.ajax({
|
||||||
|
url: 'set_graph_config',
|
||||||
|
data: { graph_tab: current_tab.replace('#','') },
|
||||||
|
async: true
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Date range changed
|
// Date range changed
|
||||||
$('#days-selection').on('change', function() {
|
$('#graph-days').on('change', function() {
|
||||||
current_range = $('input[name=date-options]:checked', '#days-selection').val();
|
current_range = $(this).val();
|
||||||
|
if (current_range < 1) {
|
||||||
|
$(this).val(7);
|
||||||
|
current_range = 7;
|
||||||
|
}
|
||||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
||||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
||||||
$('.days').html(current_range);
|
$('.days').html(current_range);
|
||||||
|
$.ajax({
|
||||||
|
url: 'set_graph_config',
|
||||||
|
data: { graph_days: current_range},
|
||||||
|
async: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User changed
|
||||||
|
$('#graph-user').on('change', function() {
|
||||||
|
selected_user_id = $(this).val() || null;
|
||||||
|
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
||||||
|
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
||||||
|
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); }
|
||||||
|
});
|
||||||
|
|
||||||
// Y-axis changed
|
// Y-axis changed
|
||||||
$('#yaxis-selection').on('change', function() {
|
$('#yaxis-selection').on('change', function() {
|
||||||
yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val();
|
yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val();
|
||||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
||||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
||||||
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); }
|
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); }
|
||||||
|
$.ajax({
|
||||||
|
url: 'set_graph_config',
|
||||||
|
data: { graph_type: yaxis},
|
||||||
|
async: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setGraphFormat(type) {
|
function setGraphFormat(type) {
|
||||||
if (type === 'plays') {
|
if (type === 'plays') {
|
||||||
yaxis_format = function() { return this.value; };
|
yaxis_format = function() { return this.value; };
|
||||||
tooltip_format = function() {
|
tooltip_format = function() {
|
||||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||||
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
|
var s = '<b>'+ moment(this.x).format('ddd MMM D') +'</b>';
|
||||||
} else {
|
} else {
|
||||||
var s = '<b>'+ this.x +'</b>';
|
var s = '<b>'+ this.x +'</b>';
|
||||||
}
|
}
|
||||||
if (this.points.length > 1) {
|
if (this.points.length > 1) {
|
||||||
var total = 0;
|
var total = 0;
|
||||||
$.each(this.points, function(i, point) {
|
$.each(this.points, function(i, point) {
|
||||||
s += '<br/>'+point.series.name+': '+point.y;
|
s += '<br/>'+point.series.name+': '+point.y;
|
||||||
total += point.y;
|
total += point.y;
|
||||||
});
|
});
|
||||||
s += '<br><b>Total: '+total+'</b>';
|
s += '<br><b>Total: '+total+'</b>';
|
||||||
} else {
|
} else {
|
||||||
$.each(this.points, function(i, point) {
|
$.each(this.points, function(i, point) {
|
||||||
s += '<br/>'+point.series.name+': '+point.y;
|
s += '<br/>'+point.series.name+': '+point.y;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
stack_labels_format = function() {
|
stack_labels_format = function() {
|
||||||
return this.total;
|
return this.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.yaxis-text').html('Play count');
|
$('.yaxis-text').html('Play count');
|
||||||
} else {
|
} else {
|
||||||
yaxis_format = function() { return moment.duration(this.value, 'seconds').format("m [mins]"); };
|
yaxis_format = function() { return moment.duration(this.value, 'hours').format('H [h] m [m]'); };
|
||||||
tooltip_format = function() {
|
tooltip_format = function() {
|
||||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||||
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
|
var s = '<b>'+ moment(this.x).format('ddd MMM D') +'</b>';
|
||||||
} else {
|
} else {
|
||||||
var s = '<b>'+ this.x +'</b>';
|
var s = '<b>'+ this.x +'</b>';
|
||||||
}
|
}
|
||||||
if (this.points.length > 1) {
|
if (this.points.length > 1) {
|
||||||
var total = 0;
|
var total = 0;
|
||||||
$.each(this.points, function(i, point) {
|
$.each(this.points, function(i, point) {
|
||||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'seconds').format('D [days] H [hrs] m [mins]');
|
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');
|
||||||
total += point.y;
|
total += point.y;
|
||||||
});
|
});
|
||||||
s += '<br/><b>Total: '+moment.duration(total, 'seconds').format('D [days] H [hrs] m [mins]')+'</b>';
|
s += '<br/><b>Total: '+moment.duration(total, 'hours').format('D [days] H [hrs] m [mins]')+'</b>';
|
||||||
} else {
|
} else {
|
||||||
$.each(this.points, function(i, point) {
|
$.each(this.points, function(i, point) {
|
||||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'seconds').format('D [days] H [hrs] m [mins]');
|
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
stack_labels_format = function() {
|
stack_labels_format = function() {
|
||||||
var s = moment.duration(this.total, 'seconds').format("H [hrs] m [mins]");
|
var s = moment.duration(this.total, 'hours').format('H [h] m [m]');
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
$('.yaxis-text').html('Play duration');
|
$('.yaxis-text').html('Play duration');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,42 +1,69 @@
|
|||||||
<%inherit file="base.html"/>
|
<%inherit file="base.html"/>
|
||||||
|
|
||||||
<%def name="headIncludes()">
|
<%def name="headIncludes()">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
|
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.css">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.colVis.css">
|
<link rel="stylesheet" href="${http_root}css/dataTables.colVis.css">
|
||||||
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
|
<link rel="stylesheet" href="${http_root}css/plexpy-dataTables.css">
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
|
|
||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<div class='table-card-header'>
|
<div class='table-card-header'>
|
||||||
<div class="header-bar">
|
<div class="header-bar">
|
||||||
<span><i class="fa fa-history"></i> History</span>
|
<span><i class="fa fa-history"></i> History</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-bar">
|
<div class="button-bar">
|
||||||
<div class="colvis-button-bar hidden-xs"></div>
|
% if _session['user_group'] == 'admin':
|
||||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
|
||||||
<i class="fa fa-trash-o"></i> Delete mode
|
|
||||||
</button>
|
|
||||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||||
|
<i class="fa fa-trash-o"></i> Delete mode
|
||||||
|
</button> 
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<div class="btn-group" id="user-selection">
|
||||||
|
<label>
|
||||||
|
<select name="history-user" id="history-user" class="btn" style="color: inherit;">
|
||||||
|
<option value="">All Users</option>
|
||||||
|
<option disabled>────────────</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
<div class="btn-group" data-toggle="buttons" id="media_type-selection">
|
||||||
|
<label class="btn btn-dark active">
|
||||||
|
<input type="radio" name="media_type-filter" id="history-all" value="" autocomplete="off"> All
|
||||||
|
</label>
|
||||||
|
<label class="btn btn-dark">
|
||||||
|
<input type="radio" name="media_type-filter" id="history-movies" value="movie" autocomplete="off"> Movies
|
||||||
|
</label>
|
||||||
|
<label class="btn btn-dark">
|
||||||
|
<input type="radio" name="media_type-filter" id="history-tv_shows" value="episode" autocomplete="off"> TV Shows
|
||||||
|
</label>
|
||||||
|
<label class="btn btn-dark">
|
||||||
|
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group colvis-button-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='table-card-back'>
|
<div class="table-card-back">
|
||||||
<table class="display" id="history_table" width="100%">
|
<table class="display history_table" id="history_table" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th align='left' id="delete_row">Delete</th>
|
<th align="left" id="delete_row">Delete</th>
|
||||||
<th align='left' id="time">Time</th>
|
<th align="left" id="time">Time</th>
|
||||||
<th align='left' id="friendly_name">User</th>
|
<th align="left" id="friendly_name">User</th>
|
||||||
<th align='left' id="ip_address">IP Address</th>
|
<th align="left" id="ip_address">IP Address</th>
|
||||||
<th align='left' id="platform">Platform</th>
|
<th align="left" id="platform">Platform</th>
|
||||||
<th align='left' id="device">Player</th>
|
<th align="left" id="device">Player</th>
|
||||||
<th align='left' id="title">Title</th>
|
<th align="left" id="title">Title</th>
|
||||||
<th align='left' id="started">Started</th>
|
<th align="left" id="started">Started</th>
|
||||||
<th align='left' id="paused_counter">Paused</th>
|
<th align="left" id="paused_counter">Paused</th>
|
||||||
<th align='left' id="stopped">Stopped</th>
|
<th align="left" id="stopped">Stopped</th>
|
||||||
<th align='left' id="duration">Duration</th>
|
<th align="left" id="duration">Duration</th>
|
||||||
<th align='left' id="percent_complete"></th>
|
<th align="left" id="percent_complete"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -46,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
<div class="modal fade" id="confirm-modal-delete" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-delete">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -69,22 +96,40 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
|
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||||
<script src="interfaces/default/js/dataTables.colVis.js"></script>
|
<script src="${http_root}js/dataTables.colVis.js"></script>
|
||||||
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
|
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||||
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
|
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
<script src="${http_root}js/tables/history_table.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
function loadHistoryTable(media_type) {
|
// Load user ids and names (for the selector)
|
||||||
|
$.ajax({
|
||||||
|
url: 'get_user_names',
|
||||||
|
type: 'get',
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
var select = $('#history-user');
|
||||||
|
data.sort(function (a, b) {
|
||||||
|
return a.friendly_name.localeCompare(b.friendly_name);
|
||||||
|
});
|
||||||
|
data.forEach(function (item) {
|
||||||
|
select.append('<option value="' + item.user_id + '">' +
|
||||||
|
item.friendly_name + '</option>');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadHistoryTable(media_type, selected_user_id) {
|
||||||
history_table_options.ajax = {
|
history_table_options.ajax = {
|
||||||
url: 'get_history',
|
url: 'get_history',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: function (d) {
|
data: function (d) {
|
||||||
return {
|
return {
|
||||||
'json_data': JSON.stringify(d),
|
json_data: JSON.stringify(d),
|
||||||
'media_type': media_type
|
media_type: media_type,
|
||||||
|
user_id: selected_user_id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,21 +139,6 @@
|
|||||||
|
|
||||||
clearSearchButton('history_table', history_table);
|
clearSearchButton('history_table', history_table);
|
||||||
|
|
||||||
$('#history_table_filter').prepend('<div class="btn-group" data-toggle="buttons" id="media_type-selection" style="padding-right: 15px;"> \
|
|
||||||
<label class="btn btn-dark active"> \
|
|
||||||
<input type="radio" name="media_type-filter" id="history-all" value="all" autocomplete="off"> All \
|
|
||||||
</label> \
|
|
||||||
<label class="btn btn-dark"> \
|
|
||||||
<input type="radio" name="media_type-filter" id="history-movies" value="movie" autocomplete="off"> Movies \
|
|
||||||
</label> \
|
|
||||||
<label class="btn btn-dark"> \
|
|
||||||
<input type="radio" name="media_type-filter" id="history-tv_shows" value="episode" autocomplete="off"> TV Shows \
|
|
||||||
</label> \
|
|
||||||
<label class="btn btn-dark"> \
|
|
||||||
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music \
|
|
||||||
</label> \
|
|
||||||
</div>');
|
|
||||||
|
|
||||||
$('#media_type-selection').on('change', function () {
|
$('#media_type-selection').on('change', function () {
|
||||||
$('#media_type-selection > label').removeClass('active');
|
$('#media_type-selection > label').removeClass('active');
|
||||||
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
|
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
|
||||||
@@ -116,19 +146,26 @@
|
|||||||
media_type = $(selected_filter).val();
|
media_type = $(selected_filter).val();
|
||||||
history_table.draw();
|
history_table.draw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#history-user').on('change', function () {
|
||||||
|
selected_user_id = $(this).val() || null;
|
||||||
|
history_table.draw();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var media_type = 'all';
|
var media_type = null;
|
||||||
loadHistoryTable(media_type);
|
var selected_user_id = "${_session['user_id']}" == "None" ? null : "${_session['user_id']}"
|
||||||
|
loadHistoryTable(media_type, selected_user_id);
|
||||||
|
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
$('#row-edit-mode').on('click', function() {
|
$('#row-edit-mode').on('click', function() {
|
||||||
$('#row-edit-mode-alert').fadeIn(200);
|
$('#row-edit-mode-alert').fadeIn(200);
|
||||||
|
|
||||||
if ($(this).hasClass('active')) {
|
if ($(this).hasClass('active')) {
|
||||||
if (history_to_delete.length > 0) {
|
if (history_to_delete.length > 0) {
|
||||||
$('#deleteCount').text(history_to_delete.length);
|
$('#deleteCount').text(history_to_delete.length);
|
||||||
$('#confirm-modal').modal();
|
$('#confirm-modal-delete').modal();
|
||||||
$('#confirm-modal').one('click', '#confirm-delete', function () {
|
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||||
for (var i = 0; i < history_to_delete.length; i++) {
|
for (var i = 0; i < history_to_delete.length; i++) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'delete_history_rows',
|
url: 'delete_history_rows',
|
||||||
@@ -157,6 +194,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
% endif
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
@@ -5,19 +5,19 @@
|
|||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
<h4 class="modal-title" id="myModalLabel">
|
<h4 class="modal-title" id="myModalLabel">
|
||||||
<strong><span id="modal_header_ip_address">
|
<strong><span id="modal_header_ip_address">
|
||||||
<i class="fa fa-history"></i> History for <span id="date-header">${data}</span>
|
<i class="fa fa-history"></i> History for <span id="date-header">${data['start_date']}</span>
|
||||||
</span></strong>
|
</span></strong>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="modal-text">
|
<div class="modal-body" id="modal-text">
|
||||||
<table class="display" id="history_table" width="100%">
|
<table class="display history_table" id="history_table_modal" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th align='left' id="started">Started</th>
|
<th align="left" id="started">Started</th>
|
||||||
<th align='left' id="stopped">Stopped</th>
|
<th align="left" id="stopped">Stopped</th>
|
||||||
<th align='left' id="friendly_name">User</th>
|
<th align="left" id="friendly_name">User</th>
|
||||||
<th align='left' id="player">Player</th>
|
<th align="left" id="player">Player</th>
|
||||||
<th align='left' id="title">Title</th>
|
<th align="left" id="title">Title</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -29,24 +29,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||||
</div>
|
</div>
|
||||||
<script src="interfaces/default/js/tables/history_table_modal.js"></script>
|
<script src="${http_root}js/tables/history_table_modal.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#date-header').html(moment('${data}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
$('#date-header').html(moment('${data["start_date"]}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
||||||
history_table_modal_options.ajax = {
|
history_table_modal_options.ajax = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: "post",
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return {
|
||||||
'grouping': false,
|
json_data: JSON.stringify(d),
|
||||||
'start_date': '${data}'
|
grouping: false,
|
||||||
};
|
user_id: "${data['user_id']}",
|
||||||
|
start_date: "${data['start_date']}",
|
||||||
|
media_type: "${data.get('media_type')}",
|
||||||
|
transcode_decision: "${data.get('transcode_decision')}"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
history_table = $('#history_table').DataTable(history_table_modal_options);
|
history_table = $('#history_table_modal').DataTable(history_table_modal_options);
|
||||||
|
|
||||||
clearSearchButton('history_table', history_table);
|
clearSearchButton('history_table_modal', history_table);
|
||||||
|
|
||||||
// Move #info-modal to parent container
|
// Move #info-modal to parent container
|
||||||
if (!($('#history-modal').next().is('#info-modal'))) {
|
if (!($('#history-modal').next().is('#info-modal'))) {
|
||||||
|
@@ -44,6 +44,11 @@ player Returns the player name for the associated stat.
|
|||||||
== Only if 'stat_id' is 'last_watched' ==
|
== Only if 'stat_id' is 'last_watched' ==
|
||||||
last_watch Returns the time the media item was last watched.
|
last_watch Returns the time the media item was last watched.
|
||||||
|
|
||||||
|
== Only if 'stat_id' is 'most_concurrent' ==
|
||||||
|
count Returns the count of the most concurrent streams.
|
||||||
|
started Returns the start time of the most concurrent streams.
|
||||||
|
stopped Returns the stop time of the most concurrent streams.
|
||||||
|
|
||||||
DOCUMENTATION :: END
|
DOCUMENTATION :: END
|
||||||
</%doc>
|
</%doc>
|
||||||
|
|
||||||
@@ -65,8 +70,8 @@ DOCUMENTATION :: END
|
|||||||
%>
|
%>
|
||||||
|
|
||||||
% if data:
|
% if data:
|
||||||
% if data[0]['stat_id']:
|
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
|
% if any(top_stat['rows'] for top_stat in data):
|
||||||
% for top_stat in data:
|
% for top_stat in data:
|
||||||
% if top_stat['stat_id'] == 'top_tv' and top_stat['rows']:
|
% if top_stat['stat_id'] == 'top_tv' and top_stat['rows']:
|
||||||
<div class="home-platforms-instance">
|
<div class="home-platforms-instance">
|
||||||
@@ -77,9 +82,13 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
% if top_stat['stat_type'] == 'total_plays':
|
% if top_stat['stat_type'] == 'total_plays':
|
||||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||||
@@ -89,31 +98,44 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -125,17 +147,26 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
<div class="home-platforms-instance-list-poster">
|
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-instance-list-poster">
|
||||||
</div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-poster">
|
||||||
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</a>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,39 +188,56 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -197,17 +245,26 @@ DOCUMENTATION :: END
|
|||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-poster">
|
||||||
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -229,9 +286,13 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
% if top_stat['stat_type'] == 'total_plays':
|
% if top_stat['stat_type'] == 'total_plays':
|
||||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||||
@@ -241,31 +302,44 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['thumb']:
|
% if top_stat['rows'][0]['thumb']:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -277,17 +351,26 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['thumb']:
|
% if top_stat['rows'][loop.index]['thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-poster">
|
||||||
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -309,39 +392,56 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['thumb']:
|
% if top_stat['rows'][0]['thumb']:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -349,17 +449,26 @@ DOCUMENTATION :: END
|
|||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['thumb']:
|
% if top_stat['rows'][loop.index]['thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-poster">
|
||||||
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -381,9 +490,13 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
% if top_stat['stat_type'] == 'total_plays':
|
% if top_stat['stat_type'] == 'total_plays':
|
||||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||||
@@ -393,31 +506,44 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-cover">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
<div class="home-platforms-cover-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-cover">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-cover">
|
||||||
|
<div class="home-platforms-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -429,17 +555,26 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-cover">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
<div class="home-platforms-list-cover-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-cover">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-cover">
|
||||||
|
<div class="home-platforms-list-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -461,39 +596,56 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-playcount">
|
<div class="home-platforms-instance-playcount">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-cover">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
<div class="home-platforms-cover-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-cover">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-cover">
|
||||||
|
<div class="home-platforms-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -501,17 +653,26 @@ DOCUMENTATION :: END
|
|||||||
<p> users</p>
|
<p> users</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-cover">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
<div class="home-platforms-list-cover-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-cover">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-cover">
|
||||||
|
<div class="home-platforms-list-cover-face" style="background-image: url(${http_root}images/cover.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -535,11 +696,11 @@ DOCUMENTATION :: END
|
|||||||
<h4>
|
<h4>
|
||||||
% if top_stat['rows'][0]['user_id']:
|
% if top_stat['rows'][0]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
|
||||||
% endif
|
|
||||||
${top_stat['rows'][0]['friendly_name']}
|
${top_stat['rows'][0]['friendly_name']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['friendly_name']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
% if top_stat['stat_type'] == 'total_plays':
|
% if top_stat['stat_type'] == 'total_plays':
|
||||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||||
@@ -551,8 +712,6 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
% if top_stat['rows'][0]['user_id']:
|
% if top_stat['rows'][0]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
|
||||||
% endif
|
% endif
|
||||||
% if top_stat['rows'][0]['user_thumb'] != '':
|
% if top_stat['rows'][0]['user_thumb'] != '':
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
@@ -560,28 +719,30 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-instance-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);"></div>
|
<div class="home-platforms-instance-oval" style="background-image: url(${http_root}images/gravatar-default.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
% if top_stat['rows'][0]['user_id']:
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
% if top_stat['rows'][loop.index]['user_id']:
|
% if top_stat['rows'][loop.index]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
|
||||||
% endif
|
|
||||||
${top_stat['rows'][loop.index]['friendly_name']}
|
${top_stat['rows'][loop.index]['friendly_name']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['friendly_name']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-playcount">
|
<div class="home-platforms-instance-list-playcount">
|
||||||
@@ -595,8 +756,6 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
% if top_stat['rows'][loop.index]['user_id']:
|
% if top_stat['rows'][loop.index]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
|
||||||
% endif
|
% endif
|
||||||
% if top_stat['rows'][loop.index]['user_thumb'] != '':
|
% if top_stat['rows'][loop.index]['user_thumb'] != '':
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
@@ -604,10 +763,12 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-instance-list-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);"></div>
|
<div class="home-platforms-instance-list-oval" style="background-image: url(${http_root}images/gravatar-default.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
% if top_stat['rows'][loop.index]['user_id']:
|
||||||
|
</a>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -639,16 +800,16 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div id="platform-stat" class="home-platforms-instance-poster" title="${top_stat['rows'][0]['platform_type']}">
|
<div id="platform-stat" class="home-platforms-instance-poster" title="${top_stat['rows'][0]['platform_type']}">
|
||||||
<script>
|
<script>
|
||||||
$("#platform-stat").html("<div class='home-platforms-instance-box' style='background-image: url(" + getPlatformImagePath('${top_stat['rows'][0]['platform_type']}') + ");'>");
|
$("#platform-stat").html("<div class='home-platforms-instance-box' style='background-image: url(" + getPlatformImagePath("${top_stat['rows'][0]['platform_type']}") + ");'>");
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
%if len(top_stat['rows']) > 1:
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
@@ -667,7 +828,7 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-poster" id="home-platforms-instance-poster-${loop.index + 1}" title="${top_stat['rows'][loop.index]['platform_type']}">
|
<div class="home-platforms-instance-poster" id="home-platforms-instance-poster-${loop.index + 1}" title="${top_stat['rows'][loop.index]['platform_type']}">
|
||||||
<script>
|
<script>
|
||||||
$("#home-platforms-instance-poster-${loop.index + 1}").html("<div class='home-platforms-instance-list-box' style='background-image: url(" + getPlatformImagePath('${top_stat['rows'][loop.index]['platform_type']}') + ");'>");
|
$("#home-platforms-instance-poster-${loop.index + 1}").html("<div class='home-platforms-instance-list-box' style='background-image: url(" + getPlatformImagePath("${top_stat['rows'][loop.index]['platform_type']}") + ");'>");
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
@@ -691,18 +852,22 @@ DOCUMENTATION :: END
|
|||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-last-user">
|
<div class="home-platforms-instance-last-user">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?source=history&rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
${top_stat['rows'][0]['title']}
|
${top_stat['rows'][0]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['title']}
|
||||||
|
% endif
|
||||||
</h4>
|
</h4>
|
||||||
<h5>
|
<h5>
|
||||||
% if top_stat['rows'][0]['user_id']:
|
% if top_stat['rows'][0]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][0]['user_id']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
|
||||||
% endif
|
|
||||||
${top_stat['rows'][0]['friendly_name']}
|
${top_stat['rows'][0]['friendly_name']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][0]['friendly_name']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
<p>
|
<p>
|
||||||
<span id="last-watch-stat">
|
<span id="last-watch-stat">
|
||||||
@@ -713,42 +878,55 @@ DOCUMENTATION :: END
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}" title="${top_stat['rows'][0]['title']}">
|
% if top_stat['rows'][0]['rating_key']:
|
||||||
|
<a href="info?source=history&rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||||
% if top_stat['rows'][0]['thumb']:
|
% if top_stat['rows'][0]['thumb']:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster">
|
<div class="home-platforms-instance-poster">
|
||||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
%if len(top_stat['rows']) > 1:
|
% else:
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<div class="home-platforms-instance-list">
|
<div class="home-platforms-instance-list">
|
||||||
% for row in top_stat['rows']:
|
% for row in top_stat['rows']:
|
||||||
%if loop.index > 0:
|
% if loop.index > 0:
|
||||||
<li>
|
<li>
|
||||||
<div class="home-platforms-instance-list-info">
|
<div class="home-platforms-instance-list-info">
|
||||||
<div class="home-platforms-instance-list-name">
|
<div class="home-platforms-instance-list-name">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?source=history&rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
${top_stat['rows'][loop.index]['title']}
|
${top_stat['rows'][loop.index]['title']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-platforms-instance-list-last-user">
|
<div class="home-platforms-instance-list-last-user">
|
||||||
<h5>
|
<h5>
|
||||||
% if top_stat['rows'][loop.index]['user_id']:
|
% if top_stat['rows'][loop.index]['user_id']:
|
||||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
||||||
% else:
|
|
||||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
|
||||||
% endif
|
|
||||||
${top_stat['rows'][loop.index]['friendly_name']}
|
${top_stat['rows'][loop.index]['friendly_name']}
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
${top_stat['rows'][loop.index]['friendly_name']}
|
||||||
|
% endif
|
||||||
</h5>
|
</h5>
|
||||||
<p>
|
<p>
|
||||||
<span id="home-platforms-instance-list-last-watch-${loop.index + 1}">
|
<span id="home-platforms-instance-list-last-watch-${loop.index + 1}">
|
||||||
@@ -759,17 +937,26 @@ DOCUMENTATION :: END
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}" title="${top_stat['rows'][loop.index]['title']}">
|
% if top_stat['rows'][loop.index]['rating_key']:
|
||||||
|
<a href="info?source=history&rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||||
% if top_stat['rows'][loop.index]['thumb']:
|
% if top_stat['rows'][loop.index]['thumb']:
|
||||||
<div class="home-platforms-instance-list-poster">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="home-platforms-instance-poster2">
|
<div class="home-platforms-instance-list-poster">
|
||||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
% else:
|
||||||
|
<div class="home-platforms-instance-list-poster">
|
||||||
|
<div class="home-platforms-list-poster-face" style="background-image: url(${http_root}images/poster.png);"></div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div class="home-platforms-instance-list-number">
|
<div class="home-platforms-instance-list-number">
|
||||||
<h4>${loop.index + 1}</h4>
|
<h4>${loop.index + 1}</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -782,8 +969,74 @@ DOCUMENTATION :: END
|
|||||||
% endif
|
% endif
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
|
% elif top_stat['stat_id'] == 'most_concurrent' and top_stat['rows']:
|
||||||
|
<div class="home-platforms-instance">
|
||||||
|
<li>
|
||||||
|
<div class="home-platforms-instance-info">
|
||||||
|
<div class="home-platforms-instance-name">
|
||||||
|
<h4>Most Concurrent Streams</h4>
|
||||||
|
</div>
|
||||||
|
<div class="home-platforms-instance-playcount">
|
||||||
|
<h4>
|
||||||
|
<span id="most-concurrent-start">
|
||||||
|
<script>
|
||||||
|
$('#most-concurrent-start').text(moment(${top_stat['rows'][0]['started']},"X").format(date_format + ' ' + time_format));
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
<h3>${top_stat['rows'][0]['count']}</h3>
|
||||||
|
<p> streams</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-instance-box" style="background-image: url(${http_root}images/home-stat_most-concurrent.png);"></div>
|
||||||
|
</div>
|
||||||
|
% if len(top_stat['rows']) > 1:
|
||||||
|
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<div class="slider">
|
||||||
|
<div class="home-platforms-instance-list">
|
||||||
|
% for row in top_stat['rows']:
|
||||||
|
% if loop.index > 0:
|
||||||
|
<li>
|
||||||
|
<div class="home-platforms-instance-list-info">
|
||||||
|
<div class="home-platforms-instance-list-name">
|
||||||
|
<h5>
|
||||||
|
${top_stat['rows'][loop.index]['title']}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="home-platforms-instance-list-playcount">
|
||||||
|
<h3>${top_stat['rows'][loop.index]['count']}</h3>
|
||||||
|
<p> streams
|
||||||
|
% if top_stat['rows'][loop.index]['started']:
|
||||||
|
- <span id="most-concurrent-start-${loop.index + 1}">
|
||||||
|
<script>
|
||||||
|
$('#most-concurrent-start-${loop.index + 1}').text(moment(${top_stat['rows'][loop.index]['started']},"X").format(date_format + ' ' + time_format));
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
% else:
|
||||||
|
- N/A
|
||||||
|
% endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-platforms-instance-poster">
|
||||||
|
<div class="home-platforms-instance-list-box" style="background-image: url(${http_root}images/home-stat_most-concurrent.png);"></div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
% endif
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
% endif
|
% endif
|
||||||
% endfor
|
% endfor
|
||||||
|
% else:
|
||||||
|
<div class="text-muted">No stats to show for the selected period.</div><br>
|
||||||
|
% endif
|
||||||
</ul>
|
</ul>
|
||||||
<script>
|
<script>
|
||||||
var topZIndex = 2;
|
var topZIndex = 2;
|
||||||
@@ -799,9 +1052,5 @@ DOCUMENTATION :: END
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
% else:
|
% else:
|
||||||
<div class="text-muted">No stats for selected period.</div><br>
|
<div class="text-muted">No stats to show for the selected period.</div><br>
|
||||||
% endif
|
|
||||||
% else:
|
|
||||||
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
|
|
||||||
</div><br>
|
|
||||||
% endif
|
% endif
|
BIN
data/interfaces/default/images/art.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 66 KiB |
BIN
data/interfaces/default/images/home-stat_most-concurrent.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/1.33.png
Normal file
After Width: | Height: | Size: 926 B |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/1.66.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/1.78.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/1.85.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/2.20.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/2.25.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
data/interfaces/default/images/media_flags/aspect_ratio/2.35.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
data/interfaces/default/images/media_flags/audio_channels/1.png
Normal file
After Width: | Height: | Size: 662 B |
BIN
data/interfaces/default/images/media_flags/audio_channels/2.png
Normal file
After Width: | Height: | Size: 873 B |
BIN
data/interfaces/default/images/media_flags/audio_channels/5.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
data/interfaces/default/images/media_flags/audio_channels/6.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
data/interfaces/default/images/media_flags/audio_channels/7.png
Normal file
After Width: | Height: | Size: 757 B |
BIN
data/interfaces/default/images/media_flags/audio_channels/8.png
Normal file
After Width: | Height: | Size: 527 B |
BIN
data/interfaces/default/images/media_flags/audio_codec/aac.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/aif.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/aifc.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/aiff.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/alac.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/ape.png
Normal file
After Width: | Height: | Size: 523 B |
BIN
data/interfaces/default/images/media_flags/audio_codec/cdda.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/dts.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/eac3.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/flac.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/m4a.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/mlp.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/mp2.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/mp3.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/mpc.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/ogg.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/pcm.png
Normal file
After Width: | Height: | Size: 827 B |
BIN
data/interfaces/default/images/media_flags/audio_codec/ra.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/shn.png
Normal file
After Width: | Height: | Size: 270 B |
BIN
data/interfaces/default/images/media_flags/audio_codec/wav.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/wave.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/wma.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/wmahd.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 778 B |
BIN
data/interfaces/default/images/media_flags/audio_codec/wmav2.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
data/interfaces/default/images/media_flags/audio_codec/wv.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
data/interfaces/default/images/media_flags/content_rating/G.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
BIN
data/interfaces/default/images/media_flags/content_rating/NR.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.2 KiB |
BIN
data/interfaces/default/images/media_flags/content_rating/PG.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
data/interfaces/default/images/media_flags/content_rating/R.png
Normal file
After Width: | Height: | Size: 928 B |
After Width: | Height: | Size: 931 B |
After Width: | Height: | Size: 1017 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 821 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
BIN
data/interfaces/default/images/media_flags/content_rating/X.png
Normal file
After Width: | Height: | Size: 990 B |
After Width: | Height: | Size: 539 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |