Compare commits
831 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
bbf774379d | ||
![]() |
24c8c4319d | ||
![]() |
3b8f9f5892 | ||
![]() |
b47ccd06f9 | ||
![]() |
8c4292f9ac | ||
![]() |
a8fbf8ab1d | ||
![]() |
38e9938666 | ||
![]() |
93c4a0652e | ||
![]() |
2fff6647fd | ||
![]() |
36f0f60c49 | ||
![]() |
d12b57e1de | ||
![]() |
deb16428ed | ||
![]() |
a75aba4aee | ||
![]() |
bb5aa2be3d | ||
![]() |
ef6ef98541 | ||
![]() |
89f581f63e | ||
![]() |
6081fa329b | ||
![]() |
112811f3e2 | ||
![]() |
ede07595c3 | ||
![]() |
08d65623dd | ||
![]() |
7540b5fb34 | ||
![]() |
10c54c2d10 | ||
![]() |
b8d9c8cc47 | ||
![]() |
bac5018b27 | ||
![]() |
c65d9898c8 | ||
![]() |
d7284c40bd | ||
![]() |
1c00f82097 | ||
![]() |
c501923f2b | ||
![]() |
81b22a8c36 | ||
![]() |
beb66396fe | ||
![]() |
aaf3de68cf | ||
![]() |
c827c9e825 | ||
![]() |
5100fdbc96 | ||
![]() |
fae3f38a88 | ||
![]() |
a8236222fb | ||
![]() |
2be4d9b6c9 | ||
![]() |
00934b04d2 | ||
![]() |
1e28b22c70 | ||
![]() |
776061605f | ||
![]() |
683e5663e1 | ||
![]() |
090011c9a5 | ||
![]() |
30b11bce98 | ||
![]() |
2a91ec1560 | ||
![]() |
908ce1ff8d | ||
![]() |
fac47ee68b | ||
![]() |
0f8c122ee3 | ||
![]() |
893c91a15d | ||
![]() |
6d152cf308 | ||
![]() |
af2d0446da | ||
![]() |
244b03ba3e | ||
![]() |
50e0629890 | ||
![]() |
c296b38b78 | ||
![]() |
f7cdfd3f30 | ||
![]() |
44cb2400d0 | ||
![]() |
d297597fa6 | ||
![]() |
168e74aa23 | ||
![]() |
35fa8a749b | ||
![]() |
21a1870884 | ||
![]() |
1d86187f79 | ||
![]() |
558f7873f5 | ||
![]() |
a20a52f5f1 | ||
![]() |
17bb57d5f5 | ||
![]() |
e6aef01508 | ||
![]() |
013d957e47 | ||
![]() |
99e064e040 | ||
![]() |
9e5334ac81 | ||
![]() |
fd43cf5dd4 | ||
![]() |
9aea663754 | ||
![]() |
223e2b2b32 | ||
![]() |
ca91adbd53 | ||
![]() |
4fffbf8a0c | ||
![]() |
c3ea35806e | ||
![]() |
1c4df69e61 | ||
![]() |
707c30b0af | ||
![]() |
4d87666a42 | ||
![]() |
b28ac1543a | ||
![]() |
1983597cf1 | ||
![]() |
69a3b5134f | ||
![]() |
53044c75dd | ||
![]() |
12056ac2ba | ||
![]() |
7c8fb58600 | ||
![]() |
da2e7635bd | ||
![]() |
f8b75eadc6 | ||
![]() |
e9bc767c3b | ||
![]() |
1644bbd4b7 | ||
![]() |
18b328a387 | ||
![]() |
98411f0715 | ||
![]() |
fcb3474312 | ||
![]() |
6362b51902 | ||
![]() |
d79d5d5b39 | ||
![]() |
80df8f6191 | ||
![]() |
fd9cf7017b | ||
![]() |
2eed2d54ca | ||
![]() |
0c33e7492a | ||
![]() |
dd137e5c36 | ||
![]() |
dea9663adf | ||
![]() |
767dd20bdc | ||
![]() |
c350943041 | ||
![]() |
c60340d88b | ||
![]() |
276c0e5c7d | ||
![]() |
054f116017 | ||
![]() |
e9017a8342 | ||
![]() |
9cff20ca16 | ||
![]() |
6cbfacaeae | ||
![]() |
8ebfa20db0 | ||
![]() |
5beb4876fb | ||
![]() |
c723d33d38 | ||
![]() |
f75fca12c8 | ||
![]() |
8671707e4d | ||
![]() |
a9316ebea1 | ||
![]() |
ef86740466 | ||
![]() |
57cb755668 | ||
![]() |
aa75cf2b73 | ||
![]() |
3f8224fec5 | ||
![]() |
0b67abb2a2 | ||
![]() |
872ef2771e | ||
![]() |
3b457304e9 | ||
![]() |
974c672a87 | ||
![]() |
b9f47df930 | ||
![]() |
4c388f60d6 | ||
![]() |
d6b31dc542 | ||
![]() |
539cd60e92 | ||
![]() |
056bcd1488 | ||
![]() |
1c58a47073 | ||
![]() |
32cf1ada8b | ||
![]() |
968132099e | ||
![]() |
7b3874dcaa | ||
![]() |
0302b2412a | ||
![]() |
4ac5329019 | ||
![]() |
37bc68573c | ||
![]() |
dc8996c4d2 | ||
![]() |
1ef9d72534 | ||
![]() |
db7225fbad | ||
![]() |
2c354ad783 | ||
![]() |
b96abc8853 | ||
![]() |
c4dc81e8fb | ||
![]() |
be753983fe | ||
![]() |
1bcb34d7eb | ||
![]() |
2243cd1de9 | ||
![]() |
1ff58a85dc | ||
![]() |
428706d9a7 | ||
![]() |
5967636ef9 | ||
![]() |
ddc9563de9 | ||
![]() |
3d1a1b5e45 | ||
![]() |
60b330573e | ||
![]() |
76c1558473 | ||
![]() |
5fe47d797f | ||
![]() |
dd5ba05c88 | ||
![]() |
26d825dc09 | ||
![]() |
3299ec7c82 | ||
![]() |
c25c48c1a6 | ||
![]() |
2680162b67 | ||
![]() |
7d3d2957c3 | ||
![]() |
c12862ffba | ||
![]() |
9968f8b6dd | ||
![]() |
2ea6ae648c | ||
![]() |
75f6ed3fc1 | ||
![]() |
39c5156d08 | ||
![]() |
2f2069e0ad | ||
![]() |
be4e8985b7 | ||
![]() |
3e344ce56c | ||
![]() |
ca4e6cde3e | ||
![]() |
1ad982f5b7 | ||
![]() |
280a5ae744 | ||
![]() |
b1e27d9bc2 | ||
![]() |
92bbf8e994 | ||
![]() |
cf49f4d6bf | ||
![]() |
721bd606cb | ||
![]() |
61865ace64 | ||
![]() |
5b88058133 | ||
![]() |
2616e14c83 | ||
![]() |
f1c4bf6249 | ||
![]() |
e884d018ed | ||
![]() |
9d925cce03 | ||
![]() |
ff8d9f9f4c | ||
![]() |
c65d8a1ec6 | ||
![]() |
2346033871 | ||
![]() |
6103d14a6f | ||
![]() |
b0600402dd | ||
![]() |
8fca6352cf | ||
![]() |
34167495ed | ||
![]() |
672377ffb7 | ||
![]() |
441572ea12 | ||
![]() |
c12c72bbd6 | ||
![]() |
dff493c8d9 | ||
![]() |
f7bffdc050 | ||
![]() |
37df262b24 | ||
![]() |
f7bc208fd1 | ||
![]() |
40260c33af | ||
![]() |
138390ff59 | ||
![]() |
a5ef749bce | ||
![]() |
667c6fe3d1 | ||
![]() |
3320f2a2f9 | ||
![]() |
59bb5139f7 | ||
![]() |
c1f32674dc | ||
![]() |
8b52548016 | ||
![]() |
200a85adcf | ||
![]() |
960c281659 | ||
![]() |
d9d04f4857 | ||
![]() |
a053789344 | ||
![]() |
0784b0a0db | ||
![]() |
b84f214030 | ||
![]() |
f15e3a47ea | ||
![]() |
82b567f15f | ||
![]() |
6e2fa9a3b4 | ||
![]() |
16b20976b6 | ||
![]() |
22a4478a06 | ||
![]() |
7327be1ff8 | ||
![]() |
8f4034540c | ||
![]() |
45a385f36e | ||
![]() |
5aa7192e90 | ||
![]() |
c7d51b90c6 | ||
![]() |
dcb7486fd6 | ||
![]() |
e9585ea15d | ||
![]() |
339361d15c | ||
![]() |
2b9817319a | ||
![]() |
a008b09e09 | ||
![]() |
fb8f6ebd8c | ||
![]() |
1c3a3876e3 | ||
![]() |
dfd0b1bf64 | ||
![]() |
e0873ff5ad | ||
![]() |
1f2efedf7f | ||
![]() |
91446d01a7 | ||
![]() |
c45a903f9f | ||
![]() |
e6556eaf27 | ||
![]() |
28d05ba9fe | ||
![]() |
25249e7538 | ||
![]() |
d36de5a535 | ||
![]() |
f921035ee7 | ||
![]() |
f901eaa625 | ||
![]() |
9738bdcb55 | ||
![]() |
6f6541fdb8 | ||
![]() |
08959fd1e6 | ||
![]() |
89a03fb263 | ||
![]() |
b9e900f540 | ||
![]() |
ed6ff9c10c | ||
![]() |
5da4fe953e | ||
![]() |
302ca85dd3 | ||
![]() |
98c0d6619a | ||
![]() |
9fdcc93e2e | ||
![]() |
74e8d7d329 | ||
![]() |
5595ef2e20 | ||
![]() |
6ee5eb13f0 | ||
![]() |
3afa52b607 | ||
![]() |
28227a3c35 | ||
![]() |
2d671fdfc4 | ||
![]() |
ff67719cf8 | ||
![]() |
1761b14fe9 | ||
![]() |
8792aa6c70 | ||
![]() |
34e548cc15 | ||
![]() |
fa25809e7a | ||
![]() |
fa71beb03f | ||
![]() |
9c955771c0 | ||
![]() |
0c6ccc5d52 | ||
![]() |
7c6619ebc5 | ||
![]() |
22ed8a3a95 | ||
![]() |
924ed70458 | ||
![]() |
de4d8fb277 | ||
![]() |
d61e699dc9 | ||
![]() |
ad183ff9fe | ||
![]() |
078f4babf5 | ||
![]() |
8a989d71ca | ||
![]() |
78f959d39a | ||
![]() |
20056718db | ||
![]() |
6eec4d1ca6 | ||
![]() |
7c725ee424 | ||
![]() |
4fa70cb234 | ||
![]() |
483f5825db | ||
![]() |
d23aaf91f7 | ||
![]() |
1f3a238ab2 | ||
![]() |
060c549259 | ||
![]() |
626e7fdf82 | ||
![]() |
adc808ac9f | ||
![]() |
fc75232519 | ||
![]() |
c85ee3aec0 | ||
![]() |
881142d4a1 | ||
![]() |
c1b5514789 | ||
![]() |
7170dbd800 | ||
![]() |
d6c21e173d | ||
![]() |
179eaf1bbe | ||
![]() |
8791babf8e | ||
![]() |
048b31c87a | ||
![]() |
e386d3ee21 | ||
![]() |
285946bf94 | ||
![]() |
578ba52215 | ||
![]() |
5126c39c26 | ||
![]() |
5ec9e41244 | ||
![]() |
5eebf6592a | ||
![]() |
7994351644 | ||
![]() |
e445228b8a | ||
![]() |
46e6250329 | ||
![]() |
2edfc1e3da | ||
![]() |
f2024b0854 | ||
![]() |
5f2cf6cb7a | ||
![]() |
73664b6a03 | ||
![]() |
af131ce16d | ||
![]() |
414c4c2ffa | ||
![]() |
d2cdc2cea2 | ||
![]() |
b860f9a5e8 | ||
![]() |
e5535b6167 | ||
![]() |
6359c02c80 | ||
![]() |
1c589fbefa | ||
![]() |
b768ad8a19 | ||
![]() |
60878ed12e | ||
![]() |
328d744efd | ||
![]() |
92a868c3c6 | ||
![]() |
7b9210a5fc | ||
![]() |
fb872596d6 | ||
![]() |
a43efef28a | ||
![]() |
e30e6dfe35 | ||
![]() |
eaadd5e2d6 | ||
![]() |
c608c7c9fc | ||
![]() |
451485d706 | ||
![]() |
849675185d | ||
![]() |
b060a23733 | ||
![]() |
bee9091182 | ||
![]() |
f8e1ba6798 | ||
![]() |
e5ce57fead | ||
![]() |
c9e2d1d200 | ||
![]() |
88b6eae3bf | ||
![]() |
c7d6ee8021 | ||
![]() |
7480508af2 | ||
![]() |
3fcc44aacf | ||
![]() |
fcc4575a86 | ||
![]() |
ebd0276eae | ||
![]() |
35521e127f | ||
![]() |
2c83554631 | ||
![]() |
56b717b1a1 | ||
![]() |
56f601e2a5 | ||
![]() |
3867dd7bdd | ||
![]() |
b7dc28c3fb | ||
![]() |
e6383d52ad | ||
![]() |
0a2ebb8815 | ||
![]() |
e44a0fed22 | ||
![]() |
2d7585d64b | ||
![]() |
df290d995b | ||
![]() |
36ee5234b1 | ||
![]() |
48e601d5ff | ||
![]() |
84aa727387 | ||
![]() |
29204cb6ba | ||
![]() |
6b75a55ce8 | ||
![]() |
527e7be13d | ||
![]() |
2d739f64cf | ||
![]() |
39ae387e0e | ||
![]() |
aaa1f0aa33 | ||
![]() |
423360e820 | ||
![]() |
52fae6df0e | ||
![]() |
58ea4a65e1 | ||
![]() |
82025457ba | ||
![]() |
9d60ed7174 | ||
![]() |
d67f903107 | ||
![]() |
389ce97d03 | ||
![]() |
98f5c33af7 | ||
![]() |
dceab3791f | ||
![]() |
d44bd2f35b | ||
![]() |
82fc314b35 | ||
![]() |
6ff902e653 | ||
![]() |
0245668907 | ||
![]() |
3436175223 | ||
![]() |
8138c27242 | ||
![]() |
5705e677d4 | ||
![]() |
c2417de895 | ||
![]() |
9b03ffbaa1 | ||
![]() |
53c98d0acc | ||
![]() |
883a183208 | ||
![]() |
85a3f15531 | ||
![]() |
28d136075c | ||
![]() |
a682cd31af | ||
![]() |
9fc44f793b | ||
![]() |
16b1b2a781 | ||
![]() |
6e14c08570 | ||
![]() |
b5f1475a90 | ||
![]() |
8efb37c4ec | ||
![]() |
ab78b59f61 | ||
![]() |
e7b1e177d2 | ||
![]() |
e41fa7a08e | ||
![]() |
0f10c21a66 | ||
![]() |
f2b9984cd6 | ||
![]() |
99dd133a9a | ||
![]() |
7bbcc575b0 | ||
![]() |
0fded38bfb | ||
![]() |
88a651645a | ||
![]() |
60380c6a99 | ||
![]() |
298d98cee9 | ||
![]() |
b91a4844e0 | ||
![]() |
f00de8e548 | ||
![]() |
dfbf435c77 | ||
![]() |
dd7390d111 | ||
![]() |
73125153c8 | ||
![]() |
c08d581df3 | ||
![]() |
ef5d9d6604 | ||
![]() |
84ffcc7948 | ||
![]() |
53830a7711 | ||
![]() |
a126703f44 | ||
![]() |
6720d696eb | ||
![]() |
7328fcd0fb | ||
![]() |
1e57e952db | ||
![]() |
d627e0efdb | ||
![]() |
2193d06363 | ||
![]() |
3fc573bd90 | ||
![]() |
9b9088bd5e | ||
![]() |
53fb8b999f | ||
![]() |
58efd299cc | ||
![]() |
8ae2f718f4 | ||
![]() |
ff0ed1abe4 | ||
![]() |
41e1fee4b4 | ||
![]() |
b308e92c9d | ||
![]() |
82808cdc97 | ||
![]() |
6d5e7135ae | ||
![]() |
4a75e372d9 | ||
![]() |
a37ca3f29c | ||
![]() |
9016fc41f9 | ||
![]() |
f56ba300ac | ||
![]() |
0a850fc3af | ||
![]() |
5124ebfe16 | ||
![]() |
4de252d8e4 | ||
![]() |
c5237547cf | ||
![]() |
af85d36762 | ||
![]() |
41c94741e2 | ||
![]() |
8b07b53b43 | ||
![]() |
445dd1250f | ||
![]() |
4df8014102 | ||
![]() |
35320649db | ||
![]() |
c62ec71cfc | ||
![]() |
b685d17969 | ||
![]() |
ba5fd4b9d3 | ||
![]() |
79757da2b1 | ||
![]() |
77f5224e13 | ||
![]() |
31805e39e6 | ||
![]() |
ff6d43c398 | ||
![]() |
81a8d93336 | ||
![]() |
609549f974 | ||
![]() |
7ba6d704cd | ||
![]() |
0ff1b4d5dd | ||
![]() |
7eac521369 | ||
![]() |
1d8a16f3c4 | ||
![]() |
685f8cabdb | ||
![]() |
70141eda22 | ||
![]() |
0204478483 | ||
![]() |
79f317709b | ||
![]() |
ac3bafcd02 | ||
![]() |
ac9a8b85ec |
6
.gitignore
vendored
@@ -21,6 +21,10 @@ cache/*
|
||||
*.crt
|
||||
*.key
|
||||
*.csr
|
||||
*.pem
|
||||
|
||||
# Mergetool
|
||||
*.orgin
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
@@ -31,7 +35,7 @@ Icon?
|
||||
Thumbs.db
|
||||
|
||||
#Ignore files generated by PyCharm
|
||||
.idea/*
|
||||
*.idea/*
|
||||
|
||||
#Ignore files generated by vi
|
||||
*.swp
|
||||
|
57
API.md
@@ -4,24 +4,77 @@ The API is still pretty new and needs some serious cleaning up on the backend bu
|
||||
## General structure
|
||||
The API endpoint is `http://ip:port + HTTP_ROOT + /api?apikey=$apikey&cmd=$command`
|
||||
|
||||
Data response in JSON formatted.
|
||||
Response example
|
||||
```
|
||||
{
|
||||
"response": {
|
||||
"data": [
|
||||
{
|
||||
"loglevel": "INFO",
|
||||
"msg": "Signal 2 caught, saving and exiting...",
|
||||
"thread": "MainThread",
|
||||
"time": "22-sep-2015 01:42:56 "
|
||||
}
|
||||
],
|
||||
"message": null,
|
||||
"result": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
General parameters:
|
||||
out_type: 'xml',
|
||||
callback: 'pong',
|
||||
'debug': 1
|
||||
|
||||
|
||||
## API methods
|
||||
|
||||
### getLogs
|
||||
Not working yet
|
||||
Possible params: sort='', search='', order='desc', regex='', start=0, end=0
|
||||
Returns the plexpy log
|
||||
|
||||
### getApikey
|
||||
Possible params: username='', password='' (required if auth is enabled)
|
||||
Returns the apikey
|
||||
|
||||
### getSettings
|
||||
No params
|
||||
Returns the config file
|
||||
|
||||
### getVersion
|
||||
No params
|
||||
Returns some version information: git_path, install_type, current_version, installed_version, commits_behind
|
||||
|
||||
### getHistory
|
||||
possible params: user=None, user_id=None, ,rating_key='', parent_rating_key='', grandparent_rating_key='', start_date=''
|
||||
Returns
|
||||
|
||||
### getMetadata
|
||||
Required params: rating_key
|
||||
Returns metadata about a file
|
||||
|
||||
### getSync
|
||||
Possible params: machine_id=None, user_id=None,
|
||||
Returns
|
||||
|
||||
### getUserips
|
||||
Possible params: user_id=None, user=None
|
||||
|
||||
### getPlayby
|
||||
Possible params: time_range=30, y_axis='plays', playtype='total_plays_per_month'
|
||||
|
||||
### checkGithub
|
||||
Updates the version information above and returns getVersion data
|
||||
|
||||
### shutdown
|
||||
No params
|
||||
Shut down plexpy
|
||||
|
||||
### restart
|
||||
No params
|
||||
Restart plexpy
|
||||
|
||||
### update
|
||||
No params
|
||||
Update plexpy - you may want to check the install type in get version and not allow this if type==exe
|
||||
|
459
CHANGELOG.md
@@ -1,21 +1,456 @@
|
||||
# Changelog
|
||||
|
||||
## 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)
|
||||
|
||||
* Fix sanitize player names
|
||||
* Fix recently added notification delay
|
||||
* Fix recently added metadata queries
|
||||
* Fix multiple lines in notification body text
|
||||
* Fix UTF-8 encoding in Prowl notifications subject line
|
||||
* Change to only log IPv4 addresses
|
||||
* Add global toggle for recently added notifcations
|
||||
* Add feature to delete users
|
||||
* Add channel support for Telegram notification agent
|
||||
* Add icon for Apple tvOS
|
||||
* Add icon for Microsoft Edge
|
||||
|
||||
|
||||
## v1.2.7 (2015-11-27)
|
||||
|
||||
* Fix IP address option in notifications
|
||||
|
||||
|
||||
## v1.2.6 (2015-11-27)
|
||||
|
||||
* Fixes for IP logging in PMS < 0.9.14.x.
|
||||
* Fix issue in plexWatch importer when trying to import item with no ratingKey.
|
||||
|
||||
|
||||
## v1.2.5 (2015-11-25)
|
||||
|
||||
* Add video_decision and audio_decision to notification options
|
||||
* Fix IP address logging
|
||||
* Fix log spam if notifications disabled
|
||||
|
||||
|
||||
## v1.2.4 (2015-11-24)
|
||||
|
||||
* Add filtering by media type in the history table
|
||||
* Add IFTTT notification agent
|
||||
* Add Telegram notification agent
|
||||
* Add notifications for recently added media
|
||||
* Add notifications for server down and remote access down
|
||||
* Add more metadata to notifications options
|
||||
* Add IP address to notification options (for PMS 0.9.14 and above)
|
||||
* Add server uptime to notification options
|
||||
* Add IP address to current activity
|
||||
* Add IPv6 address logging
|
||||
* Add PMS server name to the page title
|
||||
* Fix bug in "Last Watched" statistic
|
||||
* Fix bug in search query
|
||||
* Fix bug on user pages for usernames with single quotes
|
||||
* Fix name for new Plex Media Center
|
||||
* Fix Pushover notifications with unicode characters
|
||||
* Fix bug with showing old usernames in datatables
|
||||
* Fix bug with "Please verify your server" in settings
|
||||
* Change IP lookup provider
|
||||
* Change notifications custom body text to larger text box
|
||||
* Change movie/tv logging and notifications into individual options
|
||||
|
||||
|
||||
## v1.2.3 (2015-10-18)
|
||||
|
||||
* Added "remaining time" as notification substitution.
|
||||
* Fix bug on home stats cards.
|
||||
* Fix visual bug on user page.
|
||||
|
||||
|
||||
## v1.2.2 (2015-10-12)
|
||||
|
||||
* Add server discovery on first run.
|
||||
* Add column to tables for Platform.
|
||||
* Add link to top level breadcrumbs on info pages.
|
||||
* Add ability to change notification sounds for Pushover and Boxcar.
|
||||
* Show watched percentage tooltip on progress column in history tables.
|
||||
* More logging in event an http request fails.
|
||||
* Code cleanups and other fixes.
|
||||
* Fix ordering on sync table.
|
||||
* Fix bug on home stats cards.
|
||||
* Fix bug on activity pane where music details were not shown.
|
||||
|
||||
|
||||
## v1.2.1 (2015-09-29)
|
||||
|
||||
* Fix for possible issue when paused_counter is null.
|
||||
|
||||
|
||||
## v1.2.0 (2015-09-29)
|
||||
|
||||
* Added option to group consecutive plays in the history tables.
|
||||
* Added option for websocket monitoring (still slightly experimental and disabled by default).
|
||||
* Added global search option (searches your Plex library).
|
||||
* Added option to update any items that may have had their rating keys changed.
|
||||
* Added option to disable consecutive notifications.
|
||||
* Some visual tweaks and fixes.
|
||||
* Fix bug where monitoring wouldn't start up after first run.
|
||||
* Fix bug showing incorrect transcode decisions for music tracks on history tables.
|
||||
|
||||
|
||||
## v1.1.10 (2015-09-20)
|
||||
|
||||
* Added dedicated settings section for home stats configuration with ability to show/hide selected stats and sections.
|
||||
* Added support for Twitter notifications.
|
||||
* Only show music in graphs if music logging is enabled.
|
||||
* The monitoring ignore interval now excludes paused time.
|
||||
* Fix display bug on activity panel which incorrectly reported transcoding sometimes.
|
||||
* Fix bug with Email notification TLS checkbox when it would be disabled by changing any other settings afterwards.
|
||||
* Fix issue on some Python releases where the webbrowser library isn't included.
|
||||
|
||||
|
||||
## v1.1.9 (2015-09-14)
|
||||
|
||||
* Another JonnyWong release. I'm going to stop thanking you now ;)
|
||||
* Add music plays to graphs.
|
||||
* Add info pages for music items.
|
||||
* Add music to user recently watched items.
|
||||
* Add photo views to Activity pane (photos are not logged).
|
||||
* Fix token validation message on Settings page.
|
||||
* Fix some "Mystery" platform names.
|
||||
* Fix paused time be counted for graph data.
|
||||
* Other small bug fixes.
|
||||
|
||||
|
||||
## v1.1.8 (2015-09-09)
|
||||
|
||||
* Add platform images for Windows devices. Thanks @JonnyWong.
|
||||
* Add click-through to PlexWeb preplay page from info page. Thanks @JonnyWong.
|
||||
* Fix broken delete option on info pages. Thanks @JonnyWong.
|
||||
* Fix tagline bug in PlexWatch db import tool.
|
||||
* Fix home stats text overflow bug. Thanks @JonnyWong.
|
||||
|
||||
|
||||
## v1.1.7 (2015-09-07)
|
||||
|
||||
* Show tagline in info screens for movies. Thanks @JonnyWong.
|
||||
* Add play/pause/buffer icon to activity pane. Thanks @JonnyWong.
|
||||
* Add transcoder info in activity pane info. Thanks @JonnyWong.
|
||||
* Show transcoder progress on activity progress bar. Thanks @JonnyWong.
|
||||
* Fix bug where custom notification strings would be ignored if unicode characters were present.
|
||||
* Fix text overflow issue on home stats cards. Thanks @JonnyWong.
|
||||
* Fix regression with user friendly name change input in edit screen. Thanks @JonnyWong.
|
||||
|
||||
|
||||
## v1.1.6 (2015-09-06)
|
||||
|
||||
* Home stats cards are now expandable to show multiple items. Configurable in settings. Thanks @JonnyWong.
|
||||
* Completely redesigned media info pages. Thanks @JonnyWong.
|
||||
* Redesigned activity pane to match Plex Web more closely. Thanks @JonnyWong.
|
||||
* New Library stats on home page, shows total item counts per library. Thanks @JonnyWong.
|
||||
* New last watched card in home stats. Shows last watched items. Thanks @JonnyWong.
|
||||
* Improved some layout issues on mobile devices. Thanks @JonnyWong.
|
||||
* Fixed issue where some clip/channel items are reported as episodes and causing exceptions.
|
||||
* Many styling improvements and fixes. Thanks @JonnyWong.
|
||||
* Fixed incorrect sort on home stats platform count by duration. Thanks @JonnyWong.
|
||||
* Fix issue where user refresh would continually be called as "Local" user didn't exist in database.
|
||||
* Fixed styling on graph stream modal. Thanks @JonnyWong.
|
||||
* Fixed some issues with users page editing. Thanks @JonnyWong.
|
||||
* Fix error page when clicking through to an item that no longer exists.
|
||||
|
||||
|
||||
## v1.1.5 (2015-08-27)
|
||||
|
||||
* Fix git tag being one release behind.
|
||||
|
||||
|
||||
## v1.1.4 (2015-08-26)
|
||||
|
||||
* User info is now editable from the users table. Thanks @JonnyWong.
|
||||
* Improved delete mode for history pages - able to multi-select now. Thanks @JonnyWong.
|
||||
* Improved image quality on tooltip images.
|
||||
* More styling improvements and fixes on user and info pages. Thanks @JonnyWong.
|
||||
* Added some user submitted systemd init scripts. Thanks @malle-pietje and @artbird309.
|
||||
* Fixed some background operations when saving settings.
|
||||
* Fix max width restricting home stats to 1600px.
|
||||
* Fix stream duration parameter for notifications when paused counter is null.
|
||||
|
||||
|
||||
## v1.1.3 (2015-08-22)
|
||||
|
||||
* Show human readable version info and this cool changelog in Settings -> General.
|
||||
* Add a "delete" mode to the history tables. Toggle it to show a delete button next to each history item.
|
||||
* Two digit season and episode numbers for custom notification messages. Thanks @JohnnyWong.
|
||||
* New FreeNAS init script. Thanks @JohnnyWong.
|
||||
* Lots of styling improvements! Thanks @JohnnyWong.
|
||||
* Graph page remembers last selected options. Thanks @JohnnyWong.
|
||||
* New Popular movie homepage stats. Thanks @JohnnyWong.
|
||||
* Add option for duration vs play count on home stats. (Settings -> Extra Settings). Thanks @JohnnyWong.
|
||||
* Clean up media info pages. Don't show metadata that is missing. Thanks @JohnnyWong.
|
||||
* Add clear button to search inputs. Thanks @JohnnyWong.
|
||||
* New columns on Users list. Thanks @JohnnyWong.
|
||||
* New stream duration option for custom notification messages. Thanks @JohnnyWong.
|
||||
* Rad new tooltips on the history pages. Thanks @JohnnyWong.
|
||||
* And a lot of small visual changes and fixes. Thanks @JohnnyWong.
|
||||
* Two digit season and episode numbers for custom notification messages. Thanks @JonnyWong.
|
||||
* New FreeNAS init script. Thanks @JonnyWong.
|
||||
* Lots of styling improvements! Thanks @JonnyWong.
|
||||
* Graph page remembers last selected options. Thanks @JonnyWong.
|
||||
* New Popular movie homepage stats. Thanks @JonnyWong.
|
||||
* Add option for duration vs play count on home stats. (Settings -> Extra Settings). Thanks @JonnyWong.
|
||||
* Clean up media info pages. Don't show metadata that is missing. Thanks @JonnyWong.
|
||||
* Add clear button to search inputs. Thanks @JonnyWong.
|
||||
* New columns on Users list. Thanks @JonnyWong.
|
||||
* New stream duration option for custom notification messages. Thanks @JonnyWong.
|
||||
* Rad new tooltips on the history pages. Thanks @JonnyWong.
|
||||
* And a lot of small visual changes and fixes. Thanks @JonnyWong.
|
||||
* Fixed IP address modal on user history page.
|
||||
* Fixed "invalid date" showing on monthly plays graph.
|
||||
|
||||
|
@@ -1,12 +1,45 @@
|
||||
# Contributing to PlexPy
|
||||
|
||||
## 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.
|
||||
* Visit the [Troubleshooting](../../wiki/TroubleShooting) wiki first.
|
||||
* Use [proper formatting](https://help.github.com/articles/github-flavored-markdown/). Paste your logs in code blocks.
|
||||
* Close your issue if you resolved it.
|
||||
##### 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/drzoidberg33/plexpy/wiki) for
|
||||
[ [Installation] ](https://github.com/drzoidberg33/plexpy/wiki/Installation) and
|
||||
[ [FAQs] ](https://github.com/drzoidberg33/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
||||
- For basic questions try asking on [Gitter](https://gitter.im/drzoidberg33/plexpy) 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/drzoidberg33/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/drzoidberg33/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
|
||||
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.
|
||||
|
49
PlexPy.py
@@ -1,4 +1,11 @@
|
||||
#!/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 -*-
|
||||
|
||||
# This file is part of PlexPy.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
@@ -20,7 +27,7 @@ import sys
|
||||
# Ensure lib added to path, before any other imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
||||
|
||||
from plexpy import webstart, logger
|
||||
from plexpy import webstart, logger, web_socket
|
||||
|
||||
import locale
|
||||
import time
|
||||
@@ -64,7 +71,7 @@ def main():
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Python frontend for PlexWatch.')
|
||||
description='A Python based monitoring and tracking tool for Plex Media Server.')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
||||
@@ -74,11 +81,14 @@ def main():
|
||||
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||
parser.add_argument(
|
||||
'-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(
|
||||
'--datadir', help='Specify a directory where to store your data files')
|
||||
parser.add_argument('--config', help='Specify a config file to use')
|
||||
parser.add_argument('--nolaunch', action='store_true',
|
||||
help='Prevent browser from launching on startup')
|
||||
parser.add_argument(
|
||||
'--config', help='Specify a config file to use')
|
||||
parser.add_argument(
|
||||
'--nolaunch', action='store_true', help='Prevent browser from launching on startup')
|
||||
parser.add_argument(
|
||||
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||
|
||||
@@ -93,6 +103,10 @@ def main():
|
||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
||||
verbose=plexpy.VERBOSE)
|
||||
|
||||
if args.dev:
|
||||
plexpy.DEV = True
|
||||
logger.debug(u"PlexPy is running in the dev environment.")
|
||||
|
||||
if args.daemon:
|
||||
if sys.platform == 'win32':
|
||||
sys.stderr.write(
|
||||
@@ -151,11 +165,24 @@ def main():
|
||||
# Put the database in the DATA_DIR
|
||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, 'plexpy.db')
|
||||
|
||||
if plexpy.DAEMON:
|
||||
plexpy.daemonize()
|
||||
|
||||
# Read config and start logging
|
||||
plexpy.initialize(config_file)
|
||||
|
||||
if plexpy.DAEMON:
|
||||
plexpy.daemonize()
|
||||
# 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()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if args.port:
|
||||
@@ -179,6 +206,7 @@ def main():
|
||||
'http_port': http_port,
|
||||
'http_host': plexpy.CONFIG.HTTP_HOST,
|
||||
'http_root': plexpy.CONFIG.HTTP_ROOT,
|
||||
'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
|
||||
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
|
||||
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
|
||||
'https_cert': plexpy.CONFIG.HTTPS_CERT,
|
||||
@@ -188,11 +216,8 @@ def main():
|
||||
}
|
||||
webstart.initialize(web_config)
|
||||
|
||||
# Start the background threads
|
||||
plexpy.start()
|
||||
|
||||
# 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.CONFIG.HTTP_ROOT)
|
||||
|
||||
|
172
README.md
@@ -1,123 +1,79 @@
|
||||
#PlexPy
|
||||
# PlexPy
|
||||
|
||||
[](https://gitter.im/drzoidberg33/plexpy?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).
|
||||
|
||||
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
|
||||
* PlexPy [forum thread](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
|
||||
|
||||
If you'd like to buy me a beer, hit the donate button below. All donations go to the project maintainer (primarily for the procurement of liquid refreshment).
|
||||
## Features
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G9HZK9BDJLKT6)
|
||||
* Responsive web design viewable on desktop, tablet and mobile web browsers.
|
||||
* Themed to complement Plex/Web.
|
||||
* 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!!
|
||||
|
||||
## Installation and Support
|
||||
|
||||
###Support
|
||||
-----------
|
||||
* PlexPy Wiki: https://github.com/drzoidberg33/plexpy/wiki
|
||||
|
||||
|
||||
###Features
|
||||
-----------
|
||||
* Responsive web design viewable on desktop, tablet and mobile web browsers
|
||||
|
||||
* Themed to complement Plex/Web
|
||||
|
||||
* Easy configuration setup via html form
|
||||
|
||||
* Current Plex Media Server viewing activity including:
|
||||
* number of current users
|
||||
* 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).
|
||||
* [Installation Guides](https://github.com/drzoidberg33/plexpy/wiki/Installation) shows you how to install PlexPy.
|
||||
* [FAQs](https://github.com/drzoidberg33/plexpy/wiki/Frequently-Asked-Questions-(FAQ)) in the wiki can help you with common problems.
|
||||
|
||||
**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/drzoidberg33/plexpy/wiki) for
|
||||
[ [Installation] ](https://github.com/drzoidberg33/plexpy/wiki/Installation) and
|
||||
[ [FAQs] ](https://github.com/drzoidberg33/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
||||
- For basic questions try asking on [Gitter](https://gitter.im/drzoidberg33/plexpy) 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/drzoidberg33/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/drzoidberg33/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
|
||||
|
||||
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.
|
@@ -1,13 +1,14 @@
|
||||
<%
|
||||
<%
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
from plexpy.helpers import anon_url
|
||||
%>
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>PlexPy - ${title}</title>
|
||||
<title>PlexPy - ${title} | ${server_name}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
@@ -18,12 +19,117 @@ from plexpy import version
|
||||
${next.headIncludes()}
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="interfaces/default/images/favicon.ico"/>
|
||||
<!-- touch icons -->
|
||||
<link rel="shortcut icon" href="interfaces/default/images/favicon.png">
|
||||
<link rel="apple-touch-icon" href="interfaces/default/images/icon_iphone.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="interfaces/default/images/icon_ipad.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="interfaces/default/images/icon_iphone@2x.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="interfaces/default/images/icon_ipad@2x.png">
|
||||
|
||||
<!-- Allow web app to be run in full-screen mode. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<!-- Configure the status bar. -->
|
||||
<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="interfaces/default/xml/IEconfig.xml"/>
|
||||
<!-- Android >M39 icon -->
|
||||
<link rel="manifest" href="interfaces/default/json/Android-manifest.json">
|
||||
<!-- iPad retina icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-76@2x.png" sizes="152x152" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPad retina icon (iOS < 7) -->
|
||||
<link href="interfaces/default/images/res/ios/icon-72@2x.png" sizes="144x144" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPad non-retina icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-76.png" sizes="76x76" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPad non-retina icon (iOS < 7) -->
|
||||
<link href="interfaces/default/images/res/ios/icon-72.png" sizes="72x72" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPhone 6 Plus icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-60@2x.png" sizes="120x120" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPhone retina icon (iOS < 7) -->
|
||||
<link href="interfaces/default/images/res/ios/icon@2x.png" sizes="114x114" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPhone non-retina icon (iOS < 7) -->
|
||||
<link href="interfaces/default/images/res/ios/icon.png" sizes="57x57" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPhone / iPod Touch -->
|
||||
<link href="interfaces/default/images/res/ios/icon-60@3x.png" sizes="180x180" rel="apple-touch-icon-precomposed">
|
||||
<link href="interfaces/default/images/res/ios/icon-60.png" sizes="60x60" rel="apple-touch-icon-precomposed">
|
||||
<!-- Spotlight Icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-40.png" sizes="40x40" rel="apple-touch-icon-precomposed">
|
||||
<link href="interfaces/default/images/res/ios/icon-40@2x.png" sizes="80x80" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPhone Spotlight and Settings Icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-small.png" sizes="29x29" rel="apple-touch-icon-precomposed">
|
||||
<link href="interfaces/default/images/res/ios/icon-small@2x.png" sizes="58x58" rel="apple-touch-icon-precomposed">
|
||||
<!-- iPad Spotlight and Settings Icon -->
|
||||
<link href="interfaces/default/images/res/ios/icon-50.png" sizes="50x50" rel="apple-touch-icon-precomposed">
|
||||
<link href="interfaces/default/images/res/ios/icon-50@2x.png" sizes="100x100" rel="apple-touch-icon-precomposed">
|
||||
|
||||
<!-- STARTUP IMAGES -->
|
||||
<!-- iPad retina portrait startup image -->
|
||||
<link href="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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="interfaces/default/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>
|
||||
|
||||
<body class="content">
|
||||
@@ -31,15 +137,15 @@ from plexpy import version
|
||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||
% if plexpy.CONFIG.CHECK_GITHUB and not plexpy.CURRENT_VERSION:
|
||||
<div id="updatebar" style="display: none;">
|
||||
You're running an unknown version of PlexPy. <a href="update">Update</a> or
|
||||
<a href="#" id="updateDismiss">Close</a>
|
||||
You're running an unknown version of PlexPy.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||
</div>
|
||||
% 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;">
|
||||
A <a
|
||||
href="https://github.com/${plexpy.CONFIG.GIT_USER}/plexpy/compare/${plexpy.CURRENT_VERSION}...${plexpy.LATEST_VERSION}">
|
||||
newer version</a> is available. You're ${plexpy.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or
|
||||
<a href="#" id="updateDismiss">Close</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">
|
||||
newer version</a> is available.<br />
|
||||
You're ${plexpy.COMMITS_BEHIND} commits behind.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||
</div>
|
||||
% endif
|
||||
<nav class="navbar navbar-fixed-top">
|
||||
@@ -52,42 +158,59 @@ from plexpy import version
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="home">
|
||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40px">
|
||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40">
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
% if title=="Home":
|
||||
<li class="hidden-sm hidden-xs">
|
||||
<form action="search" method="post" class="form" id="search_form">
|
||||
<div class="input-group">
|
||||
<span class="input-textbox">
|
||||
<input type="text" class="form-control" name="query" id="query" aria-label="Search" placeholder="Search..."/>
|
||||
</span>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
% if title == "Home":
|
||||
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||
% else:
|
||||
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||
% endif
|
||||
% if title=="History":
|
||||
<li class="active"><a href="history">History</a></li>
|
||||
% if title == "Libraries" or title == "Library" or title == "Info":
|
||||
<li class="active"><a href="libraries">Libraries</a></li>
|
||||
% else:
|
||||
<li><a href="history">History</a></li>
|
||||
<li><a href="libraries">Libraries</a></li>
|
||||
% endif
|
||||
% if title=="Users" or title=="User":
|
||||
% 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=="Graphs":
|
||||
% if title == "History":
|
||||
<li class="active"><a href="history">History</a></li>
|
||||
% else:
|
||||
<li><a href="history">History</a></li>
|
||||
% endif
|
||||
% if title == "Graphs":
|
||||
<li class="active"><a href="graphs">Graphs</a></li>
|
||||
% else:
|
||||
<li><a href="graphs">Graphs</a></li>
|
||||
% endif
|
||||
% if title=="Synced Items":
|
||||
% if title == "Synced Items":
|
||||
<li class="active"><a href="sync">Synced Items</a></li>
|
||||
% else:
|
||||
<li><a href="sync">Synced Items</a></li>
|
||||
% endif
|
||||
% if title=="Log":
|
||||
% if title == "Log":
|
||||
<li class="active"><a href="logs">Logs</a></li>
|
||||
% else:
|
||||
<li><a href="logs">Logs</a></li>
|
||||
% endif
|
||||
% if title=="Settings":
|
||||
% if title == "Settings":
|
||||
<li class="active"><a href="settings">Settings</a></li>
|
||||
% else:
|
||||
<li><a href="settings">Settings</a></li>
|
||||
@@ -99,7 +222,9 @@ from plexpy import version
|
||||
</div>
|
||||
|
||||
${next.headerIncludes()}
|
||||
${next.body()}
|
||||
<div class="body-container">
|
||||
${next.body()}
|
||||
</div>
|
||||
|
||||
<script src="interfaces/default/js/jquery-2.1.4.min.js"></script>
|
||||
<script src="interfaces/default/js/bootstrap3/bootstrap.min.js"></script>
|
||||
@@ -115,6 +240,43 @@ ${next.body()}
|
||||
$('#updatebar').show();
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
$('#search_form').submit(function (e) {
|
||||
if ($('#query').hasClass('active') && $('#query').val().trim() != '') {
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: 'search',
|
||||
data: { query: $('#query').val() }
|
||||
})
|
||||
} else {
|
||||
e.preventDefault();
|
||||
$('#search_button').removeClass('btn-inactive');
|
||||
$('#query').clearQueue().val('').animate({ right: '0', width: '200px' }).addClass('active').focus();
|
||||
}
|
||||
})
|
||||
$('#query').on('blur', function (e) {
|
||||
if ($(this).val().trim() == '') {
|
||||
$(this).delay(200).animate({ right: '-200px', width: '0' }, function () {
|
||||
$('#search_button').addClass('btn-inactive');
|
||||
}).removeClass('active');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
// Work around for iOS web app links opening in Safari
|
||||
$(document).ready(function () {
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
${next.javascriptIncludes()}
|
||||
</body>
|
||||
</html>
|
||||
|
401
data/interfaces/default/css/selectize.bootstrap3.css
Normal file
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* selectize.bootstrap3.css (v0.12.1) - Bootstrap 3 Theme
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 3px 12px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
color: #333333;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 1px 0 0 0;
|
||||
border-left: 1px solid rgba(0, 0, 0, 0);
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: rgba(77, 77, 77, 0);
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #333333;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: 20px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #cccccc;
|
||||
padding: 6px 12px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 5px 12px 2px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 1px 3px;
|
||||
background: #efefef;
|
||||
color: #333333;
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #428bca;
|
||||
color: #ffffff;
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #808080;
|
||||
background: #ffffff;
|
||||
border: 0 solid rgba(77, 77, 77, 0);
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #ffffff;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(255, 237, 40, 0.4);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 3px 12px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #777777;
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #f5f5f5;
|
||||
color: #262626;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #262626;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(51, 51, 51, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 17px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #333333 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #333333 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 17px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-dropdown.form-control {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 2px 0 0 0;
|
||||
z-index: 1000;
|
||||
background: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
font-size: 12px;
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown .optgroup:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
margin-left: -12px;
|
||||
margin-right: -12px;
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
padding: 5px 0;
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
.selectize-input {
|
||||
min-height: 34px;
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
}
|
||||
.has-error .selectize-input {
|
||||
border-color: #a94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.has-error .selectize-input:focus {
|
||||
border-color: #843534;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.form-control.selectize-control {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
border: none;
|
||||
background: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
@@ -19,7 +19,7 @@ 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.
|
||||
type Returns the type of session. Either 'track', 'episode' or 'movie'.
|
||||
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
|
||||
@@ -28,10 +28,16 @@ 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.
|
||||
@@ -61,160 +67,231 @@ DOCUMENTATION :: END
|
||||
% if data is not None:
|
||||
% if data['stream_count'] != '0':
|
||||
% for a in data['sessions']:
|
||||
<div class="instance" id="instance-${a['session_key']}">
|
||||
<div class="poster">
|
||||
<div class="dashboard-activity-poster-face">
|
||||
% if a['type'] == 'movie' and not a['indexes']:
|
||||
<img src="pms_image_proxy?img=${a['art']}&width=410&height=230"/>
|
||||
<div class="dashboard-instance" id="instance-${a['session_key']}">
|
||||
% if a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track':
|
||||
<a href="info?rating_key=${a['rating_key']}">
|
||||
% endif
|
||||
<div class="dashboard-activity-poster">
|
||||
% if (a['media_type'] == 'movie' 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);"></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);"></div>
|
||||
% elif a['indexes']:
|
||||
<img onload="fadeIn(this)" src="pms_image_proxy?img=${a['bif_thumb']}&width=300&height=169" style="display: none;"/>
|
||||
<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>
|
||||
% else:
|
||||
% if a['type'] == 'track':
|
||||
<div class="dashboard-activity-poster-music-bg" style="background-image: url('pms_image_proxy?img=${a['thumb']}&width=300&height=300');"></div>
|
||||
% endif
|
||||
% if a['type'] == 'clip':
|
||||
<img src="${a['thumb']}"/>
|
||||
% else:
|
||||
<img src="pms_image_proxy?img=${a['thumb']}&width=410&height=230&fallback=cover"/>
|
||||
% endif
|
||||
% endif
|
||||
<div class="dashboard-activity-poster-info-bar">
|
||||
<div class="dashboard-activity-poster-info-text">
|
||||
% if a['type'] == 'track':
|
||||
Track ${a['media_index']}
|
||||
% elif a['type'] == 'episode':
|
||||
Season ${a['parent_media_index']}, Episode ${a['media_index']}
|
||||
% 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" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300);"></div>
|
||||
% elif a['media_type'] == 'clip':
|
||||
% if a['art'][:4] == 'http':
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
|
||||
% elif a['thumb'][:4] == 'http':
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['thumb']});"></div>
|
||||
% else:
|
||||
${a['title']}
|
||||
% if a['art']:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
||||
% else:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280);"></div>
|
||||
% endif
|
||||
% endif
|
||||
% 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>
|
||||
% else:
|
||||
<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
|
||||
<div class="dashboard-activity-button-info">
|
||||
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${a['session_key']}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="stream-${a['session_key']}" class="dashboard-activity-info-details-overlay">
|
||||
<div class="dashboard-activity-info-details-content">
|
||||
<div id="platform-${a['session_key']}" title="${a['platform']}">
|
||||
<script>
|
||||
$("#platform-${a['session_key']}").html("<div class='dashboard-activity-info-platform-box' style='background-image: url(" + getPlatformImagePath('${a['platform']}') + ");'>");
|
||||
</script>
|
||||
</div>
|
||||
<div class="dashboard-activity-info-platform">
|
||||
<strong>${a['player']}</strong><br />
|
||||
% if a['state'] == 'playing':
|
||||
State <strong>Playing</strong>
|
||||
% elif a['state'] == 'paused':
|
||||
State <strong>Paused</strong>
|
||||
% elif a['state'] == 'buffering':
|
||||
State <strong>Buffering</strong>
|
||||
% endif
|
||||
</div>
|
||||
% if a['media_type'] == 'track':
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['audio_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Audio <strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'copy':
|
||||
Audio <strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'transcode':
|
||||
Audio <strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['media_type'] == 'episode' or a['media_type'] == 'movie' or a['media_type'] == 'clip':
|
||||
% if a['video_decision'] == 'direct play' and a['audio_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['video_decision'] == 'copy' and a['audio_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Video <strong>Direct Play (${a['video_codec']}) (${a['width']}x${a['height']})</strong>
|
||||
% elif a['video_decision'] == 'copy':
|
||||
Video <strong>Direct Stream (${a['transcode_video_codec']}) (${a['width']}x${a['height']})</strong>
|
||||
% elif a['video_decision'] == 'transcode':
|
||||
Video <strong>Transcode (${a['transcode_video_codec']}) (${a['transcode_width']}x${a['transcode_height']})</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Audio <strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'copy':
|
||||
Audio <strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'transcode':
|
||||
Audio <strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['media_type'] == 'photo':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['video_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>
|
||||
Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
% endif
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
% if a['media_type'] != 'photo':
|
||||
<div class="dashboard-activity-poster-info-bar">
|
||||
<div class="dashboard-activity-poster-info-ip-address">
|
||||
% if a['ip_address']:
|
||||
<span>IP: ${a['ip_address']}</span>
|
||||
% else:
|
||||
<span>IP: N/A</span>
|
||||
% 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 class="dashboard-activity-poster-info-time">
|
||||
<span class="progress_time">${a['progress']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div class='dashboard-activity-metadata-wrapper'>
|
||||
<div class='dashboard-activity-instance-overlay'>
|
||||
<div class='dashboard-activity-metadata-progress-minutes'>
|
||||
<div class='activity-progress'>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-platform" data-toggle="tooltip" data-placement="right" title data-title="${a['player']}" id="platform-${a['session_key']}">
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}">${a['friendly_name']}</a> is ${a['state']}
|
||||
% else:
|
||||
<a href="user?user=${a['user']}">${a['friendly_name']}</a> is ${a['state']}
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if a['type'] == 'episode':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||
% elif a['type'] == 'movie':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['title']}</a>
|
||||
% elif a['type'] == 'clip':
|
||||
${a['title']}
|
||||
% elif a['type'] == 'track':
|
||||
${a['grandparent_title']} - ${a['title']}
|
||||
% else:
|
||||
${a['grandparent_title']} - ${a['title']}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div id="stream-${a['session_key']}" class="collapse out">
|
||||
<div class='dashboard-activity-info-details-overlay'>
|
||||
<div class='dashboard-activity-info-details-content'>
|
||||
% if a['type'] == 'track':
|
||||
Artist: <strong>${a['grandparent_title']}</strong>
|
||||
<br>
|
||||
Album: <strong>${a['parent_title']}</strong>
|
||||
<br>
|
||||
% endif
|
||||
% if a['state'] == 'playing':
|
||||
State: <strong>Playing</strong>
|
||||
% elif a['state'] == 'paused':
|
||||
State: <strong>Paused</strong>
|
||||
% elif a['state'] == 'buffering':
|
||||
State: <strong>Buffering</strong>
|
||||
% endif
|
||||
<br>
|
||||
% if a['type'] == 'track':
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Stream: <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream: <strong>Transcoding</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['audio_decision'] != 'direct play':
|
||||
Audio: <strong>${a['transcode_audio_codec']} (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Audio: <strong>${a['audio_codec']} (${a['audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['type'] == 'episode' or a['type'] == 'movie' or a['type'] == 'clip':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream: <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream: <strong>Transcoding</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['video_decision'] != 'direct play':
|
||||
Video: <strong>${a['video_decision']} (${a['transcode_video_codec']})
|
||||
(${a['transcode_width']}x${a['transcode_height']})</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Video: <strong>${a['video_decision']} (${a['video_codec']})
|
||||
(${a['width']}x${a['height']})</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['audio_decision'] != 'direct play':
|
||||
Audio: <strong>${a['audio_decision']} ${a['transcode_audio_codec']} (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Audio: <strong>${a['audio_codec']} (${a['audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% endif
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% if a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track':
|
||||
</a>
|
||||
% endif
|
||||
<div class="dashboard-activity-progress">
|
||||
<div class="dashboard-activity-progress-bar">
|
||||
<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']}%" data-toggle="tooltip" title="Stream Progress">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-button-info">
|
||||
<button type="button" class="btn btn-activity-info btn-sm" data-toggle="collapse" data-target="#stream-${a['session_key']}">
|
||||
<i class='fa fa-info-circle'></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-wrapper">
|
||||
<a href="user?user_id=${a['user_id']}">
|
||||
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
|
||||
</a>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if a['state'] == 'playing':
|
||||
<i class="fa fa-play"></i>
|
||||
% elif a['state'] == 'paused':
|
||||
<i class="fa fa-pause"></i>
|
||||
% elif a['state'] == 'buffering':
|
||||
<i class="fa fa-spinner"></i>
|
||||
% endif
|
||||
% if a['media_type'] == 'episode':
|
||||
<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':
|
||||
<a href="info?rating_key=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
|
||||
% elif a['media_type'] == 'clip':
|
||||
<span title="${a['title']}">${a['title']}</span>
|
||||
% elif a['media_type'] == 'track':
|
||||
<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':
|
||||
<span title="${a['parent_title']}">${a['parent_title']}</span>
|
||||
% else:
|
||||
<span title="${a['title']}">${a['title']}</span>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-subtitle">
|
||||
% 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>
|
||||
% elif a['media_type'] == 'movie':
|
||||
<span title="${a['year']}">${a['year']}</span>
|
||||
% elif a['media_type'] == 'track':
|
||||
<a href="info?rating_key=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
|
||||
% elif a['media_type'] == 'photo':
|
||||
<span title="${a['title']}">${a['title']}</span>
|
||||
% else:
|
||||
<span title="${a['year']}">${a['year']}</span>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$("#platform-${a['session_key']}").html("<img src='" + getPlatformImagePath('${a['platform']}') + "'>");
|
||||
</script>
|
||||
|
||||
% endfor
|
||||
<script>
|
||||
// When using bif indexes make the image transition a little smoother.
|
||||
function fadeIn(obj) {
|
||||
$(obj).fadeIn(450);
|
||||
}
|
||||
$('.bif').each(function() {
|
||||
$(this).hide().fadeIn(1000);
|
||||
});
|
||||
|
||||
// Convert timestamps to readable times
|
||||
$('.progress_time').each(function(index) {
|
||||
$(this).html(millisecondsToMinutes($(this).text(), false));
|
||||
});
|
||||
|
||||
// Hide the info bar on page load
|
||||
$('.dashboard-activity-poster-info-bar').hide();
|
||||
|
||||
// When mouse over the activity pane, show an info bar with extra info.
|
||||
$('.dashboard-activity-poster-face').mouseenter(function() {
|
||||
$('.dashboard-activity-poster-info-bar', this).slideDown('fast');
|
||||
});
|
||||
$('.dashboard-activity-poster-face').mouseleave(function() {
|
||||
$('.dashboard-activity-poster-info-bar', this).slideUp('fast');
|
||||
// Show/Hide activity info
|
||||
$('.btn-activity-info').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$($(this).attr('data-target')).toggle();
|
||||
});
|
||||
|
||||
// Tooltips
|
||||
$('.dashboard-activity-metadata-platform').each(function() {
|
||||
$(this).tooltip();
|
||||
// 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>
|
||||
% else:
|
||||
<div class="text-muted">Nothing is currently being watched.</div><br>
|
||||
@@ -223,4 +300,4 @@ DOCUMENTATION :: END
|
||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a
|
||||
href="settings">settings</a>.
|
||||
</div><br>
|
||||
% endif
|
||||
% endif
|
||||
|
@@ -15,11 +15,13 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
% if data == '0':
|
||||
<h3>Activity</h3>
|
||||
% if data == '0':
|
||||
<h3>Activity</h3>
|
||||
% elif data == '1':
|
||||
<h3>Activity <small>${data} stream</small></h3>
|
||||
% else:
|
||||
<h3>Activity <small>${data} streams</small></h3>
|
||||
% endif
|
||||
% else:
|
||||
<h3>Activity <small>${data} stream(s)</small></h3>
|
||||
<h3>Activity</h3>
|
||||
% endif
|
||||
% else:
|
||||
<h3>Activity</h3>
|
||||
% 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,30 @@ Variable names: data [list]
|
||||
data :: Usable parameters
|
||||
|
||||
== Global keys ==
|
||||
user Return the real Plex username
|
||||
user_id Return the Plex user_id
|
||||
friendly_name Returns the friendly edited Plex username
|
||||
do_notify Returns bool value for whether the user should trigger notifications
|
||||
keep_history Returns bool value for whether the user's activity should be logged
|
||||
user_id Returns the user id of the user.
|
||||
username Returns the user's username.
|
||||
friendly_name Returns the friendly name of the user.
|
||||
email Returns the user's email address.
|
||||
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.
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data is not None:
|
||||
<%!
|
||||
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 user <strong>${data['user']}</strong></h4>
|
||||
<h4 class="modal-title">Edit user <strong>${data['username']}</strong></h4>
|
||||
</div>
|
||||
<div class="modal-body" id="modal-text">
|
||||
<fieldset>
|
||||
@@ -41,22 +50,22 @@ DOCUMENTATION :: END
|
||||
<label for="profile_url">Profile Picture URL</label>
|
||||
<div class="row">
|
||||
<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>
|
||||
<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 class="checkbox">
|
||||
<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>
|
||||
<p class="help-block">Uncheck this if you do not want to receive notifications for this user's activity.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<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>
|
||||
<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>
|
||||
% if data['user_id']:
|
||||
<div class="form-group">
|
||||
@@ -69,16 +78,34 @@ DOCUMENTATION :: END
|
||||
<div class="modal-footer">
|
||||
<div>
|
||||
<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 id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
||||
<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 user?</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>
|
||||
// Set new friendly name
|
||||
$("#save_user_name").click(function() {
|
||||
// Set user options
|
||||
$("#save_user").on('click', function () {
|
||||
var friendly_name = $("#friendly_name").val();
|
||||
var thumb = $("#profile_url").val();
|
||||
var custom_thumb = $("#custom_avatar_url").val();
|
||||
var do_notify = 0;
|
||||
var keep_history = 0;
|
||||
if ($("#do_notify").is(":checked")) {
|
||||
@@ -88,51 +115,64 @@ DOCUMENTATION :: END
|
||||
keep_history = 1;
|
||||
}
|
||||
|
||||
% if data['user_id']:
|
||||
$.ajax({
|
||||
url: 'edit_user',
|
||||
data: {user_id: '${data['user_id']}', 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);
|
||||
}
|
||||
});
|
||||
% 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
|
||||
$.ajax({
|
||||
url: 'edit_user',
|
||||
data: {
|
||||
user_id: '${data["user_id"]}',
|
||||
friendly_name: friendly_name,
|
||||
custom_thumb: custom_thumb,
|
||||
do_notify: do_notify,
|
||||
keep_history: keep_history
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#delete-all-history").click(function() {
|
||||
var r = confirm("Are you REALLY REALLY REALLY sure you want to delete all history for this user?");
|
||||
if (r == true) {
|
||||
$("#delete-all-history").on('click', function() {
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-purge', function () {
|
||||
$.ajax({
|
||||
url: 'delete_all_user_history',
|
||||
data: {user_id: '${data['user_id']}'},
|
||||
data: { user_id: '${data["user_id"]}' },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
// Move #confirm-modal to parent container
|
||||
if(!($('#edit-user-modal').next().is('#confirm-modal'))) {
|
||||
$('#confirm-modal').appendTo($('#edit-user-modal').parent());
|
||||
}
|
||||
$('#edit-user-modal > #confirm-modal').remove();
|
||||
|
||||
$('#edit-user-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').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-user-modal').css('overflow-y', 'hidden');
|
||||
});
|
||||
$('#confirm-modal').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').on('hidden.bs.modal', function() {
|
||||
$('body').addClass('modal-open');
|
||||
$('#edit-user-modal').css('overflow-y', 'auto');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
% endif
|
@@ -13,115 +13,137 @@
|
||||
</div>
|
||||
<div class="button-bar hidden-xs">
|
||||
<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">
|
||||
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off" checked> Play Count
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration
|
||||
</label>
|
||||
% endif
|
||||
</div>
|
||||
<div class="btn-group" data-toggle="buttons" id="days-selection">
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="date-options" id="graph-7" value="7" autocomplete="off"> 7 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
|
||||
<div class="btn-group" id="days-selection">
|
||||
<label>
|
||||
<input type="number" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" /> days
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<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"><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>
|
||||
% endif
|
||||
</ul>
|
||||
<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 class="row">
|
||||
<div class="col-md-12">
|
||||
<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>
|
||||
<p class="help-block">
|
||||
The total play count or duration of movies and tv played per day. Click a graph point to open up a list of items played for that specific date.
|
||||
</p>
|
||||
<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>
|
||||
% else:
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-1">
|
||||
% endif
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<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>
|
||||
<p class="help-block">
|
||||
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.
|
||||
</p>
|
||||
<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 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 movies and tv 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 movies and tv 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 movies and tv 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 movies and tv 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">
|
||||
% endif
|
||||
<div class="row">
|
||||
<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>
|
||||
<p class="help-block">
|
||||
The total play count or duration of movies and tv by the transcode decision. Click a graph point to open up a list of items played for that specific date.
|
||||
The total play count or duration of tv, movies, and music by the transcode decision. Click a graph point to open up a list of items played for that specific date.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_stream_type">
|
||||
@@ -135,7 +157,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-expand"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by their original resolution (pre-transcoding).
|
||||
The combined total of tv and movies by their original resolution (pre-transcoding).
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_source_resolution" style="float: left;">
|
||||
@@ -148,7 +170,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-expand"></i> <span class="yaxis-text">Play count</span> by stream resolution <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by their streamed resolution (post-transcoding).
|
||||
The combined total of tv and movies by their streamed resolution (post-transcoding).
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_stream_resolution">
|
||||
@@ -163,7 +185,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by platform and stream type <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by platform and stream type.
|
||||
The combined total of tv, movies, and music by platform and stream type.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_platform_by_stream_type" style="float: left;">
|
||||
@@ -176,7 +198,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by user and stream type <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by user and stream type.
|
||||
The combined total of tv, movies, and music by user and stream type.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_user_by_stream_type" style="float: left;">
|
||||
@@ -189,12 +211,16 @@
|
||||
</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">
|
||||
% endif
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4><i class="fa fa-calendar"></i> Plays by Month <small>Last 12 months</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by month.
|
||||
The combined total of tv, movies, and music by month.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_month">
|
||||
@@ -223,7 +249,7 @@
|
||||
|
||||
<script>
|
||||
// Modal popup dialog
|
||||
function selectHandler(selectedDate) {
|
||||
function selectHandler(selectedDate, selectedSeries) {
|
||||
|
||||
try
|
||||
{
|
||||
@@ -233,10 +259,25 @@
|
||||
var y = dateValue.getFullYear();
|
||||
var dateString = '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
|
||||
|
||||
var media_type = 'all';
|
||||
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({
|
||||
"url": "history_table_modal",
|
||||
url: "history_table_modal",
|
||||
type: 'post',
|
||||
data: { 'start_date': dateString },
|
||||
data: {
|
||||
start_date: dateString,
|
||||
media_type: media_type,
|
||||
transcode_decision: transcode_decision
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$('#history-modal').modal('show');
|
||||
$("#history-modal").html(xhr.responseText);
|
||||
@@ -245,7 +286,7 @@
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
console.log("Failed to retrieve data");
|
||||
console.log("Failed to retrieve history modal data.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -263,38 +304,18 @@
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
// Save graph state to cookies
|
||||
$('input[name=yaxis-options]').change(function() {
|
||||
setCookie('graphType', $(this).val(), 365, '/');
|
||||
});
|
||||
$('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 from config
|
||||
var yaxis = "${config['graph_type']}";
|
||||
var current_range = ${config['graph_days']};
|
||||
var current_tab = "${'#' + config['graph_tab']}";
|
||||
|
||||
// Initial values for graph if no saved state
|
||||
var yaxis = 'plays';
|
||||
var current_range = 30;
|
||||
var current_tab = '#tabs-1';
|
||||
|
||||
// Read saved graph state from cookies and set initial values
|
||||
if(getCookie('graphType')) {
|
||||
var yaxis = getCookie('graphType');
|
||||
$('input[name=yaxis-options][value=' + yaxis + ']').prop('checked', true).trigger('click');
|
||||
}
|
||||
if(getCookie('graphDate')) {
|
||||
var current_range = getCookie('graphDate');
|
||||
$('input[name=date-options][value=' + current_range + ']').prop('checked', true).trigger('click');
|
||||
$('.days').html(current_range);
|
||||
}
|
||||
if(getCookie('graphTab')) {
|
||||
var current_tab = getCookie('graphTab');
|
||||
$('a[data-toggle=tab][href=' + current_tab + ']').trigger('click');
|
||||
}
|
||||
$('.days').html(current_range);
|
||||
|
||||
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
||||
|
||||
function loadGraphsTab1(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
@@ -319,6 +340,7 @@
|
||||
hc_plays_by_day_options.yAxis.min = 0;
|
||||
hc_plays_by_day_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_day_options.series = data.series;
|
||||
hc_plays_by_day_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_day = new Highcharts.Chart(hc_plays_by_day_options);
|
||||
}
|
||||
});
|
||||
@@ -331,6 +353,7 @@
|
||||
success: function(data) {
|
||||
hc_plays_by_dayofweek_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_dayofweek_options.series = data.series;
|
||||
hc_plays_by_dayofweek_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_dayofweek = new Highcharts.Chart(hc_plays_by_dayofweek_options);
|
||||
}
|
||||
});
|
||||
@@ -343,6 +366,7 @@
|
||||
success: function(data) {
|
||||
hc_plays_by_hourofday_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_hourofday_options.series = data.series;
|
||||
hc_plays_by_hourofday_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_hourofday = new Highcharts.Chart(hc_plays_by_hourofday_options);
|
||||
}
|
||||
});
|
||||
@@ -355,6 +379,7 @@
|
||||
success: function(data) {
|
||||
hc_plays_by_platform_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_platform_options.series = data.series;
|
||||
hc_plays_by_platform_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_platform = new Highcharts.Chart(hc_plays_by_platform_options);
|
||||
}
|
||||
});
|
||||
@@ -367,12 +392,15 @@
|
||||
success: function(data) {
|
||||
hc_plays_by_user_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_user_options.series = data.series;
|
||||
hc_plays_by_user_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_user = new Highcharts.Chart(hc_plays_by_user_options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadGraphsTab2(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
@@ -451,6 +479,8 @@
|
||||
}
|
||||
|
||||
function loadGraphsTab3(yaxis) {
|
||||
$('#days-selection').hide();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
@@ -462,46 +492,68 @@
|
||||
hc_plays_by_month_options.yAxis.min = 0;
|
||||
hc_plays_by_month_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_month_options.series = data.series;
|
||||
hc_plays_by_month_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_month = new Highcharts.Chart(hc_plays_by_month_options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
$('#graph-tabs a[href="#tabs-1"]').on('shown.bs.tab', function (e) {
|
||||
e.preventDefault();
|
||||
current_tab = $(this).attr('href');
|
||||
$('#days-selection').show();
|
||||
loadGraphsTab1(current_range, yaxis);
|
||||
$.ajax({
|
||||
url: 'set_graph_config',
|
||||
data: { graph_tab: current_tab.replace('#','') },
|
||||
async: true
|
||||
});
|
||||
})
|
||||
|
||||
// Tab2 opened
|
||||
$('#graph-tabs a[href="#tabs-2"]').on('shown.bs.tab', function (e) {
|
||||
e.preventDefault();
|
||||
current_tab = $(this).attr('href');
|
||||
$('#days-selection').show();
|
||||
loadGraphsTab2(current_range, yaxis);
|
||||
$.ajax({
|
||||
url: 'set_graph_config',
|
||||
data: { graph_tab: current_tab.replace('#','') },
|
||||
async: true
|
||||
});
|
||||
})
|
||||
|
||||
// Tab3 opened
|
||||
$('#graph-tabs a[href="#tabs-3"]').on('shown.bs.tab', function (e) {
|
||||
e.preventDefault();
|
||||
current_tab = $(this).attr('href');
|
||||
$('#days-selection').hide();
|
||||
console.log('loading....');
|
||||
loadGraphsTab3(yaxis);
|
||||
$.ajax({
|
||||
url: 'set_graph_config',
|
||||
data: { graph_tab: current_tab.replace('#','') },
|
||||
async: true
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
// Date range changed
|
||||
$('#days-selection').on('change', function() {
|
||||
current_range = $('input[name=date-options]:checked', '#days-selection').val();
|
||||
$('#graph-days').on('change', function() {
|
||||
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-2') { loadGraphsTab2(current_range, yaxis); }
|
||||
$('.days').html(current_range);
|
||||
$.ajax({
|
||||
url: 'set_graph_config',
|
||||
data: { graph_days: current_range},
|
||||
async: true
|
||||
});
|
||||
});
|
||||
|
||||
// Y-axis changed
|
||||
@@ -510,6 +562,11 @@
|
||||
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); }
|
||||
$.ajax({
|
||||
url: 'set_graph_config',
|
||||
data: { graph_type: yaxis},
|
||||
async: true
|
||||
});
|
||||
});
|
||||
|
||||
function setGraphFormat(type) {
|
||||
|
@@ -14,25 +14,29 @@
|
||||
<span><i class="fa fa-history"></i> History</span>
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-danger" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode"><i class="fa fa-trash-o"></i> Delete mode</button> 
|
||||
<div class="colvis-button-bar hidden-xs"></div>
|
||||
<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>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="delete_row">Delete</th>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete"></th>
|
||||
<th align="left" id="delete_row">Delete</th>
|
||||
<th align="left" id="time">Time</th>
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="device">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
<th align="left" id="paused_counter">Paused</th>
|
||||
<th align="left" id="stopped">Stopped</th>
|
||||
<th align="left" id="duration">Duration</th>
|
||||
<th align="left" id="percent_complete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -42,6 +46,24 @@
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
||||
<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 Delete</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</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-delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
@@ -54,32 +76,87 @@
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
history_table_options.ajax = {
|
||||
"url": "get_history",
|
||||
type: "post",
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ) };
|
||||
$(document).ready(function () {
|
||||
function loadHistoryTable(media_type) {
|
||||
history_table_options.ajax = {
|
||||
url: 'get_history',
|
||||
type: 'post',
|
||||
data: function (d) {
|
||||
return {
|
||||
json_data: JSON.stringify(d),
|
||||
media_type: media_type
|
||||
};
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
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 > label').removeClass('active');
|
||||
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
|
||||
$(selected_filter).closest('label').addClass('active');
|
||||
media_type = $(selected_filter).val();
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
var media_type = 'all';
|
||||
loadHistoryTable(media_type);
|
||||
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
|
||||
$('#row-edit-mode').click(function() {
|
||||
if ($(this).hasClass('active')) {
|
||||
$('.delete-control').each(function() {
|
||||
if (history_to_delete.length > 0) {
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-delete', function () {
|
||||
for (var i = 0; i < history_to_delete.length; i++) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
data: { row_id: history_to_delete[i] },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
$('.delete-control').each(function () {
|
||||
$(this).addClass('hidden');
|
||||
$('#row-edit-mode-alert').fadeOut(200);
|
||||
});
|
||||
|
||||
} else {
|
||||
history_to_delete = [];
|
||||
$('.delete-control').each(function() {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -5,19 +5,19 @@
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||
<h4 class="modal-title" id="myModalLabel">
|
||||
<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>
|
||||
</h4>
|
||||
</div>
|
||||
<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>
|
||||
<tr>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
<th align="left" id="stopped">Stopped</th>
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -27,24 +27,55 @@
|
||||
<div class="modal-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<script src="interfaces/default/js/tables/history_table_modal.js"></script>
|
||||
<script>
|
||||
$(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 = {
|
||||
"url": "get_history",
|
||||
type: "post",
|
||||
url: 'get_history',
|
||||
type: 'post',
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ),
|
||||
'start_date': '${data}'
|
||||
};
|
||||
return {
|
||||
json_data: JSON.stringify(d),
|
||||
grouping: false,
|
||||
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
|
||||
if (!($('#history-modal').next().is('#info-modal'))) {
|
||||
$('#info-modal').appendTo($('#history-modal').parent());
|
||||
}
|
||||
$('#history-modal > #info-modal').remove();
|
||||
|
||||
$('#history-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');
|
||||
|
||||
$('#info-modal').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);
|
||||
$('#history-modal').css('overflow-y', 'hidden');
|
||||
setTimeout(function () {
|
||||
$('#info-modal').css('z-index', '1060');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||
}, 0);
|
||||
});
|
||||
$('#info-modal').on('hidden.bs.modal', function () {
|
||||
$('body').addClass('modal-open');
|
||||
$('#history-modal').css('overflow-y', 'auto');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
|
@@ -17,26 +17,37 @@ data[array_index]['rows'] :: Usable parameters
|
||||
|
||||
row_id Return the db row id for a metadata item if one exists
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'top_music' or 'popular_music' or 'last_watched' ==
|
||||
thumb Return the thumb for the media item.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_music' or 'popular_music' ==
|
||||
grandparent_thumb Returns location of the item's thumbnail. Use with pms_image_proxy.
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
title Returns the title for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_user' or 'top_platform' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_music' or 'top_user' or 'top_platform' ==
|
||||
total_plays Returns the count for the associated stat.
|
||||
total_duration Returns the total duration for the associated stat.
|
||||
|
||||
== Only of 'stat_id' is 'popular_tv' or 'popular_movies' ==
|
||||
== Only of 'stat_id' is 'popular_tv' or 'popular_movies' or 'popular_music' ==
|
||||
users_watched Returns the count for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_user' ==
|
||||
thumb Returns url of the user's gravatar. Returns '' if none exists.
|
||||
== Only if 'stat_id' is 'top_user' or 'last_watched' ==
|
||||
user_thumb Returns url of the user's gravatar. Returns '' if none exists.
|
||||
user Returns the username for the associated stat.
|
||||
user_id Returns the user id for the associated stat.
|
||||
friendly_name Returns the friendly name of the user for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_platform' ==
|
||||
platform_type Returns the platform name for the associated stat.
|
||||
== Only if 'stat_id' is 'top_platform' or 'last_watched' ==
|
||||
player Returns the player name for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is '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
|
||||
</%doc>
|
||||
@@ -45,216 +56,819 @@ DOCUMENTATION :: END
|
||||
from plexpy import helpers
|
||||
|
||||
# Human readable duration
|
||||
def hd(minutes):
|
||||
if int(minutes) > 60:
|
||||
hours = int(helpers.cast_to_float(minutes) / 60)
|
||||
minutes = int(helpers.cast_to_float(minutes) % hours)
|
||||
def hd(seconds):
|
||||
minutes = helpers.cast_to_float(seconds) / 60
|
||||
if minutes > 60:
|
||||
hours = int(minutes / 60)
|
||||
minutes = int(minutes % 60)
|
||||
if minutes > 0:
|
||||
return "<h3>" + str(hours) + "</h3><p>hrs</p><h3>" + str(minutes) + "</h3><p>mins</p>"
|
||||
else:
|
||||
return "<h3>" + str(hours) + "</h3><p>hrs</p>"
|
||||
else:
|
||||
return "<h3>" + minutes + "</h3><p>mins</p>"
|
||||
return "<h3>" + str(int(minutes)) + "</h3><p>mins</p>"
|
||||
%>
|
||||
|
||||
% if data:
|
||||
% if data[0]['rows'] or data[2]['rows']:
|
||||
<ul class="list-unstyled">
|
||||
% for a in data:
|
||||
% if a['stat_id'] == 'top_tv' and a['rows']:
|
||||
% if any(top_stat['rows'] for top_stat in data):
|
||||
% for top_stat in data:
|
||||
% if top_stat['stat_id'] == 'top_tv' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched TV</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
% if a['stat_type'] == 'total_plays':
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${a['rows'][0]['total_duration'] | hd}
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['grandparent_thumb']:
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['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>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'popular_tv' and a['rows']:
|
||||
% elif top_stat['stat_id'] == 'popular_tv' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular TV</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['users_watched']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['grandparent_thumb'] != '':
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['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>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_movies' and a['rows']:
|
||||
% elif top_stat['stat_id'] == 'top_movies' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched Movie</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
% if a['stat_type'] == 'total_plays':
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${a['rows'][0]['total_duration'] | hd}
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['thumb']:
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['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>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'popular_movies' and a['rows']:
|
||||
% elif top_stat['stat_id'] == 'popular_movies' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular Movie</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['users_watched']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['thumb']:
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['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>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_users' and a['rows']:
|
||||
% elif top_stat['stat_id'] == 'top_music' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Listened to Artist</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||
<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=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'popular_music' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular Artist</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||
<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=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_users' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active User</h4>
|
||||
<h5>
|
||||
% if a['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${a['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${a['rows'][0]['user']}">
|
||||
% endif
|
||||
${a['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
% if a['stat_type'] == 'total_plays':
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
% 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']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||
% endif
|
||||
${top_stat['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${a['rows'][0]['total_duration'] | hd}
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if a['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${a['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']}">
|
||||
% else:
|
||||
<a href="user?user=${a['rows'][0]['user']}">
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||
% endif
|
||||
% if a['rows'][0]['thumb'] != '':
|
||||
% if top_stat['rows'][0]['user_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(${a['rows'][0]['thumb']});">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(${top_stat['rows'][0]['user_thumb']});"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
% 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']}">
|
||||
% 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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% 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']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}" title="${top_stat['rows'][loop.index]['friendly_name']}">
|
||||
% endif
|
||||
% if top_stat['rows'][loop.index]['user_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-list-oval" style="background-image: url(${top_stat['rows'][loop.index]['user_thumb']});"></div>
|
||||
</div>
|
||||
% else:
|
||||
<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>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_platforms' and a['rows']:
|
||||
% elif top_stat['stat_id'] == 'top_platforms' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active Platform</h4>
|
||||
<h5>${a['rows'][0]['platform_type']}</h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
% if a['stat_type'] == 'total_plays':
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4 title="${top_stat['rows'][0]['platform_type']}">${top_stat['rows'][0]['platform_type']}</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${a['rows'][0]['total_duration'] | hd}
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div id="platform-stat" class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-box" style="background-image: url(interfaces/default/images/platforms/default.png);">
|
||||
<div id="platform-stat" class="home-platforms-instance-poster" title="${top_stat['rows'][0]['platform_type']}">
|
||||
<script>
|
||||
$("#platform-stat").html("<div class='home-platforms-instance-box' style='background-image: url(" + getPlatformImagePath('${top_stat['rows'][0]['platform_type']}') + ");'>");
|
||||
</script>
|
||||
</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 title="${top_stat['rows'][loop.index]['platform_type']}">
|
||||
${top_stat['rows'][loop.index]['platform_type']}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-platforms-instance-poster" id="home-platforms-instance-poster-${loop.index + 1}" title="${top_stat['rows'][loop.index]['platform_type']}">
|
||||
<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']}') + ");'>");
|
||||
</script>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'last_watched' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Last Watched</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-last-user">
|
||||
<h4>
|
||||
<a href="info?source=history&rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h5>
|
||||
% 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']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||
% endif
|
||||
${top_stat['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
<p>
|
||||
<span id="last-watch-stat">
|
||||
<script>
|
||||
$('#last-watch-stat').text(moment(${top_stat['rows'][0]['last_watch']},"X").format(date_format));
|
||||
</script>
|
||||
</span> - ${top_stat['rows'][0]['player']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
% 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>
|
||||
<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']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-last-user">
|
||||
<h5>
|
||||
% 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']}">
|
||||
% 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']}
|
||||
</a>
|
||||
</h5>
|
||||
<p>
|
||||
<span id="home-platforms-instance-list-last-watch-${loop.index + 1}">
|
||||
<script>
|
||||
$('#home-platforms-instance-list-last-watch-${loop.index + 1}').text(moment(${top_stat['rows'][loop.index]['last_watch']},"X").format(date_format));
|
||||
</script>
|
||||
</span> - ${top_stat['rows'][loop.index]['player']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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']:
|
||||
<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>
|
||||
% else:
|
||||
<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>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</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(interfaces/default/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(interfaces/default/images/home-stat_most-concurrent.png);"></div>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
<script>
|
||||
$("#platform-stat").html("<div class='home-platforms-instance-box' style='background-image: url(" + getPlatformImagePath('${a['rows'][0]['platform_type']}') + ");'>");
|
||||
</script>
|
||||
% endif
|
||||
% endfor
|
||||
% else:
|
||||
<div class="text-muted">No stats to show for the selected period.</div><br>
|
||||
% endif
|
||||
</ul>
|
||||
<script>
|
||||
var topZIndex = 2;
|
||||
$('.home-platforms-instance-list-chevron').on('click', function() {
|
||||
var instanceBoxChevron = $(this);
|
||||
var instanceBox = $(this).parents('.home-platforms-instance');
|
||||
var instanceBoxSlider = instanceBox.find('.slider');
|
||||
|
||||
topZIndex++;
|
||||
instanceBoxChevron.toggleClass('active');
|
||||
instanceBoxSlider.css('z-index', topZIndex);
|
||||
instanceBoxSlider.stop().slideToggle(500);
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
<div class="text-muted">No stats for 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>
|
||||
<div class="text-muted">No stats to show for the selected period.</div><br>
|
||||
% endif
|
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 |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |