Compare commits
1660 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f587ed698 | ||
![]() |
1032fdfe7a | ||
![]() |
818e7723ff | ||
![]() |
a69008e179 | ||
![]() |
91c647f9ae | ||
![]() |
36b80aa6d3 | ||
![]() |
c35fcc727c | ||
![]() |
749e1fcebe | ||
![]() |
084732706d | ||
![]() |
2aff7713cd | ||
![]() |
683a782723 | ||
![]() |
5108e1bb09 | ||
![]() |
d8298a12eb | ||
![]() |
042b48c1fd | ||
![]() |
8fac54aa71 | ||
![]() |
244008d539 | ||
![]() |
502b807e45 | ||
![]() |
35914b9a48 | ||
![]() |
24ac34d5e2 | ||
![]() |
a5807f21b4 | ||
![]() |
e3b71a729e | ||
![]() |
ebb287e1ee | ||
![]() |
bd3497b2bf | ||
![]() |
034f3ee308 | ||
![]() |
a946879fc1 | ||
![]() |
9f964b5a87 | ||
![]() |
ed0b41cd19 | ||
![]() |
dc87591992 | ||
![]() |
d05e80e573 | ||
![]() |
522684b2ab | ||
![]() |
feab16b351 | ||
![]() |
ee041db63d | ||
![]() |
2479533d07 | ||
![]() |
d045fd5834 | ||
![]() |
8407f27fed | ||
![]() |
b505286caf | ||
![]() |
feb762ce8b | ||
![]() |
8acdb5af83 | ||
![]() |
5af1294f71 | ||
![]() |
87d2d273d3 | ||
![]() |
b5c52ac71e | ||
![]() |
efe9a15f72 | ||
![]() |
525f1e4b0b | ||
![]() |
d18820b832 | ||
![]() |
7e024fd736 | ||
![]() |
c9c5989474 | ||
![]() |
ce9f96d3be | ||
![]() |
7362dd0bf4 | ||
![]() |
9905ebc144 | ||
![]() |
8f8010884b | ||
![]() |
37afd141be | ||
![]() |
a3643b4302 | ||
![]() |
02cfd8d9b7 | ||
![]() |
941ce439b4 | ||
![]() |
a08bce2073 | ||
![]() |
4e9c8322c3 | ||
![]() |
89bfe85be3 | ||
![]() |
98d994591c | ||
![]() |
a29bc7f4f9 | ||
![]() |
288f4c5f7f | ||
![]() |
a6bf78ed56 | ||
![]() |
8dbb05931e | ||
![]() |
ac8a712ff0 | ||
![]() |
39406c25c3 | ||
![]() |
48d7c2c54c | ||
![]() |
0217188274 | ||
![]() |
fd762e71de | ||
![]() |
4d5c3b6df0 | ||
![]() |
7df54e4d1b | ||
![]() |
5d085de9d3 | ||
![]() |
a8a4299086 | ||
![]() |
86f0e8425c | ||
![]() |
d2e879be4a | ||
![]() |
544114fffe | ||
![]() |
3b3e207b11 | ||
![]() |
84aad638ac | ||
![]() |
2bb691966e | ||
![]() |
8f5e788270 | ||
![]() |
7c43ea2f46 | ||
![]() |
8146e1e3cf | ||
![]() |
51b1ff6d4a | ||
![]() |
403e8dfbea | ||
![]() |
9d08717c83 | ||
![]() |
66167d5960 | ||
![]() |
624863d826 | ||
![]() |
d4b3810fbc | ||
![]() |
6056e1d3b9 | ||
![]() |
1a293d525f | ||
![]() |
b87eb68bdd | ||
![]() |
8620546d07 | ||
![]() |
a082109045 | ||
![]() |
559a9b393e | ||
![]() |
ae41b22e59 | ||
![]() |
754fd24421 | ||
![]() |
ab34a74210 | ||
![]() |
cfa6de4d91 | ||
![]() |
a5608c7a1e | ||
![]() |
88a7b52e51 | ||
![]() |
e444bad4de | ||
![]() |
5403b0b547 | ||
![]() |
51b5e615f5 | ||
![]() |
700547b63b | ||
![]() |
3f3d1962c7 | ||
![]() |
655a359ef4 | ||
![]() |
90647628c9 | ||
![]() |
681c3ed6e3 | ||
![]() |
7f255943c6 | ||
![]() |
b6e73b5dea | ||
![]() |
eacb7f6ae5 | ||
![]() |
7b300bb87e | ||
![]() |
a81ad27d85 | ||
![]() |
8eed14ff3b | ||
![]() |
82446acdf0 | ||
![]() |
88770b8805 | ||
![]() |
f9f05bbea3 | ||
![]() |
17dd767c22 | ||
![]() |
25b1dc6dd8 | ||
![]() |
b2b1277e37 | ||
![]() |
8e1a588ced | ||
![]() |
9eddfafeae | ||
![]() |
d24a922ccb | ||
![]() |
bbc6482c99 | ||
![]() |
36ff1fb674 | ||
![]() |
f0aa793262 | ||
![]() |
681627a656 | ||
![]() |
87c6ad66fb | ||
![]() |
4ab9eb3bfa | ||
![]() |
2d56ac027b | ||
![]() |
836c4293d6 | ||
![]() |
07092e8aa5 | ||
![]() |
66743c1401 | ||
![]() |
bfe34e060b | ||
![]() |
5ed4236a22 | ||
![]() |
868aeb3902 | ||
![]() |
cbcdac5b04 | ||
![]() |
d473bb3058 | ||
![]() |
066a95d209 | ||
![]() |
c7cc476623 | ||
![]() |
bd44eb7fe4 | ||
![]() |
6ec4f51077 | ||
![]() |
b4a4f60b04 | ||
![]() |
dc4e6edc9a | ||
![]() |
60b362b19e | ||
![]() |
7e81ce8c06 | ||
![]() |
c7f9e2f721 | ||
![]() |
cab8b1c041 | ||
![]() |
16f270691d | ||
![]() |
d94a1efe75 | ||
![]() |
12755970b7 | ||
![]() |
93e4853ea2 | ||
![]() |
5e0c0365fb | ||
![]() |
c2713c53dd | ||
![]() |
90443b4028 | ||
![]() |
e0109ed179 | ||
![]() |
a53afe05a2 | ||
![]() |
a5d2467bfe | ||
![]() |
8447663e27 | ||
![]() |
64d67d8209 | ||
![]() |
78034b82a9 | ||
![]() |
f77bd6c17b | ||
![]() |
2621da7d36 | ||
![]() |
e1dca1509a | ||
![]() |
df016243dd | ||
![]() |
be72693fec | ||
![]() |
33a1ebdb1a | ||
![]() |
030f9d334b | ||
![]() |
dc743ac378 | ||
![]() |
0010cbe21f | ||
![]() |
3a5d5918de | ||
![]() |
3380e39de2 | ||
![]() |
7d31079897 | ||
![]() |
c287b6df77 | ||
![]() |
dab1f8ba20 | ||
![]() |
a26de7f6c2 | ||
![]() |
ab32b2cbc2 | ||
![]() |
503c249fc3 | ||
![]() |
2a03ce757e | ||
![]() |
373a15524f | ||
![]() |
13036183d3 | ||
![]() |
170591c79e | ||
![]() |
a15d225a5f | ||
![]() |
a0106874e2 | ||
![]() |
ab157d1c0e | ||
![]() |
0b95c9fe2e | ||
![]() |
d693514ca9 | ||
![]() |
56987b3aaa | ||
![]() |
3ca1bd5d78 | ||
![]() |
5d2219f2f8 | ||
![]() |
56dc28eed3 | ||
![]() |
3e723d4373 | ||
![]() |
f5e341e655 | ||
![]() |
3c81100957 | ||
![]() |
304378f93b | ||
![]() |
de6b6e8124 | ||
![]() |
d15223fb1a | ||
![]() |
d29a12b6db | ||
![]() |
9100e25a21 | ||
![]() |
7672f1955e | ||
![]() |
5f52171fc4 | ||
![]() |
31ac82ad71 | ||
![]() |
38ca4e37a6 | ||
![]() |
3c55550702 | ||
![]() |
7dff6b121b | ||
![]() |
d77d889695 | ||
![]() |
318a21438f | ||
![]() |
7175b57a28 | ||
![]() |
e1e5a050c2 | ||
![]() |
58996c1115 | ||
![]() |
7301fe5f6e | ||
![]() |
a27c423569 | ||
![]() |
19680d3bc7 | ||
![]() |
ecaca4e5dc | ||
![]() |
191de0b577 | ||
![]() |
ebcc073b32 | ||
![]() |
043b3fd57b | ||
![]() |
dd50502dcb | ||
![]() |
f159a1014d | ||
![]() |
abb801535c | ||
![]() |
2732dbf1b1 | ||
![]() |
095d893005 | ||
![]() |
5d8455d141 | ||
![]() |
aa3450bfcc | ||
![]() |
770f12b632 | ||
![]() |
45c2ccdffe | ||
![]() |
fc14c3165f | ||
![]() |
0fad245148 | ||
![]() |
79609c384e | ||
![]() |
09054ddb4b | ||
![]() |
6f912d4aa2 | ||
![]() |
96033a8214 | ||
![]() |
5ca65f4797 | ||
![]() |
d2fccbde68 | ||
![]() |
e6b48d7baf | ||
![]() |
3e51310511 | ||
![]() |
32b43202c2 | ||
![]() |
446170f8de | ||
![]() |
c5a9ecd4ac | ||
![]() |
2af5f817a3 | ||
![]() |
4e55cf3cd4 | ||
![]() |
eeb0478813 | ||
![]() |
33739f1cb2 | ||
![]() |
515e6a8071 | ||
![]() |
2b22f8eb4f | ||
![]() |
e9725a0081 | ||
![]() |
8fd159d2fe | ||
![]() |
3d7e6c8b2c | ||
![]() |
0c048d61b1 | ||
![]() |
f05b8e5cd1 | ||
![]() |
0b38fec827 | ||
![]() |
547dc9ed33 | ||
![]() |
896a37bea9 | ||
![]() |
3f90037db3 | ||
![]() |
380ca11ced | ||
![]() |
ab3a288e49 | ||
![]() |
638e225f80 | ||
![]() |
5089ede207 | ||
![]() |
a3e6e76158 | ||
![]() |
7c4c7bfc90 | ||
![]() |
644fea6665 | ||
![]() |
a1349ff8a6 | ||
![]() |
71c20002b8 | ||
![]() |
157af84226 | ||
![]() |
9b4536f132 | ||
![]() |
29ab470e42 | ||
![]() |
c67fa480a7 | ||
![]() |
0a1a691c73 | ||
![]() |
48588f23bf | ||
![]() |
cf14fbc3f0 | ||
![]() |
e471d5207d | ||
![]() |
5722a52082 | ||
![]() |
08c32e875e | ||
![]() |
7d3ee3afb3 | ||
![]() |
def8600f5c | ||
![]() |
74a68f3c7d | ||
![]() |
64c9247dd1 | ||
![]() |
1bfcd34247 | ||
![]() |
19864e97e6 | ||
![]() |
ec5c5e1420 | ||
![]() |
803f4e14ca | ||
![]() |
6cc254b80a | ||
![]() |
59593ab1aa | ||
![]() |
65a0a0eb7d | ||
![]() |
f4206b401f | ||
![]() |
99f8d24b3e | ||
![]() |
26b06e453d | ||
![]() |
54ab646048 | ||
![]() |
12c9aa3d6a | ||
![]() |
1ae8544f2d | ||
![]() |
eae9e66c75 | ||
![]() |
ad041a1691 | ||
![]() |
1aee3b6c8f | ||
![]() |
04d4ffb63d | ||
![]() |
80b318b45c | ||
![]() |
19969a8b1f | ||
![]() |
b84888356f | ||
![]() |
c9436195f3 | ||
![]() |
98cfb50571 | ||
![]() |
b67884ea7f | ||
![]() |
c7081b4417 | ||
![]() |
83f1519932 | ||
![]() |
6c6493fe31 | ||
![]() |
1564cdf55e | ||
![]() |
ffcb1144b6 | ||
![]() |
4ad644ab7f | ||
![]() |
f1783df8d6 | ||
![]() |
b1a3dd1a46 | ||
![]() |
52eac2b437 | ||
![]() |
ca20eda606 | ||
![]() |
4e3b95950f | ||
![]() |
9d01335395 | ||
![]() |
63814584e9 | ||
![]() |
bf7e432c0c | ||
![]() |
5a14c229a1 | ||
![]() |
79581fb83e | ||
![]() |
90c587e6e1 | ||
![]() |
b2b728a3cc | ||
![]() |
645ef86c75 | ||
![]() |
450fda18f1 | ||
![]() |
f6162a8df8 | ||
![]() |
a8c5f48b60 | ||
![]() |
edc0b96167 | ||
![]() |
9b4b3e0ecb | ||
![]() |
7bd2c00636 | ||
![]() |
15faccfa2f | ||
![]() |
11e10e7d0e | ||
![]() |
8ef48fc548 | ||
![]() |
29632b0805 | ||
![]() |
e908c45cf2 | ||
![]() |
c37e934f42 | ||
![]() |
41f91956b8 | ||
![]() |
8b2bd5ce79 | ||
![]() |
28e4151157 | ||
![]() |
66d45293e6 | ||
![]() |
243eeeff67 | ||
![]() |
18520e24d1 | ||
![]() |
87743171b7 | ||
![]() |
452f0747c8 | ||
![]() |
a20747044c | ||
![]() |
e60241ca73 | ||
![]() |
c69e68a4a8 | ||
![]() |
561d994328 | ||
![]() |
33acf9402d | ||
![]() |
0a42cb4135 | ||
![]() |
19695c220b | ||
![]() |
1fbc0165e6 | ||
![]() |
4dd05e217a | ||
![]() |
b77a409414 | ||
![]() |
b2157df026 | ||
![]() |
0b8d5954f9 | ||
![]() |
0d60c7b728 | ||
![]() |
de58136314 | ||
![]() |
4e718056b6 | ||
![]() |
480913362e | ||
![]() |
ad2f61132a | ||
![]() |
0e2df4ba14 | ||
![]() |
c342273742 | ||
![]() |
0cb147cd8f | ||
![]() |
11b2a17fb3 | ||
![]() |
23486ff235 | ||
![]() |
a9e24bc0c5 | ||
![]() |
3c22496ea2 | ||
![]() |
b8164ca556 | ||
![]() |
19565b3d0a | ||
![]() |
50ce17ac72 | ||
![]() |
e51a425389 | ||
![]() |
3df7642193 | ||
![]() |
38ad9c9893 | ||
![]() |
fb75b9566f | ||
![]() |
80a6118bf9 | ||
![]() |
6b2bc0108b | ||
![]() |
bd07cf5a04 | ||
![]() |
f07ad960dc | ||
![]() |
824e975598 | ||
![]() |
4f44c64e4a | ||
![]() |
a449670667 | ||
![]() |
7cd38581a6 | ||
![]() |
507ecee2e3 | ||
![]() |
95a6c62dd9 | ||
![]() |
b8c1fef700 | ||
![]() |
7a8301a8dc | ||
![]() |
19f029cff0 | ||
![]() |
55bdde808b | ||
![]() |
abab2f3f6b | ||
![]() |
c586fe1d07 | ||
![]() |
1b0c32f4e7 | ||
![]() |
e667f4487f | ||
![]() |
507be2e6e3 | ||
![]() |
5ff3c433b2 | ||
![]() |
7c825f72aa | ||
![]() |
36e604dc91 | ||
![]() |
b6c5068bc4 | ||
![]() |
af78f98106 | ||
![]() |
b209c29749 | ||
![]() |
e28965f8b3 | ||
![]() |
117c228672 | ||
![]() |
359273e26e | ||
![]() |
b22ae58f79 | ||
![]() |
07d8fbb7f4 | ||
![]() |
9edce2d387 | ||
![]() |
1787fff8c3 | ||
![]() |
1b5e5f37d0 | ||
![]() |
b76143116c | ||
![]() |
20e3eebd6a | ||
![]() |
f42f1182f2 | ||
![]() |
7e7609743a | ||
![]() |
a00f36f83b | ||
![]() |
e353d0518c | ||
![]() |
5efe2be7b2 | ||
![]() |
53d0bef260 | ||
![]() |
ecd0a199f1 | ||
![]() |
019787b32d | ||
![]() |
1ca1f9975c | ||
![]() |
2d76991a86 | ||
![]() |
2fee437010 | ||
![]() |
4e7c91617b | ||
![]() |
86e1e99b33 | ||
![]() |
595af9e70b | ||
![]() |
d23c4ef319 | ||
![]() |
1d9d16b69b | ||
![]() |
a6ee70b7ec | ||
![]() |
7921681fad | ||
![]() |
256ddde07d | ||
![]() |
e606bd512f | ||
![]() |
ffb38ef599 | ||
![]() |
f9e7c2bdb6 | ||
![]() |
76cc61e0c2 | ||
![]() |
9ba6b230e6 | ||
![]() |
31f03e0114 | ||
![]() |
7e7693f206 | ||
![]() |
6cae12d467 | ||
![]() |
1235f73332 | ||
![]() |
75dfb82a88 | ||
![]() |
a2eeda64df | ||
![]() |
718049b9f3 | ||
![]() |
fdff9d7454 | ||
![]() |
976418de0f | ||
![]() |
e51e9493b1 | ||
![]() |
8535fa4e0a | ||
![]() |
16b05a47c0 | ||
![]() |
ff7fee6abf | ||
![]() |
7f2e740bc0 | ||
![]() |
3df31bb01b | ||
![]() |
c6a36cb4cf | ||
![]() |
2873d8bba3 | ||
![]() |
d77696be59 | ||
![]() |
1bd2f332d9 | ||
![]() |
e4a2616ffa | ||
![]() |
99f93a8d50 | ||
![]() |
e80d88c9ff | ||
![]() |
8d2c9a7764 | ||
![]() |
de589f59a1 | ||
![]() |
627f552976 | ||
![]() |
67bac92849 | ||
![]() |
e1dc299cba | ||
![]() |
3f668ae5c6 | ||
![]() |
0d54e9f2d6 | ||
![]() |
ef6ef868e0 | ||
![]() |
5d8da23c3f | ||
![]() |
43f285951d | ||
![]() |
7714ecc7dd | ||
![]() |
6a39201961 | ||
![]() |
f0ca7385b9 | ||
![]() |
567bba0c44 | ||
![]() |
7559c0c6f8 | ||
![]() |
58bcd068f2 | ||
![]() |
7047829943 | ||
![]() |
b6a634ea6f | ||
![]() |
15a871889f | ||
![]() |
9d2cfaa3f5 | ||
![]() |
8625d3f127 | ||
![]() |
f07a7fa2cf | ||
![]() |
c6a4c4d6b3 | ||
![]() |
df55f70991 | ||
![]() |
37951cd69b | ||
![]() |
cfd3099626 | ||
![]() |
3db6c98c27 | ||
![]() |
3c04b04b98 | ||
![]() |
d207d4a508 | ||
![]() |
df4e466fdf | ||
![]() |
5417747473 | ||
![]() |
cf6847d777 | ||
![]() |
464fa1f8a3 | ||
![]() |
665a6435ef | ||
![]() |
2d64ba4a0e | ||
![]() |
a6fd7b581f | ||
![]() |
58712a2d05 | ||
![]() |
39da58d3bc | ||
![]() |
daec864f50 | ||
![]() |
54cd860c13 | ||
![]() |
fe210646c3 | ||
![]() |
006c778dca | ||
![]() |
bd636b756b | ||
![]() |
d21b74f231 | ||
![]() |
4354f72578 | ||
![]() |
f5ca522e6c | ||
![]() |
3c1d0d3128 | ||
![]() |
5d2f1d7014 | ||
![]() |
a17c3f8138 | ||
![]() |
45f002a797 | ||
![]() |
4a16ee6865 | ||
![]() |
76e9e24c41 | ||
![]() |
92a3589588 | ||
![]() |
1e10ddec99 | ||
![]() |
ad827e62f6 | ||
![]() |
6dd7e8445a | ||
![]() |
9364e5ab91 | ||
![]() |
8a556a50ab | ||
![]() |
532ff59dfe | ||
![]() |
9a48383eb4 | ||
![]() |
07524242c0 | ||
![]() |
3b0e4bf254 | ||
![]() |
da3abf11a2 | ||
![]() |
aee752e04e | ||
![]() |
b4fa6befdf | ||
![]() |
cd007ee0eb | ||
![]() |
bbe309e7bd | ||
![]() |
f414d7aa16 | ||
![]() |
be2989ead1 | ||
![]() |
1c8e581cf1 | ||
![]() |
a81dfe83a9 | ||
![]() |
4b9b8d61f2 | ||
![]() |
4fa5fb909c | ||
![]() |
2d7fb82dd6 | ||
![]() |
48152c9ba3 | ||
![]() |
a5771dccf5 | ||
![]() |
581b8375b9 | ||
![]() |
ec685407bb | ||
![]() |
be9a1dcf06 | ||
![]() |
1f7e8b4d9a | ||
![]() |
9058c5accb | ||
![]() |
a0799e8197 | ||
![]() |
21309ba280 | ||
![]() |
b66018a5c3 | ||
![]() |
f0e14b6de5 | ||
![]() |
845c3d419a | ||
![]() |
00e281dfb7 | ||
![]() |
b3da08ce74 | ||
![]() |
50753db4ff | ||
![]() |
862c9cea87 | ||
![]() |
427f24dffe | ||
![]() |
68072d8340 | ||
![]() |
1ac4d43db2 | ||
![]() |
e0b78adfee | ||
![]() |
0c481fc005 | ||
![]() |
9f6ee3e761 | ||
![]() |
81dca01302 | ||
![]() |
0c62a83145 | ||
![]() |
c780b9fd65 | ||
![]() |
0e180cb242 | ||
![]() |
41101921ed | ||
![]() |
80ee8f9617 | ||
![]() |
1f55b5457e | ||
![]() |
b3fe6145e2 | ||
![]() |
527af7063d | ||
![]() |
25455e8194 | ||
![]() |
c27c1379d0 | ||
![]() |
42a7ae36c2 | ||
![]() |
ce846d65cb | ||
![]() |
dc25ef857f | ||
![]() |
6f0c650a72 | ||
![]() |
8348424758 | ||
![]() |
9701474662 | ||
![]() |
e284a17034 | ||
![]() |
abf55df26d | ||
![]() |
6488a7436a | ||
![]() |
cceff2e8dd | ||
![]() |
161e6f70d0 | ||
![]() |
d4e5a750e0 | ||
![]() |
b1264bedd8 | ||
![]() |
5323ae2943 | ||
![]() |
af77e51307 | ||
![]() |
b4e8689e92 | ||
![]() |
8ec30a77ff | ||
![]() |
29c7853380 | ||
![]() |
cd417aaf44 | ||
![]() |
428a5cc0ff | ||
![]() |
d128d7c8e6 | ||
![]() |
8027199bd5 | ||
![]() |
099a887cc7 | ||
![]() |
e17c551555 | ||
![]() |
f7166f37f5 | ||
![]() |
6bc12e17fd | ||
![]() |
ea41d06023 | ||
![]() |
5147baab05 | ||
![]() |
ae5777e612 | ||
![]() |
1b5a7d78d4 | ||
![]() |
7ba2b7828c | ||
![]() |
1eac1002b2 | ||
![]() |
f41280f5b9 | ||
![]() |
8653b5b928 | ||
![]() |
5b5ebadfea | ||
![]() |
12c94ee79e | ||
![]() |
d536a10d20 | ||
![]() |
2db3b093b9 | ||
![]() |
8aa94cc32d | ||
![]() |
2e83b42ba9 | ||
![]() |
42b1a0d3de | ||
![]() |
9d49a2bd11 | ||
![]() |
f3349c64a9 | ||
![]() |
565dea5ecf | ||
![]() |
2ebe849b8e | ||
![]() |
1c50e615cf | ||
![]() |
ed2d34e979 | ||
![]() |
c404016700 | ||
![]() |
14b0353ba4 | ||
![]() |
fbe136a350 | ||
![]() |
4fcfea943e | ||
![]() |
279d27d081 | ||
![]() |
84bfe99017 | ||
![]() |
7c0c7eea30 | ||
![]() |
77b0f69b0c | ||
![]() |
e71e588a91 | ||
![]() |
beb8df9dda | ||
![]() |
014cac7789 | ||
![]() |
41caaa7730 | ||
![]() |
997e2cdfd8 | ||
![]() |
a939d1bfd8 | ||
![]() |
59f12c71a9 | ||
![]() |
0528ce983f | ||
![]() |
f2f28f48cb | ||
![]() |
006e7c214d | ||
![]() |
fc37a8afa3 | ||
![]() |
c2243320ec | ||
![]() |
f5969f271a | ||
![]() |
c7063b5973 | ||
![]() |
23e0379beb | ||
![]() |
08619244f0 | ||
![]() |
5029b19d37 | ||
![]() |
4bdf520bce | ||
![]() |
0a493b9349 | ||
![]() |
a612de52f9 | ||
![]() |
a85d4a678d | ||
![]() |
08fdcac240 | ||
![]() |
40060255ee | ||
![]() |
d2da193978 | ||
![]() |
441c56854d | ||
![]() |
e6023a4702 | ||
![]() |
d031d74bc6 | ||
![]() |
2952d1360a | ||
![]() |
301ef85e89 | ||
![]() |
ddf671abd1 | ||
![]() |
f60c978d80 | ||
![]() |
42a895b095 | ||
![]() |
c405f04e9c | ||
![]() |
bc5ce52236 | ||
![]() |
896922de4f | ||
![]() |
203a6d47b4 | ||
![]() |
8e13e2deb2 | ||
![]() |
d18c2ffddb | ||
![]() |
50969c24cc | ||
![]() |
f3070763fa | ||
![]() |
ca093875b4 | ||
![]() |
f77e061ff1 | ||
![]() |
0df1bd137d | ||
![]() |
651125ef2c | ||
![]() |
a5eb0e7faa | ||
![]() |
3513f7fe2c | ||
![]() |
d845b3f7b0 | ||
![]() |
9318c4742d | ||
![]() |
e85cdd5609 | ||
![]() |
83f80b9288 | ||
![]() |
76fe771d8c | ||
![]() |
c6ad09fe8f | ||
![]() |
a3014638c8 | ||
![]() |
8b15c63b0d | ||
![]() |
589adf4df9 | ||
![]() |
b07d85f233 | ||
![]() |
f0e5855a8e | ||
![]() |
5997aa5cd9 | ||
![]() |
8a2696adf7 | ||
![]() |
fd1bd7f215 | ||
![]() |
74876ea3c9 | ||
![]() |
f6fc142c47 | ||
![]() |
78fd83746e | ||
![]() |
2794a4b550 | ||
![]() |
7244b15821 | ||
![]() |
45a1c119ac | ||
![]() |
82b42f92a9 | ||
![]() |
c4fc831968 | ||
![]() |
83c1ca195f | ||
![]() |
3166b91cb7 | ||
![]() |
914e3a3988 | ||
![]() |
cb2a38addc | ||
![]() |
9fee0d711a | ||
![]() |
989ebcd3b7 | ||
![]() |
a46b24213b | ||
![]() |
873f857c82 | ||
![]() |
b9a22461c1 | ||
![]() |
c01731ebe9 | ||
![]() |
89b837393d | ||
![]() |
cea851d38e | ||
![]() |
992f0baf93 | ||
![]() |
e272a0eecc | ||
![]() |
0c95297659 | ||
![]() |
628cef11fa | ||
![]() |
2c5d131019 | ||
![]() |
85a02771a7 | ||
![]() |
67e5c7de2c | ||
![]() |
00ff9c5736 | ||
![]() |
ca472ff597 | ||
![]() |
d875f21647 | ||
![]() |
403ed7f63f | ||
![]() |
3e67177f73 | ||
![]() |
e49517ef05 | ||
![]() |
f44121e2f9 | ||
![]() |
de6215a933 | ||
![]() |
876ff65694 | ||
![]() |
1c142ef3f4 | ||
![]() |
bfe5209c05 | ||
![]() |
83ac03578d | ||
![]() |
e34472084c | ||
![]() |
4dce0ec015 | ||
![]() |
304c2429bb | ||
![]() |
957c7aeab9 | ||
![]() |
6a0b0327c3 | ||
![]() |
07a1136839 | ||
![]() |
70f006d06d | ||
![]() |
651f4ebf5f | ||
![]() |
fc7b396e45 | ||
![]() |
eac31d10f1 | ||
![]() |
270d4f510a | ||
![]() |
285599cf90 | ||
![]() |
01ec812e1c | ||
![]() |
9cf51d403a | ||
![]() |
dfac33723a | ||
![]() |
655f1c249e | ||
![]() |
e31e531f9c | ||
![]() |
d7cef5add7 | ||
![]() |
3742f33d08 | ||
![]() |
82ac33dd75 | ||
![]() |
03eec610fa | ||
![]() |
66191d2ff9 | ||
![]() |
9e64463e70 | ||
![]() |
ebf014406d | ||
![]() |
8c7c0101cd | ||
![]() |
4d6179dfdd | ||
![]() |
21f6a076cb | ||
![]() |
bb938d4482 | ||
![]() |
411e798f8e | ||
![]() |
0c5fa37d8b | ||
![]() |
fd4a2bdb4d | ||
![]() |
38d2345cd2 | ||
![]() |
cc45ad0b63 | ||
![]() |
0ab9cdf97c | ||
![]() |
580595e5fa | ||
![]() |
ca573ce73a | ||
![]() |
c75099decc | ||
![]() |
d874697eef | ||
![]() |
cb5252b8d4 | ||
![]() |
5f28ead6e9 | ||
![]() |
bf99c0e290 | ||
![]() |
5aaa014207 | ||
![]() |
a13f84fbf6 | ||
![]() |
1618921a57 | ||
![]() |
864063676f | ||
![]() |
17649bf36a | ||
![]() |
8f22b118be | ||
![]() |
6f97036446 | ||
![]() |
ffcde69352 | ||
![]() |
1520062878 | ||
![]() |
228777963f | ||
![]() |
08a9b59ca4 | ||
![]() |
7da1edfcc5 | ||
![]() |
f63b5514f3 | ||
![]() |
486ddb55b2 | ||
![]() |
6720b44f43 | ||
![]() |
0c7db9c1ee | ||
![]() |
7df455309e | ||
![]() |
0a4b3f02db | ||
![]() |
b8fc6e6b0f | ||
![]() |
39b8f49894 | ||
![]() |
c3b5621dda | ||
![]() |
5d58d9f5d4 | ||
![]() |
52a6f639b0 | ||
![]() |
cfcd32a10b | ||
![]() |
9e8d55e0f3 | ||
![]() |
ed6a42f747 | ||
![]() |
ffdd9c9cbf | ||
![]() |
f45bd49421 | ||
![]() |
e9cfc59400 | ||
![]() |
b98faa0671 | ||
![]() |
bfb6aee908 | ||
![]() |
5b6f876ddc | ||
![]() |
6737f45948 | ||
![]() |
1b42f95643 | ||
![]() |
b3f43f956e | ||
![]() |
abe75c9744 | ||
![]() |
1b1e4639fd | ||
![]() |
6d0327f662 | ||
![]() |
19e379f084 | ||
![]() |
2f291b167e | ||
![]() |
d47daba8c1 | ||
![]() |
27183483a9 | ||
![]() |
31b6ff003a | ||
![]() |
0e58369873 | ||
![]() |
94ddf041aa | ||
![]() |
bca9da3964 | ||
![]() |
1ca1b5aefa | ||
![]() |
9eec985fb9 | ||
![]() |
d1551bd8c7 | ||
![]() |
91716527a4 | ||
![]() |
83c304290b | ||
![]() |
5ee986593c | ||
![]() |
7187aff1a6 | ||
![]() |
44f39e7fea | ||
![]() |
f42d7b0da0 | ||
![]() |
50941055fb | ||
![]() |
a361296930 | ||
![]() |
61507c03a0 | ||
![]() |
8595fb97ce | ||
![]() |
7a37408459 | ||
![]() |
303ccce8b4 | ||
![]() |
d8cc76c7d7 | ||
![]() |
fd35295093 | ||
![]() |
72764c614e | ||
![]() |
b8106186fb | ||
![]() |
eb3c189ab6 | ||
![]() |
16c7d27508 | ||
![]() |
be50ecd033 | ||
![]() |
4170ed55a6 | ||
![]() |
31c9ecaf44 | ||
![]() |
e1bd5ed49e | ||
![]() |
d97cacff14 | ||
![]() |
8ed2f0eafa | ||
![]() |
438e525319 | ||
![]() |
2a2237f542 | ||
![]() |
06db6826ed | ||
![]() |
08a8b5fee0 | ||
![]() |
82f4c99025 | ||
![]() |
7b2a7aff9f | ||
![]() |
1206d13978 | ||
![]() |
8bdf1af021 | ||
![]() |
f0595b8b0a | ||
![]() |
258ec197d7 | ||
![]() |
7d3711bf5a | ||
![]() |
8925a0dc78 | ||
![]() |
38ccd26b96 | ||
![]() |
7e8fddc50c | ||
![]() |
bc1452d1d0 | ||
![]() |
d285107cfb | ||
![]() |
354700fcbb | ||
![]() |
ef85fba2e5 | ||
![]() |
960b601384 | ||
![]() |
21d9091b43 | ||
![]() |
e881c32797 | ||
![]() |
57f1af05f5 | ||
![]() |
254f41a2cc | ||
![]() |
59ce3404c9 | ||
![]() |
11c7342299 | ||
![]() |
8b0959aa69 | ||
![]() |
9cd8ed12b9 | ||
![]() |
78b10d7ab5 | ||
![]() |
a322ec2b23 | ||
![]() |
1f23654735 | ||
![]() |
e1112b95c7 | ||
![]() |
4e043109bf | ||
![]() |
58e670443d | ||
![]() |
86a9230da8 | ||
![]() |
86f84766c1 | ||
![]() |
fdc7078e5c | ||
![]() |
c649ebfcc0 | ||
![]() |
6aa0d4cd0b | ||
![]() |
1a2e205c1f | ||
![]() |
5dd04cb8ab | ||
![]() |
62d05e5e08 | ||
![]() |
1c087ec856 | ||
![]() |
010c12da67 | ||
![]() |
9bdac38561 | ||
![]() |
790ca9c90a | ||
![]() |
58f72d2d9c | ||
![]() |
285e6513ed | ||
![]() |
412bc8cf2d | ||
![]() |
45cd8b8a00 | ||
![]() |
ae2227959e | ||
![]() |
b50c92f919 | ||
![]() |
93a1d9c164 | ||
![]() |
0b10e68c60 | ||
![]() |
73ac4076ac | ||
![]() |
5968b82a0b | ||
![]() |
ce1d2a0fd9 | ||
![]() |
de3f813b46 | ||
![]() |
4797b1a3b7 | ||
![]() |
3e996d284d | ||
![]() |
420c5a0836 | ||
![]() |
c6b953055a | ||
![]() |
1cd0c112a6 | ||
![]() |
492d28ea37 | ||
![]() |
4eb7e03b67 | ||
![]() |
e029f329eb | ||
![]() |
47de9a752c | ||
![]() |
51c9aa2887 | ||
![]() |
82499a53d4 | ||
![]() |
df15302f2c | ||
![]() |
039b51262d | ||
![]() |
465add46d4 | ||
![]() |
bce965b402 | ||
![]() |
95ce293169 | ||
![]() |
5d604c2cad | ||
![]() |
ed2d3ca277 | ||
![]() |
0478f40d02 | ||
![]() |
a4be73da3b | ||
![]() |
762192518f | ||
![]() |
fa51df192d | ||
![]() |
1a5cc02097 | ||
![]() |
a07f54ca33 | ||
![]() |
6a8cbe92a9 | ||
![]() |
16d9376ec9 | ||
![]() |
4356f5c72a | ||
![]() |
076dc94292 | ||
![]() |
fbc527010a | ||
![]() |
5b4a22276d | ||
![]() |
b55a563fce | ||
![]() |
2a701a6dfe | ||
![]() |
8931fb4758 | ||
![]() |
2124165319 | ||
![]() |
0d701129a0 | ||
![]() |
ebd8625e1e | ||
![]() |
b68ca67386 | ||
![]() |
17a7019c60 | ||
![]() |
54af92251c | ||
![]() |
d9edeb747d | ||
![]() |
b69b722a37 | ||
![]() |
669c23ea09 | ||
![]() |
2b3ba8e7fa | ||
![]() |
9a761e7d30 | ||
![]() |
9d00e052f0 | ||
![]() |
7c159e97de | ||
![]() |
ba8e4ff33c | ||
![]() |
9b067a437c | ||
![]() |
aba39d06bf | ||
![]() |
469d22a833 | ||
![]() |
43bd49ce5b | ||
![]() |
79dc190ccc | ||
![]() |
495659e9cd | ||
![]() |
2fec2c9e4c | ||
![]() |
9cba66634d | ||
![]() |
b2f63bf231 | ||
![]() |
9c9ef22730 | ||
![]() |
5d84ec3be2 | ||
![]() |
2150961d27 | ||
![]() |
53bca5a3d3 | ||
![]() |
cd3938eb33 | ||
![]() |
eb0b88bfcf | ||
![]() |
b9bbf8bbca | ||
![]() |
65b3d0c0de | ||
![]() |
93b8f32f68 | ||
![]() |
28bb164e8e | ||
![]() |
4911cc76a3 | ||
![]() |
26ac539bc4 | ||
![]() |
75ae6b16a4 | ||
![]() |
2835b1d28f | ||
![]() |
748aad16d7 | ||
![]() |
2c2fbb8583 | ||
![]() |
20edcbf7fa | ||
![]() |
db81dc39ba | ||
![]() |
c3b0aef1ef | ||
![]() |
50e29efdfe | ||
![]() |
285e41bc88 | ||
![]() |
ea9d0fc449 | ||
![]() |
9cdd2eef81 | ||
![]() |
2f8833236a | ||
![]() |
2b680eeb6d | ||
![]() |
809f120db0 | ||
![]() |
6d9ef8bbc3 | ||
![]() |
a26d6ec6bb | ||
![]() |
2d26ced3fc | ||
![]() |
d74cd4bf24 | ||
![]() |
f040d897a7 | ||
![]() |
ed2f87f57b | ||
![]() |
9b9e31f54c | ||
![]() |
b3cfcf660e | ||
![]() |
e5bcd1f94e | ||
![]() |
2b6fa769f7 | ||
![]() |
3ccc82f343 | ||
![]() |
f4273cafb6 | ||
![]() |
59d63f61d9 | ||
![]() |
9d9103a83b | ||
![]() |
0b085b6d03 | ||
![]() |
f77538f179 | ||
![]() |
f7810f7f95 | ||
![]() |
4d28e4603f | ||
![]() |
8b787e4ae0 | ||
![]() |
f5ba168172 | ||
![]() |
1df6dadbdd | ||
![]() |
3dc29144a3 | ||
![]() |
951167ce17 | ||
![]() |
906e4055d8 | ||
![]() |
2f5526388a | ||
![]() |
82341642f4 | ||
![]() |
c96b1eb09d | ||
![]() |
f5bfa67c69 | ||
![]() |
47797ffcd4 | ||
![]() |
a73053e380 | ||
![]() |
bc042fead7 | ||
![]() |
ed6779e937 | ||
![]() |
ee7ca68f87 | ||
![]() |
32693b6378 | ||
![]() |
984e5588c8 | ||
![]() |
a42a1af867 | ||
![]() |
03de680915 | ||
![]() |
8c6e142314 | ||
![]() |
b12bde4f79 | ||
![]() |
1120aa3841 | ||
![]() |
652ca73126 | ||
![]() |
8706e72f6a | ||
![]() |
319d521773 | ||
![]() |
d9474cdcc5 | ||
![]() |
e49a34177a | ||
![]() |
67d203e011 | ||
![]() |
0d38b3de16 | ||
![]() |
38116a14f3 | ||
![]() |
b28f0b65f0 | ||
![]() |
13ab4a9363 | ||
![]() |
7cb7783a34 | ||
![]() |
d1a13dad38 | ||
![]() |
b4e06dea99 | ||
![]() |
0f92dc0fdf | ||
![]() |
6a58895d37 | ||
![]() |
1709a2b7df | ||
![]() |
febb3da0c1 | ||
![]() |
552a428985 | ||
![]() |
38e04bd42a | ||
![]() |
8f0ba5ba4f | ||
![]() |
c67aedceb1 | ||
![]() |
b3a7fbd9b5 | ||
![]() |
29522428de | ||
![]() |
77bd52b2ae | ||
![]() |
d8112e7628 | ||
![]() |
ffa208e73f | ||
![]() |
61ead15c38 | ||
![]() |
407e2ae481 | ||
![]() |
5fb16edf43 | ||
![]() |
8eb5c475bb | ||
![]() |
84090310f7 | ||
![]() |
fc98e2f052 | ||
![]() |
bedcfa9520 | ||
![]() |
bb152b590b | ||
![]() |
3623732cf7 | ||
![]() |
05ba89f164 | ||
![]() |
cb5053476d | ||
![]() |
cee656a053 | ||
![]() |
cfc7d529e1 | ||
![]() |
a93dc68e6c | ||
![]() |
2d91cfd3db | ||
![]() |
36e81f44cb | ||
![]() |
cb0e65337f | ||
![]() |
1c627f4649 | ||
![]() |
16cbfed20b | ||
![]() |
f6a3bc57e2 | ||
![]() |
594443d1dc | ||
![]() |
c3378e1653 | ||
![]() |
bc57dd650c | ||
![]() |
311a8c6fa3 | ||
![]() |
bdb43c0e9e | ||
![]() |
8033b47596 | ||
![]() |
9d5052cc68 | ||
![]() |
f4c9dc8a5f | ||
![]() |
a3f0a78df0 | ||
![]() |
b70363e005 | ||
![]() |
65eab801e8 | ||
![]() |
9e764248d3 | ||
![]() |
a660a1c44b | ||
![]() |
33458c1bdb | ||
![]() |
e5530182cd | ||
![]() |
9ecabc3faf | ||
![]() |
8b58f6b861 | ||
![]() |
9e41bf529d | ||
![]() |
36398fe958 | ||
![]() |
69cfbea5f3 | ||
![]() |
1e1e3beca6 | ||
![]() |
a726154b1d | ||
![]() |
c2ccb51ef5 | ||
![]() |
a1d7062b1f | ||
![]() |
2e1e3f8409 | ||
![]() |
885604ef06 | ||
![]() |
cb8a5504f6 | ||
![]() |
325fa19e46 | ||
![]() |
06b684c899 | ||
![]() |
363d1b07ca | ||
![]() |
663b9a610a | ||
![]() |
f598d5046e | ||
![]() |
ae381f7762 | ||
![]() |
acc18b8d68 | ||
![]() |
6f33d29a51 | ||
![]() |
be82e64add | ||
![]() |
3ee000ed7d | ||
![]() |
0f338edacd | ||
![]() |
085d937946 | ||
![]() |
0cbc4b9546 | ||
![]() |
9e1c4b1a88 | ||
![]() |
b47f542df7 | ||
![]() |
baed101ef1 | ||
![]() |
c9ee6e3af9 | ||
![]() |
8ed7688277 | ||
![]() |
27716d080f | ||
![]() |
4311d12603 | ||
![]() |
6aa786698e | ||
![]() |
b0eb98c667 | ||
![]() |
955b69a9bf | ||
![]() |
5f5bfa864d | ||
![]() |
6ec6c69dba | ||
![]() |
19f3286a82 | ||
![]() |
36a3cae9c2 | ||
![]() |
86215c34be | ||
![]() |
4ad421d4d0 | ||
![]() |
e79f6d5617 | ||
![]() |
baf44a97b4 | ||
![]() |
c14053a199 | ||
![]() |
1e5153d69e | ||
![]() |
fed38bd046 | ||
![]() |
89d298ea65 | ||
![]() |
cd35fa1802 | ||
![]() |
150453bff3 | ||
![]() |
49833b3c51 | ||
![]() |
4a0f0238b0 | ||
![]() |
83b97111a0 | ||
![]() |
43bbf32098 | ||
![]() |
a70817f421 | ||
![]() |
9ae441b75a | ||
![]() |
21fcbd85d8 | ||
![]() |
e1b61214b7 | ||
![]() |
2d10b0748c | ||
![]() |
f4e719749a | ||
![]() |
600bca7e8b | ||
![]() |
a3a62b1d94 | ||
![]() |
dbe783d31d | ||
![]() |
f0d8492b66 | ||
![]() |
7587eb9ac2 | ||
![]() |
fe2fdafbb1 | ||
![]() |
e50c77d8c6 | ||
![]() |
81f9f52353 | ||
![]() |
6e5b02d326 | ||
![]() |
d09c7b13b3 | ||
![]() |
ff532a5c6c | ||
![]() |
698275633f | ||
![]() |
c5dff312e1 | ||
![]() |
972412e712 | ||
![]() |
453c46df00 | ||
![]() |
f001e19728 | ||
![]() |
3da8cc1e7f | ||
![]() |
68d124ff04 | ||
![]() |
4921458782 | ||
![]() |
86aa21a8bb | ||
![]() |
65de742f96 | ||
![]() |
4043398e01 | ||
![]() |
7be651f5cf | ||
![]() |
5ddd4d045e | ||
![]() |
1cc7e8725d | ||
![]() |
6fe115fd0d | ||
![]() |
d58dfec5ea | ||
![]() |
3e6f5ac70e | ||
![]() |
a1821fabf9 | ||
![]() |
b6461f4f9e | ||
![]() |
0781018e4e | ||
![]() |
2f8e768c5c | ||
![]() |
1622b0fa29 | ||
![]() |
e147ce9039 | ||
![]() |
2aa059a170 | ||
![]() |
ae60b21375 | ||
![]() |
03faebe776 | ||
![]() |
f66afc4cae | ||
![]() |
b327413bfa | ||
![]() |
54776e2712 | ||
![]() |
4b8d4488d7 | ||
![]() |
a2a1b66fc3 | ||
![]() |
d2bdb597f6 | ||
![]() |
85a7819469 | ||
![]() |
5689dfd3e3 | ||
![]() |
3c76ead1ab | ||
![]() |
be7fbdf5d8 | ||
![]() |
fc21f043ae | ||
![]() |
11659df89d | ||
![]() |
2bfa770f60 | ||
![]() |
4679121115 | ||
![]() |
3ecc90d21a | ||
![]() |
fd587fe108 | ||
![]() |
0f851ec2a3 | ||
![]() |
4d057a1c5e | ||
![]() |
c8b13ff5e1 | ||
![]() |
545dd08535 | ||
![]() |
c0a5a8d775 | ||
![]() |
d462ebe8e5 | ||
![]() |
5d7ba8cf14 | ||
![]() |
af9786f149 | ||
![]() |
6b990ee78a | ||
![]() |
f87102ccc7 | ||
![]() |
63398089cd | ||
![]() |
89694b5069 | ||
![]() |
4f8a5211f8 | ||
![]() |
b45df26fdc | ||
![]() |
c0b0181475 | ||
![]() |
62600a450a | ||
![]() |
4be41336b3 | ||
![]() |
3abea4ad3c | ||
![]() |
b2304992e5 | ||
![]() |
f9825410dc | ||
![]() |
24205dc86e | ||
![]() |
9fcd0da83d | ||
![]() |
e99bc73e46 | ||
![]() |
d8ad9adabd | ||
![]() |
11aa7d0140 | ||
![]() |
6f97173b00 | ||
![]() |
54a7367fb6 | ||
![]() |
51a12099e4 | ||
![]() |
00c0c96b1b | ||
![]() |
0aa2537d1e | ||
![]() |
14489e1db9 | ||
![]() |
bff4eaba56 | ||
![]() |
4a5d2f8502 | ||
![]() |
72af2fa281 | ||
![]() |
cf70b3e989 | ||
![]() |
7ebd74a54a | ||
![]() |
9a650b5cd6 | ||
![]() |
ffa1331802 | ||
![]() |
dacb4ea7d6 | ||
![]() |
ae5889dbac | ||
![]() |
7b169e9439 | ||
![]() |
541d2904d3 | ||
![]() |
2080bbcbca | ||
![]() |
f4c38008a1 | ||
![]() |
2d38b15f1b | ||
![]() |
13257b9f86 | ||
![]() |
22f106b357 | ||
![]() |
352b5aadba | ||
![]() |
f86c9ea947 | ||
![]() |
2f5f0ba1e1 | ||
![]() |
6a72923182 | ||
![]() |
d62f7b2a5f | ||
![]() |
8ca6255ff3 | ||
![]() |
d3b3afd593 | ||
![]() |
57cb5ff6cf | ||
![]() |
39f3da6cde | ||
![]() |
5a236c9357 | ||
![]() |
4b0eab57a8 | ||
![]() |
74a232630a | ||
![]() |
71d023ab77 | ||
![]() |
786a374233 | ||
![]() |
41899872cd | ||
![]() |
076659db52 | ||
![]() |
8f665622d6 | ||
![]() |
5cc6e0b172 | ||
![]() |
bff22900cb | ||
![]() |
5e79c9fd62 | ||
![]() |
92f55c254c | ||
![]() |
39034e38f6 | ||
![]() |
3c7b9558fe | ||
![]() |
c8f7f40b46 | ||
![]() |
2a764cf190 | ||
![]() |
ba6ef4d629 | ||
![]() |
67d3505733 | ||
![]() |
252145cf58 | ||
![]() |
dbc62542ef | ||
![]() |
005829ab72 | ||
![]() |
448c8b0e8a | ||
![]() |
ff0e724ee5 | ||
![]() |
568e4a5ee8 | ||
![]() |
fbf4a524c1 | ||
![]() |
29db2e958f | ||
![]() |
cc7bcbf9d5 | ||
![]() |
98d4484e6c | ||
![]() |
0b126278f9 | ||
![]() |
cc919415bb | ||
![]() |
a3f398390c | ||
![]() |
5ae89368f1 | ||
![]() |
c8f1cb0a0a | ||
![]() |
f783b08b78 | ||
![]() |
85e0c6d3cd | ||
![]() |
2259a96058 | ||
![]() |
afed5841e7 | ||
![]() |
52361cd505 | ||
![]() |
b743cca7bc | ||
![]() |
1d01d0bff1 | ||
![]() |
c45a488962 | ||
![]() |
6731c44541 | ||
![]() |
b04ed83963 | ||
![]() |
c35b79e642 | ||
![]() |
ed8c7c1052 | ||
![]() |
498a074222 | ||
![]() |
3fe6db4d42 | ||
![]() |
6e5cd82dfb | ||
![]() |
cbf3488de9 | ||
![]() |
c72314fb71 | ||
![]() |
c4af6feb92 | ||
![]() |
08537c1d69 | ||
![]() |
425da82f5f | ||
![]() |
2cfbf7c39a | ||
![]() |
d4eed9f8fd | ||
![]() |
fe10170826 | ||
![]() |
8dc3b0b250 | ||
![]() |
75da1220af | ||
![]() |
37a2c3c631 | ||
![]() |
5c5722714d | ||
![]() |
2ba529f9e3 | ||
![]() |
fd760ff015 | ||
![]() |
daab1d917b | ||
![]() |
4c3a63a7e1 | ||
![]() |
7cc58b84da | ||
![]() |
2bac4ac1a7 | ||
![]() |
c5b2b86786 | ||
![]() |
5652a2b6c2 | ||
![]() |
bd19f543a2 | ||
![]() |
cc1e888227 | ||
![]() |
d73e379dcf | ||
![]() |
0569abd00d | ||
![]() |
7f5d9bec87 | ||
![]() |
b39e7bbb6d | ||
![]() |
70270a8e3b | ||
![]() |
efdc050a28 | ||
![]() |
e8a65df7f0 | ||
![]() |
59628a72fb | ||
![]() |
a4d6c6c0d8 | ||
![]() |
bea82c6640 | ||
![]() |
1ba3bdfbda | ||
![]() |
98b4000bc0 | ||
![]() |
14f6824931 | ||
![]() |
795d7d0a93 | ||
![]() |
673fa2b556 | ||
![]() |
0e2504fc78 | ||
![]() |
2afca9f2b4 | ||
![]() |
464d2a541d | ||
![]() |
fa8c5e0982 | ||
![]() |
5e15884d8f | ||
![]() |
b5e9ff3b4e | ||
![]() |
fed7d4cc34 | ||
![]() |
d7ab066ff8 | ||
![]() |
4100917016 | ||
![]() |
5d2c1ffb88 | ||
![]() |
ddb0f198a9 | ||
![]() |
13438e3e25 | ||
![]() |
05a410b327 | ||
![]() |
23fa64d289 | ||
![]() |
1920c9b7e3 | ||
![]() |
eedd0d9c07 | ||
![]() |
9ef389d335 | ||
![]() |
6542997520 | ||
![]() |
a58b2e2038 | ||
![]() |
6860e348dc | ||
![]() |
5e094e7597 | ||
![]() |
2f2cb8386b | ||
![]() |
965fd170bd | ||
![]() |
2610d29b60 | ||
![]() |
064131c842 | ||
![]() |
00b6bf8394 | ||
![]() |
2a885d709d | ||
![]() |
5bed46c0aa | ||
![]() |
8b27c7e01a | ||
![]() |
177902a286 | ||
![]() |
48b0f7dc27 | ||
![]() |
d5f4a1a48a | ||
![]() |
de9f60aa7f | ||
![]() |
c93b65b299 | ||
![]() |
3c6a6cdc5b | ||
![]() |
b669f3d715 | ||
![]() |
f663fac220 | ||
![]() |
bc42e79bb5 | ||
![]() |
ca29333cd0 | ||
![]() |
f9f478e100 | ||
![]() |
97c414d1ad | ||
![]() |
7afbd98d17 | ||
![]() |
1f5c60588e | ||
![]() |
284ab45a17 | ||
![]() |
eab6365af9 | ||
![]() |
de86516a0a | ||
![]() |
3e50e11933 | ||
![]() |
e2ac8be451 | ||
![]() |
0e53252a27 | ||
![]() |
b1ecff3d10 | ||
![]() |
0fee4fee2a | ||
![]() |
66282d817c | ||
![]() |
932c93e573 | ||
![]() |
71d30af582 | ||
![]() |
1c8428c3ea | ||
![]() |
e38e98d9e7 | ||
![]() |
85b3f081bf | ||
![]() |
3926d97fc6 | ||
![]() |
13ac8f2ea4 | ||
![]() |
d94f991ab5 | ||
![]() |
d476d2e96a | ||
![]() |
635bf364ac | ||
![]() |
e1c7a37f62 | ||
![]() |
9d780701f5 | ||
![]() |
0bd40405b5 | ||
![]() |
25c2f95e48 | ||
![]() |
5d738e58eb | ||
![]() |
70325f9247 | ||
![]() |
38c9c5a6ea | ||
![]() |
c90dd147bb | ||
![]() |
322f106e75 | ||
![]() |
91a5529438 | ||
![]() |
8f7dd2df6a | ||
![]() |
2fcd55eb60 | ||
![]() |
9359567a8a | ||
![]() |
42bfacfb19 | ||
![]() |
71131c699e | ||
![]() |
6ebfc516a6 | ||
![]() |
5c952b1d86 | ||
![]() |
1d9a4e0b99 | ||
![]() |
ebae628d8d | ||
![]() |
9865460fe5 | ||
![]() |
39884b71fe | ||
![]() |
82b7128c04 | ||
![]() |
16756ddb8c | ||
![]() |
877002961f | ||
![]() |
7e9e68ecd8 | ||
![]() |
6419190272 | ||
![]() |
ac42563c5e | ||
![]() |
98c1063e07 | ||
![]() |
a4dfc57cbe | ||
![]() |
db543b8912 | ||
![]() |
49fb4540a2 | ||
![]() |
e2120393a2 | ||
![]() |
0b301fff3f | ||
![]() |
eeb351e991 | ||
![]() |
1095e29b4d | ||
![]() |
be058eaff7 | ||
![]() |
f409dda2ef | ||
![]() |
f409cdda8f | ||
![]() |
9cd6396c35 | ||
![]() |
ee754ea533 | ||
![]() |
36de20dd75 | ||
![]() |
a957e8eb4f | ||
![]() |
14a90d84ec | ||
![]() |
fae9bc618a | ||
![]() |
3248e6500e | ||
![]() |
c17bf79d79 | ||
![]() |
1ff1270bfa | ||
![]() |
b1a2cf33d8 | ||
![]() |
b2292e98c1 | ||
![]() |
4d156a8911 | ||
![]() |
7193b6518b | ||
![]() |
cff6b44109 | ||
![]() |
fb7ad9438e | ||
![]() |
afc265a188 | ||
![]() |
01fe7bf612 | ||
![]() |
1cb75bd053 | ||
![]() |
0eaea4d011 | ||
![]() |
67377a2561 | ||
![]() |
a8aae9f1f5 | ||
![]() |
a9ce92decb | ||
![]() |
c19162295a | ||
![]() |
58796c45ed | ||
![]() |
d94b348780 | ||
![]() |
95f92bd292 | ||
![]() |
bc52ac3559 | ||
![]() |
8bbc6a6611 | ||
![]() |
8902b93a26 | ||
![]() |
ae36af807d | ||
![]() |
fd256625c6 | ||
![]() |
bee543a25a | ||
![]() |
55eb79cb52 | ||
![]() |
35965a8320 | ||
![]() |
8a902ae3e6 | ||
![]() |
52bed5bf98 | ||
![]() |
9e83f6d779 | ||
![]() |
0873beaed2 | ||
![]() |
0ba5012464 | ||
![]() |
73ff28465d | ||
![]() |
7484d65dbb | ||
![]() |
4a120e7a54 | ||
![]() |
8d63d85821 | ||
![]() |
5cec84a802 | ||
![]() |
48da41690d | ||
![]() |
1c82241f30 | ||
![]() |
b1ea3bcd4e | ||
![]() |
05e485b55e | ||
![]() |
c62e0e4e99 | ||
![]() |
3c6a1a02b8 | ||
![]() |
cc287607cd | ||
![]() |
b24e9a2185 | ||
![]() |
01791eac52 | ||
![]() |
651b57a93f | ||
![]() |
cc857364f4 | ||
![]() |
f29d7c8cfb | ||
![]() |
af4d0248d9 | ||
![]() |
2990664b2b | ||
![]() |
ed52038bc4 | ||
![]() |
ee125dfadc | ||
![]() |
196048cf38 | ||
![]() |
5faf357045 | ||
![]() |
4e0f06f24d | ||
![]() |
717530fff7 | ||
![]() |
4a5e38dc1e | ||
![]() |
419f8dadad | ||
![]() |
2dcae5e219 | ||
![]() |
36a99f70a3 | ||
![]() |
c98cf858c1 | ||
![]() |
dd463f00a7 | ||
![]() |
2459340c6f | ||
![]() |
76db5ffa3a | ||
![]() |
2db0e9c280 | ||
![]() |
db6dbe6c19 | ||
![]() |
ecedd4d231 | ||
![]() |
1809b95e2d | ||
![]() |
0d7e261bd1 | ||
![]() |
908941fb82 | ||
![]() |
fbacc4f789 | ||
![]() |
3c1290e8fd | ||
![]() |
35528ef602 | ||
![]() |
c0f0cb0d9e | ||
![]() |
4a65dc1d6e | ||
![]() |
b4a25e33bb | ||
![]() |
7f1a08dd04 | ||
![]() |
6152a1e913 | ||
![]() |
002cb93187 | ||
![]() |
381c3da31c | ||
![]() |
10e4d562ab | ||
![]() |
2a85e11ad9 | ||
![]() |
95b55760ad | ||
![]() |
636f898da8 | ||
![]() |
5fedac691d | ||
![]() |
979d68957e | ||
![]() |
a5b0837cf5 | ||
![]() |
8ba68dcfcf | ||
![]() |
8f367d140f | ||
![]() |
771885f27f | ||
![]() |
09aac22909 | ||
![]() |
1de3c0d559 | ||
![]() |
0988b68c8c | ||
![]() |
325ad4094e | ||
![]() |
16a09407e4 | ||
![]() |
84256f42c6 | ||
![]() |
7befbef6ec | ||
![]() |
754df5bea7 | ||
![]() |
78ee646558 | ||
![]() |
3d6f89d309 | ||
![]() |
e321479712 | ||
![]() |
9328b7e586 | ||
![]() |
50ace54cd0 | ||
![]() |
ad365c7dd0 | ||
![]() |
11427dbecd | ||
![]() |
b490831a50 | ||
![]() |
af76017a79 | ||
![]() |
43409b3089 | ||
![]() |
bfad769f93 | ||
![]() |
7e5dce1c14 | ||
![]() |
8ba4bebe01 | ||
![]() |
78a87db017 | ||
![]() |
b73a259f68 | ||
![]() |
65f27ee605 | ||
![]() |
6d5b5e15d5 | ||
![]() |
f31c4dcccd | ||
![]() |
1e616fa585 | ||
![]() |
0d2666f7d3 | ||
![]() |
0dd8970668 | ||
![]() |
89cda3dcff | ||
![]() |
1d48688518 | ||
![]() |
0b59f5e29c | ||
![]() |
5aebc8d191 | ||
![]() |
6e00c5da04 | ||
![]() |
50b06e041c | ||
![]() |
1c539f00dd | ||
![]() |
87ca432ec8 | ||
![]() |
ca33f4a2a5 | ||
![]() |
9409303f24 | ||
![]() |
94a3d35c90 | ||
![]() |
851de4934b | ||
![]() |
2942640eb9 | ||
![]() |
1cef037db5 | ||
![]() |
a00d43092d | ||
![]() |
45c2f50018 | ||
![]() |
ef8c6e82e6 | ||
![]() |
3eebb58da5 | ||
![]() |
447c50fd03 | ||
![]() |
0620ebebcf | ||
![]() |
cf081ee291 | ||
![]() |
3c29b8e9c5 | ||
![]() |
6143da5a6a | ||
![]() |
664f71575c | ||
![]() |
9ae111b8a1 | ||
![]() |
18682c7a2e | ||
![]() |
b21c50dfcf | ||
![]() |
49b6965e8e | ||
![]() |
c6cc2b8831 | ||
![]() |
f9f65eae53 | ||
![]() |
b51d442673 | ||
![]() |
5863b62ccf | ||
![]() |
66bb922012 | ||
![]() |
53876e8f0d | ||
![]() |
cb7ba7fdde | ||
![]() |
9cf6793b24 | ||
![]() |
6e62ffdd22 | ||
![]() |
c042d9e39a | ||
![]() |
1262de2ae2 | ||
![]() |
307230cec8 | ||
![]() |
4fa2711e78 | ||
![]() |
5c9c2f9ab8 | ||
![]() |
604155b41b | ||
![]() |
6e4198e7be | ||
![]() |
14d4940d05 | ||
![]() |
a6b0cdef97 | ||
![]() |
6265943607 | ||
![]() |
de39f7691c | ||
![]() |
921a219beb | ||
![]() |
b9c95d49a6 | ||
![]() |
fc0be6bce2 | ||
![]() |
8db891cfe6 | ||
![]() |
f6e77cc578 | ||
![]() |
a3782f9150 | ||
![]() |
7546c7ef42 | ||
![]() |
53de8cda30 | ||
![]() |
1fb7473dc5 | ||
![]() |
cc9d09bd54 | ||
![]() |
42ff4a2f62 | ||
![]() |
3fa5f80fc4 | ||
![]() |
9b5b7ef8db | ||
![]() |
560acf62fe | ||
![]() |
27d12922da | ||
![]() |
37b92f3d88 | ||
![]() |
79d5c0c92e | ||
![]() |
0bdaedd486 | ||
![]() |
018a201688 | ||
![]() |
a5bd7e6563 | ||
![]() |
a055feccd5 | ||
![]() |
ba68a9b52b | ||
![]() |
49669dc7e0 | ||
![]() |
5bdf79606e | ||
![]() |
ff3a9e47df | ||
![]() |
a18ba24f4a | ||
![]() |
baeb744a7c | ||
![]() |
d07add383f | ||
![]() |
a1f18bc133 | ||
![]() |
e00c23bc49 | ||
![]() |
0228a356e4 | ||
![]() |
b0fa0d534e | ||
![]() |
1157fda96c | ||
![]() |
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 |
14
.gitignore
vendored
@@ -12,15 +12,21 @@
|
||||
*.db*
|
||||
*.db-journal
|
||||
*.ini
|
||||
release.lock
|
||||
version.lock
|
||||
logs/*
|
||||
cache/*
|
||||
*.mmdb
|
||||
|
||||
# HTTPS Cert/Key #
|
||||
##################
|
||||
*.crt
|
||||
*.key
|
||||
*.csr
|
||||
/*.crt
|
||||
/*.key
|
||||
/*.csr
|
||||
/*.pem
|
||||
|
||||
# Mergetool
|
||||
*.orgin
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
@@ -31,7 +37,7 @@ Icon?
|
||||
Thumbs.db
|
||||
|
||||
#Ignore files generated by PyCharm
|
||||
.idea/*
|
||||
*.idea/*
|
||||
|
||||
#Ignore files generated by vi
|
||||
*.swp
|
||||
|
1023
CHANGELOG.md
@@ -1,15 +1,7 @@
|
||||
# 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.
|
||||
|
||||
* 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.
|
||||
# Contributing to Tautulli
|
||||
|
||||
## Pull Requests
|
||||
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.
|
||||
If you think you can contribute code to the Tautulli repository, do not hesitate to submit a pull request.
|
||||
|
||||
### Branches
|
||||
All pull requests should be based on the `dev` branch, to minimize cross merges. When you want to develop a new feature, clone the repository with `git clone origin/dev -b FEATURE_NAME`. Use meaningful commit messages.
|
||||
@@ -17,12 +9,12 @@ All pull requests should be based on the `dev` branch, to minimize cross merges.
|
||||
### Python Code
|
||||
|
||||
#### Compatibility
|
||||
The code should work with Python 2.6 and 2.7. Note that PlexPy runs on different platforms, including Network Attached Storage devices such as Synology.
|
||||
The code should work with Python 2.7. Note that Tautulli runs on different platforms, including Network Attached Storage devices such as Synology.
|
||||
|
||||
Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling.
|
||||
|
||||
#### Code conventions
|
||||
Although PlexPy did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
|
||||
Although Tautulli did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
|
||||
|
||||
* 4 space indentation
|
||||
* 80 characters per line
|
||||
@@ -38,12 +30,12 @@ Although PlexPy did not adapt a code convention in the past, we try to follow th
|
||||
Document your code. Use docstrings See [PEP-257](https://www.python.org/dev/peps/pep-0257/) for more information.
|
||||
|
||||
#### Continuous Integration
|
||||
PlexPy has a configuration file for [travis-ci](https://travis-ci.org/). You can add your forked repo to Travis to have it check your code against PEP8, PyLint, and PyFlakes for you. Your pull request will show a green check mark or a red cross on each tested commit, depending on if linting passes.
|
||||
Tautulli has a configuration file for [travis-ci](https://travis-ci.org/). You can add your forked repo to Travis to have it check your code against PEP8, PyLint, and PyFlakes for you. Your pull request will show a green check mark or a red cross on each tested commit, depending on if linting passes.
|
||||
|
||||
### HTML/Template code
|
||||
|
||||
#### Compatibility
|
||||
HTML5 compatible browsers are targetted. There is no specific mobile version of PlexPy yet.
|
||||
HTML5 compatible browsers are targetted. There is no specific mobile version of Tautulli yet.
|
||||
|
||||
#### Conventions
|
||||
* 4 space indentation
|
||||
|
41
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!---
|
||||
Reporting Issues:
|
||||
* To ensure that a developer has enough information to work with please include all of the information below.
|
||||
Please provide as much detail as possible. Screenshots can be very useful to see the problem.
|
||||
* Use proper markdown syntax to structure your post (i.e. code/log in code blocks).
|
||||
See: https://help.github.com/articles/basic-writing-and-formatting-syntax/
|
||||
* Include a link to your **FULL** log file that has the error(not just a few lines!).
|
||||
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||
|
||||
Feature Requests:
|
||||
* Feature requests are handled on FeatHub: http://feathub.com/Tautulli/Tautulli
|
||||
* Do not post them on the GitHub issues tracker.
|
||||
-->
|
||||
|
||||
**Version:**
|
||||
|
||||
**Branch:**
|
||||
|
||||
**Commit hash:**
|
||||
|
||||
**Operating system:**
|
||||
|
||||
**Python version:**
|
||||
|
||||
**What you did?**
|
||||
|
||||
**What happened?**
|
||||
|
||||
**What you expected?**
|
||||
|
||||
**How can we reproduce your issue?**
|
||||
<!-- Provide a detailed step-by-step. -->
|
||||
|
||||
**What are your (relevant) settings?**
|
||||
|
||||
**Link to logs:**
|
||||
|
||||
|
||||
<!--
|
||||
Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||
-->
|
228
PlexPy.py
@@ -1,232 +1,28 @@
|
||||
#!/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.
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PlexPy is distributed in the hope that it will be useful,
|
||||
# Tautulli is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
from Tautulli import main
|
||||
|
||||
# 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, web_socket
|
||||
|
||||
import locale
|
||||
import time
|
||||
import signal
|
||||
import argparse
|
||||
import plexpy
|
||||
|
||||
# Register signals, such as CTRL + C
|
||||
signal.signal(signal.SIGINT, plexpy.sig_handler)
|
||||
signal.signal(signal.SIGTERM, plexpy.sig_handler)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
PlexPy application entry point. Parses arguments, setups encoding and
|
||||
initializes the application.
|
||||
"""
|
||||
|
||||
# Fixed paths to PlexPy
|
||||
if hasattr(sys, 'frozen'):
|
||||
plexpy.FULL_PATH = os.path.abspath(sys.executable)
|
||||
else:
|
||||
plexpy.FULL_PATH = os.path.abspath(__file__)
|
||||
|
||||
plexpy.PROG_DIR = os.path.dirname(plexpy.FULL_PATH)
|
||||
plexpy.ARGS = sys.argv[1:]
|
||||
|
||||
# From sickbeard
|
||||
plexpy.SYS_PLATFORM = sys.platform
|
||||
plexpy.SYS_ENCODING = None
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
plexpy.SYS_ENCODING = locale.getpreferredencoding()
|
||||
except (locale.Error, IOError):
|
||||
pass
|
||||
|
||||
# for OSes that are poorly configured I'll just force UTF-8
|
||||
if not plexpy.SYS_ENCODING or plexpy.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
||||
plexpy.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
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')
|
||||
parser.add_argument(
|
||||
'-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
parser.add_argument(
|
||||
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||
parser.add_argument(
|
||||
'-p', '--port', type=int, help='Force PlexPy to run on a specified port')
|
||||
parser.add_argument(
|
||||
'--datadir', help='Specify a directory where to store your data files')
|
||||
parser.add_argument('--config', help='Specify a config file to use')
|
||||
parser.add_argument('--nolaunch', action='store_true',
|
||||
help='Prevent browser from launching on startup')
|
||||
parser.add_argument(
|
||||
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
plexpy.VERBOSE = True
|
||||
if args.quiet:
|
||||
plexpy.QUIET = True
|
||||
|
||||
# Do an intial setup of the logger.
|
||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
||||
verbose=plexpy.VERBOSE)
|
||||
|
||||
if args.daemon:
|
||||
if sys.platform == 'win32':
|
||||
sys.stderr.write(
|
||||
"Daemonizing not supported under Windows, starting normally\n")
|
||||
else:
|
||||
plexpy.DAEMON = True
|
||||
plexpy.QUIET = True
|
||||
|
||||
if args.pidfile:
|
||||
plexpy.PIDFILE = str(args.pidfile)
|
||||
|
||||
# If the pidfile already exists, plexpy may still be running, so
|
||||
# exit
|
||||
if os.path.exists(plexpy.PIDFILE):
|
||||
raise SystemExit("PID file '%s' already exists. Exiting." %
|
||||
plexpy.PIDFILE)
|
||||
|
||||
# The pidfile is only useful in daemon mode, make sure we can write the
|
||||
# file properly
|
||||
if plexpy.DAEMON:
|
||||
plexpy.CREATEPID = True
|
||||
|
||||
try:
|
||||
with open(plexpy.PIDFILE, 'w') as fp:
|
||||
fp.write("pid\n")
|
||||
except IOError as e:
|
||||
raise SystemExit("Unable to write PID file: %s", e)
|
||||
else:
|
||||
logger.warn("Not running in daemon mode. PID file creation " \
|
||||
"disabled.")
|
||||
|
||||
# Determine which data directory and config file to use
|
||||
if args.datadir:
|
||||
plexpy.DATA_DIR = args.datadir
|
||||
else:
|
||||
plexpy.DATA_DIR = plexpy.PROG_DIR
|
||||
|
||||
if args.config:
|
||||
config_file = args.config
|
||||
else:
|
||||
config_file = os.path.join(plexpy.DATA_DIR, 'config.ini')
|
||||
|
||||
# Try to create the DATA_DIR if it doesn't exist
|
||||
if not os.path.exists(plexpy.DATA_DIR):
|
||||
try:
|
||||
os.makedirs(plexpy.DATA_DIR)
|
||||
except OSError:
|
||||
raise SystemExit(
|
||||
'Could not create data directory: ' + plexpy.DATA_DIR + '. Exiting....')
|
||||
|
||||
# Make sure the DATA_DIR is writeable
|
||||
if not os.access(plexpy.DATA_DIR, os.W_OK):
|
||||
raise SystemExit(
|
||||
'Cannot write to the data directory: ' + plexpy.DATA_DIR + '. Exiting...')
|
||||
|
||||
# Put the database in the DATA_DIR
|
||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, 'plexpy.db')
|
||||
|
||||
# Read config and start logging
|
||||
plexpy.initialize(config_file)
|
||||
|
||||
if plexpy.DAEMON:
|
||||
plexpy.daemonize()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if args.port:
|
||||
http_port = args.port
|
||||
logger.info('Using forced web server port: %i', http_port)
|
||||
else:
|
||||
http_port = int(plexpy.CONFIG.HTTP_PORT)
|
||||
|
||||
# Check if pyOpenSSL is installed. It is required for certificate generation
|
||||
# and for CherryPy.
|
||||
if plexpy.CONFIG.ENABLE_HTTPS:
|
||||
try:
|
||||
import OpenSSL
|
||||
except ImportError:
|
||||
logger.warn("The pyOpenSSL module is missing. Install this " \
|
||||
"module to enable HTTPS. HTTPS will be disabled.")
|
||||
plexpy.CONFIG.ENABLE_HTTPS = False
|
||||
|
||||
# Try to start the server. Will exit here is address is already in use.
|
||||
web_config = {
|
||||
'http_port': http_port,
|
||||
'http_host': plexpy.CONFIG.HTTP_HOST,
|
||||
'http_root': plexpy.CONFIG.HTTP_ROOT,
|
||||
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
|
||||
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
|
||||
'https_cert': plexpy.CONFIG.HTTPS_CERT,
|
||||
'https_key': plexpy.CONFIG.HTTPS_KEY,
|
||||
'http_username': plexpy.CONFIG.HTTP_USERNAME,
|
||||
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
|
||||
}
|
||||
webstart.initialize(web_config)
|
||||
|
||||
# Start the background threads
|
||||
plexpy.start()
|
||||
|
||||
# Open connection for websocket
|
||||
if plexpy.CONFIG.MONITORING_USE_WEBSOCKET:
|
||||
try:
|
||||
web_socket.start_thread()
|
||||
except:
|
||||
logger.warn(u"Websocket :: Unable to open connection.")
|
||||
# Fallback to polling
|
||||
plexpy.POLLING_FAILOVER = True
|
||||
plexpy.initialize_scheduler()
|
||||
|
||||
# Open webbrowser
|
||||
if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch:
|
||||
plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, http_port,
|
||||
plexpy.CONFIG.HTTP_ROOT)
|
||||
|
||||
# Wait endlessy for a signal to happen
|
||||
while True:
|
||||
if not plexpy.SIGNAL:
|
||||
try:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
plexpy.SIGNAL = 'shutdown'
|
||||
else:
|
||||
logger.info('Received signal: %s', plexpy.SIGNAL)
|
||||
|
||||
if plexpy.SIGNAL == 'shutdown':
|
||||
plexpy.shutdown()
|
||||
elif plexpy.SIGNAL == 'restart':
|
||||
plexpy.shutdown(restart=True)
|
||||
else:
|
||||
plexpy.shutdown(restart=True, update=True)
|
||||
|
||||
plexpy.SIGNAL = None
|
||||
|
||||
# Call main()
|
||||
# Call main() from Tautulli.py
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
137
README.md
@@ -1,121 +1,48 @@
|
||||
#PlexPy
|
||||
# Tautulli
|
||||
|
||||
[](https://gitter.im/drzoidberg33/plexpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://discord.gg/tQcWEUp)
|
||||
[](https://www.reddit.com/r/Tautulli/)
|
||||
[](https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server)
|
||||
|
||||
A python based web application for monitoring, analytics and notifications for Plex Media Server (www.plex.tv).
|
||||
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
|
||||
|
||||
This project is based on code from Headphones (https://github.com/rembo10/headphones) and PlexWatchWeb (https://github.com/ecleese/plexWatchWeb).
|
||||
This project is based on code from [Headphones](https://github.com/rembo10/headphones) and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb).
|
||||
|
||||
* PlexPy forum thread: https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program
|
||||
## Features
|
||||
|
||||
* 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!!
|
||||
|
||||
###Support
|
||||
-----------
|
||||
* PlexPy Wiki: https://github.com/drzoidberg33/plexpy/wiki
|
||||
## Preview
|
||||
|
||||
* [Full preview gallery available on our website](http://tautulli.com)
|
||||
|
||||
###Features
|
||||
-----------
|
||||
* Responsive web design viewable on desktop, tablet and mobile web browsers
|
||||

|
||||
|
||||
* Themed to complement Plex/Web
|
||||
## Installation and Support
|
||||
|
||||
* Easy configuration setup via html form
|
||||
* Read the [Installation Guides](https://github.com/Tautulli/Tautulli-Wiki/wiki/Installation) for instructions to install Tautulli.
|
||||
* The [Frequently Asked Questions](https://github.com/Tautulli/Tautulli-Wiki/wiki/Frequently-Asked-Questions) in the wiki can help you with common problems.
|
||||
* Support is available on [Discord](https://discord.gg/tQcWEUp), [Reddit](https://www.reddit.com/r/Tautulli), or the [Plex Forums](https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server).
|
||||
|
||||
* 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
|
||||
## Issues & Feature Requests
|
||||
|
||||
* Recently added media and how long ago it was added
|
||||
|
||||
* Global watching history with search/filtering & dynamic column sorting
|
||||
* date
|
||||
* user
|
||||
* platform
|
||||
* ip address
|
||||
* title
|
||||
* stream information details
|
||||
* start time
|
||||
* paused duration length
|
||||
* stop time
|
||||
* duration length
|
||||
* watched progress
|
||||
* show/hide columns
|
||||
* delete mode - allows deletion of specific history items
|
||||
|
||||
* Full user list with general information and comparison stats
|
||||
|
||||
* Individual user information
|
||||
* username and gravatar (if available)
|
||||
* daily, weekly, monthly, all time stats for play count and duration length
|
||||
* individual platform stats for each user
|
||||
* public ip address history with last seen date and geo tag location
|
||||
* recently watched content
|
||||
* watching history
|
||||
* synced items
|
||||
* assign users custom friendly names within PlexPy
|
||||
* assign users custom avatar URL within PlexPy
|
||||
* disable history logging per user
|
||||
* disable notifications per user
|
||||
* option to purge all history per user.
|
||||
|
||||
* Rich analytics presented using Highcharts graphing
|
||||
* user-selectable time periods of 30, 90 or 365 days
|
||||
* daily watch count and duration
|
||||
* totals by day of week and hours of the day
|
||||
* totals by top 10 platform
|
||||
* totals by top 10 users
|
||||
* detailed breakdown by transcode decision
|
||||
* source and stream resolutions
|
||||
* transcode decision counts by user and platform
|
||||
* total monthly counts
|
||||
|
||||
* Content information pages
|
||||
* movies (includes watching history)
|
||||
* tv shows (includes watching history)
|
||||
* tv seasons
|
||||
* tv episodes (includes watching history)
|
||||
|
||||
* Full sync list data on all users syncing items from your library
|
||||
|
||||
## Installation and Notes
|
||||
|
||||
* [Installation page](../../wiki/Installation) shows you how to install PlexPy.
|
||||
* [Usage guide](../../wiki/Usage-guide) introduces you to PlexPy.
|
||||
* [Troubleshooting page](../../wiki/TroubleShooting) in the wiki can help you with common problems.
|
||||
|
||||
**Issues** can be reported on the GitHub issue tracker considering these rules:
|
||||
|
||||
1. Analyze your log, you just might find the solution yourself!
|
||||
2. You read the wiki and searched existing issues, but this is not solving your problem.
|
||||
3. Post the issue with a clear title, description and the HP log and use [proper markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your text (code/log in code blocks).
|
||||
4. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||
|
||||
**Feature requests** can be reported on the GitHub issue tracker too:
|
||||
|
||||
1. Search for similar existing 'issues', feature requests can be recognized by the label 'Request'.
|
||||
2. If a similar Request exists, post a comment (+1, or add a new idea to the existing request), otherwise you can create a new one.
|
||||
|
||||
If you **comply with these rules** you can [post your request/issue](http://github.com/drzoidberg33/plexpy/issues).
|
||||
|
||||
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
||||
* Please see the [Issues Repository](https://github.com/Tautulli/Tautulli-Issues).
|
||||
|
||||
## 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.
|
||||
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.
|
267
Tautulli.py
Executable file
@@ -0,0 +1,267 @@
|
||||
#!/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 Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Tautulli is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure lib added to path, before any other imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))
|
||||
|
||||
import argparse
|
||||
import locale
|
||||
import signal
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
from plexpy import config, database, logger, webstart
|
||||
|
||||
|
||||
# Register signals, such as CTRL + C
|
||||
signal.signal(signal.SIGINT, plexpy.sig_handler)
|
||||
signal.signal(signal.SIGTERM, plexpy.sig_handler)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Tautulli application entry point. Parses arguments, setups encoding and
|
||||
initializes the application.
|
||||
"""
|
||||
|
||||
# Fixed paths to Tautulli
|
||||
if hasattr(sys, 'frozen'):
|
||||
plexpy.FULL_PATH = os.path.abspath(sys.executable)
|
||||
else:
|
||||
plexpy.FULL_PATH = os.path.abspath(__file__)
|
||||
|
||||
plexpy.PROG_DIR = os.path.dirname(plexpy.FULL_PATH)
|
||||
plexpy.ARGS = sys.argv[1:]
|
||||
|
||||
# From sickbeard
|
||||
plexpy.SYS_PLATFORM = sys.platform
|
||||
plexpy.SYS_ENCODING = None
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
plexpy.SYS_LANGUAGE, plexpy.SYS_ENCODING = locale.getdefaultlocale()
|
||||
except (locale.Error, IOError):
|
||||
pass
|
||||
|
||||
# for OSes that are poorly configured I'll just force UTF-8
|
||||
if not plexpy.SYS_ENCODING or plexpy.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
||||
plexpy.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
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')
|
||||
parser.add_argument(
|
||||
'-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
parser.add_argument(
|
||||
'-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||
parser.add_argument(
|
||||
'-p', '--port', type=int, help='Force Tautulli to run on a specified port')
|
||||
parser.add_argument(
|
||||
'--dev', action='store_true', help='Start Tautulli 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(
|
||||
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||
parser.add_argument(
|
||||
'--nofork', action='store_true', help='Start Tautulli as a service, do not fork when restarting')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
plexpy.VERBOSE = True
|
||||
if args.quiet:
|
||||
plexpy.QUIET = True
|
||||
|
||||
# Do an intial setup of the logger.
|
||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
||||
verbose=plexpy.VERBOSE)
|
||||
|
||||
if args.dev:
|
||||
plexpy.DEV = True
|
||||
logger.debug(u"Tautulli is running in the dev environment.")
|
||||
|
||||
if args.daemon:
|
||||
if sys.platform == 'win32':
|
||||
sys.stderr.write(
|
||||
"Daemonizing not supported under Windows, starting normally\n")
|
||||
else:
|
||||
plexpy.DAEMON = True
|
||||
plexpy.QUIET = True
|
||||
|
||||
if args.nofork:
|
||||
plexpy.NOFORK = True
|
||||
logger.info("Tautulli is running as a service, it will not fork when restarted.")
|
||||
|
||||
if args.pidfile:
|
||||
plexpy.PIDFILE = str(args.pidfile)
|
||||
|
||||
# If the pidfile already exists, plexpy may still be running, so
|
||||
# exit
|
||||
if os.path.exists(plexpy.PIDFILE):
|
||||
try:
|
||||
with open(plexpy.PIDFILE, 'r') as fp:
|
||||
pid = int(fp.read())
|
||||
os.kill(pid, 0)
|
||||
except IOError as e:
|
||||
raise SystemExit("Unable to read PID file: %s", e)
|
||||
except OSError:
|
||||
logger.warn("PID file '%s' already exists, but PID %d is " \
|
||||
"not running. Ignoring PID file." %
|
||||
(plexpy.PIDFILE, pid))
|
||||
else:
|
||||
# The pidfile exists and points to a live PID. plexpy may
|
||||
# still be running, so exit.
|
||||
raise SystemExit("PID file '%s' already exists. Exiting." %
|
||||
plexpy.PIDFILE)
|
||||
|
||||
# The pidfile is only useful in daemon mode, make sure we can write the
|
||||
# file properly
|
||||
if plexpy.DAEMON:
|
||||
plexpy.CREATEPID = True
|
||||
|
||||
try:
|
||||
with open(plexpy.PIDFILE, 'w') as fp:
|
||||
fp.write("pid\n")
|
||||
except IOError as e:
|
||||
raise SystemExit("Unable to write PID file: %s", e)
|
||||
else:
|
||||
logger.warn("Not running in daemon mode. PID file creation " \
|
||||
"disabled.")
|
||||
|
||||
# Determine which data directory and config file to use
|
||||
if args.datadir:
|
||||
plexpy.DATA_DIR = args.datadir
|
||||
else:
|
||||
plexpy.DATA_DIR = plexpy.PROG_DIR
|
||||
|
||||
if args.config:
|
||||
config_file = args.config
|
||||
else:
|
||||
config_file = os.path.join(plexpy.DATA_DIR, config.FILENAME)
|
||||
|
||||
# Try to create the DATA_DIR if it doesn't exist
|
||||
if not os.path.exists(plexpy.DATA_DIR):
|
||||
try:
|
||||
os.makedirs(plexpy.DATA_DIR)
|
||||
except OSError:
|
||||
raise SystemExit(
|
||||
'Could not create data directory: ' + plexpy.DATA_DIR + '. Exiting....')
|
||||
|
||||
# Make sure the DATA_DIR is writeable
|
||||
if not os.access(plexpy.DATA_DIR, os.W_OK):
|
||||
raise SystemExit(
|
||||
'Cannot write to the data directory: ' + plexpy.DATA_DIR + '. Exiting...')
|
||||
|
||||
# Put the database in the DATA_DIR
|
||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, database.FILENAME)
|
||||
|
||||
# Move 'plexpy.db' to 'tautulli.db'
|
||||
if os.path.isfile(os.path.join(plexpy.DATA_DIR, 'plexpy.db')) and \
|
||||
not os.path.isfile(os.path.join(plexpy.DATA_DIR, plexpy.DB_FILE)):
|
||||
try:
|
||||
os.rename(os.path.join(plexpy.DATA_DIR, 'plexpy.db'), plexpy.DB_FILE)
|
||||
except OSError as e:
|
||||
raise SystemExit("Unable to rename plexpy.db to tautulli.db: %s", e)
|
||||
|
||||
if plexpy.DAEMON:
|
||||
plexpy.daemonize()
|
||||
|
||||
# Read config and start logging
|
||||
plexpy.initialize(config_file)
|
||||
|
||||
# Start the background threads
|
||||
plexpy.start()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if args.port:
|
||||
http_port = args.port
|
||||
logger.info('Using forced web server port: %i', http_port)
|
||||
else:
|
||||
http_port = int(plexpy.CONFIG.HTTP_PORT)
|
||||
|
||||
# Check if pyOpenSSL is installed. It is required for certificate generation
|
||||
# and for CherryPy.
|
||||
if plexpy.CONFIG.ENABLE_HTTPS:
|
||||
try:
|
||||
import OpenSSL
|
||||
except ImportError:
|
||||
logger.warn("The pyOpenSSL module is missing. Install this " \
|
||||
"module to enable HTTPS. HTTPS will be disabled.")
|
||||
plexpy.CONFIG.ENABLE_HTTPS = False
|
||||
|
||||
# Try to start the server. Will exit here is address is already in use.
|
||||
web_config = {
|
||||
'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,
|
||||
'https_cert_chain': plexpy.CONFIG.HTTPS_CERT_CHAIN,
|
||||
'https_key': plexpy.CONFIG.HTTPS_KEY,
|
||||
'http_username': plexpy.CONFIG.HTTP_USERNAME,
|
||||
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
|
||||
'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
|
||||
}
|
||||
webstart.initialize(web_config)
|
||||
|
||||
# Open webbrowser
|
||||
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)
|
||||
|
||||
# Wait endlessy for a signal to happen
|
||||
while True:
|
||||
if not plexpy.SIGNAL:
|
||||
try:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
plexpy.SIGNAL = 'shutdown'
|
||||
else:
|
||||
logger.info('Received signal: %s', plexpy.SIGNAL)
|
||||
|
||||
if plexpy.SIGNAL == 'shutdown':
|
||||
plexpy.shutdown()
|
||||
elif plexpy.SIGNAL == 'restart':
|
||||
plexpy.shutdown(restart=True)
|
||||
elif plexpy.SIGNAL == 'checkout':
|
||||
plexpy.shutdown(restart=True, checkout=True)
|
||||
else:
|
||||
plexpy.shutdown(restart=True, update=True)
|
||||
|
||||
plexpy.SIGNAL = None
|
||||
|
||||
# Call main()
|
||||
if __name__ == "__main__":
|
||||
main()
|
@@ -2,11 +2,18 @@
|
||||
<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">Import PlexWatch Database</h4>
|
||||
<h4 class="modal-title">Import ${app} Database</h4>
|
||||
</div>
|
||||
<div class="modal-body" id="modal-text">
|
||||
<p class="help-block">
|
||||
Please ensure your PlexWatch database is at version 0.3.2 or higher.
|
||||
<%
|
||||
v = ''
|
||||
if app == 'PlexWatch':
|
||||
v = '0.3.2'
|
||||
elif app == 'Plexivity':
|
||||
v = '0.9.8'
|
||||
%>
|
||||
<strong>Please ensure your ${app} database is at version ${v} or higher.</strong>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="db_location">Database Location</label>
|
||||
@@ -15,7 +22,7 @@
|
||||
<input type="text" class="form-control" id="db_location" name="db_location" value="" required>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Enter the path and file name for the PlexWatch database you wish to import.</p>
|
||||
<p class="help-block">Enter the path and file name for the ${app} database you wish to import.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="table_name">Table Name</label>
|
||||
@@ -23,7 +30,7 @@
|
||||
<div class="col-xs-4">
|
||||
<select id="table_name" class="form-control" name="table_name">
|
||||
<option value="processed">processed</option>
|
||||
<option value="processed">grouped</option>
|
||||
<option value="grouped">grouped</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,7 +48,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div>
|
||||
<span id="status-message"></span>
|
||||
<span id="status-message" style="padding-right: 25px;"></span>
|
||||
<input type="button" id="import_db" class="btn btn-bright" value="Import">
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,8 +61,13 @@
|
||||
var table_name = $("#table_name").val();
|
||||
var import_ignore_interval = $("#import_ignore_interval").val();
|
||||
$.ajax({
|
||||
url: 'get_plexwatch_export_data',
|
||||
data: {database_path: database_path, table_name:table_name, import_ignore_interval:import_ignore_interval},
|
||||
url: 'import_database',
|
||||
data: {
|
||||
app: "${app}",
|
||||
database_path: database_path,
|
||||
table_name: table_name,
|
||||
import_ignore_interval: import_ignore_interval
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
@@ -1,46 +1,68 @@
|
||||
<%
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
from plexpy.helpers import anon_url
|
||||
from plexpy.notifiers import BROWSER_NOTIFIERS
|
||||
%>
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>PlexPy - ${title} | ${server_name}</title>
|
||||
<title>Tautulli - ${title} | ${server_name}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<link href="interfaces/default/css/bootstrap3/bootstrap.css" rel="stylesheet">
|
||||
<link href="interfaces/default/css/plexpy.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
|
||||
<link href="interfaces/default/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="${http_root}css/bootstrap3/bootstrap.css" rel="stylesheet">
|
||||
<link href="${http_root}css/pnotify.custom.min.css" rel="stylesheet" />
|
||||
<link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet">
|
||||
<link href="${http_root}css/opensans.min.css" rel="stylesheet">
|
||||
<link href="${http_root}css/font-awesome.min.css" rel="stylesheet">
|
||||
${next.headIncludes()}
|
||||
|
||||
<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">
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.5">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.5">
|
||||
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.5">
|
||||
|
||||
<!-- ICONS -->
|
||||
<!-- Android -->
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5">
|
||||
<meta name="theme-color" content="#282a2d">
|
||||
<!-- Apple -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.5">
|
||||
<link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.5" color="#282a2d">
|
||||
<meta name="apple-mobile-web-app-title" content="Tautulli">
|
||||
<!-- Microsoft -->
|
||||
<meta name="application-name" content="Tautulli">
|
||||
<meta name="msapplication-config" content="${http_root}images/favicon/browserconfig.xml?v=2.0.5">
|
||||
</head>
|
||||
|
||||
<body class="content">
|
||||
<div class="container">
|
||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
% 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 are running an unknown version of Tautulli.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
</div>
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.COMMITS_BEHIND > 0 and plexpy.common.BRANCH in ('master', 'beta') and plexpy.common.RELEASE != plexpy.LATEST_RELEASE:
|
||||
<div id="updatebar" style="display: none;">
|
||||
A <a
|
||||
href="https://github.com/${plexpy.CONFIG.GIT_USER}/plexpy/compare/${plexpy.CURRENT_VERSION}...${plexpy.LATEST_VERSION}" target="_blank">
|
||||
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/%s/releases/tag/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.LATEST_RELEASE))}" target="_blank">
|
||||
new release (${plexpy.LATEST_RELEASE})</a> of Tautulli is available!<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
</div>
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.COMMITS_BEHIND > 0 and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.INSTALL_TYPE != 'win':
|
||||
<div id="updatebar" style="display: none;">
|
||||
A <a href="${anon_url('https://github.com/%s/%s/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CURRENT_VERSION, plexpy.LATEST_VERSION))}" target="_blank">
|
||||
newer version</a> of Tautulli is available!<br />
|
||||
You are ${plexpy.COMMITS_BEHIND} commit${'s' if plexpy.COMMITS_BEHIND > 1 else ''} behind.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
</div>
|
||||
% else:
|
||||
<div id="updatebar" style="display: none;"></div>
|
||||
% endif
|
||||
% endif
|
||||
<nav class="navbar navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
@@ -51,17 +73,17 @@ from plexpy import version
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="home">
|
||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40">
|
||||
<a class="navbar-brand" href="home" title="Tautulli">
|
||||
<img src="${http_root}images/logo-tautulli-45.png" height="45" alt="PlexPy">
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<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..."/>
|
||||
<input type="text" class="form-control" name="query" id="query" aria-label="Search" placeholder="Search Plex library..."/>
|
||||
</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>
|
||||
@@ -69,41 +91,67 @@ from plexpy import version
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
% if title=="Home":
|
||||
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></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>
|
||||
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||
% endif
|
||||
% if title=="Users" or title=="User":
|
||||
<li class="active"><a href="users">Users</a></li>
|
||||
% if title == "Libraries" or title == "Library" or title == "Info":
|
||||
<li class="active"><a href="libraries">Libraries</a></li>
|
||||
% else:
|
||||
<li><a href="users">Users</a></li>
|
||||
<li><a href="libraries">Libraries</a></li>
|
||||
% endif
|
||||
% if title=="History":
|
||||
% if title == "Users" or title == "User":
|
||||
<li class="active"><a href="users">Users</a></li>
|
||||
% else:
|
||||
<li><a href="users">Users</a></li>
|
||||
% endif
|
||||
% if title == "History":
|
||||
<li class="active"><a href="history">History</a></li>
|
||||
% else:
|
||||
<li><a href="history">History</a></li>
|
||||
% endif
|
||||
% if title=="Graphs":
|
||||
<li class="active"><a href="graphs">Graphs</a></li>
|
||||
% if title == "Graphs":
|
||||
<li class="active"><a href="graphs">Graphs</a></li>
|
||||
% else:
|
||||
<li><a href="graphs">Graphs</a></li>
|
||||
<li><a href="graphs">Graphs</a></li>
|
||||
% endif
|
||||
% if title=="Synced Items":
|
||||
<li class="active"><a href="sync">Synced Items</a></li>
|
||||
% if title == "Synced Items":
|
||||
<li class="active"><a href="sync">Synced Items</a></li>
|
||||
% else:
|
||||
<li><a href="sync">Synced Items</a></li>
|
||||
<li><a href="sync">Synced Items</a></li>
|
||||
% endif
|
||||
% if title=="Log":
|
||||
<li class="active"><a href="logs">Logs</a></li>
|
||||
% if title == "Settings":
|
||||
<li class="dropdown active">
|
||||
% else:
|
||||
<li><a href="logs">Logs</a></li>
|
||||
% endif
|
||||
% if title=="Settings":
|
||||
<li class="active"><a href="settings">Settings</a></li>
|
||||
% else:
|
||||
<li><a href="settings">Settings</a></li>
|
||||
<li class="dropdown">
|
||||
% endif
|
||||
<% href = 'settings' if _session['user_group'] == 'admin' else '#' %>
|
||||
<a href="#" class="dropdown-toggle" aria-haspopup="true" data-toggle="dropdown" data-hover="dropdown" data-href="${href}"><i class="fa fa-lg fa-cogs"></i> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" id="settings-dropdown-menu">
|
||||
% if _session['user_group'] == 'admin':
|
||||
<li><a href="settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="logs"><i class="fa fa-fw fa-list-alt"></i> View Logs</a></li>
|
||||
<li><a href="${anon_url('https://github.com/%s/%s-Wiki/wiki/Frequently-Asked-Questions' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li>
|
||||
<li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="#" data-target="#donate-modal" data-toggle="modal"><i class="fa fa-fw fa-heart"></i> Donate</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
% if plexpy.CONFIG.CHECK_GITHUB:
|
||||
<li><a href="#" id="nav-update"><i class="fa fa-fw fa-arrow-circle-up"></i> Check for Updates</a></li>
|
||||
% endif
|
||||
<li><a href="#" id="nav-restart"><i class="fa fa-fw fa-refresh"></i> Restart</a></li>
|
||||
<li><a href="#" id="nav-shutdown"><i class="fa fa-fw fa-power-off"></i> Shutdown</a></li>
|
||||
% else:
|
||||
<li><a href="#" data-target="#admin-login-modal" data-toggle="modal"><i class="fa fa-fw fa-lock"></i> Admin Login</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
% endif
|
||||
% if _session['exp']:
|
||||
<li><a href="${http_root}auth/logout"><i class="fa fa-fw fa-sign-out"></i> Sign Out</a></li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,50 +159,308 @@ from plexpy import version
|
||||
</div>
|
||||
|
||||
${next.headerIncludes()}
|
||||
|
||||
<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>
|
||||
<script src="interfaces/default/js/script.js"></script>
|
||||
${next.modalIncludes()}
|
||||
|
||||
% if _session['user_group'] != 'admin':
|
||||
<div id="admin-login-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="admin-login-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form id="login-form">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||
<h4 class="modal-title">Admin Login</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="col-md-6" style="margin: auto;">
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label">
|
||||
Username
|
||||
</label>
|
||||
<input type="text" id="username" name="username" class="form-control" autocorrect="off" autocapitalize="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">
|
||||
Password
|
||||
</label>
|
||||
<input type="password" id="password" name="password" class="form-control">
|
||||
</div>
|
||||
<div class="form-footer">
|
||||
<div class="remember-group">
|
||||
<label class="control-label">
|
||||
<input type="checkbox" id="remember_me" name="remember_me" title="for 30 days" value="1" checked="checked" /> Remember me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="incorrect-login" style="padding-right: 25px; display: none;">Incorrect username or password.</span>
|
||||
<button id="sign-in" type="submit" class="btn btn-bright login-button"><i class="fa fa-sign-in"></i> Sign In</button>
|
||||
</div>
|
||||
<input type="hidden" id="admin_login" name="admin_login" value="1" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div id="donate-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="crypto-donate-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||
<h4 class="modal-title">Tautulli Donation</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="text-align: center;">
|
||||
<h4>
|
||||
<strong>Thank you for supporting Tautulli!</strong>
|
||||
</h4>
|
||||
<p>
|
||||
Please select a donation method.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;">
|
||||
<li class="active"><a href="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li>
|
||||
<li><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
|
||||
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="bitcoin" data-name="Bitcoin" data-address="3FdfJAyNWU15Sf11U9FTgPHuP1hPz32eEN">Bitcoin</a></li>
|
||||
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="bitcoincash" data-name="Bitcoin Cash" data-address="1H2atabxAQGaFAWYQEiLkXKSnK9CZZvt2n">Bitcoin Cash</a></li>
|
||||
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="ethereum" data-name="Ethereum" data-address="0x77ae4c2b8de1a1ccfa93553db39971da58c873d3">Ethereum</a></li>
|
||||
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="litecoin" data-name="Litecoin" data-address="LWpPmUqQYHBhMV83XSCsHzPmKLhJt6r57J">Litecoin</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="patreon-donation" style="text-align: center">
|
||||
<p>
|
||||
Click the button below to continue to Patreon.
|
||||
</p>
|
||||
<a href="${anon_url('https://www.patreon.com/bePatron?u=10078609')}" target="_blank">
|
||||
<img src="images/become_a_patron_button.png" alt="Become a Patron" height="40">
|
||||
</a>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center">
|
||||
<p>
|
||||
Click the button below to continue to PayPal.
|
||||
</p>
|
||||
<a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6XPPKTDSX9QFL&lc=US&item_name=Tautulli¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted')}" target="_blank">
|
||||
<img src="images/gold-rect-paypal-34px.png" alt="PayPal">
|
||||
</a>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="crypto-donation">
|
||||
<label>QR Code</label>
|
||||
<pre id="crypto_qr_code" style="text-align: center"></pre>
|
||||
<label><span id="crypto_type_label"></span> Address</label>
|
||||
<pre id="crypto_address" style="text-align: center"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Close">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
<div id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||
<h4 class="modal-title">Confirm</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="confirm-message" style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-button">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="${http_root}js/jquery-2.1.4.min.js"></script>
|
||||
<script src="${http_root}js/bootstrap.min.js"></script>
|
||||
<script src="${http_root}js/bootstrap-hover-dropdown.min.js"></script>
|
||||
<script src="${http_root}js/pnotify.custom.min.js"></script>
|
||||
<script src="${http_root}js/script.js${cache_param}"></script>
|
||||
<script src="${http_root}js/jquery.qrcode.min.js"></script>
|
||||
% if _session['user_group'] == 'admin' and BROWSER_NOTIFIERS:
|
||||
<script src="${http_root}js/ajaxNotifications.js"></script>
|
||||
% endif
|
||||
<script>
|
||||
$('#updateDismiss').click(function() {
|
||||
$('#updatebar').slideUp('slow');
|
||||
% if _session['user_group'] == 'admin':
|
||||
$('body').on('click', '#updateDismiss', function() {
|
||||
$('#updatebar').fadeOut();
|
||||
// Set cookie to remember dismiss decision for 1 hour.
|
||||
setCookie('updateDismiss', 'true', 1/24);
|
||||
});
|
||||
|
||||
if (!getCookie('updateDismiss')) {
|
||||
$('#updatebar').show();
|
||||
if ($('#updatebar').html().length > 0) {
|
||||
$('#updatebar').show();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
|
||||
function checkUpdate(_callback) {
|
||||
// Allow the update bar to show again if previously dismissed.
|
||||
setCookie('updateDismiss', 'true', 0);
|
||||
$.ajax({
|
||||
url: 'update_check',
|
||||
complete: function (xhr, status) {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = '';
|
||||
if (result.update === null) {
|
||||
msg = 'You are running an unknown version of Tautulli.<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === true && result.release === true) {
|
||||
msg = 'A <a href="' + result.release_url + '" target="_blank">new release (' + result.latest_release + ')</a> of Tautulli is available!<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === true && result.release === false) {
|
||||
msg = 'A <a href="' + result.compare_url + '" target="_blank">newer version</a> of Tautulli is available!<br />' +
|
||||
'You are '+ result.commits_behind + ' commit' + (result.commits_behind > 1 ? 's' : '') + ' behind.<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === false) {
|
||||
showMsg('<i class="fa fa-check"></i> ' + result.message, false, true, 2000);
|
||||
}
|
||||
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("#nav-shutdown").click(function() {
|
||||
$("#confirm-message").text("Are you sure you want to shutdown Tautulli?");
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-button', function () {
|
||||
window.location.href = "shutdown";
|
||||
});
|
||||
});
|
||||
|
||||
$("#nav-restart").click(function() {
|
||||
$("#confirm-message").text("Are you sure you want to restart Tautulli?");
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-button', function () {
|
||||
window.location.href = "restart";
|
||||
});
|
||||
});
|
||||
|
||||
$('#nav-update').click(function () {
|
||||
$(this).html('<i class="fa fa-fw fa-spin fa-refresh"></i> Checking');
|
||||
checkUpdate(function () { $('#nav-update').html('<i class="fa fa-fw fa-arrow-circle-up"></i> Check for Updates'); });
|
||||
});
|
||||
|
||||
$('#donation_type a.crypto-donation').on('shown.bs.tab', function () {
|
||||
var crypto_coin = $(this).data('coin');
|
||||
var crypto_name = $(this).data('name');
|
||||
var crypto_address = $(this).data('address')
|
||||
$('#crypto_qr_code').empty().qrcode({
|
||||
text: crypto_coin + ":" + crypto_address
|
||||
});
|
||||
$('#crypto_type_label').html(crypto_name);
|
||||
$('#crypto_address').html(crypto_address);
|
||||
});
|
||||
% endif
|
||||
|
||||
$('.dropdown-toggle').click(function (e) {
|
||||
if (!(('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))) {
|
||||
window.location.href = $(this).data('href');
|
||||
}
|
||||
});
|
||||
|
||||
$('#search_form').submit(function (e) {
|
||||
if ($('#query').hasClass('active') && $('#query').val().trim() != '') {
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: 'search',
|
||||
data: { 'query': $('#query').val() }
|
||||
data: { query: $('#query').val() }
|
||||
})
|
||||
} else {
|
||||
e.preventDefault();
|
||||
$('#search_button').removeClass('btn-inactive');
|
||||
$('#query').clearQueue().val('').animate({ right: '0', width: '250px' }).addClass('active').focus();
|
||||
$('#query').clearQueue().val('').animate({ right: '0', width: '200px' }).addClass('active').focus();
|
||||
}
|
||||
})
|
||||
$('#query').on('blur', function (e) {
|
||||
if ($(this).val().trim() == '') {
|
||||
$(this).delay(200).animate({ right: '-250px', width: '0' }, function () {
|
||||
$(this).delay(200).animate({ right: '-200px', width: '0' }, function () {
|
||||
$('#search_button').addClass('btn-inactive');
|
||||
}).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
// Work around for iOS web app links opening in Safari
|
||||
if (("standalone" in window.navigator) && window.navigator.standalone) {
|
||||
// For iOS Apps
|
||||
$('a').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
var new_location = $(this).attr('href');
|
||||
if (new_location != undefined && new_location.substr(0, 1) != '#' && $(this).attr('data-method') == undefined) {
|
||||
window.location = new_location;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Allow stacked bootstrap modals
|
||||
$(document).on('show.bs.modal', '.modal', function (event) {
|
||||
var zIndex = 1040 + (10 * $('.modal:visible').length);
|
||||
$(this).css('z-index', zIndex);
|
||||
setTimeout(function() {
|
||||
$('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
|
||||
}, 0);
|
||||
});
|
||||
$(document).on('hidden.bs.modal', '.modal', function () {
|
||||
$('.modal:visible').length && $(document.body).addClass('modal-open');
|
||||
});
|
||||
});
|
||||
|
||||
% if _session['user_group'] != 'admin':
|
||||
$('#admin-login-modal').on('shown.bs.modal', function () {
|
||||
$('#admin-login-modal #username').focus()
|
||||
})
|
||||
|
||||
$('#login-form').submit(function(event) {
|
||||
event.preventDefault();
|
||||
$('#sign-in').prop('disabled', true).html('<i class="fa fa-refresh fa-spin"></i> Sign In');
|
||||
$.ajax({
|
||||
url: '${http_root}auth/signin',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
dataType: 'json',
|
||||
statusCode: {
|
||||
200: function() {
|
||||
window.location = "${http_root}";
|
||||
},
|
||||
401: function() {
|
||||
$('#incorrect-login').show();
|
||||
$('#username').focus();
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$('#sign-in').prop('disabled', false).html('<i class="fa fa-sign-in"></i> Sign In');
|
||||
}
|
||||
});
|
||||
});
|
||||
% endif
|
||||
</script>
|
||||
${next.javascriptIncludes()}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<%def name="modalIncludes()"></%def>
|
||||
<%def name="javascriptIncludes()"></%def>
|
||||
<%def name="headIncludes()"></%def>
|
||||
<%def name="headerIncludes()"></%def>
|
||||
|
135
data/interfaces/default/configuration_table.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<%doc>
|
||||
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||
|
||||
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||
|
||||
Filename: configuration_table.html
|
||||
Version: 0.1
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
<%!
|
||||
import os
|
||||
import sys
|
||||
import plexpy
|
||||
from plexpy import common, logger
|
||||
from plexpy.helpers import anon_url
|
||||
%>
|
||||
|
||||
<table class="config-info-table small-muted">
|
||||
<tbody>
|
||||
% if plexpy.CURRENT_VERSION:
|
||||
<tr>
|
||||
<td>Git Branch:</td>
|
||||
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/%s/tree/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CONFIG.GIT_BRANCH}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Git Commit Hash:</td>
|
||||
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/%s/commit/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CURRENT_VERSION))}">${plexpy.CURRENT_VERSION}</a></td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
<td>Configuration File:</td>
|
||||
<td><a class="no-highlight" href="download_config" data-toggle="tooltip" data-placement="right" title="Download Configuration">${plexpy.CONFIG_FILE}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Database File:</td>
|
||||
<td><a class="no-highlight" href="download_database" data-toggle="tooltip" data-placement="right" title="Download Database">${plexpy.DB_FILE}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log File:</td>
|
||||
<td><a class="no-highlight" href="download_log" data-toggle="tooltip" data-placement="right" title="Download Log">${os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Backup Directory:</td>
|
||||
<td>${plexpy.CONFIG.BACKUP_DIR}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cache Directory:</td>
|
||||
<td>${plexpy.CONFIG.CACHE_DIR}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GeoLite2 Database:</td>
|
||||
% if plexpy.CONFIG.GEOIP_DB:
|
||||
<td>${plexpy.CONFIG.GEOIP_DB} | <a class="no-highlight" href="#" id="reinstall_geoip_db">Reinstall / Update</a> | <a class="no-highlight" href="#" id="uninstall_geoip_db">Uninstall</a></td>
|
||||
% else:
|
||||
<td><a class="no-highlight" href="#" id="install_geoip_db">Click here to install the GeoLite2 database.</a></td>
|
||||
% endif
|
||||
</tr>
|
||||
% if plexpy.ARGS:
|
||||
<tr>
|
||||
<td>Arguments:</td>
|
||||
<td>${plexpy.ARGS}</td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
<td>Platform:</td>
|
||||
<td>${common.PLATFORM} ${common.PLATFORM_VERSION}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Python Version:</td>
|
||||
<td>${sys.version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="top-line">Resources:</td>
|
||||
<td class="top-line">
|
||||
<a class="no-highlight" href="${anon_url('http://tautulli.com')}" target="_blank">Tautulli Website</a> |
|
||||
<a class="no-highlight" href="${anon_url('https://github.com/%s/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">GitHub Source</a> |
|
||||
<a class="no-highlight guidelines-modal-link" href="${anon_url('https://github.com/%s/%s-Issues' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" data-id="issue">GitHub Issues</a> |
|
||||
<a class="no-highlight" href="${anon_url('https://github.com/%s/%s-Wiki' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">GitHub Wiki</a> |
|
||||
<a class="no-highlight guidelines-modal-link" href="${anon_url('http://feathub.com/%s/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" data-id="feature request">FeatHub Feature Requests</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Support:</td>
|
||||
<td>
|
||||
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/tQcWEUp')}" target="_blank">Tautulli Discord Server</a> |
|
||||
<a class="no-highlight support-modal-link" href="${anon_url('https://www.reddit.com/r/Tautulli')}" target="_blank">Tautulli Subreddit</a> |
|
||||
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server')}" target="_blank">Plex Forums</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$("#install_geoip_db, #reinstall_geoip_db").click(function () {
|
||||
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
|
||||
'The database is used to lookup IP address geolocation info.<br />' +
|
||||
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
|
||||
'and requires <strong>100MB</strong> of free space to install in your Tautulli directory.<br />'
|
||||
var url = 'install_geoip_db';
|
||||
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', getConfigurationTable);
|
||||
});
|
||||
|
||||
$("#uninstall_geoip_db").click(function () {
|
||||
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
|
||||
'You will not be able to lookup IP address geolocation info.';
|
||||
var url = 'uninstall_geoip_db';
|
||||
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', getConfigurationTable);
|
||||
});
|
||||
|
||||
$('.guidelines-modal-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$('#guidelines-type').text($(this).data('id'))
|
||||
$('#guidelines-modal').modal();
|
||||
$('#guidelines-continue').attr('href', $(this).attr('href')).on('click', function () {
|
||||
$('#guidelines-modal').modal('hide');
|
||||
});
|
||||
});
|
||||
$('.support-modal-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$('#support-modal').modal();
|
||||
$('#support-continue').attr('href', $(this).attr('href')).on('click', function () {
|
||||
$('#support-modal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
$('body').tooltip({
|
||||
selector: '[data-toggle="tooltip"]',
|
||||
container: 'body'
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
904
data/interfaces/default/css/jquery.scrollbar.css
Normal file
@@ -0,0 +1,904 @@
|
||||
/*************** SCROLLBAR BASE CSS ***************/
|
||||
.scroll-wrapper {
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
.scroll-wrapper > .scroll-content {
|
||||
border: none !important;
|
||||
box-sizing: content-box !important;
|
||||
height: auto;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
max-height: none;
|
||||
max-width: none !important;
|
||||
overflow: scroll !important;
|
||||
padding: 0;
|
||||
position: relative !important;
|
||||
top: 0;
|
||||
width: auto !important;
|
||||
}
|
||||
.scroll-wrapper > .scroll-content::-webkit-scrollbar {
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
.scroll-wrapper.scroll--rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.scroll-element {
|
||||
box-sizing: content-box;
|
||||
display: none;
|
||||
}
|
||||
.scroll-element div {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.scroll-element .scroll-bar,
|
||||
.scroll-element .scroll-arrow {
|
||||
cursor: default;
|
||||
}
|
||||
.scroll-element.scroll-x.scroll-scrollx_visible, .scroll-element.scroll-y.scroll-scrolly_visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.scroll-textarea {
|
||||
border: 1px solid #cccccc;
|
||||
border-top-color: #999999;
|
||||
}
|
||||
.scroll-textarea > .scroll-content {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.scroll-textarea > .scroll-content > textarea {
|
||||
border: none !important;
|
||||
box-sizing: border-box;
|
||||
height: 100% !important;
|
||||
margin: 0;
|
||||
max-height: none !important;
|
||||
max-width: none !important;
|
||||
overflow: scroll !important;
|
||||
outline: none;
|
||||
padding: 2px;
|
||||
position: relative !important;
|
||||
top: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
.scroll-textarea > .scroll-content > textarea::-webkit-scrollbar {
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
/*************** SIMPLE INNER SCROLLBAR ***************/
|
||||
.scrollbar-inner > .scroll-element,
|
||||
.scrollbar-inner > .scroll-element div {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-x {
|
||||
bottom: 2px;
|
||||
height: 8px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
right: 2px;
|
||||
top: 0;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element .scroll-element_outer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element .scroll-element_outer,
|
||||
.scrollbar-inner > .scroll-element .scroll-element_track,
|
||||
.scrollbar-inner > .scroll-element .scroll-bar {
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element .scroll-element_track,
|
||||
.scrollbar-inner > .scroll-element .scroll-bar {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
|
||||
filter: alpha(opacity=40);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element .scroll-element_track {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element .scroll-bar {
|
||||
background-color: #c2c2c2;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element:hover .scroll-bar {
|
||||
background-color: #919191;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-draggable .scroll-bar {
|
||||
background-color: #919191;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-inner > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.scrollbar-inner > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
/*************** SIMPLE OUTER SCROLLBAR ***************/
|
||||
.scrollbar-outer > .scroll-element,
|
||||
.scrollbar-outer > .scroll-element div {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-x {
|
||||
bottom: 0;
|
||||
height: 12px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-x .scroll-element_outer {
|
||||
height: 8px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-y .scroll-element_outer {
|
||||
left: 2px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element .scroll-element_outer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element .scroll-element_track {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element .scroll-element_outer,
|
||||
.scrollbar-outer > .scroll-element .scroll-element_track,
|
||||
.scrollbar-outer > .scroll-element .scroll-bar {
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element .scroll-bar {
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element .scroll-bar:hover {
|
||||
background-color: #c2c2c2;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-draggable .scroll-bar {
|
||||
background-color: #919191;
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-outer > .scroll-content.scroll-scrolly_visible {
|
||||
left: -12px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-content.scroll-scrollx_visible {
|
||||
top: -12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-x .scroll-bar {
|
||||
min-width: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-y .scroll-bar {
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-outer > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
|
||||
left: -14px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
|
||||
top: -14px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -14px;
|
||||
}
|
||||
|
||||
.scrollbar-outer > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -14px;
|
||||
}
|
||||
|
||||
/*************** SCROLLBAR MAC OS X ***************/
|
||||
.scrollbar-macosx > .scroll-element,
|
||||
.scrollbar-macosx > .scroll-element div {
|
||||
background: none;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element .scroll-element_track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element .scroll-bar {
|
||||
background-color: #6C6E71;
|
||||
display: block;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
-webkit-border-radius: 7px;
|
||||
-moz-border-radius: 7px;
|
||||
border-radius: 7px;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
-o-transition: opacity 0.2s linear;
|
||||
-ms-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.scrollbar-macosx:hover > .scroll-element .scroll-bar,
|
||||
.scrollbar-macosx > .scroll-element.scroll-draggable .scroll-bar {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
|
||||
filter: alpha(opacity=70);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-x {
|
||||
bottom: 0px;
|
||||
height: 0px;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
overflow: visible;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
right: 0px;
|
||||
top: 0;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-macosx > .scroll-element.scroll-x .scroll-bar {
|
||||
height: 7px;
|
||||
min-width: 10px;
|
||||
top: -9px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-y .scroll-bar {
|
||||
left: -9px;
|
||||
min-height: 10px;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-x .scroll-element_outer {
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-x .scroll-element_size {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-y .scroll-element_outer {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-y .scroll-element_size {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-macosx > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -11px;
|
||||
}
|
||||
|
||||
.scrollbar-macosx > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
/*************** SCROLLBAR LIGHT ***************/
|
||||
.scrollbar-light > .scroll-element,
|
||||
.scrollbar-light > .scroll-element div {
|
||||
border: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element .scroll-element_outer {
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element .scroll-element_size {
|
||||
background: #dbdbdb;
|
||||
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2RiZGJkYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlOGU4ZTgiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+");
|
||||
background: -moz-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
|
||||
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #dbdbdb), color-stop(100%, #e8e8e8));
|
||||
background: -webkit-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
|
||||
background: -o-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
|
||||
background: -ms-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
|
||||
background: linear-gradient(to right, #dbdbdb 0%, #e8e8e8 100%);
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-x {
|
||||
bottom: 0;
|
||||
height: 17px;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element .scroll-bar {
|
||||
background: #fefefe;
|
||||
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZlZmVmZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmNWY1ZjUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+");
|
||||
background: -moz-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
|
||||
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #fefefe), color-stop(100%, #f5f5f5));
|
||||
background: -webkit-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
|
||||
background: -o-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
|
||||
background: -ms-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
|
||||
background: linear-gradient(to right, #fefefe 0%, #f5f5f5 100%);
|
||||
border: 1px solid #dbdbdb;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-light > .scroll-content.scroll-scrolly_visible {
|
||||
left: -17px;
|
||||
margin-left: 17px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-content.scroll-scrollx_visible {
|
||||
top: -17px;
|
||||
margin-top: 17px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-x .scroll-bar {
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y .scroll-bar {
|
||||
left: 0px;
|
||||
min-height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-x .scroll-element_outer {
|
||||
height: 12px;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-x .scroll-element_size {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y .scroll-element_outer {
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y .scroll-element_size {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-light > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
|
||||
left: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-light > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
/*************** SCROLLBAR RAIL ***************/
|
||||
.scrollbar-rail > .scroll-element,
|
||||
.scrollbar-rail > .scroll-element div {
|
||||
border: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element .scroll-element_size {
|
||||
background-color: #999;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element .scroll-element_outer:hover .scroll-element_size {
|
||||
background-color: #666;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-x {
|
||||
bottom: 0;
|
||||
height: 12px;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
padding: 3px 0 2px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
padding: 0 2px 0 3px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element .scroll-bar {
|
||||
background-color: #d0b9a0;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element .scroll-element_outer:hover .scroll-bar {
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-rail > .scroll-content.scroll-scrolly_visible {
|
||||
left: -17px;
|
||||
margin-left: 17px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-content.scroll-scrollx_visible {
|
||||
margin-top: 17px;
|
||||
top: -17px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-x .scroll-bar {
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y .scroll-bar {
|
||||
left: 1px;
|
||||
min-height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-x .scroll-element_outer {
|
||||
height: 15px;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-x .scroll-element_size {
|
||||
height: 2px;
|
||||
left: -10px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y .scroll-element_outer {
|
||||
top: 5px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y .scroll-element_size {
|
||||
left: 5px;
|
||||
top: -10px;
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-rail > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -25px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -25px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
|
||||
left: -25px;
|
||||
}
|
||||
|
||||
.scrollbar-rail > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
|
||||
top: -25px;
|
||||
}
|
||||
|
||||
/*************** SCROLLBAR DYNAMIC ***************/
|
||||
.scrollbar-dynamic > .scroll-element,
|
||||
.scrollbar-dynamic > .scroll-element div {
|
||||
background: none;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x {
|
||||
bottom: 2px;
|
||||
height: 7px;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
right: 2px;
|
||||
top: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element .scroll-element_outer {
|
||||
opacity: 0.3;
|
||||
-webkit-border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element .scroll-element_size {
|
||||
background-color: #cccccc;
|
||||
opacity: 0;
|
||||
-webkit-border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
border-radius: 12px;
|
||||
-webkit-transition: opacity 0.2s;
|
||||
-moz-transition: opacity 0.2s;
|
||||
-o-transition: opacity 0.2s;
|
||||
-ms-transition: opacity 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element .scroll-bar {
|
||||
background-color: #6c6e71;
|
||||
-webkit-border-radius: 7px;
|
||||
-moz-border-radius: 7px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-bar {
|
||||
bottom: 0;
|
||||
height: 7px;
|
||||
min-width: 24px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-bar {
|
||||
left: auto;
|
||||
min-height: 24px;
|
||||
right: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-element_outer {
|
||||
bottom: 0;
|
||||
top: auto;
|
||||
left: 2px;
|
||||
-webkit-transition: height 0.2s;
|
||||
-moz-transition: height 0.2s;
|
||||
-o-transition: height 0.2s;
|
||||
-ms-transition: height 0.2s;
|
||||
transition: height 0.2s;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-element_outer {
|
||||
left: auto;
|
||||
right: 0;
|
||||
top: 2px;
|
||||
-webkit-transition: width 0.2s;
|
||||
-moz-transition: width 0.2s;
|
||||
-o-transition: width 0.2s;
|
||||
-ms-transition: width 0.2s;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-element_size {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-element_size {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -11px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
/* hover & drag */
|
||||
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer,
|
||||
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer {
|
||||
overflow: hidden;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
|
||||
filter: alpha(opacity=70);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer .scroll-element_size,
|
||||
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer .scroll-element_size {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer .scroll-bar,
|
||||
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer .scroll-bar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x:hover .scroll-element_outer,
|
||||
.scrollbar-dynamic > .scroll-element.scroll-x.scroll-draggable .scroll-element_outer {
|
||||
height: 20px;
|
||||
min-height: 7px;
|
||||
}
|
||||
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y:hover .scroll-element_outer,
|
||||
.scrollbar-dynamic > .scroll-element.scroll-y.scroll-draggable .scroll-element_outer {
|
||||
min-width: 7px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
/*************** SCROLLBAR GOOGLE CHROME ***************/
|
||||
.scrollbar-chrome > .scroll-element,
|
||||
.scrollbar-chrome > .scroll-element div {
|
||||
border: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element div {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element .scroll-element_track {
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x {
|
||||
bottom: 0;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element .scroll-bar {
|
||||
background-color: #d9d9d9;
|
||||
border: 1px solid #bdbdbd;
|
||||
cursor: default;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element .scroll-bar:hover {
|
||||
background-color: #c2c2c2;
|
||||
border-color: #a9a9a9;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-draggable .scroll-bar {
|
||||
background-color: #919191;
|
||||
border-color: #7e7e7e;
|
||||
}
|
||||
|
||||
/* scrollbar height/width & offset from container borders */
|
||||
.scrollbar-chrome > .scroll-content.scroll-scrolly_visible {
|
||||
left: -16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-content.scroll-scrollx_visible {
|
||||
top: -16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x .scroll-bar {
|
||||
height: 8px;
|
||||
min-width: 10px;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y .scroll-bar {
|
||||
left: 3px;
|
||||
min-height: 10px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_outer {
|
||||
border-left: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_track {
|
||||
height: 14px;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_size {
|
||||
height: 14px;
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_outer {
|
||||
border-top: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_track {
|
||||
top: -3px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_size {
|
||||
top: -4px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* update scrollbar offset if both scrolls are visible */
|
||||
.scrollbar-chrome > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
|
||||
left: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
|
||||
left: -19px;
|
||||
}
|
||||
|
||||
.scrollbar-chrome > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
|
||||
top: -19px;
|
||||
}
|
1
data/interfaces/default/css/opensans.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
@font-face{font-family:'Open Sans';font-weight:400;font-style:normal;src:url(../fonts/Open-Sans-regular/Open-Sans-regular.eot);src:url(../fonts/Open-Sans-regular/Open-Sans-regular.eot?#iefix) format('embedded-opentype'),local('Open Sans'),local('Open-Sans-regular'),url(../fonts/Open-Sans-regular/Open-Sans-regular.woff2) format('woff2'),url(../fonts/Open-Sans-regular/Open-Sans-regular.woff) format('woff'),url(../fonts/Open-Sans-regular/Open-Sans-regular.ttf) format('truetype'),url(../fonts/Open-Sans-regular/Open-Sans-regular.svg#OpenSans) format('svg')}@font-face{font-family:'Open Sans';font-weight:600;font-style:normal;src:url(../fonts/Open-Sans-600/Open-Sans-600.eot);src:url(../fonts/Open-Sans-600/Open-Sans-600.eot?#iefix) format('embedded-opentype'),local('Open Sans Semibold'),local('Open-Sans-600'),url(../fonts/Open-Sans-600/Open-Sans-600.woff2) format('woff2'),url(../fonts/Open-Sans-600/Open-Sans-600.woff) format('woff'),url(../fonts/Open-Sans-600/Open-Sans-600.ttf) format('truetype'),url(../fonts/Open-Sans-600/Open-Sans-600.svg#OpenSans) format('svg')}
|
1
data/interfaces/default/css/pnotify.custom.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.ui-pnotify{top:36px;right:36px;position:absolute;height:auto;z-index:2}body>.ui-pnotify{position:fixed;z-index:100040}.ui-pnotify-modal-overlay{background-color:rgba(0,0,0,.4);top:0;left:0;position:absolute;height:100%;width:100%;z-index:1}body>.ui-pnotify-modal-overlay{position:fixed;z-index:100039}.ui-pnotify.ui-pnotify-in{display:block!important}.ui-pnotify.ui-pnotify-move{transition:left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-slow{transition:opacity .6s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-slow.ui-pnotify.ui-pnotify-move{transition:opacity .6s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-normal{transition:opacity .4s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-normal.ui-pnotify.ui-pnotify-move{transition:opacity .4s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-fast{transition:opacity .2s linear;opacity:0}.ui-pnotify.ui-pnotify-fade-fast.ui-pnotify.ui-pnotify-move{transition:opacity .2s linear,left .5s ease,top .5s ease,right .5s ease,bottom .5s ease}.ui-pnotify.ui-pnotify-fade-in{opacity:1}.ui-pnotify .ui-pnotify-shadow{-webkit-box-shadow:0 6px 28px 0 rgba(0,0,0,.1);-moz-box-shadow:0 6px 28px 0 rgba(0,0,0,.1);box-shadow:0 6px 28px 0 rgba(0,0,0,.1)}.ui-pnotify-container{background-position:0 0;padding:.8em;height:100%;margin:0}.ui-pnotify-container:after{content:" ";visibility:hidden;display:block;height:0;clear:both}.ui-pnotify-container.ui-pnotify-sharp{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.ui-pnotify-title{display:block;margin-bottom:.4em;margin-top:0}.ui-pnotify-text{display:block}.ui-pnotify-icon,.ui-pnotify-icon span{display:block;float:left;margin-right:.2em}.ui-pnotify.stack-bottomleft,.ui-pnotify.stack-topleft{left:25px;right:auto}.ui-pnotify.stack-bottomleft,.ui-pnotify.stack-bottomright{bottom:25px;top:auto}.ui-pnotify.stack-modal{left:50%;right:auto;margin-left:-150px}.ui-pnotify-closer,.ui-pnotify-sticker{float:right;margin-left:.2em}.ui-pnotify-container{position:relative;left:0}@media (max-width:480px){.ui-pnotify-mobile-able.ui-pnotify{position:fixed;top:0;right:0;left:0;width:auto!important;font-size:1.2em;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;-ms-font-smoothing:antialiased;font-smoothing:antialiased}.ui-pnotify-mobile-able.ui-pnotify .ui-pnotify-shadow{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-bottom-width:5px}.ui-pnotify-mobile-able .ui-pnotify-container{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft,.ui-pnotify-mobile-able.ui-pnotify.stack-topleft{left:0;right:0}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft,.ui-pnotify-mobile-able.ui-pnotify.stack-bottomright{left:0;right:0;bottom:0;top:auto}.ui-pnotify-mobile-able.ui-pnotify.stack-bottomleft .ui-pnotify-shadow,.ui-pnotify-mobile-able.ui-pnotify.stack-bottomright .ui-pnotify-shadow{border-top-width:5px;border-bottom-width:1px}}
|
1
data/interfaces/default/css/selectize.min.css
vendored
Normal file
@@ -34,7 +34,7 @@ table.display {
|
||||
margin: 0 auto;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 25px;
|
||||
/* Note Firefox 3.5 and before have a bug with border-collapse
|
||||
* ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 )
|
||||
@@ -332,4 +332,88 @@ div.box {
|
||||
}
|
||||
td.no-wrap, th.no-wrap {
|
||||
white-space:nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Custom styles
|
||||
*/
|
||||
|
||||
table.display,
|
||||
table.display tr.shown + tr table[id^='history_child'],
|
||||
table.display tr.shown + tr table[id^='media_info_child'],
|
||||
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] {
|
||||
table-layout: auto;
|
||||
}
|
||||
table.display tr.shown + tr div.slider {
|
||||
display: none;
|
||||
}
|
||||
table.display tr.shown + tr > td {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
table.display tr.shown + tr:hover {
|
||||
background-color: rgba(255,255,255,0);
|
||||
}
|
||||
table.display tr.shown + tr:hover a,
|
||||
table.display tr.shown + tr td:hover a,
|
||||
table.display tr.shown + tr td:hover a .fa,
|
||||
table.display tr.shown + tr .pagination > .active > a,
|
||||
table.display tr.shown + tr .pagination > .active > a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
table.display tr.shown + tr table[id^='history_child'] td:hover a,
|
||||
table.display tr.shown + tr table[id^='history_child'] td:hover a .fa,
|
||||
table.display tr.shown + tr table[id^='media_info_child'] > tr > td:hover a,
|
||||
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] td:hover a {
|
||||
color: #cc7b19;
|
||||
}
|
||||
table.display tr.shown + tr .pagination > .disabled > a,
|
||||
table.display tr.shown + tr .pagination > .disabled > a:hover {
|
||||
color: #444444;
|
||||
}
|
||||
table.display tr.shown + tr .pagination > li > a:hover {
|
||||
color: #e9a049;
|
||||
}
|
||||
table.display tr.odd td,
|
||||
table.display tr.even td {
|
||||
padding: 5px 10px !important;
|
||||
}
|
||||
table[id^='history_child'] {
|
||||
margin-top: 0;
|
||||
opacity: .6;
|
||||
}
|
||||
table[id^='media_info_child'] {
|
||||
margin-top: 0;
|
||||
}
|
||||
div[id^='history_child'] thead th,
|
||||
div[id^='media_info_child'] thead th {
|
||||
line-height: 0;
|
||||
height: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
div[id^='history_child'] div.row,
|
||||
div[id^='media_info_child'] div.row {
|
||||
margin: 0;
|
||||
}
|
||||
div[id^='history_child'] div.col-sm-12,
|
||||
div[id^='media_info_child'] div.col-sm-12 {
|
||||
padding: 0;
|
||||
}
|
||||
div[id^='history_child'] div.dataTables_scrollBody,
|
||||
div[id^='media_info_child'] div.dataTables_scrollBody {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
div[id^='media_info_child'] div[id^='media_info_child'] div.dataTables_scrollHead thead th {
|
||||
line-height: 25px;
|
||||
height: 35px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.current-activity-row {
|
||||
background-color: rgba(255,255,255,.1) !important;
|
||||
}
|
||||
.current-activity-row:hover {
|
||||
background-color: rgba(255,255,255,0.125) !important;
|
||||
}
|
@@ -1,291 +0,0 @@
|
||||
<%doc>
|
||||
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||
|
||||
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||
|
||||
Filename: current_activity.html
|
||||
Version: 0.1
|
||||
Variable names: data [list]
|
||||
|
||||
data :: Usable parameters
|
||||
|
||||
data['stream_count'] Returns the current number of active streams
|
||||
data['sessions'] Returns an array containing session data
|
||||
|
||||
data[sessions] :: Usable parameters
|
||||
|
||||
== Global keys ==
|
||||
session_key Returns a unique session id for the active stream
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
media_index Returns the index of the media item.
|
||||
parent_media_index Returns the index of the media item's parent.
|
||||
media_type Returns the type of session. Either 'track', 'episode' or 'movie'.
|
||||
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
|
||||
bif_thumb Returns the location of the item's bif thumbnail. Use with pms_image_proxy.
|
||||
art Returns the location of the item's artwork
|
||||
progress_percent Returns the current progress of the item. 0 to 100.
|
||||
user Returns the name of the user owning the session.
|
||||
user_id Returns the Plex user id if available.
|
||||
machine_id Returns the machine id of the players being used.
|
||||
friendly_name Returns the friendlly name of the user owning the session.
|
||||
user_thumb Returns the profile picture of the user owning the session.
|
||||
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||
title Returns the name of the episode, movie or music track.
|
||||
year Returns the year of the episode, movie, or clip.
|
||||
ip_address Returns the ip address of the stream.
|
||||
player Returns the name of the platform used to play the stream.
|
||||
platform Returns the type of platform used to play the stream.
|
||||
throttled Returns true if the transcode session is throttled.
|
||||
transcode_progress Returns the current transcode progress of the item. 0 to 100.
|
||||
transcode_speed Returns the current transcode speed of the item.
|
||||
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
audio_codec Returns the name of the audio codec.
|
||||
audio_channels Returns the number of audio channels.
|
||||
grandparent_title Returns the title of the item's grandparent.
|
||||
parent_title Returns the title of the item's parent.
|
||||
video_decision Returns the video transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
video_codec Returns the name of the video codec.
|
||||
height Returns the value of the video height.
|
||||
width Returns the value of the video width.
|
||||
container Returns the value of the media container.
|
||||
bitrate Returns the value of the media bitrate.
|
||||
video_resolution Returns the value of the video resolution.
|
||||
video_framerate Returns the value of the video framerate.
|
||||
video_aspect_ratio Returns the value of the video aspect ratio.
|
||||
transcode_audio_channels Returns the amount of audio channels if there is a transcode session.
|
||||
transcode_audio_codec Returns the name of the audio codec if there is a transcode session.
|
||||
transcode_video_codec Returns the name of the video codec if there is a transcode session.
|
||||
transcode_width Returns the video width if there is a transcode session.
|
||||
transcode_height Returns the video height if there is a transcode session.
|
||||
transcode_container Returns the value of media container if there is a transcode session.
|
||||
transcode_protocol Returns the value of media protocol if there is a transcode session.
|
||||
indexes Returns true if the media has media indexes and are enabled
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data is not None:
|
||||
% if data['stream_count'] != '0':
|
||||
% for a in data['sessions']:
|
||||
<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?item_id=${a['rating_key']}">
|
||||
% endif
|
||||
<div class="dashboard-activity-poster">
|
||||
% if a['media_type'] == 'movie' and not a['indexes']:
|
||||
<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']:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
||||
% elif a['indexes']:
|
||||
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280); display: none;"></div>
|
||||
% else:
|
||||
% 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:
|
||||
% 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
|
||||
</div>
|
||||
<div class="dashboard-activity-poster-info-time">
|
||||
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</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']}%">${a['transcode_progress']}%</div>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</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?item_id=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||
% elif a['media_type'] == 'movie':
|
||||
<a href="info?item_id=${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?item_id=${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?item_id=${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">
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
% else:
|
||||
<a href="user?user=${a['user']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% endfor
|
||||
<script>
|
||||
// When using bif indexes make the image transition a little smoother.
|
||||
$('.bif').each(function() {
|
||||
$(this).hide().fadeIn(1000);
|
||||
});
|
||||
|
||||
// Convert timestamps to readable times
|
||||
$('.progress_time').each(function(index) {
|
||||
$(this).html(millisecondsToMinutes($(this).text(), false));
|
||||
});
|
||||
|
||||
// Show/Hide activity info
|
||||
$('.btn-activity-info').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$($(this).attr('data-target')).toggle();
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
<div class="text-muted">Nothing is currently being watched.</div><br>
|
||||
% endif
|
||||
% else:
|
||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a
|
||||
href="settings">settings</a>.
|
||||
</div><br>
|
||||
% endif
|
@@ -15,11 +15,24 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
% if data == '0':
|
||||
<h3>Activity</h3>
|
||||
<%
|
||||
s = '('
|
||||
if data['direct_play']:
|
||||
s += str(data['direct_play']) + ' direct play' + ('s' if data['direct_play'] > 1 else '') + ', '
|
||||
if data['direct_stream']:
|
||||
s += str(data['direct_stream']) + ' direct stream' + ('s' if data['direct_stream'] > 1 else '') + ', '
|
||||
if data['transcode']:
|
||||
s += str(data['transcode']) + ' transcode' + ('s' if data['transcode'] > 1 else '') + ', '
|
||||
s = s.rstrip(', ')
|
||||
s += ')'
|
||||
%>
|
||||
% if data['stream_count'] == '0':
|
||||
<h3>Activity</h3>
|
||||
% elif data['stream_count'] == '1':
|
||||
<h3>Activity <small>${data['stream_count']} stream ${s}</small></h3>
|
||||
% else:
|
||||
<h3>Activity <small>${data['stream_count']} streams ${s}</small></h3>
|
||||
% endif
|
||||
% else:
|
||||
<h3>Activity <small>${data} stream(s)</small></h3>
|
||||
<h3>Activity</h3>
|
||||
% endif
|
||||
% else:
|
||||
<h3>Activity</h3>
|
||||
% endif
|
477
data/interfaces/default/current_activity_instance.html
Normal file
@@ -0,0 +1,477 @@
|
||||
<%doc>
|
||||
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
|
||||
|
||||
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
|
||||
|
||||
Filename: current_activity_instance.html
|
||||
Version: 0.1
|
||||
Variable names: data {dict}
|
||||
|
||||
data :: Usable parameters
|
||||
|
||||
== Global keys ==
|
||||
session_key Returns a unique session id for the active stream
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
media_index Returns the index of the media item.
|
||||
parent_media_index Returns the index of the media item's parent.
|
||||
media_type Returns the type of session. Either 'track', 'episode' or 'movie'.
|
||||
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
|
||||
bif_thumb Returns the location of the item's bif thumbnail. Use with pms_image_proxy.
|
||||
art Returns the location of the item's artwork
|
||||
progress_percent Returns the current progress of the item. 0 to 100.
|
||||
user Returns the name of the user owning the session.
|
||||
user_id Returns the Plex user id if available.
|
||||
machine_id Returns the machine id of the players being used.
|
||||
friendly_name Returns the friendlly name of the user owning the session.
|
||||
user_thumb Returns the profile picture of the user owning the session.
|
||||
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||
title Returns the name of the episode, movie or music track.
|
||||
year Returns the year of the episode, movie, or clip.
|
||||
ip_address Returns the ip address of the stream.
|
||||
player Returns the name of the platform used to play the stream.
|
||||
platform Returns the type of platform used to play the stream.
|
||||
transcode_throttled Returns true if the transcode session is throttled.
|
||||
transcode_progress Returns the current transcode progress of the item. 0 to 100.
|
||||
transcode_speed Returns the current transcode speed of the item.
|
||||
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
audio_codec Returns the name of the audio codec.
|
||||
audio_channels Returns the number of audio channels.
|
||||
grandparent_title Returns the title of the item's grandparent.
|
||||
parent_title Returns the title of the item's parent.
|
||||
video_decision Returns the video transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
video_codec Returns the name of the video codec.
|
||||
height Returns the value of the video height.
|
||||
width Returns the value of the video width.
|
||||
container Returns the value of the media container.
|
||||
bitrate Returns the value of the media bitrate.
|
||||
video_resolution Returns the value of the video resolution.
|
||||
video_framerate Returns the value of the video framerate.
|
||||
video_aspect_ratio Returns the value of the video aspect ratio.
|
||||
transcode_audio_channels Returns the amount of audio channels if there is a transcode session.
|
||||
transcode_audio_codec Returns the name of the audio codec if there is a transcode session.
|
||||
transcode_video_codec Returns the name of the video codec if there is a transcode session.
|
||||
transcode_width Returns the video width if there is a transcode session.
|
||||
transcode_height Returns the video height if there is a transcode session.
|
||||
transcode_container Returns the value of media container if there is a transcode session.
|
||||
transcode_protocol Returns the value of media protocol if there is a transcode session.
|
||||
indexes Returns true if the media has media indexes and are enabled
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if session is not None:
|
||||
<%
|
||||
from collections import defaultdict
|
||||
from urllib import quote
|
||||
from plexpy import helpers
|
||||
from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES, EXTRA_TYPES
|
||||
import plexpy
|
||||
%>
|
||||
<%
|
||||
data = defaultdict(lambda: 'Unknown', **session)
|
||||
sk = data['session_key']
|
||||
|
||||
href = 'info?rating_key={}'.format(data['rating_key']) if data['rating_key'] else '#'
|
||||
parent_href = 'info?rating_key={}'.format(data['parent_rating_key']) if data['parent_rating_key'] else '#'
|
||||
grandparent_href = 'info?rating_key={}'.format(data['grandparent_rating_key']) if data['grandparent_rating_key'] else '#'
|
||||
user_href = 'user?user_id={}'.format(data['user_id']) if data['user_id'] else '#'
|
||||
%>
|
||||
<div class="dashboard-activity-instance" id="activity-instance-${sk}" data-key="${sk}" data-id="${data['session_id']}"
|
||||
data-rating_key="${data['rating_key']}" data-parent_rating_key="${data['parent_rating_key']}" data-grandparent_rating_key="${data['grandparent_rating_key']}">
|
||||
<div class="dashboard-activity-container">
|
||||
<div class="dashboard-activity-background-overlay">
|
||||
% if data['channel_stream'] == 0:
|
||||
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art&refresh=true);"></div>
|
||||
% else:
|
||||
% if (data['art'] and data['art'].startswith('http')) or (data['thumb'] and data['thumb'].startswith('http')):
|
||||
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(${data['art']});"></div>
|
||||
% else:
|
||||
<!--Hacky solution to escape the image url until I come up with something better-->
|
||||
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(pms_image_proxy?img=${quote(data['art'] or data['thumb'])}&width=500&height=280&fallback=art&refresh=true&clip=true);"></div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="dashboard-activity-poster-container hidden-xs">
|
||||
% if data['media_type'] == 'track':
|
||||
<div id="poster-${sk}-bg" class="dashboard-activity-poster-blur" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
% endif
|
||||
% if data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a id="poster-url-${sk}" href="${href}" title="${data['title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a id="poster-url-${sk}" href="${grandparent_href}" title="${data['grandparent_title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['grandparent_thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
</a>
|
||||
% elif data['media_type'] == 'track':
|
||||
<a id="poster-url-${sk}" href="${parent_href}" title="${data['parent_title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
</a>
|
||||
% elif data['media_type'] in ('photo', 'clip'):
|
||||
% if data['extra_type']:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['art'].replace('/art', '/thumb') or data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['parent_thumb'] or data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
% endif
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(images/art.png);"></div>
|
||||
% endif
|
||||
% else:
|
||||
% if data['channel_icon'].startswith('http'):
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster-blur" style="background-image: url(${data['channel_icon']});"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(${data['channel_icon']});"></div>
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster-blur" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-info-icon">
|
||||
<%
|
||||
if not _session['user_group'] == 'admin' or not data['session_id']:
|
||||
no_terminate = '-no-terminate'
|
||||
else:
|
||||
no_terminate = ''
|
||||
%>
|
||||
<div id="platform-${sk}" class="dashboard-activity-info-platform${no_terminate} svg-icon platform-${data['platform_name']}" title="${data['platform']}"></div>
|
||||
% if _session['user_group'] == 'admin' and plexpy.CONFIG.PMS_PLEXPASS and data['session_id']:
|
||||
<div class="dashboard-activity-terminate-session" id="terminate-button-${sk}" data-key="${sk}" data-id="${data['session_id']}" data-toggle="tooltip" title="Terminate Stream">
|
||||
<i class="fa fa-times" style="padding-top: 8px;"></i>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-info-container">
|
||||
<div class="dashboard-activity-info-scroller scrollbar-macosx">
|
||||
<div class="dashboard-activity-info scoller-content">
|
||||
<ul class="list-unstyled dashboard-activity-info-list">
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Product</div>
|
||||
<div class="sub-value platform-right">${data['product']}</div>
|
||||
</li>
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Player</div>
|
||||
<div class="sub-value platform-right">${data['player']}</div>
|
||||
</li>
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Quality</div>
|
||||
<div class="sub-value platform-right" id="stream_quality-${sk}">
|
||||
% if data['media_type'] != 'photo' and data['quality_profile'] != 'Unknown':
|
||||
<%
|
||||
br = helpers.cast_to_int(data['stream_bitrate']) or ''
|
||||
if br:
|
||||
if br > 1000:
|
||||
br = '(' + str(round(br / 1000.0, 1)) + ' Mbps)'
|
||||
else:
|
||||
br = '(' + str(br) + ' kbps)'
|
||||
%>
|
||||
${data['quality_profile']} ${br}
|
||||
% else:
|
||||
${data['quality_profile']}
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% if data['optimized_version'] == 1:
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Optimized</div>
|
||||
<div class="sub-value" id="optimized_version-${sk}">
|
||||
${data['optimized_version_profile']} (${data['optimized_version_title']})
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% if data['synced_version'] == 1:
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Synced</div>
|
||||
<div class="sub-value" id="synced_version-${sk}">
|
||||
${data['synced_version_profile']}
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
<ul class="list-unstyled dashboard-activity-info-list">
|
||||
<li class="dashboard-activity-info-item">
|
||||
% if _session['user_group'] == 'admin':
|
||||
<div class="sub-heading"><span class="raw-stream-info-modal" data-toggle="modal" data-target="#raw-stream-info-modal" data-key="${sk}">Stream</span></div>
|
||||
% else:
|
||||
<div class="sub-heading">Stream</div>
|
||||
% endif
|
||||
<div class="sub-value" id="transcode_decision-${sk}">
|
||||
% if data['transcode_decision'] == 'transcode':
|
||||
Transcode
|
||||
% if data['transcode_throttled'] == 1:
|
||||
(Throttled)
|
||||
% else:
|
||||
(Speed: ${data['transcode_speed']})
|
||||
% endif
|
||||
% elif data['transcode_decision'] == 'copy':
|
||||
Direct Stream
|
||||
% else:
|
||||
Direct Play
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Container</div>
|
||||
<div class="sub-value" id="transcode_container-${sk}">
|
||||
% if data['stream_container_decision'] == 'transcode':
|
||||
Transcode (${data['container'].upper()} <i class="fa fa-long-arrow-right"></i> ${data['stream_container'].upper()})
|
||||
% else:
|
||||
Direct Play (${data['container'].upper()})
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip', 'photo'):
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Video</div>
|
||||
<div class="sub-value" id="video_decision-${sk}">
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip'):
|
||||
% if data['stream_video_decision'] == 'transcode':
|
||||
<%
|
||||
hw_d = ' (HW)' if data['transcode_hw_decoding'] else ''
|
||||
hw_e = ' (HW)' if data['transcode_hw_encoding'] else ''
|
||||
%>
|
||||
Transcode (${data['video_codec'].upper()}${hw_d} ${VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])} <i class="fa fa-long-arrow-right"></i> ${data['stream_video_codec'].upper()}${hw_e} ${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
|
||||
% elif data['stream_video_decision'] == 'copy':
|
||||
Direct Stream (${data['stream_video_codec'].upper()} ${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
|
||||
% else:
|
||||
Direct Play (${data['video_codec'].upper()} ${VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])})
|
||||
% endif
|
||||
% elif data['media_type'] == 'photo':
|
||||
Direct Play (${data['width']}x${data['height']})
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip', 'track'):
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Audio</div>
|
||||
<div class="sub-value" id="audio_decision-${sk}">
|
||||
% if data['stream_audio_decision'] == 'transcode':
|
||||
Transcode (${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()} <i class="fa fa-long-arrow-right"></i> ${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% elif data['stream_audio_decision'] == 'copy':
|
||||
Direct Stream (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% else:
|
||||
Direct Play (${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip'):
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Subtitle</div>
|
||||
<div class="sub-value" id="subtitle_decision-${sk}">
|
||||
% if data['subtitles'] == 1:
|
||||
% if data['stream_subtitle_decision'] == 'transcode':
|
||||
Transcode (${data['subtitle_codec'].upper()} <i class="fa fa-long-arrow-right"></i> ${data['stream_subtitle_codec'].upper()})
|
||||
% elif data['stream_subtitle_decision'] == 'copy':
|
||||
Direct Stream (${data['subtitle_codec'].upper()})
|
||||
% elif data['stream_subtitle_decision'] == 'burn':
|
||||
Burn (${data['subtitle_codec'].upper()})
|
||||
% else:
|
||||
Direct Play (${data['stream_subtitle_codec'].upper() if data['synced_version'] else data['subtitle_codec'].upper()})
|
||||
% endif
|
||||
% else:
|
||||
None
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
<ul class="list-unstyled dashboard-activity-info-list">
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Location</div>
|
||||
<div class="sub-value time-right">
|
||||
<span id="location-${sk}">${data['location'].upper()}</span>:
|
||||
% if data['ip_address'] != 'N/A':
|
||||
<span class="ip-container"><span class="ip-address">${data['ip_address']}</span></span>
|
||||
% if data['relay']:
|
||||
<span data-toggle="tooltip" title="Plex Relay"><i class="fa fa-exclamation-circle"></i></span>
|
||||
% else:
|
||||
<a href="#" class="external_ip-modal" data-toggle="modal" data-target="#ip-info-modal" data-ip="${data['ip_address']}">
|
||||
<span id="external_ip-${sk}" class="external-ip-tooltip" data-toggle="tooltip" title="Lookup External IP" style="display: none;"><i class="fa fa-map-marker"></i></span>
|
||||
</a>
|
||||
<script>
|
||||
isPrivateIP("${data['ip_address']}").then(function () {
|
||||
$("#external_ip-${sk}").hide();
|
||||
}, function () {
|
||||
$("#external_ip-${sk}").show();
|
||||
});
|
||||
</script>
|
||||
% endif
|
||||
% else:
|
||||
N/A
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Bandwidth</div>
|
||||
<div class="sub-value time-right">
|
||||
% if data['media_type'] != 'photo' and data['bandwidth'] != 'Unknown':
|
||||
<%
|
||||
bw = helpers.cast_to_int(data['bandwidth'])
|
||||
if bw > 1000:
|
||||
bw = str(round(bw / 1000.0, 1)) + ' Mbps'
|
||||
else:
|
||||
bw = str(bw) + ' kbps'
|
||||
%>
|
||||
<span id="stream-bandwidth-${sk}">${bw}</span>
|
||||
<span id="streaming-brain-${sk}" data-toggle="tooltip" title="Streaming Brain Estimate (Required Bandwidth)"><i class="fa fa-info-circle"></i></span>
|
||||
% elif data['synced_version'] == 1 or data['channel_stream'] == 1:
|
||||
<span id="stream-bandwidth-${sk}">None</span>
|
||||
% else:
|
||||
<span id="stream-bandwidth-${sk}">Unknown</span>
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% if data['media_type'] != 'photo':
|
||||
<div class="dashboard-activity-info-time">
|
||||
% if data['live'] == 1:
|
||||
<br />Live
|
||||
% elif data['view_offset']:
|
||||
ETA:
|
||||
<span id="stream-eta-${sk}">
|
||||
<script>
|
||||
$("#stream-eta-${sk}").html(moment().add(parseInt("${data['stream_duration']}") - parseInt("${data['view_offset']}"), 'milliseconds').format(time_format));
|
||||
</script>
|
||||
</span><br /><span class="progress_time_offset" id="stream-view-offset-${sk}" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}">
|
||||
<script>
|
||||
$("#stream-view-offset-${sk}").html(millisecondsToMinutes(parseInt("${data['view_offset']}"), false));
|
||||
</script>
|
||||
</span> / <span class="progress_time_total" id="stream-duration-${sk}">
|
||||
<script>
|
||||
$("#stream-duration-${sk}").html(millisecondsToMinutes(parseInt("${data['stream_duration']}"), false));
|
||||
</script>
|
||||
</span>
|
||||
% else:
|
||||
ETA: Unknown<br />0:00 / <span class="progress_time_total" id="stream-duration-${sk}">
|
||||
<script>
|
||||
$("#stream-duration-${sk}").html(millisecondsToMinutes(parseInt("${data['stream_duration']}"), false));
|
||||
</script>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-progress">
|
||||
<div class="dashboard-activity-progress-bar">
|
||||
% if data['live'] == 1:
|
||||
<div id="progress-bar-${sk}" class="progress-bar" style="width: 100%" data-toggle="tooltip" title="Stream Progress Live">Live</div>
|
||||
% else:
|
||||
<div id="buffer-bar-${sk}" class="buffer-bar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress ${data['transcode_progress']}%">${data['transcode_progress']}%</div>
|
||||
<div id="progress-bar-${sk}" class="progress-bar" style="width: ${data['progress_percent']}%" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-wrapper">
|
||||
<a href="${user_href}" title="${data['friendly_name']}">
|
||||
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${data['user_thumb']});"></div>
|
||||
</a>
|
||||
<div class="dashboard-activity-metadata-title-container">
|
||||
<div id="play-state-${sk}" class="dashboard-activity-metadata-play_state-icon" title="${data['state'].capitalize()}">
|
||||
% if data['state'] == 'playing':
|
||||
<i class="fa fa-fw fa-play"></i>
|
||||
% elif data['state'] == 'paused':
|
||||
<i class="fa fa-fw fa-pause"></i>
|
||||
% elif data['state'] == 'buffering':
|
||||
<i class="fa fa-fw fa-spinner"></i>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a href="${grandparent_href}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
|
||||
- <a href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% elif data['media_type'] == 'track':
|
||||
<a id="metadata-grandparent_title-${sk}" href="${grandparent_href}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
|
||||
- <a id="metadata-title-${sk}" href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% elif data['media_type'] == 'photo':
|
||||
<span title="${data['parent_title']}">${data['parent_title']}</span>
|
||||
% elif data['media_type'] == 'clip':
|
||||
<span title="${data['title']}">${data['title']}</span>
|
||||
% else:
|
||||
<span title="${data['title']}">${data['title']}</span>
|
||||
% endif
|
||||
% elif data['media_type'] == 'episode' and data['grandparent_title']:
|
||||
<span title="${data['grandparent_title']}">${data['grandparent_title']}</span>
|
||||
- <span title="${data['title']}">${data['title']}</span>
|
||||
% else:
|
||||
<span title="${data['title']}">${data['title']}</span>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-subtitle-container">
|
||||
% if data['live'] == 1:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Plex Live TV">
|
||||
<i class="fa fa-fw fa-television"></i>
|
||||
</div>
|
||||
% elif data['channel_stream'] == 0:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="${data['media_type'].capitalize()}">
|
||||
% if data['media_type'] == 'movie':
|
||||
<i class="fa fa-fw fa-film"></i>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<i class="fa fa-fw fa-television"></i>
|
||||
% elif data['media_type'] == 'track':
|
||||
<i class="fa fa-fw fa-music"></i>
|
||||
% elif data['media_type'] == 'photo':
|
||||
<i class="fa fa-fw fa-picture-o"></i>
|
||||
% elif data['media_type'] == 'clip':
|
||||
<i class="fa fa-fw fa-video-camera"></i>
|
||||
% endif
|
||||
</div>
|
||||
% else:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Channel">
|
||||
<i class="fa fa-fw fa-cloud"></i>
|
||||
</div>
|
||||
% endif
|
||||
<div class="dashboard-activity-metadata-subtitle">
|
||||
% if data['live'] == 1:
|
||||
<span title="Plex Live TV" class="sub-heading">Plex Live TV</span>
|
||||
% elif data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a href="${parent_href}" title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</a>
|
||||
· <a href="${href}" title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</a>
|
||||
% elif data['media_type'] == 'track':
|
||||
<a id="metadata-parent_title-${sk}" href="${parent_href}" title="${data['parent_title']}" class="sub-heading">${data['parent_title']}</a>
|
||||
% elif data['media_type'] == 'photo':
|
||||
<span title="${data['title']}" class="sub-heading">${data['title']}</span>
|
||||
% else:
|
||||
% if data['extra_type']:
|
||||
<% extra_type = EXTRA_TYPES.get(data['extra_type'], data['sub_type'].capitalize()) %>
|
||||
<span title="${data['year']} (${extra_type})" class="sub-heading">${data['year']} (${extra_type})</span>
|
||||
% else:
|
||||
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
|
||||
% endif
|
||||
% endif
|
||||
% elif data['channel_title']:
|
||||
<span title="${data['channel_title']}" class="sub-heading">${data['channel_title']}</span>
|
||||
% if data['media_type'] == 'episode' and data['parent_media_index'] and data['media_index']:
|
||||
(<span title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</span>
|
||||
· <span title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</span>)
|
||||
% elif data['media_type'] == 'episode' and data['originally_available_at']:
|
||||
(<span title="${data['originally_available_at']}" class="sub-heading">${data['originally_available_at']}</span>)
|
||||
% endif
|
||||
% else:
|
||||
<span title="Channel" class="sub-heading">Channel</span>
|
||||
% if data['media_type'] == 'episode' and data['parent_media_index'] and data['media_index']:
|
||||
(<span title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</span>
|
||||
· <span title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</span>)
|
||||
% elif data['media_type'] == 'episode' and data['originally_available_at']:
|
||||
(<span title="${data['originally_available_at']}" class="sub-heading">${data['originally_available_at']}</span>)
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
<a href="${user_href}" title="${data['friendly_name']}">${data['friendly_name']}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
111
data/interfaces/default/edit_library.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<%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 Tautulli. To reset to default, leave this field empty and save.</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>
|
||||
% 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>
|
||||
<script>
|
||||
// Save library options
|
||||
$("#save_library").on('click', function () {
|
||||
var custom_thumb = $("#custom_thumb_url").val();
|
||||
var keep_history = 0;
|
||||
if ($("#keep_history").is(":checked")) {
|
||||
keep_history = 1;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: 'edit_library',
|
||||
data: {
|
||||
section_id: '${data["section_id"]}',
|
||||
custom_thumb: custom_thumb,
|
||||
keep_history: keep_history
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#delete-all-history').click(function () {
|
||||
var msg = 'Are you REALLY sure you want to purge all history for the <strong>${data["section_name"]}</strong> library?<br>' +
|
||||
'This is permanent and cannot be undone!';
|
||||
var url = 'delete_all_library_history';
|
||||
confirmAjaxCall(url, msg, { section_id: '${data["section_id"]}' }, null, function () { 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();
|
||||
});
|
||||
</script>
|
||||
% endif
|
@@ -10,21 +10,31 @@ 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.
|
||||
allow_guest Returns bool value for whether to allow guest access 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>
|
||||
@@ -35,28 +45,28 @@ DOCUMENTATION :: END
|
||||
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${data['friendly_name']}" size="30">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Replace all occurances of the username with this name.</p>
|
||||
<p class="help-block">Replace all occurrences of the username with this name.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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 Tautulli. 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="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 receive notifications for this user's activity.</p>
|
||||
<p class="help-block">Uncheck this if you do not want to keep any history on this user's activity.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${data['keep_history']}> Keep history
|
||||
<input type="checkbox" id="allow_guest" name="allow_guest" value="1" ${helpers.checked(data['allow_guest'])}> Allow Guest Access
|
||||
</label>
|
||||
<p class="help-block">Uncheck this if you do not want this keep any history on this user's activity.</p>
|
||||
<p class="help-block">Uncheck this if you do not want to allow this user to login to Tautulli.</p>
|
||||
</div>
|
||||
% if data['user_id']:
|
||||
<div class="form-group">
|
||||
@@ -69,115 +79,55 @@ 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">
|
||||
</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>
|
||||
<input type="button" id="save_user" class="btn btn-bright" value="Save">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Set new friendly name
|
||||
$("#save_user_name").click(function() {
|
||||
var friendly_name = $("#friendly_name").val();
|
||||
var thumb = $("#profile_url").val();
|
||||
var do_notify = 0;
|
||||
// Set user options
|
||||
$("#save_user").on('click', function () {
|
||||
var friendly_name = $("input#friendly_name").val();
|
||||
var custom_thumb = $("#custom_avatar_url").val();
|
||||
var keep_history = 0;
|
||||
if ($("#do_notify").is(":checked")) {
|
||||
do_notify = 1;
|
||||
}
|
||||
var allow_guest = 0;
|
||||
if ($("#keep_history").is(":checked")) {
|
||||
keep_history = 1;
|
||||
}
|
||||
if ($("#allow_guest").is(":checked")) {
|
||||
allow_guest = 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
|
||||
});
|
||||
|
||||
$("#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']}'},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'edit_user',
|
||||
data: {
|
||||
user_id: '${data["user_id"]}',
|
||||
friendly_name: friendly_name,
|
||||
custom_thumb: custom_thumb,
|
||||
keep_history: keep_history,
|
||||
allow_guest: allow_guest
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#delete-all-history').click(function () {
|
||||
var msg = 'Are you REALLY sure you want to purge all history for the <strong>${data["username"]}</strong> user?<br>' +
|
||||
'This is permanent and cannot be undone!';
|
||||
var url = 'delete_all_user_history';
|
||||
confirmAjaxCall(url, msg, { user_id: '${data["user_id"]}' }, null, function () { 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');
|
||||
});
|
||||
// Move #confirm-modal-purge to parent container
|
||||
if (!($('#edit-user-modal').next().is('#confirm-modal-purge'))) {
|
||||
$('#confirm-modal-purge').appendTo($('#edit-user-modal').parent());
|
||||
}
|
||||
$('#edit-user-modal > #confirm-modal-purge').remove();
|
||||
});
|
||||
</script>
|
||||
|
||||
% endif
|
202
data/interfaces/default/fonts/Open-Sans-600/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.eot
Normal file
1637
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.svg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.ttf
Normal file
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.woff
Normal file
BIN
data/interfaces/default/fonts/Open-Sans-600/Open-Sans-600.woff2
Normal file
202
data/interfaces/default/fonts/Open-Sans-regular/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
After Width: | Height: | Size: 105 KiB |
@@ -1,8 +1,8 @@
|
||||
<%inherit file="base.html"/>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
|
||||
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.css">
|
||||
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
@@ -11,112 +11,147 @@
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-bar-chart"></i> Graphs</span>
|
||||
</div>
|
||||
<div class="button-bar hidden-xs">
|
||||
<div class="btn-group" data-toggle="buttons" id="yaxis-selection">
|
||||
<div class="button-bar">
|
||||
<div class="btn-group" id="user-selection">
|
||||
<label>
|
||||
<select name="graph-user" id="graph-user" class="btn" style="color: inherit;">
|
||||
<option value="">All Users</option>
|
||||
<option disabled>────────────</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group" style="margin-right: 2px;" 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
|
||||
</label>
|
||||
<div class="input-group pull-right" style="width: 1px;" id="days-selection">
|
||||
<span class="input-group-addon btn-dark inactive">Last</span>
|
||||
<input type="number" class="form-control number-input" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" data-default="7" data-toggle="tooltip" title="Min: 1 day" />
|
||||
<span class="input-group-addon btn-dark inactive">days</span>
|
||||
</div>
|
||||
<div class="input-group pull-right" style="width: 1px;" id="months-selection">
|
||||
<span class="input-group-addon btn-dark inactive">Last</span>
|
||||
<input type="number" class="form-control number-input" name="graph-months" id="graph-months" value="${config['graph_months']}" min="1" data-default="12" data-toggle="tooltip" title="Min: 1 month" />
|
||||
<span class="input-group-addon btn-dark inactive">months</span>
|
||||
</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 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>
|
||||
% 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 tv, movies, and music played per day of the week.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_dayofweek" style="float: left;">
|
||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-clock-o"></i> <span class="yaxis-text">Play count</span> by hour of day <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of tv, movies, and music played per hour of the day.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_hourofday">
|
||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by top 10 platforms <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of tv, movies, and music played by top 10 most active platforms.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_platform" style="float: left;">
|
||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by top 10 users <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of tv, movies, and music played by top 10 most active users.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_user">
|
||||
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% if config['graph_tab'] == 'tabs-2':
|
||||
<div role="tabpanel" class="tab-pane active" id="tabs-2">
|
||||
% else:
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||
% 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>
|
||||
@@ -189,10 +224,14 @@
|
||||
</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>
|
||||
<h4><i class="fa fa-calendar"></i> Plays by month <small>Last <span class="months">12</span> months</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of tv, movies, and music by month.
|
||||
</p>
|
||||
@@ -206,24 +245,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="history-modal" tabindex="-1" role="dialog" aria-labelledby="history-modal">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modalIncludes()">
|
||||
<div class="modal fade" id="history-modal" tabindex="-1" role="dialog" aria-labelledby="history-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script src="interfaces/default/js/moment-duration-format.js"></script>
|
||||
<script src="interfaces/default/js/highcharts/js/highcharts.js"></script>
|
||||
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
|
||||
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
|
||||
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||
<script src="${http_root}js/moment-duration-format.js"></script>
|
||||
<script src="${http_root}js/highcharts/js/highcharts.js"></script>
|
||||
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||
|
||||
<script>
|
||||
var selected_user_id = null
|
||||
|
||||
// Modal popup dialog
|
||||
function selectHandler(selectedDate) {
|
||||
function selectHandler(selectedDate, selectedSeries) {
|
||||
|
||||
try
|
||||
{
|
||||
@@ -233,91 +279,116 @@
|
||||
var y = dateValue.getFullYear();
|
||||
var dateString = '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
|
||||
|
||||
var media_type = null;
|
||||
var transcode_decision = null;
|
||||
switch(selectedSeries) {
|
||||
case "TV": media_type = 'episode'; break;
|
||||
case "Movies": media_type = 'movie'; break;
|
||||
case "Music": media_type = 'track'; break;
|
||||
case "Direct Play": transcode_decision = 'direct play'; break;
|
||||
case "Direct Stream": transcode_decision = 'copy'; break;
|
||||
case "Transcode": transcode_decision = 'transcode'; break;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
"url": "history_table_modal",
|
||||
type: 'post',
|
||||
data: { 'start_date': dateString },
|
||||
url: "history_table_modal",
|
||||
cache: false,
|
||||
async: true,
|
||||
data: {
|
||||
user_id: selected_user_id,
|
||||
start_date: dateString,
|
||||
media_type: media_type,
|
||||
transcode_decision: transcode_decision
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$('#history-modal').modal('show');
|
||||
$("#history-modal").html(xhr.responseText);
|
||||
$('#history-modal').modal();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
console.log("Failed to retrieve data");
|
||||
console.log("Failed to retrieve history modal data.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_day.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_dayofweek.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_hourofday.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_platform.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_user.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_stream_type.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_source_resolution.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_stream_resolution.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_platform_by_stream_type.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_user_by_stream_type.js"></script>
|
||||
<script src="interfaces/default/js/graphs/plays_by_month.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_day.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_dayofweek.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_hourofday.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_platform.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_user.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_stream_type.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_source_resolution.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_stream_resolution.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_user_by_stream_type.js"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_month.js"></script>
|
||||
<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_day_range = ${config['graph_days']};
|
||||
var current_month_range = ${config['graph_months']};
|
||||
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_day_range);
|
||||
$('.months').html(current_month_range);
|
||||
|
||||
// Load user ids and names (for the selector)
|
||||
$.ajax({
|
||||
url: 'get_user_names',
|
||||
type: 'get',
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
var select = $('#graph-user');
|
||||
data.sort(function(a, b) {
|
||||
return a.friendly_name.localeCompare(b.friendly_name);
|
||||
});
|
||||
data.forEach(function(item) {
|
||||
select.append('<option value="' + item.user_id + '">' +
|
||||
item.friendly_name + '</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
||||
|
||||
function dataSecondsToHours(data) {
|
||||
$.each(data.series, function (i, series) {
|
||||
series.data = $.map(series.data, function (value) {
|
||||
return value / 60 / 60;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadGraphsTab1(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
$('#months-selection').hide();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
url: "get_plays_by_date",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
var dateArray = [];
|
||||
for (var i = 0; i < data.categories.length; i++) {
|
||||
dateArray.push(moment(data.categories[i], 'YYYY-MM-DD').valueOf());
|
||||
$.each(data.categories, function (i, day) {
|
||||
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
|
||||
// Highlight the weekend
|
||||
if ((moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||
(moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||
hc_plays_by_day_options.xAxis.plotBands.push({
|
||||
from: i-0.5,
|
||||
to: i+0.5,
|
||||
color: 'rgba(80,80,80,0.3)'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_day_options.yAxis.min = 0;
|
||||
hc_plays_by_day_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_day_options.series = data.series;
|
||||
@@ -329,9 +400,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_dayofweek",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(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;
|
||||
@@ -342,9 +414,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_hourofday",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(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;
|
||||
@@ -355,9 +428,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_top_10_platforms",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(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;
|
||||
@@ -368,9 +442,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_top_10_users",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(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;
|
||||
@@ -380,27 +455,31 @@
|
||||
}
|
||||
|
||||
function loadGraphsTab2(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
$('#months-selection').hide();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
url: "get_plays_by_stream_type",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
var dateArray = [];
|
||||
for (var i = 0; i < data.categories.length; i++) {
|
||||
dateArray.push(moment(data.categories[i], 'YYYY-MM-DD').valueOf());
|
||||
$.each(data.categories, function (i, day) {
|
||||
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
|
||||
// Highlight the weekend
|
||||
if ((moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||
(moment(data.categories[i], 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||
hc_plays_by_stream_type_options.xAxis.plotBands.push({
|
||||
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
|
||||
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
|
||||
hc_plays_by_day_options.xAxis.plotBands.push({
|
||||
from: i-0.5,
|
||||
to: i+0.5,
|
||||
color: 'rgba(80,80,80,0.3)'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_stream_type_options.yAxis.min = 0;
|
||||
hc_plays_by_stream_type_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_stream_type_options.series = data.series;
|
||||
@@ -411,9 +490,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_source_resolution",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_source_resolution_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_source_resolution_options.series = data.series;
|
||||
var hc_plays_by_source_resolution = new Highcharts.Chart(hc_plays_by_source_resolution_options);
|
||||
@@ -423,9 +503,10 @@
|
||||
$.ajax({
|
||||
url: "get_plays_by_stream_resolution",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_stream_resolution_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_stream_resolution_options.series = data.series;
|
||||
var hc_plays_by_stream_resolution = new Highcharts.Chart(hc_plays_by_stream_resolution_options);
|
||||
@@ -435,9 +516,10 @@
|
||||
$.ajax({
|
||||
url: "get_stream_type_by_top_10_platforms",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_platform_by_stream_type_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_platform_by_stream_type_options.series = data.series;
|
||||
var hc_plays_by_platform_by_stream_type = new Highcharts.Chart(hc_plays_by_platform_by_stream_type_options);
|
||||
@@ -447,9 +529,10 @@
|
||||
$.ajax({
|
||||
url: "get_stream_type_by_top_10_users",
|
||||
type: 'get',
|
||||
data: { time_range: time_range, y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_user_by_stream_type_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_user_by_stream_type_options.series = data.series;
|
||||
var hc_plays_by_user_by_stream_type = new Highcharts.Chart(hc_plays_by_user_by_stream_type_options);
|
||||
@@ -457,15 +540,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
function loadGraphsTab3(yaxis) {
|
||||
function loadGraphsTab3(time_range, yaxis) {
|
||||
$('#days-selection').hide();
|
||||
$('#months-selection').show();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
url: "get_plays_per_month",
|
||||
type: 'get',
|
||||
data: { y_axis: yaxis },
|
||||
data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
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;
|
||||
@@ -476,104 +563,125 @@
|
||||
}
|
||||
|
||||
// Set initial state
|
||||
loadGraphsTab1(current_range, yaxis);
|
||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, 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);
|
||||
loadGraphsTab1(current_day_range, yaxis);
|
||||
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
|
||||
})
|
||||
|
||||
// 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);
|
||||
loadGraphsTab2(current_day_range, yaxis);
|
||||
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
|
||||
})
|
||||
|
||||
// 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);
|
||||
loadGraphsTab3(current_month_range, yaxis);
|
||||
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
|
||||
})
|
||||
|
||||
|
||||
// Date range changed
|
||||
$('#days-selection').on('change', function() {
|
||||
current_range = $('input[name=date-options]:checked', '#days-selection').val();
|
||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); }
|
||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); }
|
||||
$('.days').html(current_range);
|
||||
$('#graph-days').tooltip({ container: 'body', placement: 'top', html: true });
|
||||
$('#graph-days').on('change', function() {
|
||||
forceMinMax($(this));
|
||||
current_day_range = $(this).val();
|
||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
$('.days').html(current_day_range);
|
||||
$.post('set_graph_config', { graph_days: current_day_range });
|
||||
});
|
||||
|
||||
// Month range changed
|
||||
$('#graph-months').tooltip({ container: 'body', placement: 'top', html: true });
|
||||
$('#graph-months').on('change', function() {
|
||||
forceMinMax($(this));
|
||||
current_month_range = $(this).val();
|
||||
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
|
||||
$('.months').html(current_month_range);
|
||||
$.post('set_graph_config', { graph_months: current_month_range });
|
||||
});
|
||||
|
||||
// User changed
|
||||
$('#graph-user').on('change', function() {
|
||||
selected_user_id = $(this).val() || null;
|
||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
|
||||
});
|
||||
|
||||
// Y-axis changed
|
||||
$('#yaxis-selection').on('change', function() {
|
||||
yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val();
|
||||
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); }
|
||||
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
|
||||
$.post('set_graph_config', { graph_type: yaxis });
|
||||
});
|
||||
|
||||
function setGraphFormat(type) {
|
||||
if (type === 'plays') {
|
||||
yaxis_format = function() { return this.value; };
|
||||
tooltip_format = function() {
|
||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
|
||||
} else {
|
||||
var s = '<b>'+ this.x +'</b>';
|
||||
}
|
||||
if (this.points.length > 1) {
|
||||
var total = 0;
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+point.y;
|
||||
total += point.y;
|
||||
});
|
||||
s += '<br><b>Total: '+total+'</b>';
|
||||
} else {
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+point.y;
|
||||
});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||
var s = '<b>'+ moment(this.x).format('ddd MMM D') +'</b>';
|
||||
} else {
|
||||
var s = '<b>'+ this.x +'</b>';
|
||||
}
|
||||
if (this.points.length > 1) {
|
||||
var total = 0;
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+point.y;
|
||||
total += point.y;
|
||||
});
|
||||
s += '<br><b>Total: '+total+'</b>';
|
||||
} else {
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+point.y;
|
||||
});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
stack_labels_format = function() {
|
||||
return this.total;
|
||||
}
|
||||
|
||||
return this.total;
|
||||
}
|
||||
$('.yaxis-text').html('Play count');
|
||||
} else {
|
||||
yaxis_format = function() { return moment.duration(this.value, 'seconds').format("m [mins]"); };
|
||||
yaxis_format = function() { return moment.duration(this.value, 'hours').format('H [h] m [m]'); };
|
||||
tooltip_format = function() {
|
||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
|
||||
} else {
|
||||
var s = '<b>'+ this.x +'</b>';
|
||||
}
|
||||
if (this.points.length > 1) {
|
||||
var total = 0;
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'seconds').format('D [days] H [hrs] m [mins]');
|
||||
total += point.y;
|
||||
});
|
||||
s += '<br/><b>Total: '+moment.duration(total, 'seconds').format('D [days] H [hrs] m [mins]')+'</b>';
|
||||
} else {
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'seconds').format('D [days] H [hrs] m [mins]');
|
||||
});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
|
||||
var s = '<b>'+ moment(this.x).format('ddd MMM D') +'</b>';
|
||||
} else {
|
||||
var s = '<b>'+ this.x +'</b>';
|
||||
}
|
||||
if (this.points.length > 1) {
|
||||
var total = 0;
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');
|
||||
total += point.y;
|
||||
});
|
||||
s += '<br/><b>Total: '+moment.duration(total, 'hours').format('D [days] H [hrs] m [mins]')+'</b>';
|
||||
} else {
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');
|
||||
});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
stack_labels_format = function() {
|
||||
var s = moment.duration(this.total, 'seconds').format("H [hrs] m [mins]");
|
||||
return s;
|
||||
}
|
||||
var s = moment.duration(this.total, 'hours').format('H [h] m [m]');
|
||||
return s;
|
||||
}
|
||||
$('.yaxis-text').html('Play duration');
|
||||
}
|
||||
|
||||
|
@@ -1,67 +1,100 @@
|
||||
<%inherit file="base.html"/>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
|
||||
<link rel="stylesheet" href="interfaces/default/css/dataTables.colVis.css">
|
||||
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.colVis.css">
|
||||
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
|
||||
<div class='container-fluid'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> History</span>
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<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>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-trash-o"></i> Delete mode
|
||||
</button> 
|
||||
</div>
|
||||
% endif
|
||||
% if _session['user_group'] == 'admin':
|
||||
<div class="btn-group" id="user-selection">
|
||||
<label>
|
||||
<select name="history-user" id="history-user" class="btn" style="color: inherit;">
|
||||
<option value="">All Users</option>
|
||||
<option disabled>────────────</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
% endif
|
||||
<div class="btn-group" data-toggle="buttons" id="media_type-selection">
|
||||
<label class="btn btn-dark active">
|
||||
<input type="radio" name="media_type-filter" id="history-all" value="" autocomplete="off"> All
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-movies" value="movie" autocomplete="off"> Movies
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-tv_shows" value="episode" autocomplete="off"> TV Shows
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-dark refresh-history-button" id="refresh-history-list"><i class="fa fa-refresh"></i> Refresh history</button>
|
||||
</div>
|
||||
<div class="btn-group colvis-button-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<div class="table-card-back">
|
||||
<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="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>
|
||||
<th align="left" id="delete_row">Delete</th>
|
||||
<th align="left" id="date">Date</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>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</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>
|
||||
</%def>
|
||||
|
||||
<%def name="modalIncludes()">
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</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-delete" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-delete">
|
||||
<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>
|
||||
@@ -69,46 +102,53 @@
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
|
||||
<script src="interfaces/default/js/dataTables.colVis.js"></script>
|
||||
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.colVis.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function loadHistoryTable(media_type) {
|
||||
// Load user ids and names (for the selector)
|
||||
$.ajax({
|
||||
url: 'get_user_names',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
var select = $('#history-user');
|
||||
data.sort(function (a, b) {
|
||||
return a.friendly_name.localeCompare(b.friendly_name);
|
||||
});
|
||||
data.forEach(function (item) {
|
||||
select.append('<option value="' + item.user_id + '">' +
|
||||
item.friendly_name + '</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function loadHistoryTable(media_type, selected_user_id) {
|
||||
history_table_options.ajax = {
|
||||
url: 'get_history',
|
||||
type: 'post',
|
||||
type: 'POST',
|
||||
data: function (d) {
|
||||
return {
|
||||
'json_data': JSON.stringify(d),
|
||||
'media_type': media_type
|
||||
json_data: JSON.stringify(d),
|
||||
media_type: media_type,
|
||||
user_id: selected_user_id
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
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] });
|
||||
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');
|
||||
@@ -116,30 +156,38 @@
|
||||
media_type = $(selected_filter).val();
|
||||
history_table.draw();
|
||||
});
|
||||
|
||||
$('#history-user').on('change', function () {
|
||||
selected_user_id = $(this).val() || null;
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
var media_type = 'all';
|
||||
loadHistoryTable(media_type);
|
||||
var media_type = null;
|
||||
var selected_user_id = "${_session['user_group']}" == "admin" ? null : "${_session['user_id']}";
|
||||
loadHistoryTable(media_type, selected_user_id);
|
||||
|
||||
% if _session['user_group'] == 'admin':
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
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++) {
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
history_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
data: { row_id: history_to_delete[i] },
|
||||
type: 'POST',
|
||||
data: { row_id: row },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
@@ -157,6 +205,11 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
% endif
|
||||
});
|
||||
|
||||
$("#refresh-history-list").click(function() {
|
||||
history_table.draw();
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -5,19 +5,34 @@
|
||||
<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>
|
||||
% if data.get('media_type'):
|
||||
<% h = {'episode': 'TV Show', 'track': 'Music'} %>
|
||||
<i class="fa fa-history"></i> ${h.get(data['media_type'], data['media_type'].title())} History for <span id="date-header">${data['start_date']}</span>
|
||||
% elif data.get('transcode_decision'):
|
||||
<% h = {'copy': 'Direct Stream'} %>
|
||||
<i class="fa fa-history"></i> ${h.get(data['transcode_decision'], data['transcode_decision'].title())} History for <span id="date-header">${data['start_date']}</span>
|
||||
% else:
|
||||
<i class="fa fa-history"></i> History for <span id="date-header">${data['start_date']}</span>
|
||||
% endif
|
||||
</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="player">Player</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align="left" id="delete_row">Delete</th>
|
||||
<th align="left" id="date">Date</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>
|
||||
@@ -27,51 +42,31 @@
|
||||
<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 src="${http_root}js/tables/history_table.js${cache_param}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#date-header').html(moment('${data}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
||||
history_table_modal_options.ajax = {
|
||||
"url": "get_history",
|
||||
type: "post",
|
||||
$('#date-header').html(moment('${data["start_date"]}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
||||
history_table_options.ajax = {
|
||||
url: 'get_history',
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ),
|
||||
'grouping': false,
|
||||
'start_date': '${data}'
|
||||
};
|
||||
return {
|
||||
json_data: JSON.stringify(d),
|
||||
user_id: "${data['user_id']}",
|
||||
start_date: "${data['start_date']}",
|
||||
media_type: "${data.get('media_type')}",
|
||||
transcode_decision: "${data.get('transcode_decision')}"
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
history_table = $('#history_table').DataTable(history_table_modal_options);
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
history_table = $('#history_table_modal').DataTable(history_table_options);
|
||||
history_table.columns([0, 3, 4, 8, 10, 11]).visible(false);
|
||||
|
||||
// Move #info-modal to parent container
|
||||
if (!($('#history-modal').next().is('#info-modal'))) {
|
||||
$('#info-modal').appendTo($('#history-modal').parent());
|
||||
}
|
||||
$('#history-modal > #info-modal').remove();
|
||||
clearSearchButton('history_table_modal', history_table);
|
||||
|
||||
$('#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');
|
||||
$('#history-modal').on('shown.bs.modal', function() {
|
||||
history_table.columns.adjust().draw();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -39,11 +39,16 @@ 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' or 'last_watched' ==
|
||||
platform_type Returns the platform name for the associated stat.
|
||||
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>
|
||||
|
||||
@@ -52,756 +57,192 @@ DOCUMENTATION :: END
|
||||
|
||||
# Human readable duration
|
||||
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>" + str(int(minutes)) + "</h3><p>mins</p>"
|
||||
m, s = divmod(helpers.cast_to_int(seconds), 60)
|
||||
h, m = divmod(m, 60)
|
||||
return str(h).zfill(1) + ':' + str(m).zfill(2)
|
||||
%>
|
||||
|
||||
% if data:
|
||||
% if data[0]['stat_id']:
|
||||
<ul class="list-unstyled">
|
||||
% 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>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${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?item_id=${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=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?item_id=${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?item_id=${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 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>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${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=${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=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?item_id=${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?item_id=${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 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>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${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?item_id=${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?item_id=${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?item_id=${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 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>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${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=${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?item_id=${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?item_id=${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 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?item_id=${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?item_id=${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?item_id=${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?item_id=${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?item_id=${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=${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?item_id=${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?item_id=${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>
|
||||
</div>
|
||||
<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:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% 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']}">
|
||||
% if data and any(top_stat['rows'] for top_stat in data):
|
||||
% for top_stat in data:
|
||||
% if top_stat['rows']:
|
||||
<%
|
||||
stat_id = top_stat['stat_id']
|
||||
row0 = top_stat['rows'][0]
|
||||
%>
|
||||
<div class="dashboard-stats-instance" id="stats-instance-${stat_id}" data-stat_id="${stat_id}">
|
||||
<div class="dashboard-stats-container">
|
||||
<div class="dashboard-stats-background-overlay">
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
% if row0['art']:
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(pms_image_proxy?img=${row0['art']}&width=500&height=280&fallback=art);"></div>
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}" title="${top_stat['rows'][0]['friendly_name']}">
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(images/art.png);"></div>
|
||||
% endif
|
||||
% if top_stat['rows'][0]['user_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<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>
|
||||
</div>
|
||||
% elif stat_id == 'top_platforms':
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background platform-${row0['platform_name']} no-image"></div>
|
||||
% else:
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background flat"></div>
|
||||
% endif
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
<div class="dashboard-stats-poster-container hidden-xs">
|
||||
% if stat_id in ('top_music', 'popular_music'):
|
||||
<div id="stats-thumb-${stat_id}-bg" class="dashboard-stats-poster-blur" style="background-image: url(pms_image_proxy?img=${row0['thumb']}&width=300&height=300&fallback=cover);"></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 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>
|
||||
</div>
|
||||
<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>
|
||||
<% height, type = ('300', 'cover') if stat_id in ('top_music', 'popular_music') else ('450', 'poster') %>
|
||||
<% href = 'info?rating_key={}'.format(row0['rating_key']) if row0['rating_key'] else '#' %>
|
||||
<a id="stats-thumb-url-${stat_id}" href="${href}" title="${row0['title']}">
|
||||
% if row0['thumb']:
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${type}" style="background-image: url(pms_image_proxy?img=${row0['thumb']}&width=300&height=${height}&fallback=${type});"></div>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${type}" style="background-image: url(images/${type}.png);"></div>
|
||||
% endif
|
||||
</a>
|
||||
</div>
|
||||
% elif stat_id == 'top_users':
|
||||
<% user_href = 'user?user_id={}'.format(row0['user_id']) if row0['user_id'] else '#' %>
|
||||
<a id="stats-thumb-url-${stat_id}" href="${user_href}" title="${row0['friendly_name']}" class="hidden-xs">
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-circle" style="background-image: url(${row0['user_thumb'] or 'images/gravatar-default.png'})"></div>
|
||||
</a>
|
||||
% elif stat_id == 'top_platforms':
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon platform-${row0['platform_name']} transparent hidden-xs"></div>
|
||||
% elif stat_id == 'most_concurrent':
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon stats-${stat_id} transparent hidden-xs"></div>
|
||||
% endif
|
||||
<div class="dashboard-stats-info-container">
|
||||
<div id="stats-title-${stat_id}" class="dashboard-stats-info-title">
|
||||
<h4>${top_stat['stat_title']}</h4>
|
||||
% if stat_id.startswith('top') and top_stat.get('stat_type') == 'total_plays':
|
||||
<span class="dashboard-stats-stats-units">plays</span>
|
||||
% elif stat_id.startswith('top') and top_stat.get('stat_type') == 'total_duration':
|
||||
<span class="dashboard-stats-stats-units">hh:mm</span>
|
||||
% elif stat_id.startswith('popular'):
|
||||
<span class="dashboard-stats-stats-units">users</span>
|
||||
% elif stat_id == 'last_watched':
|
||||
<span class="dashboard-stats-stats-units" id="last-watched-header-info">${row0['friendly_name']}</span>
|
||||
% elif stat_id == 'most_concurrent':
|
||||
<span class="dashboard-stats-stats-units" id="most-concurrent-header-info">streams</span>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<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}
|
||||
<div class="dashboard-stats-info-scroller scrollbar-macosx">
|
||||
<div class="dashboard-stats-info scoller-content">
|
||||
<ul class="list-unstyled dashboard-stats-info-list">
|
||||
% for row in top_stat['rows']:
|
||||
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}" data-stat_id="${stat_id}" data-rating_key="${row.get('rating_key')}" data-title="${row.get('title')}"
|
||||
data-art="${row.get('art')}" data-thumb="${row.get('thumb')}" data-platform="${row.get('platform_name')}"
|
||||
data-user_id="${row.get('user_id')}" data-friendly_name="${row.get('friendly_name')}" data-user_thumb="${row.get('user_thumb')}"
|
||||
data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}">
|
||||
<div class="sub-list">${loop.index + 1}</div>
|
||||
<div class="sub-value">
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
<% href = 'info?rating_key={}'.format(row['rating_key']) if row['rating_key'] else '#' %>
|
||||
<a href="${href}" title="${row['title']}">
|
||||
${row['title']}
|
||||
</a>
|
||||
% elif stat_id == 'top_users':
|
||||
<% user_href = 'user?user_id={}'.format(row['user_id']) if row['user_id'] else '#' %>
|
||||
<a href="${user_href}" title="${row['friendly_name']}">
|
||||
${row['friendly_name']}
|
||||
</a>
|
||||
% elif stat_id == 'top_platforms':
|
||||
${row['platform']}
|
||||
% elif stat_id == 'most_concurrent':
|
||||
${row['title']}
|
||||
% 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 class="sub-count">
|
||||
% if stat_id.startswith('top') and top_stat.get('stat_type') == 'total_plays':
|
||||
${row['total_plays']}
|
||||
% elif stat_id.startswith('top') and top_stat.get('stat_type') == 'total_duration':
|
||||
${row['total_duration'] | hd}
|
||||
% elif stat_id.startswith('popular'):
|
||||
${row['users_watched']}
|
||||
% elif stat_id == 'most_concurrent':
|
||||
${row['count']}
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
</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&item_id=${top_stat['rows'][0]['row_id']}" 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]['platform_type']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}" title="${top_stat['rows'][0]['title']}">
|
||||
% if top_stat['rows'][0]['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&item_id=${top_stat['rows'][loop.index]['row_id']}" 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]['platform_type']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}" title="${top_stat['rows'][loop.index]['title']}">
|
||||
% if top_stat['rows'][loop.index]['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>
|
||||
</div>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
% endfor
|
||||
<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);
|
||||
$('.dashboard-stats-instance .dashboard-stats-info-scroller').scrollbar()
|
||||
|
||||
function changeImages(elem) {
|
||||
var stat_id = $(elem).data('stat_id');
|
||||
var art = $(elem).data('art');
|
||||
var thumb = $(elem).data('thumb');
|
||||
var user_id = $(elem).data('user_id');
|
||||
var user_thumb = $(elem).data('user_thumb');
|
||||
var rating_key = $(elem).data('rating_key');
|
||||
var [height, fallback] = ($.inArray(stat_id, ['top_music', 'popular_music']) > -1) ? [300, 'cover'] : [450, 'poster'];
|
||||
var href;
|
||||
|
||||
if (stat_id == 'most_concurrent') {
|
||||
return
|
||||
} else if (stat_id == 'top_users') {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + (user_thumb || 'images/gravatar-default.png') + ')');
|
||||
if (user_id) {
|
||||
href = 'user?user_id=' + user_id;
|
||||
} else {
|
||||
href = '#';
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('friendly_name'));
|
||||
} else if (stat_id == 'top_platforms') {
|
||||
$('#stats-thumb-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform'));
|
||||
$('#stats-background-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform'));
|
||||
} else {
|
||||
if (rating_key) {
|
||||
href = 'info?rating_key=' + rating_key;
|
||||
} else {
|
||||
href = '#';
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('title'));
|
||||
if (art) {
|
||||
$('#stats-background-' + stat_id).css('background-image', 'url(pms_image_proxy?img=' + art + '&width=500&height=280&fallback=art)');
|
||||
} else {
|
||||
$('#stats-background-' + stat_id).css('background-image', 'url(images/art.png)');
|
||||
}
|
||||
if (thumb) {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(pms_image_proxy?img=' + thumb + '&width=300&height=' + height + '&fallback=' + fallback + ')');
|
||||
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(pms_image_proxy?img=' + thumb + '&width=300&height=' + height + '&fallback=' + fallback + ')');
|
||||
} else {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(images/' + fallback + '.png)');
|
||||
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(images/' + fallback + '.png)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('.dashboard-stats-info-item').mouseenter(function () {
|
||||
changeImages(this)
|
||||
if ($(this).data('stat_id') == 'last_watched') {
|
||||
var friendly_name = $(this).data('friendly_name');
|
||||
var last_watch = moment($(this).data('last_watch'), 'X').format(date_format);
|
||||
$('#last-watched-header-info').html(friendly_name);
|
||||
} else if ($(this).data('stat_id') == 'most_concurrent') {
|
||||
var started = moment($(this).data('started'), 'X').format(date_format + ' ' + time_format);
|
||||
$('#most-concurrent-header-info').html(started);
|
||||
}
|
||||
});
|
||||
$('.dashboard-stats-instance').mouseleave(function () {
|
||||
changeImages($(this).find('.dashboard-stats-info-item').first())
|
||||
if ($(this).data('stat_id') == 'last_watched') {
|
||||
$('#last-watched-header-info').text($(this).find('.dashboard-stats-info-item').first().data('friendly_name'));
|
||||
} else if ($(this).data('stat_id') == 'most_concurrent') {
|
||||
$('#most-concurrent-header-info').text('streams');
|
||||
}
|
||||
});
|
||||
</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
|
BIN
data/interfaces/default/images/13AD46AC.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
data/interfaces/default/images/13AD46AD.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
data/interfaces/default/images/93124B54.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
data/interfaces/default/images/93124B55.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
data/interfaces/default/images/art.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
data/interfaces/default/images/become_a_patron_button.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
data/interfaces/default/images/en-play-badge.png
Normal file
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 4.3 KiB |
BIN
data/interfaces/default/images/favicon/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
9
data/interfaces/default/images/favicon/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="${http_root}images/favicon/mstile-150x150.png?v=2.0.5"/>
|
||||
<TileColor>#282a2d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
data/interfaces/default/images/favicon/favicon-16x16.png
Normal file
After Width: | Height: | Size: 553 B |
BIN
data/interfaces/default/images/favicon/favicon-32x32.png
Normal file
After Width: | Height: | Size: 971 B |
BIN
data/interfaces/default/images/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
18
data/interfaces/default/images/favicon/manifest.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Tautulli",
|
||||
"icons": [
|
||||
{
|
||||
"src": "${http_root}images/favicon/android-chrome-192x192.png?v=2.0.5",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "${http_root}images/favicon/android-chrome-256x256.png?v=2.0.5",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#282a2d",
|
||||
"background_color": "#282a2d",
|
||||
"display": "standalone"
|
||||
}
|
BIN
data/interfaces/default/images/favicon/mstile-150x150.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none"><path d="M5695 6555 c-135 -34 -244 -94 -342 -189 -40 -39 -73 -76 -73 -83 0 -7 -4 -13 -10 -13 -14 0 -87 -156 -106 -225 -22 -83 -26 -234 -8 -320 17 -79 86 -230 133 -288 l30 -39 -48 -71 c-39 -57 -159 -228 -251 -357 -69 -97 -398 -564 -416 -590 -13 -19 -60 -87 -105 -150 -45 -63 -107 -151 -138 -195 -30 -44 -59 -84 -63 -90 -7 -9 -251 -354 -346 -490 -92 -131 -173 -245 -175 -245 -1 0 -34 9 -72 21 -130 38 -325 31 -454 -18 -168 -63 -313 -196 -385 -354 -39 -87 -65 -183 -68 -256 0 -24 -3 -43 -4 -43 -2 0 -43 46 -91 102 -49 57 -100 117 -115 133 -14 17 -128 149 -253 295 -125 146 -251 292 -279 324 -56 65 -77 89 -108 126 -58 68 -152 178 -172 200 -12 14 -50 57 -83 96 l-61 71 27 44 c58 93 91 217 92 342 2 161 -38 294 -125 412 -133 181 -316 279 -542 292 -470 27 -833 -434 -699 -887 74 -251 275 -437 530 -490 132 -28 334 -6 421 45 l42 24 173 -197 c96 -108 186 -210 200 -227 15 -16 163 -187 330 -380 458 -529 491 -567 526 -605 18 -19 31 -35 30 -36 -6 -5 -265 -161 -277 -167 -8 -4 -34 -20 -58 -35 -194 -124 -634 -382 -651 -382 -12 0 -46 20 -75 44 -60 49 -180 112 -242 127 -21 5 -48 12 -59 15 -11 4 -65 9 -121 11 -81 4 -117 1 -182 -15 -261 -66 -462 -270 -528 -537 -10 -40 -11 -217 -2 -258 5 -23 11 -51 14 -61 29 -145 147 -312 284 -403 123 -82 224 -114 370 -118 83 -3 124 2 240 29 36 9 133 57 187 94 60 41 111 91 153 152 14 19 28 37 32 40 19 15 71 140 89 217 17 73 20 107 16 198 -4 61 -7 121 -9 134 -3 28 -46 0 482 321 179 108 379 228 444 265 104 59 120 65 133 52 13 -13 12 -22 -10 -78 -49 -123 -58 -165 -62 -262 -7 -149 25 -286 89 -383 47 -72 91 -128 125 -158 19 -17 39 -36 45 -42 27 -25 136 -94 150 -94 8 0 17 -4 20 -9 3 -5 16 -11 28 -14 13 -3 50 -12 83 -21 74 -19 278 -15 345 7 198 65 358 196 435 358 16 34 20 36 49 28 17 -4 49 -10 71 -14 22 -3 99 -16 170 -30 72 -13 144 -26 160 -29 28 -5 101 -18 170 -31 17 -3 80 -14 140 -25 61 -11 124 -22 140 -25 17 -4 49 -9 72 -12 40 -5 42 -7 48 -47 14 -98 29 -147 73 -235 36 -75 61 -110 121 -171 154 -154 280 -210 480 -213 134 -2 180 5 273 40 212 83 371 262 427 481 24 93 25 255 2 342 -64 241 -245 428 -481 501 -62 18 -97 23 -200 22 -107 0 -136 -4 -205 -26 -44 -15 -109 -43 -145 -64 -83 -48 -208 -171 -250 -245 -17 -32 -35 -60 -38 -61 -4 -2 -46 4 -93 13 -48 10 -104 20 -125 23 -22 3 -46 8 -54 11 -8 3 -33 7 -55 10 -38 5 -58 9 -122 21 -16 3 -53 10 -83 15 -30 6 -66 12 -79 15 -13 2 -103 19 -200 36 -169 30 -207 42 -196 60 10 16 -28 155 -62 224 -19 39 -54 96 -78 127 l-45 58 40 52 c96 125 143 266 143 433 1 164 -27 263 -108 391 -19 30 -35 57 -35 61 0 3 31 49 69 102 57 81 450 638 625 889 28 40 62 88 76 107 14 18 194 274 400 568 291 414 379 534 393 531 10 -2 27 -6 37 -9 78 -25 240 -29 338 -9 433 87 677 573 489 974 -93 200 -255 332 -478 389 -87 22 -227 25 -304 6z"/></g></svg>
|
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 66 KiB |
BIN
data/interfaces/default/images/gold-rect-paypal-34px.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="483.904px" height="409.539px" viewBox="66.712 194.513 483.904 409.539"
|
||||
enable-background="new 66.712 194.513 483.904 409.539" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="none" d="M41.209,627.586c0-151.979,0-303.963,0-456.118c177.822,0,355.688,0,533.881,0c0,151.922,0,304.021,0,456.118
|
||||
C397.214,627.586,219.233,627.586,41.209,627.586z M461.314,421.571c-110.652,0-220.389,0-329.873,0c0,18.87,0,36.226,0,54.729
|
||||
c110.29,0,219.734,0,329.824,0c0,6.858-0.271,12.563,0.058,18.231c0.659,11.344,8.853,15.958,18.112,9.46
|
||||
c22.087-15.499,43.824-31.503,65.474-47.611c7.678-5.715,7.621-10.63-0.234-16.467c-21.352-15.862-42.87-31.508-64.563-46.902
|
||||
c-9.542-6.773-17.488-2.864-18.691,8.976C460.813,407.959,461.314,414.046,461.314,421.571z M130.996,574.741
|
||||
c74.536,0,148.057,0,222.423,0c0,6.464-0.617,11.836,0.192,16.98c0.662,4.21,2.17,10.746,4.826,11.622
|
||||
c4.329,1.427,10.994,0.66,14.77-1.943c21.297-14.688,42.098-30.1,62.911-45.474c8.887-6.564,8.992-11.503,0.233-18.001
|
||||
c-20.777-15.419-41.647-30.727-62.83-45.579c-3.569-2.503-10.381-4.832-13.03-3.118c-3.676,2.376-5.784,8.447-6.81,13.308
|
||||
c-1.126,5.336-0.257,11.091-0.257,17.757c-75.009,0-148.525,0-222.43,0C130.996,538.558,130.996,555.663,130.996,574.741z
|
||||
M130.83,281.392c49.453,0,97.705,0,146.88,0c0,6.838-0.232,12.506,0.049,18.148c0.585,11.758,9.078,16.246,18.887,9.342
|
||||
c21.767-15.316,43.232-31.064,64.624-46.902c8.01-5.932,7.995-11.301-0.134-17.342c-21.053-15.65-42.257-31.103-63.625-46.318
|
||||
c-10.735-7.647-18.836-3.478-19.734,9.846c-0.378,5.611-0.063,11.27-0.063,18.431c-49.546,0-98.1,0-146.885,0
|
||||
C130.83,245.229,130.83,262.325,130.83,281.392z M188.205,379.701c0,6.939-0.606,12.297,0.19,17.438
|
||||
c0.649,4.198,2.088,10.688,4.753,11.61c4.316,1.496,11.054,0.904,14.8-1.673c21.612-14.857,42.736-30.435,63.834-46.026
|
||||
c8.481-6.265,8.471-11.812-0.159-18.227c-21.054-15.647-42.241-31.123-63.625-46.315c-10.656-7.571-18.899-3.211-19.724,10.07
|
||||
c-0.365,5.875-0.062,11.793-0.062,18.885c-20.45,0-38.938,0-57.202,0c0,18.619,0,35.973,0,54.238
|
||||
C149.953,379.701,168.131,379.701,188.205,379.701z M66.712,280.18c17.568,0,33.699,0,49.59,0c0-18.261,0-35.579,0-53.046
|
||||
c-16.925,0-33.07,0-49.59,0C66.712,245.159,66.712,262.481,66.712,280.18z M66.869,325.488c0,18.556,0,35.876,0,53.543
|
||||
c16.862,0,33,0,49.278,0c0-18.274,0-35.852,0-53.543C99.359,325.488,83.5,325.488,66.869,325.488z M116.673,422.747
|
||||
c-17.484,0-33.644,0-49.668,0c0,17.835,0,34.781,0,51.779c17.017,0,33.178,0,49.668,0
|
||||
C116.673,456.924,116.673,440.208,116.673,422.747z M116.497,520.842c-17.01,0-33.43,0-49.552,0c0,18.244,0,35.566,0,53.021
|
||||
c16.944,0,33.084,0,49.552,0C116.497,555.793,116.497,538.471,116.497,520.842z"/>
|
||||
<path fill="#FFFFFF" d="M461.314,421.571c0-7.525-0.503-13.612,0.105-19.586c1.203-11.839,9.149-15.749,18.691-8.976
|
||||
c21.691,15.396,43.211,31.04,64.563,46.902c7.855,5.837,7.912,10.752,0.234,16.467c-21.648,16.108-43.387,32.112-65.474,47.611
|
||||
c-9.261,6.498-17.453,1.884-18.112-9.46c-0.329-5.67-0.058-11.373-0.058-18.231c-110.09,0-219.534,0-329.824,0
|
||||
c0-18.503,0-35.857,0-54.729C240.927,421.571,350.662,421.571,461.314,421.571z"/>
|
||||
<path fill="#FFFFFF" d="M130.996,574.741c0-19.078,0-36.185,0-54.448c73.904,0,147.42,0,222.43,0
|
||||
c0-6.666-0.869-12.421,0.257-17.757c1.024-4.859,3.134-10.932,6.81-13.308c2.649-1.714,9.461,0.615,13.029,3.118
|
||||
c21.184,14.854,42.054,30.16,62.831,45.579c8.759,6.498,8.652,11.437-0.233,18.001c-20.813,15.374-41.614,30.786-62.911,45.474
|
||||
c-3.774,2.604-10.439,3.369-14.77,1.943c-2.656-0.876-4.165-7.412-4.826-11.622c-0.811-5.146-0.192-10.518-0.192-16.98
|
||||
C279.053,574.741,205.532,574.741,130.996,574.741z"/>
|
||||
<path fill="#FFFFFF" d="M130.83,281.392c0-19.067,0-36.163,0-54.795c48.785,0,97.339,0,146.885,0
|
||||
c0-7.161-0.315-12.82,0.063-18.431c0.898-13.323,8.999-17.493,19.734-9.846c21.368,15.215,42.572,30.668,63.625,46.318
|
||||
c8.129,6.041,8.144,11.411,0.133,17.342c-21.391,15.838-42.855,31.586-64.623,46.902c-9.81,6.904-18.302,2.417-18.887-9.342
|
||||
c-0.281-5.643-0.049-11.31-0.049-18.148C228.535,281.392,180.283,281.392,130.83,281.392z"/>
|
||||
<path fill="#FFFFFF" d="M188.205,379.701c-20.074,0-38.252,0-57.192,0c0-18.265,0-35.619,0-54.238c18.264,0,36.752,0,57.202,0
|
||||
c0-7.092-0.303-13.01,0.062-18.885c0.825-13.281,9.067-17.641,19.724-10.07c21.383,15.193,42.57,30.668,63.625,46.315
|
||||
c8.63,6.416,8.64,11.962,0.159,18.227c-21.099,15.591-42.222,31.169-63.834,46.026c-3.746,2.577-10.484,3.169-14.8,1.673
|
||||
c-2.666-0.923-4.104-7.412-4.753-11.61C187.599,391.999,188.205,386.64,188.205,379.701z"/>
|
||||
<path fill="#FFFFFF" d="M66.712,280.18c0-17.699,0-35.021,0-53.046c16.52,0,32.665,0,49.59,0c0,17.468,0,34.786,0,53.046
|
||||
C100.411,280.18,84.281,280.18,66.712,280.18z"/>
|
||||
<path fill="#FFFFFF" d="M66.869,325.488c16.63,0,32.49,0,49.278,0c0,17.692,0,35.27,0,53.543c-16.278,0-32.416,0-49.278,0
|
||||
C66.869,361.364,66.869,344.044,66.869,325.488z"/>
|
||||
<path fill="#E5A00D" d="M116.673,422.747c0,17.461,0,34.177,0,51.779c-16.49,0-32.652,0-49.668,0c0-16.998,0-33.944,0-51.779
|
||||
C83.029,422.747,99.188,422.747,116.673,422.747z"/>
|
||||
<path fill="#FFFFFF" d="M116.497,520.842c0,17.629,0,34.951,0,53.021c-16.468,0-32.608,0-49.552,0c0-17.455,0-34.777,0-53.021
|
||||
C83.067,520.842,99.487,520.842,116.497,520.842z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
5
data/interfaces/default/images/libraries/artist.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>artist</title>
|
||||
<path fill="#fff" d="M9.201 24.681c0-6.699 0-13.358 0-20.035 7.594-1.505 15.157-3.004 22.768-4.513 0 0.172 0 0.319 0 0.465 0 7.067-0.026 14.135 0.021 21.202 0.010 1.498-0.59 2.57-1.716 3.423-1.999 1.512-4.26 2.145-6.751 1.88-0.504-0.054-1.020-0.205-1.481-0.418-1.502-0.695-1.856-2.122-0.908-3.48 0.826-1.184 1.99-1.924 3.302-2.433 1.362-0.528 2.774-0.843 4.252-0.719 0.324 0.027 0.646 0.084 0.994 0.13 0-4.345 0-8.679 0-13.050-6.062 1.204-12.099 2.404-18.16 3.608 0 0.196 0 0.345 0 0.495 0 5.174-0.006 10.349 0.004 15.523 0.003 1.409-0.802 2.302-1.854 3.056-0.889 0.637-1.859 1.114-2.906 1.426-1.524 0.453-3.067 0.627-4.619 0.169-0.952-0.281-1.789-0.736-2.010-1.83-0.136-0.673 0.098-1.269 0.459-1.822 0.772-1.183 1.947-1.853 3.193-2.388 1.662-0.714 3.394-1.043 5.207-0.698 0.048 0.009 0.099 0.005 0.206 0.010z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 979 B |
5
data/interfaces/default/images/libraries/movie.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>movie</title>
|
||||
<path fill="#fff" d="M0 1.416c10.695 0 21.315 0 32 0 0 9.719 0 19.417 0 29.168-10.635 0-21.293 0-32 0 0-9.699 0-19.399 0-29.168zM9.202 15.129c4.535 0 9.065 0 13.609 0 0-3.798 0-7.579 0-11.355-4.563 0-9.075 0-13.609 0 0 3.801 0 7.551 0 11.355zM9.215 28.909c4.581 0 9.093 0 13.61 0 0-3.818 0-7.585 0-11.382-4.549 0-9.062 0-13.61 0 0 3.803 0 7.57 0 11.382zM6.813 5.983c0-0.753 0-1.467 0-2.209-1.138 0-2.235 0-3.33 0 0 0.766 0 1.48 0 2.209 1.131 0 2.21 0 3.33 0zM25.231 3.754c0 0.783 0 1.494 0 2.219 1.141 0 2.235 0 3.33 0 0-0.763 0-1.476 0-2.219-1.12 0-2.198 0-3.33 0zM25.233 12.938c0 0.777 0 1.492 0 2.19 1.149 0 2.248 0 3.335 0 0-0.754 0-1.452 0-2.19-1.116 0-2.197 0-3.335 0zM25.227 22.074c0 0.783 0 1.496 0 2.218 1.139 0 2.235 0 3.335 0 0-0.758 0-1.471 0-2.218-1.119 0-2.196 0-3.335 0zM3.472 26.689c0 0.768 0 1.481 0 2.221 1.133 0 2.226 0 3.34 0 0-0.763 0-1.475 0-2.221-1.119 0-2.197 0-3.34 0zM28.579 26.711c-1.112 0-2.225 0-3.353 0 0 0.749 0 1.462 0 2.202 1.137 0 2.233 0 3.353 0 0-0.748 0-1.447 0-2.202zM6.817 15.155c0-0.774 0-1.468 0-2.21-1.133 0-2.229 0-3.338 0 0 0.761 0 1.473 0 2.21 1.127 0 2.207 0 3.338 0zM3.463 19.73c1.165 0 2.242 0 3.346 0 0-0.759 0-1.469 0-2.189-1.143 0-2.239 0-3.346 0 0 0.748 0 1.446 0 2.189zM28.559 19.733c0-0.764 0-1.479 0-2.188-1.144 0-2.241 0-3.34 0 0 0.752 0 1.448 0 2.188 1.114 0 2.195 0 3.34 0zM6.791 24.304c0-0.798 0-1.511 0-2.215-1.145 0-2.225 0-3.312 0 0 0.766 0 1.481 0 2.215 1.129 0 2.211 0 3.312 0zM3.489 8.378c0 0.771 0 1.464 0 2.145 1.138 0 2.219 0 3.319 0 0-0.74 0-1.431 0-2.145-1.129 0-2.211 0-3.319 0zM28.554 10.538c0-0.775 0-1.464 0-2.137-1.146 0-2.242 0-3.319 0 0 0.747 0 1.438 0 2.137 1.131 0 2.21 0 3.319 0z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
6
data/interfaces/default/images/libraries/photo.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>photo</title>
|
||||
<path fill="#fff" d="M25.208 2.266c0 1.514 0 2.976 0 4.513 2.281 0 4.505 0 6.792 0 0 7.681 0 15.287 0 22.955-10.643 0-21.3 0-32 0 0-7.626 0-15.236 0-22.913 2.258 0 4.486 0 6.792 0 0-1.546 0-3.029 0-4.555 6.152 0 12.226 0 18.415 0zM16.008 9.209c-5.026-0.004-9.12 4.079-9.123 9.099-0.004 4.961 4.099 9.069 9.074 9.085 5.022 0.016 9.124-4.047 9.159-9.070 0.035-4.985-4.087-9.109-9.109-9.114z"></path>
|
||||
<path fill="#fff" d="M20.601 18.292c0.003 2.551-2.070 4.626-4.61 4.613-2.558-0.013-4.595-2.069-4.591-4.634 0.003-2.524 2.038-4.557 4.577-4.572 2.562-0.015 4.621 2.030 4.624 4.593z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 746 B |
12
data/interfaces/default/images/libraries/playlist.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>playlist</title>
|
||||
<path fill="#fff" d="M9.167 10.268c7.653 0 15.208 0 22.82 0 0 1.147 0 2.268 0 3.451-7.583 0-15.172 0-22.82 0 0-1.134 0-2.274 0-3.451z"></path>
|
||||
<path fill="#fff" d="M9.14 21.73c0-1.152 0-2.257 0-3.424 7.609 0 15.212 0 22.86 0 0 1.132 0 2.252 0 3.424-7.607 0-15.191 0-22.86 0z"></path>
|
||||
<path fill="#fff" d="M9.217 5.679c0-1.145 0-2.23 0-3.363 7.562 0 15.101 0 22.683 0 0 1.113 0 2.213 0 3.363-7.539 0-15.080 0-22.683 0z"></path>
|
||||
<path fill="#fff" d="M9.225 29.708c0-1.132 0-2.214 0-3.336 7.558 0 15.086 0 22.668 0 0 1.086 0 2.186 0 3.336-7.526 0-15.071 0-22.668 0z"></path>
|
||||
<path fill="#fff" d="M0.007 10.279c1.58 0 3.043 0 4.551 0 0 1.153 0 2.272 0 3.444-1.511 0-3.011 0-4.551 0 0-1.157 0-2.292 0-3.444z"></path>
|
||||
<path fill="#fff" d="M4.527 2.292c0 1.183 0 2.284 0 3.424-1.496 0-2.957 0-4.479 0 0-1.142 0-2.276 0-3.424 1.51 0 2.971 0 4.479 0z"></path>
|
||||
<path fill="#fff" d="M4.571 18.3c0 1.151 0 2.254 0 3.416-1.515 0-3.019 0-4.571 0 0-1.127 0-2.247 0-3.416 1.513 0 3.001 0 4.571 0z"></path>
|
||||
<path fill="#fff" d="M4.54 26.352c0 1.137 0 2.218 0 3.354-1.494 0-2.975 0-4.506 0 0-1.094 0-2.192 0-3.354 1.489 0 2.965 0 4.506 0z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
6
data/interfaces/default/images/libraries/show.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>show</title>
|
||||
<path fill="#fff" d="M0 26.565c0-8.38 0-16.706 0-25.089 10.661 0 21.307 0 32 0 0 8.343 0 16.686 0 25.089-10.637 0-21.283 0-32 0zM3.514 4.937c0 5.355 0 10.634 0 15.901 8.375 0 16.691 0 24.994 0 0-5.331 0-10.612 0-15.901-8.356 0-16.656 0-24.994 0z"></path>
|
||||
<path fill="#fff" d="M6.874 30.524c0-0.553 0-1.056 0-1.602 6.084 0 12.136 0 18.25 0 0 0.509 0 1.029 0 1.602-6.050 0-12.12 0-18.25 0z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 555 B |
5
data/interfaces/default/images/libraries/video.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>video</title>
|
||||
<path fill="#fff" d="M20.662 27.439c-6.971 0-13.797 0-20.662 0 0-7.639 0-15.235 0-22.878 6.846 0 13.672 0 20.588 0 0 2.962 0 5.932 0 9.091 3.87-2.316 7.591-4.542 11.412-6.828 0 6.153 0 12.176 0 18.33-3.769-2.257-7.494-4.488-11.338-6.789 0 3.070 0 6.035 0 9.075z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 430 B |
BIN
data/interfaces/default/images/logo-circle.png
Normal file
After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 8.2 KiB |
BIN
data/interfaces/default/images/logo-tautulli-100.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
data/interfaces/default/images/logo-tautulli-45.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
data/interfaces/default/images/logo-tautulli.png
Normal file
After Width: | Height: | Size: 22 KiB |
73
data/interfaces/default/images/logo-tautulli.svg
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="900px"
|
||||
height="300px" viewBox="0 0 900 300" enable-background="new 0 0 900 300" xml:space="preserve">
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path fill="none" d="M401.343,203.396c7.493,0,14.836,0,22.407,0c-3.766-12.354-7.495-24.586-11.333-37.176
|
||||
C408.665,178.818,405.017,191.064,401.343,203.396z"/>
|
||||
<path fill="#FFFFFF" d="M436.772,189.627c-4.399-12.521-8.814-25.034-13.224-37.553c-7.552,0-15.105,0-22.657,0.002
|
||||
c-0.14,0.611-0.221,1.246-0.428,1.836c-8.502,24.335-17.017,48.666-25.526,72.996c-1.45,4.146-2.887,8.298-4.362,12.535
|
||||
c6.686,0,13.198,0,19.792,0c2.095-6.873,4.172-13.693,6.248-20.514c10.593,0,21.056,0,31.594,0
|
||||
c2.102,6.894,4.177,13.707,6.245,20.489c6.66,0,13.146,0,19.772,0c-0.229-0.676-0.408-1.213-0.597-1.747
|
||||
C448.014,221.656,442.4,205.639,436.772,189.627z M401.343,203.396c3.674-12.332,7.322-24.578,11.074-37.176
|
||||
c3.837,12.59,7.567,24.82,11.333,37.176C416.179,203.396,408.836,203.396,401.343,203.396z"/>
|
||||
<path fill="#FFFFFF" d="M371.072,152.459c-21.916,0-43.668,0-65.421,0c0,5.111,0,10.227,0,15.341c7.836,0,15.672,0,23.691,0
|
||||
c0,24.046,0,47.786,0,71.621c6.164,0,12.144,0,18.361,0c0-23.901,0-47.698,0-71.682c7.948,0,15.66,0,23.369,0
|
||||
C371.072,162.549,371.072,157.549,371.072,152.459z"/>
|
||||
<path fill="#FFFFFF" d="M532.615,152.555c-6.176,0-12.154,0-18.349,0c0,0.842,0,1.592,0,2.34c0,16.42,0.021,32.838-0.021,49.258
|
||||
c-0.006,2.675-0.119,5.373-0.494,8.018c-0.997,7.07-4.634,11.153-11.06,12.587c-3.731,0.83-7.491,0.754-11.238,0
|
||||
c-5.745-1.157-9.344-4.668-10.58-10.297c-0.678-3.088-0.958-6.317-0.976-9.487c-0.094-16.738-0.045-33.478-0.045-50.217
|
||||
c0-0.732,0-1.467,0-2.148c-6.296,0-12.275,0-18.329,0c0,0.591,0,1.035,0,1.48c0,18.209-0.013,36.416,0.018,54.625
|
||||
c0.003,1.908,0.127,3.835,0.396,5.727c1.632,11.408,7.683,19.563,18.562,23.516c11.146,4.052,22.489,3.83,33.593-0.393
|
||||
c7.937-3.017,13.483-8.627,16.483-16.617c1.474-3.93,2.044-8.024,2.042-12.211c-0.006-18.146-0.002-36.289-0.002-54.436
|
||||
C532.615,153.744,532.615,153.188,532.615,152.555z"/>
|
||||
<path fill="#FFFFFF" d="M688.532,152.555c-6.16,0-12.14,0-18.349,0c0,0.924,0,1.62,0,2.318c0,16.482,0.021,32.967-0.021,49.449
|
||||
c-0.006,2.609-0.125,5.244-0.49,7.824c-1.003,7.127-4.74,11.291-11.226,12.654c-3.674,0.771-7.373,0.702-11.056-0.037
|
||||
c-5.74-1.156-9.354-4.652-10.596-10.281c-0.682-3.086-0.964-6.317-0.979-9.487c-0.094-16.738-0.044-33.478-0.044-50.216
|
||||
c0-0.739,0-1.478,0-2.18c-6.278,0-12.26,0-18.332,0c0,0.582,0,1.024,0,1.467c0.003,18.271-0.011,36.543,0.021,54.815
|
||||
c0.002,1.847,0.132,3.707,0.391,5.533c1.626,11.411,7.669,19.567,18.546,23.533c11.213,4.09,22.621,3.836,33.771-0.445
|
||||
c7.851-3.019,13.345-8.612,16.315-16.541c1.521-4.052,2.06-8.274,2.056-12.592c-0.018-17.953-0.008-35.905-0.008-53.858
|
||||
C688.532,153.893,688.532,153.27,688.532,152.555z"/>
|
||||
<path fill="#FFFFFF" d="M542.405,167.793c7.813,0,15.522,0,23.479,0c0,23.979,0,47.771,0,71.648c6.172,0,12.102,0,18.425,0
|
||||
c0-23.948,0-47.738,0-71.729c7.947,0,15.655,0,23.378,0c0-5.158,0-10.115,0-15.115c-21.819,0-43.53,0-65.281,0
|
||||
C542.405,157.691,542.405,162.648,542.405,167.793z"/>
|
||||
<path fill="#FFFFFF" d="M725.407,152.58c-6.199,0-12.177,0-18.193,0c0,29.021,0,57.92,0,86.857c17.832,0,35.528,0,53.335,0
|
||||
c0-5.191,0-10.146,0-15.307c-11.72,0-23.322,0-35.142,0C725.407,200.143,725.407,176.361,725.407,152.58z"/>
|
||||
<path fill="#FFFFFF" d="M790.668,152.593c-6.197,0-12.172,0-18.216,0c0,28.985,0,57.833,0,86.831c17.905,0,35.6,0,53.371,0
|
||||
c0-5.105,0-10.107,0-15.316c-11.729,0-23.335,0-35.155,0C790.668,200.119,790.668,176.339,790.668,152.593z"/>
|
||||
<path fill="#FFFFFF" d="M838.183,239.464c6.083,0,12.021,0,18.096,0c0-29.09,0-57.987,0-86.879c-6.12,0-12.098,0-18.096,0
|
||||
C838.183,181.611,838.183,210.514,838.183,239.464z"/>
|
||||
<ellipse fill="#E5A00D" cx="847.229" cy="129.416" rx="12.459" ry="12.44"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_2_1_">
|
||||
<g>
|
||||
|
||||
<image overflow="visible" opacity="0.2" width="279" height="216" xlink:href="13AD46AC.png" transform="matrix(1 0 0 1 15.5 73.5)">
|
||||
</image>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M243.77,212.601c-10.482,0-19.533,6.118-23.783,14.977l-37.755-5.478
|
||||
c-0.133-14.445-11.881-26.114-26.356-26.114c-5.163,0-9.975,1.489-14.041,4.051l-53.649-60.787
|
||||
c3.091-4.319,4.914-9.605,4.914-15.319c0-14.56-11.802-26.362-26.363-26.362c-14.56,0-26.363,11.803-26.363,26.362
|
||||
c0,14.559,11.803,26.361,26.363,26.361c4.484,0,8.704-1.122,12.399-3.096l54.223,61.438c-2.439,3.994-3.846,8.688-3.846,13.712
|
||||
c0,14.56,11.803,26.363,26.364,26.363c10.422,0,19.43-6.05,23.708-14.828l37.832,5.491c0.22,14.369,11.929,25.953,26.354,25.953
|
||||
c14.56,0,26.363-11.804,26.363-26.362S258.33,212.601,243.77,212.601z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<image overflow="visible" opacity="0.2" width="279" height="279" xlink:href="13AD46AD.png" transform="matrix(1 0 0 1 15.5 10.5)">
|
||||
</image>
|
||||
<g>
|
||||
<path fill="#E5A00D" d="M243.77,34.779c-14.56,0-26.363,11.802-26.363,26.362c0,7.17,2.867,13.667,7.512,18.42l-58.424,82.548
|
||||
c-3.25-1.431-6.84-2.23-10.619-2.23c-14.56,0-26.363,11.8-26.363,26.36c0,2.592,0.38,5.094,1.078,7.46L86.2,221.181
|
||||
c-4.82-5.271-11.754-8.58-19.461-8.58c-14.56,0-26.363,11.803-26.363,26.361s11.803,26.362,26.363,26.362
|
||||
c14.561,0,26.363-11.804,26.363-26.362c0-2.518-0.36-4.951-1.019-7.256l44.477-27.534c4.815,5.183,11.686,8.429,19.318,8.429
|
||||
c14.561,0,26.364-11.803,26.364-26.361c0-6.424-2.301-12.31-6.119-16.883l58.973-83.322c2.72,0.948,5.637,1.468,8.677,1.468
|
||||
c14.561,0,26.363-11.802,26.363-26.361C270.135,46.582,258.33,34.779,243.77,34.779z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.7 KiB |
BIN
data/interfaces/default/images/logo.png
Normal file
After Width: | Height: | Size: 17 KiB |
32
data/interfaces/default/images/logo.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="298.961px" height="300px" viewBox="0 0 298.961 300" enable-background="new 0 0 298.961 300" xml:space="preserve">
|
||||
<g>
|
||||
|
||||
<image overflow="visible" opacity="0.2" width="289" height="224" xlink:href="93124B54.png" transform="matrix(1 0 0 1 4.9805 70.5)">
|
||||
</image>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M241.811,215.25c-10.935,0-20.375,6.382-24.809,15.623l-39.383-5.715
|
||||
c-0.139-15.068-12.393-27.242-27.493-27.242c-5.385,0-10.405,1.554-14.646,4.229l-55.963-63.414
|
||||
c3.224-4.505,5.127-10.02,5.127-15.981c0-15.188-12.312-27.5-27.5-27.5s-27.5,12.312-27.5,27.5s12.312,27.5,27.5,27.5
|
||||
c4.677,0,9.079-1.171,12.934-3.23l56.56,64.089c-2.544,4.168-4.012,9.064-4.012,14.307c0,15.188,12.312,27.5,27.5,27.5
|
||||
c10.872,0,20.269-6.311,24.731-15.467l39.463,5.727c0.229,14.99,12.443,27.074,27.49,27.074c15.188,0,27.5-12.313,27.5-27.5
|
||||
S256.998,215.25,241.811,215.25z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<image overflow="visible" opacity="0.2" width="289" height="289" xlink:href="93124B55.png" transform="matrix(1 0 0 1 4.9805 5.5)">
|
||||
</image>
|
||||
<g>
|
||||
<path fill="#E5A00D" d="M241.811,29.75c-15.188,0-27.5,12.312-27.5,27.5c0,7.48,2.99,14.258,7.836,19.216l-60.943,86.113
|
||||
c-3.389-1.493-7.135-2.329-11.076-2.329c-15.188,0-27.5,12.313-27.5,27.5c0,2.704,0.397,5.314,1.125,7.783l-46.306,28.668
|
||||
c-5.028-5.5-12.261-8.951-20.3-8.951c-15.188,0-27.5,12.313-27.5,27.5s12.312,27.5,27.5,27.5s27.5-12.313,27.5-27.5
|
||||
c0-2.627-0.376-5.165-1.064-7.57l46.396-28.724c5.022,5.407,12.189,8.794,20.15,8.794c15.188,0,27.5-12.313,27.5-27.5
|
||||
c0-6.701-2.399-12.84-6.382-17.611l61.515-86.92c2.837,0.988,5.88,1.532,9.052,1.532c15.188,0,27.5-12.312,27.5-27.5
|
||||
S256.998,29.75,241.811,29.75z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 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 |