Compare commits
2730 Commits
0.15.0
...
throw-erro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb7f7bb4d1 | ||
|
|
389e49183c | ||
|
|
4c1a5f84fa | ||
|
|
b0bb156918 | ||
|
|
5565204273 | ||
|
|
d950a0ef5d | ||
|
|
b2c0f7efc3 | ||
|
|
9e58bc19c4 | ||
|
|
0d7e2afba4 | ||
|
|
e0d063d306 | ||
|
|
ed0ad587dc | ||
|
|
56fe591eee | ||
|
|
7b9693bca4 | ||
|
|
fd0417b14c | ||
|
|
89c12f2585 | ||
|
|
5c71adb137 | ||
|
|
4c23062d1e | ||
|
|
190b45086c | ||
|
|
008983c8e3 | ||
|
|
cb8a375c5e | ||
|
|
848a60aa9b | ||
|
|
88ea794fdb | ||
|
|
870350fd61 | ||
|
|
1e25894f7e | ||
|
|
223e74569a | ||
|
|
0a4d793556 | ||
|
|
5503914abe | ||
|
|
906466d7fb | ||
|
|
e2532de766 | ||
|
|
3ac4390d11 | ||
|
|
389ae7a573 | ||
|
|
176247091d | ||
|
|
e22cc73f32 | ||
|
|
c23ad25899 | ||
|
|
9cc0418769 | ||
|
|
925a2c8e77 | ||
|
|
390734d86e | ||
|
|
218f7be1a7 | ||
|
|
246955b68a | ||
|
|
d24506baed | ||
|
|
b0be2237fa | ||
|
|
945394fb1a | ||
|
|
19886f71f5 | ||
|
|
23855c09dc | ||
|
|
618ab01cd7 | ||
|
|
349d90b60e | ||
|
|
9e7c2dcdbb | ||
|
|
f353589a53 | ||
|
|
fc97929dee | ||
|
|
3d77642a1e | ||
|
|
b55c1a35bf | ||
|
|
5df700a56e | ||
|
|
0c64a86ebe | ||
|
|
644ba46945 | ||
|
|
4ab54f284c | ||
|
|
210c2a8855 | ||
|
|
0ed4f1a9a4 | ||
|
|
7e648840a1 | ||
|
|
5e090778ae | ||
|
|
049ed114fd | ||
|
|
88007a08f2 | ||
|
|
6c4ed282bb | ||
|
|
c8765385df | ||
|
|
0ed22c0be0 | ||
|
|
3ffef4b9fb | ||
|
|
ceeebceb3a | ||
|
|
0338b26d5c | ||
|
|
6577cc0c8c | ||
|
|
586000ca3d | ||
|
|
abc81ebd08 | ||
|
|
cd6c73e487 | ||
|
|
69ad57338f | ||
|
|
cbcf31a4f9 | ||
|
|
b19beac5b4 | ||
|
|
dc6d088e30 | ||
|
|
b0ad27af23 | ||
|
|
5f23c6358a | ||
|
|
da776c935f | ||
|
|
0a315783ef | ||
|
|
65061b0514 | ||
|
|
063e5e359e | ||
|
|
f460afca35 | ||
|
|
66a08ace1d | ||
|
|
68e65e968a | ||
|
|
1ba0ab982b | ||
|
|
8752b8bb3f | ||
|
|
168f86ef89 | ||
|
|
85927853f9 | ||
|
|
57f60bf173 | ||
|
|
d16c3ed40a | ||
|
|
e90f4a2cbf | ||
|
|
d00839ec68 | ||
|
|
ea35d92493 | ||
|
|
beb9894c47 | ||
|
|
5556a996cd | ||
|
|
f7b1dcc271 | ||
|
|
eb226e1dcf | ||
|
|
8332ad0f16 | ||
|
|
06af5a004e | ||
|
|
ce32873ef8 | ||
|
|
99bc230fe6 | ||
|
|
0e3820b634 | ||
|
|
eba84dcd78 | ||
|
|
864f27ef72 | ||
|
|
a8b72d8342 | ||
|
|
15d2cfba90 | ||
|
|
2851d993ad | ||
|
|
3f1d88282e | ||
|
|
cc9ce916c6 | ||
|
|
c7d468578f | ||
|
|
e0f6988eb5 | ||
|
|
09ff370ddc | ||
|
|
32ecf30c82 | ||
|
|
a62adccd3d | ||
|
|
c75bf35f59 | ||
|
|
3013782b7b | ||
|
|
81abebd144 | ||
|
|
140b6c1227 | ||
|
|
ac685f65e9 | ||
|
|
f6b1cbd5d0 | ||
|
|
7db6e43e3f | ||
|
|
6c73614a47 | ||
|
|
b5c54b4e41 | ||
|
|
8f1c84e6d4 | ||
|
|
d1df4af734 | ||
|
|
23cd04fe88 | ||
|
|
dbc7c97e0b | ||
|
|
2313ca8f72 | ||
|
|
9418ba1687 | ||
|
|
233491940c | ||
|
|
7708e7ab08 | ||
|
|
600bd38630 | ||
|
|
f7290dfcb6 | ||
|
|
e22f7583eb | ||
|
|
c0e85f5687 | ||
|
|
3593f67eb6 | ||
|
|
1b251264a6 | ||
|
|
93c1a1d42e | ||
|
|
d7f6b6b018 | ||
|
|
fd99f20404 | ||
|
|
2b1e1b11a3 | ||
|
|
96ac7f9f35 | ||
|
|
33da64a669 | ||
|
|
38eb4ccbc4 | ||
|
|
a57414307e | ||
|
|
7ad111e2f6 | ||
|
|
a402128a7d | ||
|
|
d3ab4a5145 | ||
|
|
4ae77bcef9 | ||
|
|
e84416e56d | ||
|
|
59ccc9d73e | ||
|
|
1533a28817 | ||
|
|
7e4840867e | ||
|
|
f2f3f045e5 | ||
|
|
b12149bafd | ||
|
|
307c1b0b62 | ||
|
|
b201745988 | ||
|
|
352266481e | ||
|
|
c201ea53ba | ||
|
|
958867e92b | ||
|
|
b50de2f2ed | ||
|
|
ad37db4c82 | ||
|
|
c2d91c9544 | ||
|
|
6eedca6e7e | ||
|
|
0bd1d0bb05 | ||
|
|
1f359f5a13 | ||
|
|
fe53b5503c | ||
|
|
d66ef8fe22 | ||
|
|
6ff3a633f7 | ||
|
|
46891437e9 | ||
|
|
b729597728 | ||
|
|
f99d139025 | ||
|
|
b72b917af2 | ||
|
|
749869fdca | ||
|
|
e56a694878 | ||
|
|
8cb4d1dc28 | ||
|
|
68f1351507 | ||
|
|
66205286e4 | ||
|
|
32be373552 | ||
|
|
b1128c17f1 | ||
|
|
319bbd2f81 | ||
|
|
d8fb4f0a87 | ||
|
|
2e195575a6 | ||
|
|
28efeaa4f2 | ||
|
|
137534f901 | ||
|
|
c9594d46af | ||
|
|
1097648f0a | ||
|
|
17e6213448 | ||
|
|
2ea423032e | ||
|
|
2dead1a19b | ||
|
|
e18b10297b | ||
|
|
fd66084388 | ||
|
|
ca27e096f3 | ||
|
|
125997f45f | ||
|
|
da8a2c7bbb | ||
|
|
81ca205caa | ||
|
|
f06d5b973b | ||
|
|
fd890f9c0a | ||
|
|
835237382f | ||
|
|
7dd699370f | ||
|
|
ef8c7184de | ||
|
|
92026c1f98 | ||
|
|
9bd9dcc41c | ||
|
|
81b97ba52f | ||
|
|
ae36777d14 | ||
|
|
3d7ad82f3e | ||
|
|
059796c60d | ||
|
|
b0342b7449 | ||
|
|
b867dd82ca | ||
|
|
a122286d48 | ||
|
|
837db800bc | ||
|
|
44fe39821a | ||
|
|
fbc6b14424 | ||
|
|
9c00140464 | ||
|
|
00d8deda0e | ||
|
|
b84ce6a556 | ||
|
|
f5fb4c6c64 | ||
|
|
ac686fefe1 | ||
|
|
e690e166b0 | ||
|
|
6a02dd8842 | ||
|
|
66e2c01a3e | ||
|
|
7f2176d7fc | ||
|
|
4434889270 | ||
|
|
96afc1a45d | ||
|
|
dbe49610a1 | ||
|
|
62fadb54ee | ||
|
|
3832383936 | ||
|
|
595c3fb833 | ||
|
|
1d25c55c0b | ||
|
|
6376e78e0b | ||
|
|
1f08d2929c | ||
|
|
04953d97ad | ||
|
|
64fe4de3fb | ||
|
|
58008445e2 | ||
|
|
b7f0b054b8 | ||
|
|
3bbd709bd6 | ||
|
|
dbb1e3f5d8 | ||
|
|
8144308aee | ||
|
|
0503d2a9f3 | ||
|
|
fafd4d9396 | ||
|
|
3431a1b1de | ||
|
|
026ea52445 | ||
|
|
1e60b9a322 | ||
|
|
7450cb1f21 | ||
|
|
cdd5a9e935 | ||
|
|
c4cc50ca39 | ||
|
|
9b09d369d9 | ||
|
|
12ab11413f | ||
|
|
352f3640cf | ||
|
|
7940e91cbe | ||
|
|
577a2356a0 | ||
|
|
38ef0b10e7 | ||
|
|
c72d3c4a0e | ||
|
|
eb7e48b059 | ||
|
|
ab62fa1c4f | ||
|
|
a7cf1f6cca | ||
|
|
80d89f31fe | ||
|
|
a0f566fef3 | ||
|
|
c93d362dd4 | ||
|
|
db72f5d011 | ||
|
|
03704384a8 | ||
|
|
5b17ec0b56 | ||
|
|
d4acd03698 | ||
|
|
2ea986326d | ||
|
|
da53de2097 | ||
|
|
cf755dec22 | ||
|
|
d25e5e1849 | ||
|
|
593648780f | ||
|
|
bb04ff5942 | ||
|
|
12db276eb8 | ||
|
|
596fc3e908 | ||
|
|
9c54b94265 | ||
|
|
3c1cfce95a | ||
|
|
2f9f3142e2 | ||
|
|
92070e502c | ||
|
|
48b1154a71 | ||
|
|
bcdfb98454 | ||
|
|
f26c1f8095 | ||
|
|
790a24bdeb | ||
|
|
5d2b9392d5 | ||
|
|
6be242fc15 | ||
|
|
8158c5042b | ||
|
|
c70cdd88c7 | ||
|
|
bc6d6b6550 | ||
|
|
9584d8e16d | ||
|
|
4693c678d0 | ||
|
|
2c7d668f81 | ||
|
|
de2f963717 | ||
|
|
e83da76dc7 | ||
|
|
643730221d | ||
|
|
ab63f9ef31 | ||
|
|
dc870c4cc4 | ||
|
|
dd709dec18 | ||
|
|
cd5b71aedd | ||
|
|
d33cc025ee | ||
|
|
e7159f2803 | ||
|
|
56b8a18820 | ||
|
|
5832794034 | ||
|
|
a26b197687 | ||
|
|
12dba0955a | ||
|
|
7bd425bb3d | ||
|
|
845ad17a04 | ||
|
|
67e8fcaf93 | ||
|
|
aa07ee22cf | ||
|
|
3702e8c6fe | ||
|
|
21bd4edee4 | ||
|
|
31de39a7a4 | ||
|
|
d0fc2569ff | ||
|
|
4fd1631b30 | ||
|
|
0a1614a872 | ||
|
|
1f66d7ef74 | ||
|
|
84b6429ca6 | ||
|
|
212e5ebab5 | ||
|
|
a6106077bd | ||
|
|
fa4ce6b4e6 | ||
|
|
4130136718 | ||
|
|
c7c1b8d4f1 | ||
|
|
25b60a1b90 | ||
|
|
dda4dadfa3 | ||
|
|
570dbc7b47 | ||
|
|
6c122248f5 | ||
|
|
4a369bb3c0 | ||
|
|
95cf57e3bd | ||
|
|
0e6f7a4dc1 | ||
|
|
6f21834e71 | ||
|
|
cf6ad254fb | ||
|
|
fabbecf4c2 | ||
|
|
8262aa4138 | ||
|
|
95d86ebf22 | ||
|
|
6aa7db2358 | ||
|
|
5637c6e651 | ||
|
|
6f705b053a | ||
|
|
1f3f2788d4 | ||
|
|
ed3c202099 | ||
|
|
0396eec398 | ||
|
|
62dcec49e4 | ||
|
|
2f6afb5e86 | ||
|
|
a3a2b2aafb | ||
|
|
4900ce24fa | ||
|
|
135aaf56fd | ||
|
|
62d7abdd9e | ||
|
|
91b079f4df | ||
|
|
2c6cd74dc1 | ||
|
|
68ac18dc98 | ||
|
|
dbc74164ab | ||
|
|
76c7b2ee9c | ||
|
|
d6913c1eb9 | ||
|
|
9aa00b2f0a | ||
|
|
af02917766 | ||
|
|
d93a7b315d | ||
|
|
a813955ad3 | ||
|
|
1e3425fdee | ||
|
|
a2b326e306 | ||
|
|
0fa0e8e3f3 | ||
|
|
dbae7502e5 | ||
|
|
f47552c0c4 | ||
|
|
74836828c9 | ||
|
|
c152243b4d | ||
|
|
2a40c9a595 | ||
|
|
3cea493d49 | ||
|
|
6991d0851f | ||
|
|
fb2a331f79 | ||
|
|
c888524523 | ||
|
|
75c9dbaf6b | ||
|
|
c01a29fe76 | ||
|
|
80417281c4 | ||
|
|
f9b8bc006f | ||
|
|
2c22b0839f | ||
|
|
ec3e67e0d2 | ||
|
|
611e7e9dd8 | ||
|
|
f4e19ac05c | ||
|
|
70663af190 | ||
|
|
ed265cfdcd | ||
|
|
5b82370bc3 | ||
|
|
47690fffc1 | ||
|
|
aa09bbe23d | ||
|
|
a09fbad8b0 | ||
|
|
357ba2f4f6 | ||
|
|
b32dd746a6 | ||
|
|
b4ea1ccc23 | ||
|
|
2aecbfbb67 | ||
|
|
6d3b907307 | ||
|
|
55da1e3e92 | ||
|
|
f3aa0d6428 | ||
|
|
ad89be7523 | ||
|
|
49a7c16de5 | ||
|
|
e414476c6e | ||
|
|
391690d570 | ||
|
|
1be4af733b | ||
|
|
e3222d99ac | ||
|
|
5b19d33387 | ||
|
|
272c85c062 | ||
|
|
7101af764a | ||
|
|
e582d25654 | ||
|
|
9991c4507d | ||
|
|
37b3248202 | ||
|
|
0585131f78 | ||
|
|
7e351b21bc | ||
|
|
440549fcc5 | ||
|
|
f7b557eed1 | ||
|
|
bdfe317e20 | ||
|
|
19805b91d9 | ||
|
|
420c458b6a | ||
|
|
6de449811d | ||
|
|
081fd541af | ||
|
|
7dcd5035c0 | ||
|
|
0f1bb3fb3b | ||
|
|
78c447829a | ||
|
|
7f445f6167 | ||
|
|
bf599284ed | ||
|
|
a21c10e209 | ||
|
|
56ee44ee09 | ||
|
|
c6df54a103 | ||
|
|
575b039170 | ||
|
|
7a32269d7f | ||
|
|
004e371051 | ||
|
|
d5f43bae92 | ||
|
|
e215a20a0a | ||
|
|
919413e2b9 | ||
|
|
45839f8d59 | ||
|
|
55b62f9fde | ||
|
|
4c3529f3d5 | ||
|
|
ab7580c0da | ||
|
|
25c079fded | ||
|
|
d8bb75be63 | ||
|
|
a8021e09a7 | ||
|
|
16530ac6de | ||
|
|
5d1a1fef0f | ||
|
|
6082887070 | ||
|
|
8da336b7aa | ||
|
|
3da0287ede | ||
|
|
ea3331840d | ||
|
|
3fc0f72f3a | ||
|
|
ea3abe6069 | ||
|
|
897f871f99 | ||
|
|
2f335b3d2c | ||
|
|
3b1837a99b | ||
|
|
cc59de0c93 | ||
|
|
997d936e9c | ||
|
|
253256bf37 | ||
|
|
e60a1836fe | ||
|
|
e66b317f02 | ||
|
|
b417ea8e3a | ||
|
|
aa8758dec2 | ||
|
|
b58b0440d6 | ||
|
|
ad6b29c09f | ||
|
|
09a585c93b | ||
|
|
a9d2d2211a | ||
|
|
fe057c7873 | ||
|
|
dfcaed93ea | ||
|
|
5e3fcad1fb | ||
|
|
8e4959a621 | ||
|
|
555bb711c9 | ||
|
|
afa3eff313 | ||
|
|
9e91fd5241 | ||
|
|
d90d347006 | ||
|
|
385fe4be22 | ||
|
|
7ec88741a6 | ||
|
|
86cb68413d | ||
|
|
05c5d293eb | ||
|
|
fd0ac3a671 | ||
|
|
f109d812a1 | ||
|
|
5d2f2690e2 | ||
|
|
505a81d087 | ||
|
|
dfab62ce48 | ||
|
|
31699468fc | ||
|
|
857096ba5f | ||
|
|
36c7d4270b | ||
|
|
d77586d02d | ||
|
|
c8fbb74835 | ||
|
|
dfee375b98 | ||
|
|
9268258773 | ||
|
|
9031ca5c3f | ||
|
|
18d68184a9 | ||
|
|
3816f92095 | ||
|
|
fd55b08a1d | ||
|
|
d0d8ba0068 | ||
|
|
18197e7e3e | ||
|
|
f75a81c9ee | ||
|
|
9979a1b910 | ||
|
|
dbba9d7687 | ||
|
|
1e3f4ed398 | ||
|
|
d199b7264e | ||
|
|
b4c532f062 | ||
|
|
b24a89f820 | ||
|
|
7112f35793 | ||
|
|
7048193f00 | ||
|
|
17ae2648ed | ||
|
|
c952754c8c | ||
|
|
85ba04b715 | ||
|
|
8af202e86b | ||
|
|
57353fe0c6 | ||
|
|
6c116e34c4 | ||
|
|
9ff26ea4d4 | ||
|
|
2194bd2812 | ||
|
|
1e6cd0b18f | ||
|
|
85d178ff8b | ||
|
|
e0d09c3cda | ||
|
|
47f529dc09 | ||
|
|
dde850a2e9 | ||
|
|
34e1a465be | ||
|
|
7d756209b1 | ||
|
|
60e870b277 | ||
|
|
bf054dfda5 | ||
|
|
59654289cb | ||
|
|
cf13c11236 | ||
|
|
ddf1e84f7c | ||
|
|
ec183e3c9a | ||
|
|
01135db80a | ||
|
|
955e3de56d | ||
|
|
cdd473e195 | ||
|
|
34fe7f04fa | ||
|
|
55c280af5d | ||
|
|
ea5a4f6d6e | ||
|
|
d169f91d64 | ||
|
|
8b29895ef3 | ||
|
|
3cfc5c164d | ||
|
|
4e55d9937c | ||
|
|
03fe8d1716 | ||
|
|
d6d7d209c1 | ||
|
|
fa044a2971 | ||
|
|
72fce387db | ||
|
|
da10cdd5ab | ||
|
|
83de3a51ea | ||
|
|
8faf8b8e57 | ||
|
|
5a349ae88b | ||
|
|
8ec992a8a3 | ||
|
|
e2fc64296d | ||
|
|
ec55b905cb | ||
|
|
492d1144e0 | ||
|
|
4f4b19a962 | ||
|
|
febd785428 | ||
|
|
8642c66710 | ||
|
|
76cc8ac66b | ||
|
|
53d349a648 | ||
|
|
b344e1aadb | ||
|
|
3c882cff6e | ||
|
|
f73aef33f0 | ||
|
|
d12dff9dcf | ||
|
|
99d770be64 | ||
|
|
71a8867a4a | ||
|
|
2bbd424fce | ||
|
|
ef8dc7272b | ||
|
|
ad4a06fca5 | ||
|
|
c04f45d5e3 | ||
|
|
fb36155022 | ||
|
|
7ace3fc989 | ||
|
|
170e754998 | ||
|
|
6a75fa08dc | ||
|
|
1c5db46a4d | ||
|
|
7e4690e43c | ||
|
|
f8b29674b2 | ||
|
|
fe55141a7b | ||
|
|
9205ccc124 | ||
|
|
698a6f3886 | ||
|
|
1668e4187e | ||
|
|
00df3e2c40 | ||
|
|
a2578ac6b4 | ||
|
|
1fee636afa | ||
|
|
154bca4635 | ||
|
|
c584e31657 | ||
|
|
66340281e6 | ||
|
|
df599c0fc3 | ||
|
|
e98aeaf293 | ||
|
|
5026698ebf | ||
|
|
bd221b7b2c | ||
|
|
ba37259258 | ||
|
|
eca8d2e7d7 | ||
|
|
cb0f7bf6b0 | ||
|
|
1ec15dc073 | ||
|
|
f124e8cf93 | ||
|
|
9621175dc9 | ||
|
|
e6ddd6d6c1 | ||
|
|
d7ebd763f5 | ||
|
|
99091e919c | ||
|
|
6ddbccbc95 | ||
|
|
0ca333715b | ||
|
|
4e44a91d08 | ||
|
|
fc5f84a0cd | ||
|
|
2289f98c1e | ||
|
|
49edf0ee33 | ||
|
|
8532727d54 | ||
|
|
336653b02c | ||
|
|
d546f1870e | ||
|
|
15c66e2b01 | ||
|
|
be92bfd4af | ||
|
|
f5cb7ba96c | ||
|
|
f013a6b1d7 | ||
|
|
519c227c4f | ||
|
|
6f4665588f | ||
|
|
eba311baa9 | ||
|
|
e92b3779ad | ||
|
|
a82d21ff78 | ||
|
|
63e1165936 | ||
|
|
84cc732281 | ||
|
|
971b6ec96f | ||
|
|
46f7ca9ffa | ||
|
|
e85bc3e0f6 | ||
|
|
dee20f92a7 | ||
|
|
12b818a83c | ||
|
|
1c91110464 | ||
|
|
4cd7a3e83f | ||
|
|
fa99c9aa85 | ||
|
|
2d949834e9 | ||
|
|
c7b74aa8b4 | ||
|
|
5e3c9cf290 | ||
|
|
508f137b30 | ||
|
|
67dd2b419a | ||
|
|
0749baae4b | ||
|
|
df9f897ebe | ||
|
|
8ef1e81294 | ||
|
|
d755d05f88 | ||
|
|
ce4a52325b | ||
|
|
714a001332 | ||
|
|
c78f84d5c6 | ||
|
|
a6c9b263da | ||
|
|
5ad2fc64b4 | ||
|
|
2ae074a9a4 | ||
|
|
c5967ad572 | ||
|
|
5ece07a807 | ||
|
|
e00f9a869d | ||
|
|
662cf2f633 | ||
|
|
16f1a23c54 | ||
|
|
fb364b532c | ||
|
|
2bebf578c7 | ||
|
|
a5b9c83256 | ||
|
|
dc0e242a63 | ||
|
|
178e317d11 | ||
|
|
3aa740d877 | ||
|
|
951d7d8339 | ||
|
|
b5bd1f507e | ||
|
|
7b9368ae75 | ||
|
|
b2c3d9fb0a | ||
|
|
6bd48ca645 | ||
|
|
cdc007c83c | ||
|
|
7dbbe27cdd | ||
|
|
b91874355c | ||
|
|
8b0ed8b110 | ||
|
|
574e35a720 | ||
|
|
32ae38b933 | ||
|
|
56e505164d | ||
|
|
d4f3139b73 | ||
|
|
97dceb3a5a | ||
|
|
212f6d6bf5 | ||
|
|
36904fa72e | ||
|
|
8c2495a399 | ||
|
|
2d1f6128be | ||
|
|
9233f71549 | ||
|
|
b2a738cf13 | ||
|
|
34a79c5f1e | ||
|
|
fd30f5775a | ||
|
|
a2600acfa9 | ||
|
|
fa2c8f42b3 | ||
|
|
db4e36ad9b | ||
|
|
38697ade31 | ||
|
|
2cf3c7eb41 | ||
|
|
4ea932dc1f | ||
|
|
7b587d8e00 | ||
|
|
9612a4d0de | ||
|
|
7141a716e7 | ||
|
|
0d33566bbc | ||
|
|
2d341c53f2 | ||
|
|
b20443241d | ||
|
|
fb28431cdc | ||
|
|
2602df8ef7 | ||
|
|
9e7ff8f8f5 | ||
|
|
5b5473e794 | ||
|
|
73c000fd44 | ||
|
|
ef51661f5d | ||
|
|
6bb9e8442a | ||
|
|
9b1721f8ad | ||
|
|
15532fdb11 | ||
|
|
39e0b3f6c5 | ||
|
|
329bf7895e | ||
|
|
61feb5a851 | ||
|
|
da06560fa9 | ||
|
|
fd120ab275 | ||
|
|
14a57fd229 | ||
|
|
0be2ab582d | ||
|
|
d00037d1d4 | ||
|
|
e10827bf1c | ||
|
|
98a5a7cb4f | ||
|
|
748282cff3 | ||
|
|
f3df1e8d3c | ||
|
|
a93058d385 | ||
|
|
89135a9cf5 | ||
|
|
47a02e3423 | ||
|
|
c9fd8167f5 | ||
|
|
c9b9ded518 | ||
|
|
250a9191cb | ||
|
|
6fab5d0554 | ||
|
|
461fae4f77 | ||
|
|
8aed1e11c3 | ||
|
|
6cf6c56dd1 | ||
|
|
042ff8da64 | ||
|
|
8b72481b8c | ||
|
|
aa0724f204 | ||
|
|
eff8b23f57 | ||
|
|
81a2300af8 | ||
|
|
2a541cb4d5 | ||
|
|
0120f44fd6 | ||
|
|
b65dced646 | ||
|
|
e7394fe7a1 | ||
|
|
fb673639f9 | ||
|
|
dc62b6ffdb | ||
|
|
1f2ccac8a0 | ||
|
|
46e4d35ccd | ||
|
|
eb7e79fdf7 | ||
|
|
37c953e633 | ||
|
|
5bb2cb7d71 | ||
|
|
692f4e5be2 | ||
|
|
7bb1471207 | ||
|
|
4752e16ad2 | ||
|
|
2a156e7313 | ||
|
|
0314dd44ea | ||
|
|
b80a3d5401 | ||
|
|
8c4ef9f955 | ||
|
|
67d2635e41 | ||
|
|
71a1ad307c | ||
|
|
9a48fd81a3 | ||
|
|
d928b9eaad | ||
|
|
790b7afcca | ||
|
|
7b9f5d74dc | ||
|
|
6eadba784d | ||
|
|
3a39045617 | ||
|
|
67cb8b7107 | ||
|
|
4962c00ba8 | ||
|
|
6764185543 | ||
|
|
0a66a68db8 | ||
|
|
8d47ec714e | ||
|
|
9607fe03af | ||
|
|
62057e676a | ||
|
|
43ff3be751 | ||
|
|
7cbd79fee5 | ||
|
|
3fba342ed2 | ||
|
|
2419c4d8ae | ||
|
|
e37fb09d41 | ||
|
|
86b1357a68 | ||
|
|
b782ab5787 | ||
|
|
6c0486646f | ||
|
|
dffe8fc49e | ||
|
|
1336ecbef2 | ||
|
|
d00e430f50 | ||
|
|
5a728243a2 | ||
|
|
37f38dbf6c | ||
|
|
2eb7c5c037 | ||
|
|
ba0bc72d0b | ||
|
|
e1219cbdef | ||
|
|
bf0a48847c | ||
|
|
3bb7fbb2f1 | ||
|
|
326a362eb8 | ||
|
|
fdc380e7f7 | ||
|
|
db04f1556e | ||
|
|
444b1c99d0 | ||
|
|
3b1a286290 | ||
|
|
6c8a5a1e7f | ||
|
|
1769b0fdce | ||
|
|
68cbc11810 | ||
|
|
edcc155482 | ||
|
|
8231216371 | ||
|
|
e1215584b4 | ||
|
|
a905175e8c | ||
|
|
e98ed41656 | ||
|
|
fd139841f4 | ||
|
|
dac0f42777 | ||
|
|
f0788162a4 | ||
|
|
c963e50cde | ||
|
|
b4a4767fb3 | ||
|
|
d6918b2976 | ||
|
|
47e273c402 | ||
|
|
ba25e8e720 | ||
|
|
058ae5cc29 | ||
|
|
f8be5d2559 | ||
|
|
792d25594c | ||
|
|
9f9020e422 | ||
|
|
302fecbdcb | ||
|
|
d74873fed1 | ||
|
|
40ed4a0506 | ||
|
|
c25d664edc | ||
|
|
9bad7e2940 | ||
|
|
6704ce3214 | ||
|
|
914cfbd953 | ||
|
|
85cf27119c | ||
|
|
7691f53520 | ||
|
|
094f835642 | ||
|
|
92eea3b18b | ||
|
|
c021b93b5c | ||
|
|
d94d4c2045 | ||
|
|
46d08237c6 | ||
|
|
3deafe9f8d | ||
|
|
998edba6f0 | ||
|
|
4f219362fe | ||
|
|
c427444946 | ||
|
|
d59323a601 | ||
|
|
a779cdd463 | ||
|
|
73a142fd9b | ||
|
|
0453d08eed | ||
|
|
a18068aedf | ||
|
|
12fba3bb7a | ||
|
|
fc2b9031d4 | ||
|
|
28a6589a1e | ||
|
|
cf9c790499 | ||
|
|
3d6dceae10 | ||
|
|
130a14d0d9 | ||
|
|
f07b4f8fc4 | ||
|
|
75f655aafc | ||
|
|
a3fd9ccea7 | ||
|
|
4136ac633a | ||
|
|
2724456882 | ||
|
|
b396a2cbfd | ||
|
|
ddb06b0cac | ||
|
|
f54e247eb4 | ||
|
|
2ac19eb8fc | ||
|
|
6cdaafdc37 | ||
|
|
ed6476b5ea | ||
|
|
b90bceb2dc | ||
|
|
f31bd5ffb9 | ||
|
|
5abe7fe123 | ||
|
|
b6792cf02e | ||
|
|
1261323c66 | ||
|
|
2a45b4eba0 | ||
|
|
a6a0bbf398 | ||
|
|
7f3ef12297 | ||
|
|
da2f592de6 | ||
|
|
ee91effb7a | ||
|
|
b13f9c25b3 | ||
|
|
f85563eb66 | ||
|
|
7b689a186d | ||
|
|
092d7df761 | ||
|
|
6c444707d7 | ||
|
|
81c006cc04 | ||
|
|
7cbd1e413f | ||
|
|
f34f8ef188 | ||
|
|
c3eb385cd3 | ||
|
|
be34f03157 | ||
|
|
4964785b13 | ||
|
|
1b7757c14f | ||
|
|
58c9f20226 | ||
|
|
f19be0c3ce | ||
|
|
f236a6872b | ||
|
|
3e0096f360 | ||
|
|
438b334320 | ||
|
|
4aa96ecab9 | ||
|
|
e5557b515e | ||
|
|
7b9d26d688 | ||
|
|
8d4b4cd14c | ||
|
|
342fc202a7 | ||
|
|
4436359d07 | ||
|
|
91f8395222 | ||
|
|
de00e86cd5 | ||
|
|
c6e086c6ff | ||
|
|
82f3eda82b | ||
|
|
05f9613e14 | ||
|
|
50bb591826 | ||
|
|
2ca23c714d | ||
|
|
b030d822f1 | ||
|
|
65b5183f01 | ||
|
|
33431844b7 | ||
|
|
325a67155d | ||
|
|
6876f88f43 | ||
|
|
ad9d3f4425 | ||
|
|
5eca7a8a30 | ||
|
|
de153ece4e | ||
|
|
2d8964d37d | ||
|
|
ceb1feb350 | ||
|
|
2ea0590b03 | ||
|
|
80a513baa5 | ||
|
|
ba48f68fc3 | ||
|
|
319587e2f1 | ||
|
|
bf7952d9c7 | ||
|
|
f29ab53aff | ||
|
|
b5b0c58de7 | ||
|
|
a1bb421eec | ||
|
|
139786b9ef | ||
|
|
301444563b | ||
|
|
9966c21c6b | ||
|
|
12c219ee6c | ||
|
|
008f79df61 | ||
|
|
fd54cf2d05 | ||
|
|
e0d26f4055 | ||
|
|
6c2170960b | ||
|
|
950deaca40 | ||
|
|
2bd286f195 | ||
|
|
75792bfa7b | ||
|
|
737c49b689 | ||
|
|
2c447a42f2 | ||
|
|
dad8f9a0ce | ||
|
|
2eac23a0b3 | ||
|
|
6a204753c0 | ||
|
|
19bb26a789 | ||
|
|
00904ae3f2 | ||
|
|
d214a0b333 | ||
|
|
4940759627 | ||
|
|
be1a390b50 | ||
|
|
f707f990e6 | ||
|
|
a120f143d7 | ||
|
|
65fbdbff6a | ||
|
|
6ec4dcfafd | ||
|
|
48191aca6e | ||
|
|
83556bace2 | ||
|
|
814c9e6c3a | ||
|
|
547abe17d9 | ||
|
|
6b8450558d | ||
|
|
c3747c2d49 | ||
|
|
245122104a | ||
|
|
b0f127d4d8 | ||
|
|
d379a36c0e | ||
|
|
a12571e748 | ||
|
|
21879da80d | ||
|
|
6cabc96f75 | ||
|
|
f4f9e75c27 | ||
|
|
2f235adadf | ||
|
|
afb284fa9b | ||
|
|
93252f33ff | ||
|
|
6cf0ff6b49 | ||
|
|
86f75758a7 | ||
|
|
0ec94405ce | ||
|
|
33780f1995 | ||
|
|
1cb715ac9f | ||
|
|
f65b628bf3 | ||
|
|
60fa552469 | ||
|
|
437bdedb07 | ||
|
|
a531f4f057 | ||
|
|
bb396a310e | ||
|
|
66e7285108 | ||
|
|
2b81a82620 | ||
|
|
b555df8377 | ||
|
|
232730e909 | ||
|
|
2c1b4b0dd8 | ||
|
|
1e0712625a | ||
|
|
88752f32bd | ||
|
|
7a93d4c7e4 | ||
|
|
c3c767eed8 | ||
|
|
6f19a3a21e | ||
|
|
06be0fb351 | ||
|
|
e2bbc9a6fa | ||
|
|
17e481c107 | ||
|
|
b8f27a42a7 | ||
|
|
575c66efd3 | ||
|
|
88ad7c8d8d | ||
|
|
1adcac175e | ||
|
|
dd8c412abc | ||
|
|
e969c1490a | ||
|
|
e91421253e | ||
|
|
35d15c7c2b | ||
|
|
20cb751ff6 | ||
|
|
0614b52f03 | ||
|
|
a629521c37 | ||
|
|
5eb83bb39c | ||
|
|
0e17d026f2 | ||
|
|
ea0b1fdf44 | ||
|
|
1d78d67af3 | ||
|
|
d9c58c4837 | ||
|
|
129779a757 | ||
|
|
c8a985cc77 | ||
|
|
db7e7aae46 | ||
|
|
cb9b84f940 | ||
|
|
33d9be0ffb | ||
|
|
2571e420f3 | ||
|
|
e17c8b1f4d | ||
|
|
8805ee7c8c | ||
|
|
7df2fd0bc8 | ||
|
|
f7f0910953 | ||
|
|
d300797e22 | ||
|
|
56dbe15943 | ||
|
|
736d63c08f | ||
|
|
5f65e92500 | ||
|
|
a65b9b2b53 | ||
|
|
cc10e8ca5d | ||
|
|
dfe0c738d6 | ||
|
|
21b96a3159 | ||
|
|
888c7e2c29 | ||
|
|
d6eaeef8a6 | ||
|
|
961c5740b7 | ||
|
|
22e8f7e287 | ||
|
|
c26b383a1b | ||
|
|
2cec1fe4bb | ||
|
|
9da1c6c6de | ||
|
|
806ddf1aca | ||
|
|
0ad2793b68 | ||
|
|
585e4617e8 | ||
|
|
76c6886791 | ||
|
|
91bc6599c6 | ||
|
|
4d44b2c3a4 | ||
|
|
b49b5fbda9 | ||
|
|
17e6093abb | ||
|
|
3dc980e800 | ||
|
|
c44c1003af | ||
|
|
39c27f0c66 | ||
|
|
678b10dbcf | ||
|
|
a1d6411f1f | ||
|
|
c4c8a10507 | ||
|
|
ee94ccdeb0 | ||
|
|
e3df9f9ead | ||
|
|
62dc629337 | ||
|
|
dccdf38ce7 | ||
|
|
84124b837d | ||
|
|
6176da3cbb | ||
|
|
24bc3e2704 | ||
|
|
f13fb80b42 | ||
|
|
0a9e19646a | ||
|
|
65e45c4079 | ||
|
|
440105976f | ||
|
|
7eba7fbcc7 | ||
|
|
d9bfb3d305 | ||
|
|
4246c7a523 | ||
|
|
e3054ccdd2 | ||
|
|
df968d1919 | ||
|
|
7661c3c061 | ||
|
|
57ed047025 | ||
|
|
bda3a26423 | ||
|
|
080d9a8dc7 | ||
|
|
984a4acc7b | ||
|
|
b10f37bea9 | ||
|
|
0947c26612 | ||
|
|
f5e7fe34a5 | ||
|
|
c6f088d6ca | ||
|
|
21e29411af | ||
|
|
f85930700b | ||
|
|
707ef1d0f7 | ||
|
|
adc12addfa | ||
|
|
bfef9d4b6e | ||
|
|
fd29cb9023 | ||
|
|
d806310665 | ||
|
|
4982bff74d | ||
|
|
347c189f3f | ||
|
|
81b12b8001 | ||
|
|
3e781b7d13 | ||
|
|
9c44e41a4c | ||
|
|
402bb01151 | ||
|
|
82a1d9dffa | ||
|
|
570541ba49 | ||
|
|
73eabb6ca2 | ||
|
|
5a52b4fe45 | ||
|
|
caa08a6379 | ||
|
|
cd02078e26 | ||
|
|
0341faeb13 | ||
|
|
77131cff91 | ||
|
|
0c7726d4e1 | ||
|
|
1f1e14fba5 | ||
|
|
9be8263f26 | ||
|
|
7b2aa5f98e | ||
|
|
26b28cea49 | ||
|
|
23049e026f | ||
|
|
9ab242ca2e | ||
|
|
aa59925374 | ||
|
|
43ea8fa706 | ||
|
|
6df85718e6 | ||
|
|
82e6f6e095 | ||
|
|
9ba3e1cdb4 | ||
|
|
b20f72b963 | ||
|
|
092b8a4e52 | ||
|
|
e5f07dedbf | ||
|
|
ca9eb0d539 | ||
|
|
142317c2be | ||
|
|
3dea670091 | ||
|
|
8435e79913 | ||
|
|
30e85b40f9 | ||
|
|
be1a43a337 | ||
|
|
abca8f7a7c | ||
|
|
7b60dac526 | ||
|
|
ea6434662d | ||
|
|
0b7a108a59 | ||
|
|
57c63f3598 | ||
|
|
ae30f32c36 | ||
|
|
a50f64f6e9 | ||
|
|
1323b94b7a | ||
|
|
6d68fbc31d | ||
|
|
d97e128dc0 | ||
|
|
86ca568d6d | ||
|
|
92b04a30f3 | ||
|
|
2b0bb69a4f | ||
|
|
67b01506c9 | ||
|
|
b20b802a8d | ||
|
|
87f46a7532 | ||
|
|
45a1407144 | ||
|
|
4a0359c04e | ||
|
|
4e629ca858 | ||
|
|
c32cae00d5 | ||
|
|
02431b3f98 | ||
|
|
0323202a03 | ||
|
|
b6670a7e3d | ||
|
|
62c1991b88 | ||
|
|
3a6085ad31 | ||
|
|
6e1b62aedf | ||
|
|
2451497b31 | ||
|
|
6699c80357 | ||
|
|
1af1474d04 | ||
|
|
06a1d2ac41 | ||
|
|
f47b588b91 | ||
|
|
262131f68e | ||
|
|
ceae48088e | ||
|
|
e655af251c | ||
|
|
3351ad8197 | ||
|
|
5054510d15 | ||
|
|
947fe4fbb3 | ||
|
|
50c8afb525 | ||
|
|
a539de4f97 | ||
|
|
8d5a867271 | ||
|
|
6a1954f8f7 | ||
|
|
0044178d49 | ||
|
|
387bddb51b | ||
|
|
41ba19b615 | ||
|
|
0e37e42abd | ||
|
|
a28945273d | ||
|
|
fd6f03655e | ||
|
|
8b62c05fe2 | ||
|
|
0e92a63d14 | ||
|
|
a4ff0b62ce | ||
|
|
dd141daefd | ||
|
|
a1001ada47 | ||
|
|
f938aa530e | ||
|
|
8d419da277 | ||
|
|
8e3ff79f22 | ||
|
|
d0c12caae9 | ||
|
|
17a99a524b | ||
|
|
41a757c3be | ||
|
|
61fc6539c2 | ||
|
|
99f04012a7 | ||
|
|
891116f13b | ||
|
|
4fcd0964cd | ||
|
|
5005212bec | ||
|
|
a279d6f433 | ||
|
|
5c74196f60 | ||
|
|
33724d40a8 | ||
|
|
d78e5281fe | ||
|
|
fceb8093f1 | ||
|
|
d984a898d4 | ||
|
|
52688106e4 | ||
|
|
66b45a8fe2 | ||
|
|
0db23f9252 | ||
|
|
23b2abc273 | ||
|
|
0350d0784a | ||
|
|
83219a499c | ||
|
|
3f4d5bc85c | ||
|
|
83b790950c | ||
|
|
16b10ec3d7 | ||
|
|
1d4dd4484d | ||
|
|
8bd2669d3d | ||
|
|
b5d2eb5c70 | ||
|
|
25362f16a0 | ||
|
|
39b34eece8 | ||
|
|
b3426fdc94 | ||
|
|
6aa65593ef | ||
|
|
66becbf46f | ||
|
|
cbf3d75087 | ||
|
|
0b0036813f | ||
|
|
c06a20e085 | ||
|
|
c60fccd7f6 | ||
|
|
46f8b04e40 | ||
|
|
12b46bbd41 | ||
|
|
f2b69fd812 | ||
|
|
0aae728e33 | ||
|
|
cea38de4ad | ||
|
|
3e4fab7070 | ||
|
|
6745ca7775 | ||
|
|
88c5e3b6fa | ||
|
|
2fdb2c7c9a | ||
|
|
7afa027b95 | ||
|
|
637a5cc14f | ||
|
|
81e77693b2 | ||
|
|
d49f884f69 | ||
|
|
34eeb29436 | ||
|
|
0495347c64 | ||
|
|
023f9b44d3 | ||
|
|
5248814053 | ||
|
|
7ddab5b8cd | ||
|
|
a26adb162c | ||
|
|
ef5df36a8a | ||
|
|
9b74cf3225 | ||
|
|
1321c90920 | ||
|
|
4b46313e19 | ||
|
|
e9add69e26 | ||
|
|
5b020e81ca | ||
|
|
c76bd7b45b | ||
|
|
21b7ae3ac3 | ||
|
|
e45da7161f | ||
|
|
7a3c000955 | ||
|
|
f75c5db372 | ||
|
|
7cd1d82d26 | ||
|
|
0085c3b0d8 | ||
|
|
4251c6cb99 | ||
|
|
ac973df8e7 | ||
|
|
a76a955a1b | ||
|
|
c89b98a1e0 | ||
|
|
dda4596c0c | ||
|
|
4ac863203d | ||
|
|
be5831d9bd | ||
|
|
1e2fa27af7 | ||
|
|
fcbaf2a978 | ||
|
|
b5c172974e | ||
|
|
7bb70924c1 | ||
|
|
a62fb6dcd1 | ||
|
|
e9c06237f2 | ||
|
|
bdb1eca741 | ||
|
|
29000a5209 | ||
|
|
88f0867442 | ||
|
|
3de06174bf | ||
|
|
5187894add | ||
|
|
d58f7bb935 | ||
|
|
84594b0e1e | ||
|
|
578bbcd181 | ||
|
|
0cdf13742a | ||
|
|
b00d24579d | ||
|
|
5c76cdaad9 | ||
|
|
5d994d179e | ||
|
|
8930020776 | ||
|
|
0637bf0dcb | ||
|
|
2abec5b62f | ||
|
|
bc937a6434 | ||
|
|
3a84e3abcf | ||
|
|
e4c223ca59 | ||
|
|
31de2d304c | ||
|
|
c83113d49b | ||
|
|
d95980b3ba | ||
|
|
08a88af965 | ||
|
|
4d4f61c922 | ||
|
|
110b77f453 | ||
|
|
ba0d205457 | ||
|
|
1ee4cae802 | ||
|
|
095c894548 | ||
|
|
9f5f5da894 | ||
|
|
aa3608f0af | ||
|
|
56ebef4352 | ||
|
|
3e5c353298 | ||
|
|
0d57a887ea | ||
|
|
b5fdd29cd5 | ||
|
|
ae61662f61 | ||
|
|
b633f8d207 | ||
|
|
c4c813fd0a | ||
|
|
f9c4cc274a | ||
|
|
cc45a0ca28 | ||
|
|
9ed1d28f76 | ||
|
|
7ca2acd24e | ||
|
|
fdca468049 | ||
|
|
c0e8feb66e | ||
|
|
0dd710c195 | ||
|
|
39110ad21c | ||
|
|
57bb8c610a | ||
|
|
cf619f24a9 | ||
|
|
fd313f0d66 | ||
|
|
67a18dcff6 | ||
|
|
54b19a04bb | ||
|
|
ca4df29670 | ||
|
|
7da0b2fd7f | ||
|
|
cfcb64c516 | ||
|
|
135ae11c20 | ||
|
|
ce68d09d26 | ||
|
|
aa55e67389 | ||
|
|
f7992d2d09 | ||
|
|
eecfc155b8 | ||
|
|
9cef7945c0 | ||
|
|
1b1932f787 | ||
|
|
9e4fd193c6 | ||
|
|
d16a748f37 | ||
|
|
f616b8e518 | ||
|
|
97b469f59c | ||
|
|
7ec93825b6 | ||
|
|
4a4867deeb | ||
|
|
965a8ea9fd | ||
|
|
3e8ddabcc1 | ||
|
|
50267a6dd6 | ||
|
|
5a8825d016 | ||
|
|
e6bdcff0dd | ||
|
|
4803285e50 | ||
|
|
bab263f426 | ||
|
|
03d5509b44 | ||
|
|
4a5f193e94 | ||
|
|
fdd66f12f0 | ||
|
|
349f073b8e | ||
|
|
84e271be4b | ||
|
|
f215fa936b | ||
|
|
75d04a92c8 | ||
|
|
90c907710c | ||
|
|
00425670d7 | ||
|
|
cb525af0a2 | ||
|
|
09f7e38eed | ||
|
|
7c49a0ba7a | ||
|
|
45e57f1ad3 | ||
|
|
eb911de928 | ||
|
|
b393e31b76 | ||
|
|
4a095eb98e | ||
|
|
d793d4ba78 | ||
|
|
2f54ec4e18 | ||
|
|
b9cd40fe1e | ||
|
|
173cd7c15e | ||
|
|
950c8f7104 | ||
|
|
b63bebb519 | ||
|
|
cf72b34866 | ||
|
|
d06cf2a07b | ||
|
|
10804927bb | ||
|
|
f178297452 | ||
|
|
311e7684b2 | ||
|
|
37ff2ac2b9 | ||
|
|
4786d586cf | ||
|
|
b7aaae4a19 | ||
|
|
008598b56f | ||
|
|
f3bc55e37e | ||
|
|
31fa3dfd59 | ||
|
|
5b47438b71 | ||
|
|
c33ee83d87 | ||
|
|
835dd1abcc | ||
|
|
576f5496c0 | ||
|
|
ff460c7736 | ||
|
|
22adbe2073 | ||
|
|
b8f0b4b583 | ||
|
|
f88e5f09b4 | ||
|
|
2b9c6c95b1 | ||
|
|
3f34db549f | ||
|
|
f4c0ee49a7 | ||
|
|
2fc0a6df93 | ||
|
|
0a9c804940 | ||
|
|
96faa9d12b | ||
|
|
42d9fd9c88 | ||
|
|
908bc7561b | ||
|
|
3de92b337d | ||
|
|
31466785ad | ||
|
|
e38b602b7d | ||
|
|
a2f5342a83 | ||
|
|
d432732959 | ||
|
|
4eb3de7b4e | ||
|
|
2e6adfb44a | ||
|
|
c85c6d0ac5 | ||
|
|
6c9dd8deb6 | ||
|
|
fe64e6dbf2 | ||
|
|
4fd51ec880 | ||
|
|
0323c521be | ||
|
|
992125febc | ||
|
|
f0bd0abc0f | ||
|
|
5c0d4c4408 | ||
|
|
5f8e47a14d | ||
|
|
9407b91060 | ||
|
|
a6e38e2ad2 | ||
|
|
8701de64ce | ||
|
|
114cac99e7 | ||
|
|
78194599e2 | ||
|
|
49d9491fda | ||
|
|
392a363d02 | ||
|
|
0a87ba6930 | ||
|
|
065c104f27 | ||
|
|
7f272f1293 | ||
|
|
e67f50f157 | ||
|
|
dd13392e36 | ||
|
|
2638c66638 | ||
|
|
1d40225159 | ||
|
|
08f11d2f22 | ||
|
|
ee20ab3041 | ||
|
|
e0402919e1 | ||
|
|
086e156a02 | ||
|
|
d0b9292aee | ||
|
|
7b0cb69938 | ||
|
|
479dd684f8 | ||
|
|
573738443c | ||
|
|
0268629c3b | ||
|
|
cf9e6c5d06 | ||
|
|
ad44c6eeee | ||
|
|
2b79d76541 | ||
|
|
cfaf3180f9 | ||
|
|
7881efa269 | ||
|
|
1ac52ce18f | ||
|
|
bb123ae0df | ||
|
|
4859b67a7b | ||
|
|
586e8b1479 | ||
|
|
5a526e2077 | ||
|
|
d3452775b3 | ||
|
|
d82734641b | ||
|
|
abbc2bad35 | ||
|
|
b491d69eec | ||
|
|
d93514d327 | ||
|
|
49ed485145 | ||
|
|
1a2ca8634d | ||
|
|
60cceabaa6 | ||
|
|
56df6b5e08 | ||
|
|
ca5d5668d9 | ||
|
|
4a45d10a8b | ||
|
|
82e1e9f0e5 | ||
|
|
2e1b93b857 | ||
|
|
37e9cefc52 | ||
|
|
1807b1492e | ||
|
|
460a6b4e99 | ||
|
|
96d87a288f | ||
|
|
a0d00410ca | ||
|
|
445ff856fe | ||
|
|
b7fe212a18 | ||
|
|
cbabf0ae7e | ||
|
|
344ccf3b03 | ||
|
|
26a7e1b049 | ||
|
|
43bd331e48 | ||
|
|
8bbb016fa4 | ||
|
|
ea43ba7124 | ||
|
|
5a59bd9998 | ||
|
|
993d731c92 | ||
|
|
f529948d81 | ||
|
|
4f3f51c583 | ||
|
|
5ba9a1f87d | ||
|
|
a0fb75efcb | ||
|
|
6e6f4d5a37 | ||
|
|
f06053b1cf | ||
|
|
df0cd30236 | ||
|
|
8806c57c63 | ||
|
|
f66cfa1299 | ||
|
|
9e84a4dbab | ||
|
|
59ab066518 | ||
|
|
960bd2a6be | ||
|
|
2cdaef1371 | ||
|
|
9b6c5c08d8 | ||
|
|
268ce4fefc | ||
|
|
bca8dace80 | ||
|
|
75e5b49c3a | ||
|
|
3cf08dc451 | ||
|
|
76c9cf62ff | ||
|
|
7a93af1786 | ||
|
|
6f899d998f | ||
|
|
73da8463ca | ||
|
|
b38fab9738 | ||
|
|
45e4d64b20 | ||
|
|
e6fba5d802 | ||
|
|
50ba3c5d5c | ||
|
|
65663fb857 | ||
|
|
8df2f38d34 | ||
|
|
c5fae84263 | ||
|
|
533d0a1fd4 | ||
|
|
a0fe229c7a | ||
|
|
f876cd5a6a | ||
|
|
62e9e9a662 | ||
|
|
a51c5c3ae7 | ||
|
|
26636eda0d | ||
|
|
074df7637b | ||
|
|
4fc8905ebf | ||
|
|
8cbb2278a7 | ||
|
|
fe7f8c3c3a | ||
|
|
91430738e7 | ||
|
|
d6585d7583 | ||
|
|
1924d75c2b | ||
|
|
06b27d932d | ||
|
|
ac1ac2cfed | ||
|
|
53cdb04be7 | ||
|
|
c106de02e9 | ||
|
|
8bc91ced4f | ||
|
|
2d0b9597cc | ||
|
|
b92fbb893b | ||
|
|
957a40df1f | ||
|
|
1eea5b350d | ||
|
|
c8e7080d1d | ||
|
|
629d0e441f | ||
|
|
5cfb878131 | ||
|
|
bfd02ac13d | ||
|
|
bd50bb1aa4 | ||
|
|
1045062147 | ||
|
|
492def3b76 | ||
|
|
afde9ac8a3 | ||
|
|
d977258c6c | ||
|
|
62943f8803 | ||
|
|
22e0859538 | ||
|
|
2c47ef31c0 | ||
|
|
d69b9f9cf8 | ||
|
|
a52e9be3e8 | ||
|
|
21f9141500 | ||
|
|
0e000e44aa | ||
|
|
ad9fe3be09 | ||
|
|
3829bdf198 | ||
|
|
6933a049b1 | ||
|
|
e3544bf4bc | ||
|
|
d5d0cb6a0c | ||
|
|
cac5f20a28 | ||
|
|
beea32661a | ||
|
|
b9e57d0283 | ||
|
|
5c0458a887 | ||
|
|
65942cd46c | ||
|
|
9a8f1a0b0a | ||
|
|
edb37c95e1 | ||
|
|
896882958d | ||
|
|
45d687c4e1 | ||
|
|
42be1321b2 | ||
|
|
f2cc16009f | ||
|
|
4336a55c2e | ||
|
|
bc5c0f65b8 | ||
|
|
b7eba3bc17 | ||
|
|
661d715446 | ||
|
|
d2c166243b | ||
|
|
5ed5eabbfe | ||
|
|
18b4b141d9 | ||
|
|
cd1e478b95 | ||
|
|
cf0ecaaf41 | ||
|
|
5f466eed79 | ||
|
|
93198438b8 | ||
|
|
1057d0eaa6 | ||
|
|
73b1284234 | ||
|
|
5f2e87f0e3 | ||
|
|
d9528f5cc3 | ||
|
|
877bb5c821 | ||
|
|
ae353cef2e | ||
|
|
909606826d | ||
|
|
e6e2d51b58 | ||
|
|
ea16c01fa2 | ||
|
|
f20d420aa7 | ||
|
|
a8649f286c | ||
|
|
fe879e6d94 | ||
|
|
94f285695e | ||
|
|
428747ab69 | ||
|
|
049e8e07ef | ||
|
|
716a845e92 | ||
|
|
47dfd4e681 | ||
|
|
555108c7fd | ||
|
|
e1af3d9bf3 | ||
|
|
e864c7541c | ||
|
|
b4a6cbbd09 | ||
|
|
fe4eef5855 | ||
|
|
c0997f951c | ||
|
|
cb378c1754 | ||
|
|
20b961c1c8 | ||
|
|
c4c2e01213 | ||
|
|
4b6d0fb517 | ||
|
|
62e46b7a36 | ||
|
|
052c5c67b8 | ||
|
|
b794c5cfcf | ||
|
|
be8a4013a7 | ||
|
|
87c25f83a4 | ||
|
|
e08bea5f51 | ||
|
|
739f610507 | ||
|
|
608313c1d1 | ||
|
|
2c1cf42994 | ||
|
|
196db1e1d0 | ||
|
|
a2f5435c48 | ||
|
|
75ec0b4fcf | ||
|
|
b4bfe27786 | ||
|
|
7617382114 | ||
|
|
b2f67cb154 | ||
|
|
c5107ddd3d | ||
|
|
8b75590d3e | ||
|
|
a7624d4724 | ||
|
|
b3099001be | ||
|
|
533c6cefee | ||
|
|
acfa9e8a55 | ||
|
|
8823753b46 | ||
|
|
c5ccefe6f7 | ||
|
|
148071a744 | ||
|
|
56fab9d178 | ||
|
|
f422a77014 | ||
|
|
e248e7ebaf | ||
|
|
aa4c623a06 | ||
|
|
cbdba66ef3 | ||
|
|
d652ab9920 | ||
|
|
e49aaa0216 | ||
|
|
61d49a1215 | ||
|
|
c481ca924b | ||
|
|
23e5b6ba72 | ||
|
|
3e88b72316 | ||
|
|
7566243151 | ||
|
|
aaefa38602 | ||
|
|
de6db4141f | ||
|
|
a61735e29a | ||
|
|
9bdfb0a32b | ||
|
|
94ecd29e35 | ||
|
|
980f5f1299 | ||
|
|
f99d62a2bc | ||
|
|
89be1975ea | ||
|
|
fec82df451 | ||
|
|
89fd35e02d | ||
|
|
3286328de4 | ||
|
|
977c34c0d7 | ||
|
|
5ec23df460 | ||
|
|
7f307e3bea | ||
|
|
fa050fb8a8 | ||
|
|
d741cbf6fd | ||
|
|
fa09327403 | ||
|
|
e27aaf9ba1 | ||
|
|
c0063ea09b | ||
|
|
e3e07aa3e6 | ||
|
|
502e8daedd | ||
|
|
3035f0119f | ||
|
|
48a3c3a0c1 | ||
|
|
fab9ae64a0 | ||
|
|
b56ebd13b6 | ||
|
|
3de39698dd | ||
|
|
b86476410f | ||
|
|
57ea45ff51 | ||
|
|
ec30f7c5d4 | ||
|
|
3cc9114f81 | ||
|
|
790167e914 | ||
|
|
9687d432fd | ||
|
|
378c0d049e | ||
|
|
abe283b38e | ||
|
|
3af42079e0 | ||
|
|
eb4843649c | ||
|
|
9432efeba5 | ||
|
|
4a3ed75ae5 | ||
|
|
fc8c555519 | ||
|
|
44f30c0e05 | ||
|
|
83b5fd252e | ||
|
|
9ae18b0b3b | ||
|
|
84487b2e52 | ||
|
|
9689376de6 | ||
|
|
a2f79a163f | ||
|
|
489d0151ad | ||
|
|
bcb44ab600 | ||
|
|
1a78bb4b58 | ||
|
|
2600695927 | ||
|
|
705e4fca06 | ||
|
|
a4ba4cf6ce | ||
|
|
0b2109576a | ||
|
|
c8cdc50d29 | ||
|
|
b29e60a97a | ||
|
|
209a986fe4 | ||
|
|
295e5c9731 | ||
|
|
08c93e94e4 | ||
|
|
ec28e9fb27 | ||
|
|
7081e8a226 | ||
|
|
01a2383d7b | ||
|
|
903b569f6c | ||
|
|
9b79e35d52 | ||
|
|
237100da18 | ||
|
|
1e33c0c288 | ||
|
|
cbc7603248 | ||
|
|
8d53f3abb8 | ||
|
|
2dcfec2639 | ||
|
|
dacb1a70ee | ||
|
|
93200c00f2 | ||
|
|
3b484c362e | ||
|
|
fbee9fe51e | ||
|
|
c451aab150 | ||
|
|
48bff9a5d2 | ||
|
|
db36d80669 | ||
|
|
6c8d3232a0 | ||
|
|
e6418fe79b | ||
|
|
34a0bb04f7 | ||
|
|
28302c82a3 | ||
|
|
5025c40ab2 | ||
|
|
6fdd30d97f | ||
|
|
b7c000e435 | ||
|
|
5ef2bbe5d6 | ||
|
|
e1d37c3b98 | ||
|
|
ec475e5783 | ||
|
|
c4b67b4cae | ||
|
|
b2cfebcce2 | ||
|
|
2bf445e165 | ||
|
|
121224b1b0 | ||
|
|
55ce1ce3ec | ||
|
|
2b14633047 | ||
|
|
88042d27ec | ||
|
|
148f3002ec | ||
|
|
ec82c1f957 | ||
|
|
9919520c28 | ||
|
|
689795e8bc | ||
|
|
5c8ab35f55 | ||
|
|
ddd7f3f384 | ||
|
|
c2f74879da | ||
|
|
5311683d43 | ||
|
|
5f03a583d1 | ||
|
|
a6970e02f9 | ||
|
|
1f7f013e0a | ||
|
|
991a04dc2a | ||
|
|
685902adab | ||
|
|
26ce0eb4b9 | ||
|
|
b30db728cc | ||
|
|
5eb098b5aa | ||
|
|
a2995ea03a | ||
|
|
82da5cfd01 | ||
|
|
909285ea46 | ||
|
|
a0bc0641c7 | ||
|
|
e7ada61881 | ||
|
|
79e99908de | ||
|
|
f1d7644184 | ||
|
|
5f365927b3 | ||
|
|
bd893cb24c | ||
|
|
e3352c3658 | ||
|
|
63d3d6049e | ||
|
|
668810858d | ||
|
|
b7131a5cd5 | ||
|
|
c9a316ad35 | ||
|
|
ec8fe21485 | ||
|
|
4a0b10984a | ||
|
|
69406b00d1 | ||
|
|
a1f3ae730a | ||
|
|
fedaef5d17 | ||
|
|
e35345f135 | ||
|
|
d0dbbd1cb1 | ||
|
|
70e14f92a4 | ||
|
|
b45f371911 | ||
|
|
f1a7ee997b | ||
|
|
51ebec7c13 | ||
|
|
c3ed1ad040 | ||
|
|
85ebf2e484 | ||
|
|
1996e6afaa | ||
|
|
799f97e847 | ||
|
|
dd2a7f91cc | ||
|
|
c86e9dfc8a | ||
|
|
40a2579821 | ||
|
|
380b64071e | ||
|
|
5ce0b1c18e | ||
|
|
a00453e151 | ||
|
|
a1aa40f500 | ||
|
|
1ad4685bb7 | ||
|
|
d8629e61d6 | ||
|
|
d4ddd7204d | ||
|
|
5f18799d8f | ||
|
|
15ba3325d9 | ||
|
|
3046350cb2 | ||
|
|
ffa9e5dfab | ||
|
|
8584654f11 | ||
|
|
c7e65ce795 | ||
|
|
cc3f019b28 | ||
|
|
920785631b | ||
|
|
b7fe1db89a | ||
|
|
5a08dfa72f | ||
|
|
c28b1f6fb9 | ||
|
|
c81ca187f8 | ||
|
|
ba148d749f | ||
|
|
606dd11b4f | ||
|
|
e254d2de8e | ||
|
|
b0b8ba7000 | ||
|
|
4d512d908d | ||
|
|
168376b046 | ||
|
|
df9e7f284c | ||
|
|
6365ee7487 | ||
|
|
7a8620a570 | ||
|
|
2c24bf3222 | ||
|
|
e036d89a86 | ||
|
|
36e9fb9d68 | ||
|
|
8b56a038b7 | ||
|
|
d5f0e23e29 | ||
|
|
3a2bd4e928 | ||
|
|
c5136ca4d6 | ||
|
|
28dac81a90 | ||
|
|
c89632d2a8 | ||
|
|
82c8f3b556 | ||
|
|
29278ff916 | ||
|
|
608b9e66f4 | ||
|
|
ac0ed14eae | ||
|
|
a06dfaf82a | ||
|
|
dfd5e30015 | ||
|
|
b19524d56a | ||
|
|
e0d25ff887 | ||
|
|
198dfffaeb | ||
|
|
c4ef055248 | ||
|
|
420ceffbb0 | ||
|
|
eeeecf9763 | ||
|
|
8df591e8d4 | ||
|
|
702ed5bfa8 | ||
|
|
75c6844b66 | ||
|
|
b39f01dcdf | ||
|
|
dba7dc4e96 | ||
|
|
ef96a50cea | ||
|
|
eed78c960d | ||
|
|
eea7ca9b72 | ||
|
|
c7c732ebc0 | ||
|
|
fb06482916 | ||
|
|
1bb24efbbc | ||
|
|
13800589a9 | ||
|
|
848add1b1b | ||
|
|
fc67dd18d0 | ||
|
|
b3e9b92344 | ||
|
|
a291b29c6f | ||
|
|
b566c4ba1a | ||
|
|
2349a6ab0c | ||
|
|
70eaf41acb | ||
|
|
912091981e | ||
|
|
aca42ff6a5 | ||
|
|
3fd885a188 | ||
|
|
2dacdf0210 | ||
|
|
5e8856e65b | ||
|
|
d42539949e | ||
|
|
28ca5b2b57 | ||
|
|
eea6f5cbfa | ||
|
|
b55dcc16ae | ||
|
|
0cb442d40e | ||
|
|
680dc1f962 | ||
|
|
30c92ce1b7 | ||
|
|
e55a09241e | ||
|
|
5f02237119 | ||
|
|
68552b6d65 | ||
|
|
44a4cea3a2 | ||
|
|
77c13e10ec | ||
|
|
0f66ff50a4 | ||
|
|
af515856c2 | ||
|
|
62e8c09183 | ||
|
|
cb40a74aaf | ||
|
|
a2c3ef94ec | ||
|
|
4184fb3ae7 | ||
|
|
20d2d141e4 | ||
|
|
0cfc3bfb79 | ||
|
|
2de206cb81 | ||
|
|
64c6dfd307 | ||
|
|
03d0b296e1 | ||
|
|
ca4408b343 | ||
|
|
527f408f6a | ||
|
|
adccca366e | ||
|
|
76cad41382 | ||
|
|
501db8f276 | ||
|
|
cc684ff0b1 | ||
|
|
7823c07f1a | ||
|
|
eb6d3d68a9 | ||
|
|
c98c7d47db | ||
|
|
194694a59b | ||
|
|
555cee3500 | ||
|
|
f4bb854372 | ||
|
|
d04b8e2fc6 | ||
|
|
21c1450e4a | ||
|
|
2b6b0e62f4 | ||
|
|
4fb360b19f | ||
|
|
0fa1567793 | ||
|
|
d7377015a2 | ||
|
|
139017ba7c | ||
|
|
ff3b53e34a | ||
|
|
b409cdece5 | ||
|
|
2478e67d16 | ||
|
|
dd35877eda | ||
|
|
4acfd2c342 | ||
|
|
df3f9a2ae8 | ||
|
|
78ae9ff093 | ||
|
|
ca781651cb | ||
|
|
417fa3cf3c | ||
|
|
92bb477f68 | ||
|
|
0f08cc5aa9 | ||
|
|
3dac33ffba | ||
|
|
fe73eccb90 | ||
|
|
b9544d9534 | ||
|
|
99ba9874b1 | ||
|
|
b41ca72d2b | ||
|
|
1ba17a0e14 | ||
|
|
480d31eb5e | ||
|
|
1973f93d4a | ||
|
|
3a2f4d4def | ||
|
|
192d2b86b6 | ||
|
|
6ca948ac3c | ||
|
|
5f932e9749 | ||
|
|
ff46c18164 | ||
|
|
de777907f2 | ||
|
|
72c4fd868a | ||
|
|
4bc2cd003d | ||
|
|
c9c6c1f769 | ||
|
|
bb7d8735cb | ||
|
|
557b0d76ab | ||
|
|
13073411cc | ||
|
|
0530045536 | ||
|
|
6d29e9c1b7 | ||
|
|
d3e4739745 | ||
|
|
3379f3ef61 | ||
|
|
9da7919062 | ||
|
|
aeed7deb2d | ||
|
|
38b617d848 | ||
|
|
dda02f783f | ||
|
|
cda322e311 | ||
|
|
934d3178a2 | ||
|
|
2cded8dcac | ||
|
|
babe0c31ad | ||
|
|
aa2ae37365 | ||
|
|
9a8b81fcbe | ||
|
|
138f38aade | ||
|
|
42914bff6f | ||
|
|
59acf95bf5 | ||
|
|
31c1046771 | ||
|
|
ec4a22687f | ||
|
|
22d9d16a7a | ||
|
|
0520e67303 | ||
|
|
d571573e52 | ||
|
|
0f643daac6 | ||
|
|
989317e5d3 | ||
|
|
cce6db4aeb | ||
|
|
46109abde5 | ||
|
|
2e16774190 | ||
|
|
812a21bce6 | ||
|
|
17e0207120 | ||
|
|
e02608edf8 | ||
|
|
1a7e160956 | ||
|
|
59d966356e | ||
|
|
5937e0289e | ||
|
|
85c212aee3 | ||
|
|
1978c3d3bd | ||
|
|
30a2c28d74 | ||
|
|
94a330532d | ||
|
|
f65e57ee82 | ||
|
|
750064c06f | ||
|
|
651fd79325 | ||
|
|
b53f988fca | ||
|
|
1595eaeb74 | ||
|
|
a54cbe896d | ||
|
|
9995a8d53f | ||
|
|
b88872d54b | ||
|
|
f02ac01f7e | ||
|
|
3ab3a5a236 | ||
|
|
fa3eb185ab | ||
|
|
4a56ba34d4 | ||
|
|
b860c69fd7 | ||
|
|
8408ceffe8 | ||
|
|
da9133c3c3 | ||
|
|
4a6e920d0e | ||
|
|
13f58d602f | ||
|
|
45fda595c3 | ||
|
|
1f15ea0bd8 | ||
|
|
99eebfa10f | ||
|
|
ccbbe81141 | ||
|
|
ad2da5969e | ||
|
|
2ce82f3da5 | ||
|
|
6bc9479e00 | ||
|
|
740e52229f | ||
|
|
1efd2390e3 | ||
|
|
564328abf9 | ||
|
|
926f819e31 | ||
|
|
45f0b9ac45 | ||
|
|
9a82e86ce9 | ||
|
|
0ad974c57b | ||
|
|
62f015fc34 | ||
|
|
b508787037 | ||
|
|
176ef19cca | ||
|
|
db83ede73c | ||
|
|
452d1e8307 | ||
|
|
92f337c67e | ||
|
|
1e4022680a | ||
|
|
e16d951da0 | ||
|
|
7b2b19b4ba | ||
|
|
8ebfaed546 | ||
|
|
0b52d52f4a | ||
|
|
6901d10d54 | ||
|
|
7caa2ae9bc | ||
|
|
cc50859912 | ||
|
|
6c16780144 | ||
|
|
b5f79444f9 | ||
|
|
b73c6e94c0 | ||
|
|
07616a9d34 | ||
|
|
e88ee26e3b | ||
|
|
7422c38e66 | ||
|
|
8ed1c77e47 | ||
|
|
8c65b8c16f | ||
|
|
1f435522b4 | ||
|
|
eb880e8de0 | ||
|
|
83b1d80a5b | ||
|
|
529aff3126 | ||
|
|
bf2bd519eb | ||
|
|
38c7dda00f | ||
|
|
056e7432bd | ||
|
|
074497b0f6 | ||
|
|
1eca969cf6 | ||
|
|
c1cbdae5ee | ||
|
|
4d4b6a2fa0 | ||
|
|
d30a972a90 | ||
|
|
24013af3bb | ||
|
|
338dc3223c | ||
|
|
c422a6dd4f | ||
|
|
8305af8f10 | ||
|
|
6435c7b921 | ||
|
|
af7c57b082 | ||
|
|
80941eacbd | ||
|
|
ceb252986e | ||
|
|
5d8de5fde2 | ||
|
|
750ef296c6 | ||
|
|
454ae8656a | ||
|
|
75450dcdbc | ||
|
|
bd2c7e3bb9 | ||
|
|
9d23cf33fd | ||
|
|
97eb01a28d | ||
|
|
9a2a636aed | ||
|
|
61c8256ef0 | ||
|
|
8e1791570e | ||
|
|
aa30d1f359 | ||
|
|
326f4bd681 | ||
|
|
7690c6c33d | ||
|
|
fece1077f2 | ||
|
|
75fc7db50d | ||
|
|
96da04576e | ||
|
|
001ec3663e | ||
|
|
21a00b77bd | ||
|
|
408f3852ec | ||
|
|
61150c74d2 | ||
|
|
7bb7003c9d | ||
|
|
920463f2ff | ||
|
|
ca1185d0be | ||
|
|
be655ee328 | ||
|
|
02d4186b11 | ||
|
|
3f97bebd69 | ||
|
|
2e378da922 | ||
|
|
b37f51bd7f | ||
|
|
eb8b0f72cc | ||
|
|
d8fe9a4d29 | ||
|
|
c97cdf551e | ||
|
|
80fc60b5e2 | ||
|
|
3b2e142542 | ||
|
|
0e58d99f4e | ||
|
|
92798abb5d | ||
|
|
bd7950b757 | ||
|
|
59a15ceef6 | ||
|
|
4011a113cc | ||
|
|
70cbe91776 | ||
|
|
f92027c44b | ||
|
|
1443335315 | ||
|
|
6ff2229a09 | ||
|
|
bb72672dd9 | ||
|
|
d96dee3aa6 | ||
|
|
bd0aaa343b | ||
|
|
3126e1ac94 | ||
|
|
a117d87f33 | ||
|
|
9dc4f8a1aa | ||
|
|
0d536d11e3 | ||
|
|
72a4962fd0 | ||
|
|
a3045a3953 | ||
|
|
c620a22017 | ||
|
|
e3593fe197 | ||
|
|
856ec03cc7 | ||
|
|
c80c5631f0 | ||
|
|
ef70668a77 | ||
|
|
ebd4691462 | ||
|
|
28554235be | ||
|
|
efbbb6fd20 | ||
|
|
9de57021a3 | ||
|
|
e21f770485 | ||
|
|
697c00dccf | ||
|
|
1caf6a3298 | ||
|
|
02fd02d482 | ||
|
|
239fb0db94 | ||
|
|
fe1d73c3e5 | ||
|
|
43da06a354 | ||
|
|
fea6b67067 | ||
|
|
f065ae54d5 | ||
|
|
3cf417766d | ||
|
|
0fb41b10e9 | ||
|
|
bc9dc3bf1e | ||
|
|
3cde5e28a8 | ||
|
|
cb8e7181c4 | ||
|
|
9a3becdecc | ||
|
|
e3c10d779d | ||
|
|
dd9f1024f4 | ||
|
|
9841f74adc | ||
|
|
b56e493d92 | ||
|
|
a2c5211b20 | ||
|
|
b7a7abed48 | ||
|
|
72bfdfd925 | ||
|
|
b80d34612a | ||
|
|
648cc0f006 | ||
|
|
1fc9506442 | ||
|
|
830692dd60 | ||
|
|
95a6759381 | ||
|
|
960b37b1c2 | ||
|
|
b1d17dea4f | ||
|
|
6b06471953 | ||
|
|
4ca957d3eb | ||
|
|
eb9b63477c | ||
|
|
80c01b055c | ||
|
|
50aec67069 | ||
|
|
7baced75e5 | ||
|
|
99743a94fb | ||
|
|
9bdfd6025b | ||
|
|
91400d2ce0 | ||
|
|
7b88d0efe3 | ||
|
|
4aada65dae | ||
|
|
0560d2cfb7 | ||
|
|
58c1a68ad9 | ||
|
|
588fc6df85 | ||
|
|
2c9e4ded40 | ||
|
|
88a538e71b | ||
|
|
513363504f | ||
|
|
0e844edacb | ||
|
|
5751bb2481 | ||
|
|
28669d940a | ||
|
|
3d87bdb6b4 | ||
|
|
1499ce43bf | ||
|
|
4d22b43d65 | ||
|
|
823603650f | ||
|
|
062867a38d | ||
|
|
f3e0c5d653 | ||
|
|
fc7f48b7db | ||
|
|
04d56420d1 | ||
|
|
a017574f74 | ||
|
|
ae24360c02 | ||
|
|
3fea1976c8 | ||
|
|
cf97dd9fcd | ||
|
|
0e3a48ff76 | ||
|
|
48cbe45a9d | ||
|
|
276bf09238 | ||
|
|
05988c1c49 | ||
|
|
d46b26e3bc | ||
|
|
236c172c6f | ||
|
|
59fcb56972 | ||
|
|
c07cd3a856 | ||
|
|
37766347a5 | ||
|
|
79da61782b | ||
|
|
8af87f1a8b | ||
|
|
494c954cbb | ||
|
|
71bc9eea28 | ||
|
|
e3b2bcfd06 | ||
|
|
142d974641 | ||
|
|
e56129111a | ||
|
|
0e1d6aa85c | ||
|
|
bcdb8cd770 | ||
|
|
7b2ca55089 | ||
|
|
f6ef0b684a | ||
|
|
02e1cdf210 | ||
|
|
b58950c574 | ||
|
|
833a60f29c | ||
|
|
f776d67c03 | ||
|
|
13e7cca1a4 | ||
|
|
0f3c477ff3 | ||
|
|
039cc30c07 | ||
|
|
25c8cd9246 | ||
|
|
c58841100a | ||
|
|
03e24cccd0 | ||
|
|
35f011758d | ||
|
|
2ebfaf76f2 | ||
|
|
0cf187dee7 | ||
|
|
bdeb325bad | ||
|
|
a1225b6d0d | ||
|
|
f0368b02c4 | ||
|
|
202de1436d | ||
|
|
7f8746fcd4 | ||
|
|
e05a25d701 | ||
|
|
6930570fa2 | ||
|
|
aba2c5b938 | ||
|
|
d82f86dcd9 | ||
|
|
159b4f9734 | ||
|
|
46a737c7a1 | ||
|
|
a731486ab7 | ||
|
|
c3e57f1fdd | ||
|
|
a9af484412 | ||
|
|
007646774e | ||
|
|
2d78e35e16 | ||
|
|
7524b5e349 | ||
|
|
2a04a48b89 | ||
|
|
3cbdaab81e | ||
|
|
8c858a5953 | ||
|
|
1812958106 | ||
|
|
4e5324916c | ||
|
|
1a77becc6a | ||
|
|
23ccaea2ff | ||
|
|
2a4b252a9d | ||
|
|
9ae4edfee5 | ||
|
|
bf48809b61 | ||
|
|
57a80a3c10 | ||
|
|
3f3e52d7ae | ||
|
|
5c69110658 | ||
|
|
be055d9dcb | ||
|
|
1e34a61911 | ||
|
|
97bd1da2a2 | ||
|
|
330ffb803f | ||
|
|
7b77f200be | ||
|
|
15a3c8408f | ||
|
|
bc1784ed2b | ||
|
|
55f0a82249 | ||
|
|
7aada3f328 | ||
|
|
dad885c051 | ||
|
|
f5c7bbfda8 | ||
|
|
f832743009 | ||
|
|
7551de6439 | ||
|
|
e03b4b7505 | ||
|
|
2d59fdd178 | ||
|
|
e61c8046f4 | ||
|
|
c0796ac3d6 | ||
|
|
68be24ffc6 | ||
|
|
9dcc87c705 | ||
|
|
d36c536107 | ||
|
|
affeeb39de | ||
|
|
f5d8a952f2 | ||
|
|
da07f99d3d | ||
|
|
eef66de68c | ||
|
|
4aa1180fce | ||
|
|
553d52a45e | ||
|
|
347b153884 | ||
|
|
1e7c176481 | ||
|
|
e390405d0c | ||
|
|
7378a84c96 | ||
|
|
b25013c4a2 | ||
|
|
6942916f13 | ||
|
|
f69f0b97f5 | ||
|
|
4361ea9686 | ||
|
|
be2ee33273 | ||
|
|
8c2ddb0255 | ||
|
|
466a5a932b | ||
|
|
8a3c6382e9 | ||
|
|
a2b45120c5 | ||
|
|
546ad52e11 | ||
|
|
1aefc5b540 | ||
|
|
1085ca4a2d | ||
|
|
9766322e99 | ||
|
|
cfb68e3bff | ||
|
|
a006963fb8 | ||
|
|
24c95c27c3 | ||
|
|
3c40c0be6b | ||
|
|
b1fc80b79a | ||
|
|
50d793e49b | ||
|
|
34c43b8349 | ||
|
|
7002a316fd | ||
|
|
1f37faad42 | ||
|
|
68cf24d100 | ||
|
|
86491da253 | ||
|
|
90249cdafa | ||
|
|
7c75111c41 | ||
|
|
7b53b6bfef | ||
|
|
fded5fd900 | ||
|
|
950965bd4a | ||
|
|
3a359319fa | ||
|
|
d3dd82c699 | ||
|
|
81f192bccb | ||
|
|
60a23febed | ||
|
|
d0e280cbac | ||
|
|
ecb62c8659 | ||
|
|
12669df92b | ||
|
|
44b2afeffa | ||
|
|
70f435e909 | ||
|
|
512d82071e | ||
|
|
3896230199 | ||
|
|
b902880a05 | ||
|
|
418526af16 | ||
|
|
45ad212459 | ||
|
|
0f49d424d3 | ||
|
|
01e42c8d6f | ||
|
|
26107bd6c3 | ||
|
|
7d3ecd2297 | ||
|
|
16056661dd | ||
|
|
059f50dad4 | ||
|
|
4c9975a7d9 | ||
|
|
9f9cc1ffb5 | ||
|
|
e768e1e277 | ||
|
|
acaf7b969a | ||
|
|
2b94975345 | ||
|
|
e6b4e12689 | ||
|
|
7eaac995bd | ||
|
|
a19cdb5e72 | ||
|
|
f54fbd057e | ||
|
|
19eceb4ecc | ||
|
|
dcff1ec25f | ||
|
|
567cda4cd3 | ||
|
|
900d8790b3 | ||
|
|
cad284519f | ||
|
|
0727acf458 | ||
|
|
d8813179be | ||
|
|
10d690c8fb | ||
|
|
52f71cdda0 | ||
|
|
2a9a348164 | ||
|
|
00346781bb | ||
|
|
4c6e92eea1 | ||
|
|
b63f469110 | ||
|
|
f6f176afc1 | ||
|
|
3de37a61c5 | ||
|
|
2d955dae48 | ||
|
|
46577fb128 | ||
|
|
37dba6ebfd | ||
|
|
66b949bed1 | ||
|
|
c9a05187fb | ||
|
|
cc956583fb | ||
|
|
14206efb09 | ||
|
|
5e6d7f5d16 | ||
|
|
7a33831d14 | ||
|
|
4f120e19fd | ||
|
|
37d064d836 | ||
|
|
824150f89b | ||
|
|
f7dc4cca2c | ||
|
|
ea39bb4334 | ||
|
|
5680d5a7be | ||
|
|
004246124b | ||
|
|
c41beae99a | ||
|
|
fe2cffb25b | ||
|
|
f71d5c429d | ||
|
|
dce5816b18 | ||
|
|
f99a7b2a8c | ||
|
|
ec36c69984 | ||
|
|
2458db03de | ||
|
|
7528b7bc1a | ||
|
|
8af33084ed | ||
|
|
f643175156 | ||
|
|
0321dda1d7 | ||
|
|
ff5d79e3ee | ||
|
|
4ee3ec09df | ||
|
|
cfe9d47fa0 | ||
|
|
607d6125fc | ||
|
|
6215259565 | ||
|
|
d034fecc89 | ||
|
|
f18d8229c0 | ||
|
|
e736626953 | ||
|
|
c2c438637a | ||
|
|
94638fe42c | ||
|
|
55ecfda39a | ||
|
|
d97a272aa5 | ||
|
|
80a1944b9d | ||
|
|
138cf943a9 | ||
|
|
c7e672e533 | ||
|
|
1b74a04efd | ||
|
|
290c7e6009 | ||
|
|
e8a56e0fea | ||
|
|
1ae7b646b3 | ||
|
|
42e2d73ce2 | ||
|
|
9e2a65a5ce | ||
|
|
fea20ea913 | ||
|
|
5b2480fff2 | ||
|
|
b0dca2a363 | ||
|
|
59bbe72798 | ||
|
|
f99a30a57e | ||
|
|
aa4cb29621 | ||
|
|
91ad4e396b | ||
|
|
351e17aacf | ||
|
|
6c8e09acdb | ||
|
|
1a7b341745 | ||
|
|
af592ea8c1 | ||
|
|
bb096a0357 | ||
|
|
3c226892c6 | ||
|
|
47f6fe069a | ||
|
|
aa3c1d930b | ||
|
|
99b0b4f5b8 | ||
|
|
bcd239ac2b | ||
|
|
2cc25b1e6e | ||
|
|
5fd3ed782f | ||
|
|
c34a24b633 | ||
|
|
775612ec5a | ||
|
|
fd43b16213 | ||
|
|
5a455ec4f7 | ||
|
|
1277c3d156 | ||
|
|
8033d1ca6d | ||
|
|
28df6881a7 | ||
|
|
e5fa5df7be | ||
|
|
f7dbf2bdd4 | ||
|
|
857c57daba | ||
|
|
5515da3c2d | ||
|
|
cfc111f855 | ||
|
|
3dd4043827 | ||
|
|
351ecfae0f | ||
|
|
b22393092b | ||
|
|
1485ee8027 | ||
|
|
60826c2d0c | ||
|
|
fb383458d7 | ||
|
|
196ee1aa8b | ||
|
|
2df97cd2f5 | ||
|
|
501b523680 | ||
|
|
6efa6691b1 | ||
|
|
c47f1ae236 | ||
|
|
aac240fe41 | ||
|
|
041debcd93 | ||
|
|
0632a2d3c8 | ||
|
|
9f40b3a873 | ||
|
|
8fad0af935 | ||
|
|
48ad744ebf | ||
|
|
556d5b0ca5 | ||
|
|
e30d70b6d4 | ||
|
|
a58f5a925a | ||
|
|
a3cc3c57fd | ||
|
|
0d0d3edeae | ||
|
|
dd0be7c522 | ||
|
|
9d2982fcd7 | ||
|
|
ebfd7d2153 | ||
|
|
818cd2454d | ||
|
|
b31d1c06f5 | ||
|
|
6cd884555c | ||
|
|
47ef74a1bb | ||
|
|
cc6d6ddd66 | ||
|
|
6a6cf015a6 | ||
|
|
ca79e81b39 | ||
|
|
a9e86cecf5 | ||
|
|
5773b1c3e5 | ||
|
|
b562b3410b | ||
|
|
f6440e9830 | ||
|
|
e43636e1e9 | ||
|
|
6783bf9903 | ||
|
|
807723c5b2 | ||
|
|
d3c4936116 | ||
|
|
bbb40aef51 | ||
|
|
485a3e29e7 | ||
|
|
1477f99c2c | ||
|
|
2e1f9d5fa9 | ||
|
|
9dea251862 | ||
|
|
17edfd6573 | ||
|
|
458e9d6cc7 | ||
|
|
485459b8b2 | ||
|
|
fcf377d26b | ||
|
|
3be1c9261f | ||
|
|
38600b3347 | ||
|
|
62f7f7a689 | ||
|
|
552f616305 | ||
|
|
a3164177f8 | ||
|
|
fa6bf21cd1 | ||
|
|
eecf76c1fb | ||
|
|
d1635cf24e | ||
|
|
b43e9ed7e7 | ||
|
|
12b2ab5da8 | ||
|
|
1c9085556c | ||
|
|
9122f8acee | ||
|
|
ef8c9f093c | ||
|
|
801dffd571 | ||
|
|
0b1c57b39f | ||
|
|
2febc268f7 | ||
|
|
58995bb3a2 | ||
|
|
8c944815bc | ||
|
|
f065a21542 | ||
|
|
27e032d10d | ||
|
|
ab3980cd38 | ||
|
|
1db648a525 | ||
|
|
ce3b5b683d | ||
|
|
9d23f1298d | ||
|
|
3f791b65b5 | ||
|
|
317d8703ca | ||
|
|
fda619f704 | ||
|
|
e4a0669da8 | ||
|
|
89725df3dc | ||
|
|
51799844c9 | ||
|
|
48de136e9d | ||
|
|
cb6f97a831 | ||
|
|
7e0cd0ab60 | ||
|
|
8521f04087 | ||
|
|
8ba45808be | ||
|
|
d876fd7f5b | ||
|
|
352e409a6e | ||
|
|
d6ec441c8e | ||
|
|
d197497349 | ||
|
|
d892ba6aa5 | ||
|
|
84b2583973 | ||
|
|
108648b427 | ||
|
|
71bf8b6b4d | ||
|
|
576067c1e5 | ||
|
|
e23bab0103 | ||
|
|
4e111c84f3 | ||
|
|
8cecce7570 | ||
|
|
0338fd42e1 | ||
|
|
b3788bc143 | ||
|
|
18d66ddded | ||
|
|
701b5ea561 | ||
|
|
86d0de4b0e | ||
|
|
a95958f9f6 | ||
|
|
69ab236f3f | ||
|
|
4cf3c6a616 | ||
|
|
da48bbf312 | ||
|
|
ac957db6d1 | ||
|
|
64464f23ae | ||
|
|
52cb239194 | ||
|
|
efd54b7523 | ||
|
|
2aca57cb82 | ||
|
|
d68baf08cb | ||
|
|
a7578aa709 | ||
|
|
a8261d376a | ||
|
|
fc346b4efd | ||
|
|
ad09e734da | ||
|
|
a674fea1c2 | ||
|
|
9e22b34fac | ||
|
|
fe24408620 | ||
|
|
c07ad0941c | ||
|
|
2f02b38b62 | ||
|
|
3ac766530d | ||
|
|
de77c71042 | ||
|
|
9c854a1757 | ||
|
|
f66fa1150e | ||
|
|
f820706e4f | ||
|
|
29e9e0f2cc | ||
|
|
2933093e17 | ||
|
|
71cd8918be | ||
|
|
c049ba59ff | ||
|
|
51c5f28443 | ||
|
|
bb1ed902a9 | ||
|
|
b016a60a75 | ||
|
|
890d485bb5 | ||
|
|
208bb2d72f | ||
|
|
267bf289c4 | ||
|
|
b3e083d866 | ||
|
|
a675c64c2d | ||
|
|
8b50c8515f | ||
|
|
1eaa377583 | ||
|
|
4345b1d930 | ||
|
|
06bf0c2622 | ||
|
|
3ac8de0a64 | ||
|
|
f237fd9847 | ||
|
|
5730280325 | ||
|
|
ab4df7e078 | ||
|
|
b52e6c99ab | ||
|
|
7dab548522 | ||
|
|
785c341822 | ||
|
|
7d2e1f63b5 | ||
|
|
e119459411 | ||
|
|
97ef2191fd | ||
|
|
e833ccf309 | ||
|
|
a4134d30fa | ||
|
|
6069fd02d3 | ||
|
|
bb15dc57a4 | ||
|
|
bdfe170c3b | ||
|
|
0fa2ba53ab | ||
|
|
4bb657debf | ||
|
|
dd12840e34 | ||
|
|
b027dcfec9 | ||
|
|
9e9b6f1542 | ||
|
|
7cd66e20d0 | ||
|
|
d93df15eff | ||
|
|
ddfd20d997 | ||
|
|
fd8af88493 | ||
|
|
bfa488f77d | ||
|
|
03be793930 | ||
|
|
37d88d5ff7 | ||
|
|
4616f889fd | ||
|
|
59cbf95c4f | ||
|
|
058711d3a8 | ||
|
|
2ddc61fa5c | ||
|
|
e04b7d0f01 | ||
|
|
2faa2ed1f4 | ||
|
|
5e2889e776 | ||
|
|
5bda36fb28 | ||
|
|
53fbb257b9 | ||
|
|
65a32d6e20 | ||
|
|
92450920d4 | ||
|
|
0099a9822e | ||
|
|
0cf86974dd | ||
|
|
716705aa15 | ||
|
|
757993064e | ||
|
|
3f738cf905 | ||
|
|
570715100b | ||
|
|
ad8750b40d | ||
|
|
757ea93393 | ||
|
|
dbd5a222d5 | ||
|
|
bba80bc80f | ||
|
|
094143bc28 | ||
|
|
24a335d304 | ||
|
|
c62b318b9e | ||
|
|
ea5c7c321a | ||
|
|
6d92775ab5 | ||
|
|
1a9360ca75 | ||
|
|
22b9bbe702 | ||
|
|
6fb44083ec | ||
|
|
ba02be08bb | ||
|
|
56fe3ede5b | ||
|
|
e48a000784 | ||
|
|
6d1c150ff5 | ||
|
|
21190a240f | ||
|
|
8a525bc131 | ||
|
|
734905d1f7 | ||
|
|
90edf2fc60 | ||
|
|
e3f37c14db | ||
|
|
c6c92184d9 | ||
|
|
c4fbc65354 | ||
|
|
54d250bde4 | ||
|
|
ef309bd8d0 | ||
|
|
6cdb6ec711 | ||
|
|
03891b66b6 | ||
|
|
42dd6326d5 | ||
|
|
5c4defdb8e | ||
|
|
f08d53b0c6 | ||
|
|
6859b85266 | ||
|
|
075adb4f03 | ||
|
|
5ce72a3461 | ||
|
|
8c2958b86d | ||
|
|
f15b7cebac | ||
|
|
f6d8df1e83 | ||
|
|
19ed5bf993 | ||
|
|
5567e2843d | ||
|
|
0a8e20fd60 | ||
|
|
558c4341e4 | ||
|
|
250860d92c | ||
|
|
64aecba7a0 | ||
|
|
3689b08237 | ||
|
|
30e567e8b6 | ||
|
|
ddd74549fe | ||
|
|
14620c32aa | ||
|
|
fb7068d415 | ||
|
|
8614ff40df | ||
|
|
aa10a9d899 | ||
|
|
a5b8feca93 | ||
|
|
486e47f985 | ||
|
|
bb5a1ad513 | ||
|
|
eac0a52f10 | ||
|
|
7ac00258cc | ||
|
|
e3a0ae8a4b | ||
|
|
2953159f8b | ||
|
|
9693363c76 | ||
|
|
a2533af116 | ||
|
|
b4aecb5b74 | ||
|
|
15aa2498b5 | ||
|
|
0372ff0c2c | ||
|
|
7a8d5a391a | ||
|
|
2a6c81a89d | ||
|
|
301871aec6 | ||
|
|
25359e5320 | ||
|
|
b6fff53b21 | ||
|
|
ae7b5fac74 | ||
|
|
26168a9520 | ||
|
|
698dfca319 | ||
|
|
3bcb98e644 | ||
|
|
2deb436ccd | ||
|
|
2b3405c4a9 | ||
|
|
677a465630 | ||
|
|
8ecb76fc0b | ||
|
|
0178013fc1 | ||
|
|
c273a8ee69 | ||
|
|
0ed56b706b | ||
|
|
4582b6cf76 | ||
|
|
05513bcd1e | ||
|
|
f5dd135ed8 | ||
|
|
9c8f85741c | ||
|
|
ca515f2eae | ||
|
|
80c1ebd768 | ||
|
|
b51fd7fc13 | ||
|
|
efe86c37b2 | ||
|
|
d20a4a8bfc | ||
|
|
9da2d11e80 | ||
|
|
5ef554aecf | ||
|
|
9a7fea0447 | ||
|
|
ae52ff93b2 | ||
|
|
80a567bf1e | ||
|
|
ce2a3361eb | ||
|
|
ca9ea109c6 | ||
|
|
2a33a746f0 | ||
|
|
e8c5246645 | ||
|
|
98295b85ab | ||
|
|
af1823db8c | ||
|
|
a2ab6b89f1 | ||
|
|
5de300fb35 | ||
|
|
62a4c82e95 | ||
|
|
d522c864d4 | ||
|
|
aa8ff7ace3 | ||
|
|
4e6a931de3 | ||
|
|
5e141e869d | ||
|
|
611555514c | ||
|
|
e1c78fcbd3 | ||
|
|
8640d6bb1e | ||
|
|
28d5bedcc7 | ||
|
|
373b890e1d | ||
|
|
aad0f90a9d | ||
|
|
5dc45c35e6 | ||
|
|
b8c87632e6 | ||
|
|
c85903383a | ||
|
|
4aededf038 | ||
|
|
4bc6501b8d | ||
|
|
a1b3b47573 | ||
|
|
c8cf4fe09c | ||
|
|
ca07d75405 | ||
|
|
c5001f3620 | ||
|
|
8d5f941829 | ||
|
|
c3bfaa1c33 | ||
|
|
ea0d52c0b8 | ||
|
|
fcb37f40f6 | ||
|
|
7f30d07f4c | ||
|
|
59744a96fa | ||
|
|
b82fb58dc4 | ||
|
|
c728214af7 | ||
|
|
305d636217 | ||
|
|
31312747e9 | ||
|
|
5ef288b840 | ||
|
|
f6615a490d | ||
|
|
bd4f5ebcdf | ||
|
|
1fd7ff5655 | ||
|
|
ab7e1b42bd | ||
|
|
a7723e6ded | ||
|
|
1b78001201 | ||
|
|
36c0eae7ed | ||
|
|
0ae43e242f | ||
|
|
bafd4f1860 | ||
|
|
388e58bf1e | ||
|
|
eee973fe86 | ||
|
|
61769c6f9c | ||
|
|
665ef9424e | ||
|
|
7a0f0ca5ce | ||
|
|
63be05146d | ||
|
|
9239cfb3c1 | ||
|
|
6fd24ad54f | ||
|
|
d70933c9f2 | ||
|
|
9ac2ddcb4d | ||
|
|
8d9569e06b | ||
|
|
02f8e657f3 | ||
|
|
3dc711ab9d | ||
|
|
702922dd88 | ||
|
|
2583c809ca | ||
|
|
b6071ce6dc | ||
|
|
186132bb98 | ||
|
|
c15790f230 | ||
|
|
13924a8353 | ||
|
|
fd84b57ac8 | ||
|
|
591a6b330a | ||
|
|
a3b767bb13 | ||
|
|
847ee61bf4 | ||
|
|
0c6cede287 | ||
|
|
ce4b07d7d7 | ||
|
|
a1f49b279f | ||
|
|
1c8075ca40 | ||
|
|
56b0952cd1 | ||
|
|
1c152f6cad | ||
|
|
57c05354c2 | ||
|
|
90b5479735 | ||
|
|
1079c4516c | ||
|
|
7381985c79 | ||
|
|
fd26f9f34e | ||
|
|
88b70973cc | ||
|
|
f0658bbd09 | ||
|
|
661e07c8db | ||
|
|
6e51189d4d | ||
|
|
dfdb7c835b | ||
|
|
f1d7aa09e4 | ||
|
|
88e6b865d9 | ||
|
|
d5c6d74f14 | ||
|
|
202f3d36c4 | ||
|
|
7a54b1d36a | ||
|
|
9091b36249 | ||
|
|
21285d9f6d | ||
|
|
2ebc773863 | ||
|
|
44f4057876 | ||
|
|
d85020079f | ||
|
|
956dc382ea | ||
|
|
99aa214859 | ||
|
|
405e98f429 | ||
|
|
a8c375fc95 | ||
|
|
4a56a2cad6 | ||
|
|
438945907d | ||
|
|
db245add0f | ||
|
|
986699bce5 | ||
|
|
d1803320f1 | ||
|
|
d4609519f0 | ||
|
|
2b4a6284e4 | ||
|
|
3c6be7e04c | ||
|
|
e738e57e26 | ||
|
|
21ebc398fa | ||
|
|
1ac611239e | ||
|
|
97e6047725 | ||
|
|
cf3f0fcc39 | ||
|
|
19c32bf993 | ||
|
|
e86eb16d91 | ||
|
|
1fcd1ff3e8 | ||
|
|
58f4212aa8 | ||
|
|
f01152eda1 | ||
|
|
11ff40bcd6 | ||
|
|
46e985b306 | ||
|
|
fdc014af67 | ||
|
|
bf11a46abe | ||
|
|
8f41130a14 | ||
|
|
e96c4732d6 | ||
|
|
a1d38a6940 | ||
|
|
9b8703cf49 | ||
|
|
c4d77bc18a | ||
|
|
c69fbb72d3 | ||
|
|
64e4791dca | ||
|
|
bc1e62ce51 | ||
|
|
79c1040796 | ||
|
|
eaf55bf12c | ||
|
|
ce528c9783 | ||
|
|
b9c7501012 | ||
|
|
ae10052aaf | ||
|
|
10abcd519f | ||
|
|
1d6c763e92 | ||
|
|
3fa0ce99f0 | ||
|
|
7380585f00 | ||
|
|
7557ffcda1 | ||
|
|
bc9d70109c | ||
|
|
7448159d6b | ||
|
|
f16273772e |
88
.ameba.yml
Normal file
88
.ameba.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
#
|
||||
# Lint
|
||||
#
|
||||
|
||||
# Exclude assigns for ECR files
|
||||
Lint/UselessAssign:
|
||||
Excluded:
|
||||
- src/invidious.cr
|
||||
- src/invidious/helpers/errors.cr
|
||||
- src/invidious/routes/**/*.cr
|
||||
|
||||
# Ignore false negative (if !db.query_one?...)
|
||||
Lint/UnreachableCode:
|
||||
Excluded:
|
||||
- src/invidious/database/base.cr
|
||||
|
||||
# Ignore shadowed variable `key` (it works for now, and that's
|
||||
# a sensitive part of the code)
|
||||
Lint/ShadowingOuterLocalVar:
|
||||
Excluded:
|
||||
- src/invidious/helpers/tokens.cr
|
||||
|
||||
|
||||
#
|
||||
# Style
|
||||
#
|
||||
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantReturn:
|
||||
Enabled: false
|
||||
|
||||
|
||||
#
|
||||
# Metrics
|
||||
#
|
||||
|
||||
# Ignore function complexity (number of if/else & case/when branches)
|
||||
# For some functions that can hardly be simplified for now
|
||||
Metrics/CyclomaticComplexity:
|
||||
Excluded:
|
||||
# get_about_info(ucid, locale) => [17/10]
|
||||
- src/invidious/channels/about.cr
|
||||
|
||||
# fetch_channel_community(ucid, continuation, ...) => [34/10]
|
||||
- src/invidious/channels/community.cr
|
||||
|
||||
# create_notification_stream(env, topics, connection_channel) => [14/10]
|
||||
- src/invidious/helpers/helpers.cr:84:5
|
||||
|
||||
# get_index(plural_form, count) => [25/10]
|
||||
- src/invidious/helpers/i18next.cr
|
||||
|
||||
# call(context) => [18/10]
|
||||
- src/invidious/helpers/static_file_handler.cr
|
||||
|
||||
# show(env) => [38/10]
|
||||
- src/invidious/routes/embed.cr
|
||||
|
||||
# get_video_playback(env) => [45/10]
|
||||
- src/invidious/routes/video_playback.cr
|
||||
|
||||
# handle(env) => [40/10]
|
||||
- src/invidious/routes/watch.cr
|
||||
|
||||
# playlist_ajax(env) => [24/10]
|
||||
- src/invidious/routes/playlists.cr
|
||||
|
||||
# fetch_youtube_comments(id, cursor, ....) => [40/10]
|
||||
# template_youtube_comments(comments, locale, ...) => [16/10]
|
||||
# content_to_comment_html(content) => [14/10]
|
||||
- src/invidious/comments.cr
|
||||
|
||||
# to_json(locale, json) => [21/10]
|
||||
# extract_video_info(video_id, ...) => [44/10]
|
||||
# process_video_params(query, preferences) => [20/10]
|
||||
- src/invidious/videos.cr
|
||||
|
||||
|
||||
|
||||
#src/invidious/playlists.cr:327:5
|
||||
#[C] Metrics/CyclomaticComplexity: Cyclomatic complexity too high [19/10]
|
||||
# fetch_playlist(plid : String)
|
||||
|
||||
#src/invidious/playlists.cr:436:5
|
||||
#[C] Metrics/CyclomaticComplexity: Cyclomatic complexity too high [11/10]
|
||||
# extract_playlist_videos(initial_data : Hash(String, JSON::Any))
|
||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://github.community/t/how-to-change-the-category/2261/3
|
||||
videojs-*.js linguist-detectable=false
|
||||
video.min.js linguist-detectable=false
|
||||
18
.github/CODEOWNERS
vendored
Normal file
18
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Default and lowest precedence. If none of the below matches, @iv-org/developers would be requested for review.
|
||||
* @iv-org/developers
|
||||
|
||||
docker-compose.yml @unixfox
|
||||
docker/ @unixfox
|
||||
kubernetes/ @unixfox
|
||||
|
||||
README.md @thefrenchghosty
|
||||
config/config.example.yml @thefrenchghosty @SamantazFox @unixfox
|
||||
|
||||
scripts/ @syeopite
|
||||
shards.lock @syeopite
|
||||
shards.yml @syeopite
|
||||
|
||||
locales/ @SamantazFox
|
||||
src/invidious/helpers/i18n.cr @SamantazFox
|
||||
|
||||
src/invidious/helpers/youtube_api.cr @SamantazFox
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://invidious.io/donate/
|
||||
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve Invidious
|
||||
title: '[Bug] '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
BEFORE TRYING TO REPORT A BUG:
|
||||
|
||||
* Read the FAQ!
|
||||
* Use the search function to check if there is already an issue open for your problem!
|
||||
|
||||
If you want to suggest a new feature please use "Feature request" instead
|
||||
If you want to suggest an enhancement to an existing feature please use "Enhancement" instead
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Steps to Reproduce**
|
||||
<!-- Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
**Logs**
|
||||
<!-- If applicable, copy the log that appear in the browser page where the error is reported. -->
|
||||
|
||||
**Screenshots**
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the problem here.
|
||||
- Browser (if applicable):
|
||||
- OS (if applicable):
|
||||
-->
|
||||
24
.github/ISSUE_TEMPLATE/enhancement.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/enhancement.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Enhancement
|
||||
about: Suggest an enhancement for an existing feature
|
||||
title: '[Enhancement] '
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
|
||||
<!-- If you want to suggest a new feature please use "Feature request" instead -->
|
||||
<!-- If you want to report a bug, please use "Bug report" instead -->
|
||||
|
||||
**Is your enhancement request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the enhancement here. -->
|
||||
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: '[Feature request] '
|
||||
labels: feature-request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
|
||||
<!-- If you want to suggest an enhancement to an existing feature please use "Enhancement" instead -->
|
||||
<!-- If you want to report a bug, please use "Bug report" instead -->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
127
.github/workflows/ci.yml
vendored
Normal file
127
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Invidious CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Every day at 00:00
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "api-only"
|
||||
pull_request:
|
||||
branches: "*"
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- LICENCE
|
||||
- TRANSLATION
|
||||
- invidious.service
|
||||
- .git*
|
||||
- .editorconfig
|
||||
|
||||
- screenshots/*
|
||||
- assets/**
|
||||
- locales/*
|
||||
- config/**
|
||||
- .github/ISSUE_TEMPLATE/*
|
||||
- kubernetes/**
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: "build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }}"
|
||||
|
||||
continue-on-error: ${{ !matrix.stable }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
stable: [true]
|
||||
crystal:
|
||||
- 1.2.2
|
||||
- 1.3.2
|
||||
- 1.4.0
|
||||
- 1.5.0
|
||||
include:
|
||||
- crystal: nightly
|
||||
stable: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install Crystal
|
||||
uses: crystal-lang/install-crystal@v1.6.0
|
||||
with:
|
||||
crystal: ${{ matrix.crystal }}
|
||||
|
||||
- name: Cache Shards
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./lib
|
||||
key: shards-${{ hashFiles('shard.lock') }}
|
||||
|
||||
- name: Install Shards
|
||||
run: |
|
||||
if ! shards check; then
|
||||
shards install
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
run: crystal spec
|
||||
|
||||
- name: Run lint
|
||||
run: |
|
||||
if ! crystal tool format --check; then
|
||||
crystal tool format
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr
|
||||
|
||||
build-docker:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build Docker
|
||||
run: docker-compose build --build-arg release=0
|
||||
|
||||
- name: Run Docker
|
||||
run: docker-compose up -d
|
||||
|
||||
- name: Test Docker
|
||||
run: while curl -Isf http://localhost:3000; do sleep 1; done
|
||||
|
||||
build-docker-arm64:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build Docker ARM64 image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile.arm64
|
||||
platforms: linux/arm64/v8
|
||||
build-args: release=0
|
||||
|
||||
- name: Test Docker
|
||||
run: while curl -Isf http://localhost:3000; do sleep 1; done
|
||||
|
||||
|
||||
77
.github/workflows/container-release.yml
vendored
Normal file
77
.github/workflows/container-release.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Build and release container
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- LICENCE
|
||||
- TRANSLATION
|
||||
- invidious.service
|
||||
- .git*
|
||||
- .editorconfig
|
||||
|
||||
- screenshots/*
|
||||
- .github/ISSUE_TEMPLATE/*
|
||||
- kubernetes/**
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Crystal
|
||||
uses: crystal-lang/install-crystal@v1.6.0
|
||||
with:
|
||||
crystal: 1.5.0
|
||||
|
||||
- name: Run lint
|
||||
run: |
|
||||
if ! crystal tool format --check; then
|
||||
crystal tool format
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker AMD64 image for Push Event
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
platforms: linux/amd64
|
||||
labels: quay.expires-after=12w
|
||||
push: true
|
||||
tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest
|
||||
build-args: release=1
|
||||
|
||||
- name: Build and push Docker ARM64 image for Push Event
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile.arm64
|
||||
platforms: linux/arm64/v8
|
||||
labels: quay.expires-after=12w
|
||||
push: true
|
||||
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64
|
||||
build-args: release=1
|
||||
24
.github/workflows/stale.yml
vendored
Normal file
24
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Documentation: https://github.com/marketplace/actions/close-stale-issues
|
||||
|
||||
name: "Stale issue handler"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 */12 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 365
|
||||
days-before-pr-stale: 45 # PRs should be active. Anything that hasn't had activity in more than 45 days should be considered abandoned.
|
||||
days-before-close: 30
|
||||
exempt-pr-labels: blocked
|
||||
stale-issue-message: 'This issue has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely outdated. If you think this issue is still relevant and applicable, you just have to post a comment and it will be unmarked.'
|
||||
stale-pr-message: 'This pull request has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely abandoned or outdated. If you think this pull request is still relevant and applicable, you just have to post a comment and it will be unmarked.'
|
||||
stale-issue-label: "stale"
|
||||
stale-pr-label: "stale"
|
||||
ascending: true
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,4 +6,4 @@
|
||||
/.vscode/
|
||||
/invidious
|
||||
/sentry
|
||||
shard.lock
|
||||
/config/config.yml
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "mocks"]
|
||||
path = mocks
|
||||
url = ../mocks
|
||||
460
CHANGELOG.md
460
CHANGELOG.md
@@ -1,3 +1,447 @@
|
||||
# Note: This is no longer updated and links to omarroths repo, which doesn't exist anymore.
|
||||
|
||||
# 0.20.0 (2019-011-06)
|
||||
|
||||
# Version 0.20.0: Custom Playlists
|
||||
|
||||
It's been quite a while since the last release! There've been [198 commits](https://github.com/omarroth/invidious/compare/0.19.0..0.20.0) from 27 contributors.
|
||||
|
||||
A couple smaller features have since been added. Channel pages and playlists in particular have received a bit of a face-lift, with both now displaying their descriptions as expected, and playlists providing video count and published information. Channels will also now provide video descriptions in their RSS feed.
|
||||
|
||||
Turkish (tr), Chinese (zh-TW, in addition to zh-CN), and Japanese (jp) are all now supported languages. Thank you as always to the hard work done by translators that makes this possible.
|
||||
|
||||
The feed menu and default home page are both now configurable for registered and unregistered users, and is quite a bit of an improvement for users looking to reduce distractions for their daily use.
|
||||
|
||||
## For Administrators
|
||||
|
||||
`feed_menu` and `default_home` are now configurable by the user, and have therefore been moved into `default_user_preferences`:
|
||||
|
||||
```yaml
|
||||
feed_menu: ["Popular", "Top"]
|
||||
default_home: Top
|
||||
|
||||
# becomes:
|
||||
|
||||
default_user_preferences:
|
||||
feed_menu: ["Popular", "Top"]
|
||||
default_home: Top
|
||||
```
|
||||
|
||||
Several new options have also been added, including the ability to set a support email for the instance using `admin_email: EMAIL`, and forcing the use of a specific connection in the case of rate-limiting using `force_resolve` (see below).
|
||||
|
||||
## For Developers
|
||||
|
||||
Authenticated endpoints are now [properly documented](https://github.com/omarroth/invidious/wiki/Authenticated-Endpoints), as well how to generate and use API tokens. My hope is that this makes some of the more [interesting](https://github.com/omarroth/invidious/wiki/Authenticated-Endpoints#get-apiv1authnotifications) endpoints more accessible for developers to use in their own applications.
|
||||
|
||||
API endpoints for interacting with custom playlists have also been added with documentation available [here](https://github.com/omarroth/invidious/wiki/Authenticated-Endpoints#get-apiv1authplaylists).
|
||||
|
||||
## Custom playlists
|
||||
|
||||
This is probably the feature that has been the longest in the pipe and that I'm quite pleased is now implemented. It is now possible to create custom playlists, which can be played and edited through Invidious. API endpoints have also been added (documentation [here](https://github.com/omarroth/invidious/wiki/Authenticated-Endpoints#get-apiv1authplaylists)).
|
||||
|
||||
Overall I'm quite pleased with how smoothly it has been rolled out and with the experience so far, and I'm exctited for how it can be extended and improved in future.
|
||||
|
||||
## [instances.invidio.us](https://instances.invidio.us)
|
||||
|
||||
It is now possible to view a list of public instances (as provided in the [wiki](https://github.com/omarroth/invidious/wiki/Invidious-Instances)) through an API or a pretty new interface [here](https://instances.invidio.us). It combines uptime information, statistics from each instance and basic information already provided in the wiki. I expect it should be much more user-friendly than compiling the information yourself, and is already used by [Invidition](https://codeberg.org/Booteille/Invidition) to provide a list of instances for users to choose from.
|
||||
|
||||
The site itself is licensed under the AGPLv3 and the source is available [here](https://github.com/omarroth/instances.invidio.us).
|
||||
|
||||
## Video unavailable [#811](https://github.com/omarroth/invidious/issues/811)
|
||||
|
||||
Many users have likely noticed this error message if using Invidious directly or through another service, such as FreeTube. This issue is caused by rate-limiting by Google, and is not a new issuee for projects like Invidious (notably [youtube-dl](https://github.com/ytdl-org/youtube-dl#http-error-429-too-many-requests-or-402-payment-required)) and appears to be affecting smaller, private instances as well.
|
||||
|
||||
There is not a permanent fix for administrators currently, however there is some information available [here](https://github.com/omarroth/invidious/issues/811#issuecomment-540017772) that may provide a temporary solution. Unfortanately, in most cases the best option is to wait for the instance to be unbanned or to move the instance to a different IP. A more informative error message is also now provided, which should help an administrator more quickly diagnose the problem.
|
||||
|
||||
For those interested, I would recommend following [#811](https://github.com/omarroth/invidious/issues/811) for any future progress on the issue.
|
||||
|
||||
## BAT verified publisher
|
||||
|
||||
I'm quite late to this announcement, however I'm pleased to mention that Invidious is now a BAT verified publisher! I would recommend looking [here](https://basicattentiontoken.org/about/) or [here](https://www.reddit.com/r/BATProject/comments/7cr7yc/new_to_bat_read_this_introduction_to_basic/) for learning more about what it is and how it works. Overall I think it makes an interesting substitute for services like Liberapay, and a (hopefully) much less-intrusive alternative to direct advertising.
|
||||
|
||||
BAT is combined under other cryptocurrencies below. Currently there's a fairly significant delay in payout, which is the reason for the large fluctuation in crypto donations between September and October (and also the reason for the late announcement).
|
||||
|
||||
## Release schedule
|
||||
|
||||
Currently I'm quite pleased with the current state of the project. There's plenty of things I'd still like to add, however at this point I expect the rate of most new additions will slow down a bit, with more focus on stabililty and any long-standing bugs.
|
||||
|
||||
Because of this, I'm planning on releasing a new version quarterly, with any necessary hotfixes being pushed as a new patch release as necessary. As always it will be possible to run Invidious directly from [master](https://github.com/omarroth/invidious/wiki/Updating) if you'd still like to have the lastest version.
|
||||
|
||||
I'll plan on providing finances each release, with a similar monthly breakdown as below.
|
||||
|
||||
## Finances for September 2019
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$64.37
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$76.04
|
||||
- Crypto : ~\$99.89 (converted from BAT, BCH, BTC)
|
||||
- Total : \$240.30
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-lb1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node6 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node7 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node8 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node9 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node10 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node11 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node12 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node13 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node14 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node15 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node16 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$135.00
|
||||
|
||||
## Finances for October 2019
|
||||
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$134.40
|
||||
- Crypto : ~\$8.29 (converted from BAT, BCH, BTC)
|
||||
- Total : \$142.69
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-lb1 (nyc1) : \$5.00 (load balancer)
|
||||
- invidious-lb2 (nyc1) : \$5.00 (load balancer)
|
||||
- invidious-lb3 (nyc1) : \$5.00 (load balancer)
|
||||
- invidious-lb4 (nyc1) : \$5.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node6 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node7 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node8 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node9 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node10 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node11 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node12 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node13 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node14 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node15 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node16 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node17 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node18 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$155.00
|
||||
|
||||
# 0.19.0 (2019-07-13)
|
||||
|
||||
# Version 0.19.0: Communities
|
||||
|
||||
Hello again everyone! Focus this month has mainly been on improving playback performance, along with a couple new features I'd like to announce. There have been [109 commits](https://github.com/omarroth/invidious/compare/0.18.0...0.19.0) this past month from 10 contributors.
|
||||
|
||||
This past month has seen the addition of Chinese (`zh-CN`) and Icelandic (`is`) translations. I would like to give a huge thanks to their respective translators, and again an enormous thanks to everyone who helps translate the site.
|
||||
|
||||
I'm delighted to mention that [FreeTube 0.6.0](https://github.com/FreeTubeApp/FreeTube) now supports 1080p thanks to the Invidious API. I would very much recommend reading the [relevant post](https://freetube.writeas.com/freetube-release-0-6-0-beta-1080p-and-a-lot-of-qol) for some more information on how it works, along with several other major improvements. Folks that are interested in adding similar functionality for their own projects should feel free to get in touch.
|
||||
|
||||
This past month there has been quite a bit of work on improving memory usage and improving download and playback speeds. As mentioned in the previous release, some extra hardware has been allocated which should also help with this. I'm still looking for ways to improve performance and feedback is always appreciated.
|
||||
|
||||
Along with performance, a couple quality of life improvements have been added, including author thumbnails and banners, clickable titles for embedded videos, and better styling for captions, among some other enhancements.
|
||||
|
||||
## Communities
|
||||
|
||||
Support for YouTube's [communities tab](https://creatoracademy.youtube.com/page/lesson/community-tab) has been added. It's a very interesting but surprisingly unknown feature. Essentially, providing comments for a channel, rather than a video, where an author can post updates for their subscribers.
|
||||
|
||||
It's commonly used to promote interesting links and foster discussion. I hope this feature helps people find more interesting content that otherwise would have been overlooked.
|
||||
|
||||
## For Developers
|
||||
|
||||
For accessing channel communities, an `/api/v1/channels/comments/:ucid` endpoint has been added, with similar behavior and schema to `/api/v1/comments/:id`, with an extra `attachment` field for top-level comments. More info on usage and available data can be found in the [wiki](https://github.com/omarroth/invidious/wiki/API#get-apiv1channelscommentsucid-apiv1channelsucidcomments).
|
||||
|
||||
An `/api/v1/auth/feeds` endpoint has been added for programmatically accessing a user's subscription feed, with options for displaying notifications and filtering an existing feed.
|
||||
|
||||
An `/api/v1/search/suggestions` endpoint has been added for retrieving suggestions for a given query.
|
||||
|
||||
## For Administrators
|
||||
|
||||
It is now possible to disable more resource intensive features, such as downloads and DASH functionality by adding `disable_proxy` to your config. See [#453](https://github.com/omarroth/invidious/issues/453) and the [Wiki](https://github.com/omarroth/invidious/wiki/Configuration) for more information and example usage. I expect this to be a big help for folks with limited bandwidth when hosting their own instances.
|
||||
|
||||
## Finances
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$38.39
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$84.85
|
||||
- Crypto : ~\$0.00 (converted from BCH, BTC)
|
||||
- Total : \$123.24
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node6 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node7 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node8 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node9 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node10 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$105.00
|
||||
|
||||
The goal on Patreon has been updated to reflect the above expenses. As mentioned above, the main reason for more hardware is to improve playback and download speeds, although I'm still looking into improving performance without allocating more hardware.
|
||||
|
||||
As always I'm grateful for everyone's support and feedback. I'll see you all next month.
|
||||
|
||||
# 0.18.0 (2019-06-06)
|
||||
|
||||
# Version 0.18.0: Native Notifications and Optimizations
|
||||
|
||||
Hope everyone has been doing well. This past month there have been [97 commits](https://github.com/omarroth/invidious/compare/0.17.0...0.18.0) from 10 contributors. For the most part changes this month have been on optimizing various parts of the site, mainly subscription feeds and support for serving images and other assets.
|
||||
|
||||
I'm quite happy to mention that support for Greek (`el`) has been added, which I hope will continue to make the site accessible for more users.
|
||||
|
||||
Subscription feeds will now only update when necessary, rather than periodically. This greatly lightens the load on DB as well as making the feeds generally more responsive when changing subscriptions, importing data, and when receiving new uploads.
|
||||
|
||||
Caching for images and other assets should be greatly improved with [#456](https://github.com/omarroth/invidious/issues/456). JavaScript has been pulled out into separate files where possible to take advantage of this, which should result in lighter pages and faster load times.
|
||||
|
||||
This past month several people have encountered issues with downloads and watching high quality video through the site, see [#532](https://github.com/omarroth/invidious/issues/532) and [#562](https://github.com/omarroth/invidious/issues/562). For this coming month I've allocated some more hardware which should help with this, and I'm also looking into optimizing how videos are currently served.
|
||||
|
||||
## For Developers
|
||||
|
||||
`viewCount` is now available for `/api/v1/popular` and all videos returned from `/api/v1/auth/notifications`. Both also now provide `"type"` for indicating available information for each object.
|
||||
|
||||
An `/authorize_token` page is now available for more easily creating new tokens for use in applications, see [this comment](https://github.com/omarroth/invidious/issues/473#issuecomment-496230812) in [#473](https://github.com/omarroth/invidious/issues/473) for more details.
|
||||
|
||||
A POST `/api/v1/auth/notifications` endpoint is also now available for correctly returning notifications for 150+ channels.
|
||||
|
||||
## For Administrators
|
||||
|
||||
There are two new schema changes for administrators: `views` for adding view count to the popular page, and `feed_needs_update` for tracking feed changes.
|
||||
|
||||
As always the relevant migration scripts are provided which should run when following instructions for [updating](https://github.com/omarroth/invidious/wiki/Updating). Otherwise, adding `check_tables: true` to your config will automatically make the required changes.
|
||||
|
||||
## Native Notifications
|
||||
|
||||
[<img src="https://omar.yt/81c3ae1839831bd9300d75e273b6552a86dc2352/native_notification.png" height="160" width="472">](https://omar.yt/81c3ae1839831bd9300d75e273b6552a86dc2352/native_notification.png "Example of native notification, available in repository under screnshots/native_notification.png")
|
||||
|
||||
It is now possible to receive [Web notifications](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API) from subscribed channels.
|
||||
|
||||
You can enable notifications by clicking "Enable web notifications" in your preferences. Generally they appear within 20-60 seconds of a new video being uploaded, and I've found them to be an enormous quality of life improvement.
|
||||
|
||||
Although it has been fairly stable, please feel free to report any issues you find [here](https://github.com/omarroth/invidious/issues) or emailing me directly at omarroth@protonmail.com.
|
||||
|
||||
Important to note for administrators is that instances require [`use_pubsub_feeds`](https://github.com/omarroth/invidious/wiki/Configuration) and must be served over HTTPS in order to correctly send web notifications.
|
||||
|
||||
## Finances
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$49.73
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$100.57
|
||||
- Crypto : ~\$11.12 (converted from BCH, BTC)
|
||||
- Total : \$161.42
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node6 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$85.00
|
||||
|
||||
See you all next month!
|
||||
|
||||
# 0.17.0 (2019-05-06)
|
||||
|
||||
# Version 0.17.0: Player and Authentication API
|
||||
|
||||
Hello everyone! This past month there have been [130 commits](https://github.com/omarroth/invidious/compare/0.16.0..0.17.0) from 11 contributors. Large focus has been on improving the player as well as adding API access for other projects to make use of Invidious.
|
||||
|
||||
There have also been significant changes in preparation of native notifications (see [#195](https://github.com/omarroth/invidious/issues/195), [#469](https://github.com/omarroth/invidious/issues/469), [#473](https://github.com/omarroth/invidious/issues/473), and [#502](https://github.com/omarroth/invidious/issues/502)), and playlists. I expect to see both of these to be added in the next release.
|
||||
|
||||
I'm quite happy to mention that new translations have been added for Esperanto (`eo`) and Ukranian (`uk`). Support for pluralization has also been added, so it should now be possible to make a more native experience for speakers in other languages. The system currently in place is a bit cumbersome, so for any help using this feature please get in touch!
|
||||
|
||||
## For Administrators
|
||||
|
||||
A `check_tables` option has been added to automatically migrate without the use of custom scripts. This method will likely prove to be much more robust, and is currently enabled for the official instance. To prevent any unintended changes to the DB, `check_tables` is disabled by default and will print commands before executing. Having this makes features that require schema changes much easier to implement, and also makes it easier to upgrade from older instances.
|
||||
|
||||
As part of [#303](https://github.com/omarroth/invidious/issues/303), a `cache_annotations` option has been added to speed up access from `/api/v1/annotations/:id`. This vastly improves the experience for videos with annotations. Currently, only videos that contain legacy annotations will be cached, which should help keep down the size of the cache. `cache_annotations` is disabled by default.
|
||||
|
||||
## For Developers
|
||||
|
||||
An authorization API has been added which allows other applications to read and modify user subscriptions and preferences (see [#473](https://github.com/omarroth/invidious/issues/473)). Support for accessing user feeds and notifications is also planned. I believe this feature is a large step forward in supporting syncing subscriptions and preferences with other services, and I'm excited to see what other developers do with this functionality.
|
||||
|
||||
Support for server-to-client push notifications is currently underway. This allows Invidious users, as well as applications using the Invidious API, to receive notifications about uploads in near real-time (see #469). An `/api/v1/auth/notifications` endpoint is currently available. I'm very excited for this to be integrated into the site, and to see how other developers use it in their own projects.
|
||||
|
||||
An `/api/v1/storyboards/:id` endpoint has been added for accessing storyboard URLs, which allows developers to add video previews to their players (see below).
|
||||
|
||||
## Player
|
||||
|
||||
Support for annotations has been merged into master with [#303](https://github.com/omarroth/invidious/issues/303), thanks @glmdgrielson! Annotations can be enabled by default or only for subscribed channels, and can also be toggled per video. I'm extremely proud of the progress made here, and I'm so thankful to everyone that has made this possible. I expect this to be the last update with regards to supporting annotations, but I do plan on continuing to improve the experience as much as possible.
|
||||
|
||||
The Invidious player now supports video previews and a corresponding API endpoint `/api/v1/storyboards/:id` has been added for developers looking to add similar functionality to their own players. Not much else to say here. Overall it's a very nice quality of life improvement and an attractive addition to the site.
|
||||
|
||||
It is now possible to select specific sources for videos provided using DASH (see [#34](https://github.com/omarroth/invidious/issues/34)). I would consider support largely feature complete, although there are still several issues to be fixed before I would consider it ready for larger rollout. You can watch videos in 1080p by setting `Default quality` to `dash` in your preferences, or by adding `&quality=dash` to the end of video URLs.
|
||||
|
||||
## Finances
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$49.73
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$63.03
|
||||
- Crypto : ~\$0.00 (converted from BCH, BTC)
|
||||
- Total : \$112.76
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$80.00
|
||||
|
||||
That's all for now. Thanks!
|
||||
|
||||
# 0.16.0 (2019-04-06)
|
||||
|
||||
# Version 0.16.0: API Improvements and Annotations
|
||||
|
||||
Hello again! This past month has seen [116 commits](https://github.com/omarroth/invidious/compare/0.15.0..0.16.0) from 13 contributors and a couple important changes I'd like to announce.
|
||||
|
||||
A privacy policy is now available [here](https://invidio.us/privacy). I've done my best to explain things as clearly as possible without oversimplifying, and would very much recommend reading it if you're concerned about your privacy and want to learn more about how Invidious uses your data. Please let me know if there is anything that needs clarification.
|
||||
|
||||
I'm also very happy to announce that a Spanish translation has been added to the site. You can use it with `?hl=es` or by setting `es` as your default locale. As always I'm extremely grateful to translators for making the site accessible to more people.
|
||||
|
||||
## For Administrators
|
||||
|
||||
Invidious now supports server-to-server [push notifications](https://developers.google.com/youtube/v3/guides/push_notifications). This uses [PubSubHubbub](https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html) to automatically handle new videos sent to an instance, which is less resource intensive and generally faster. Note that it will not pull all videos from a subscribed channel, so recommended usage is in addition to `channel_threads`. Using PubSub requires a valid `domain` that updates can be sent to, and a random string that can be used to sign updates sent to the instance. You can enable it by adding `use_pubsub_feeds: true` to your `config.yml`. See [Configuration](https://github.com/omarroth/invidious/wiki/Configuration) for more info.
|
||||
|
||||
Unfortunately there are a couple necessary changes to the DB to support `liveNow` and `premiereTimestamp` in subscription feeds. Migration scripts have been provided that should be used automatically if following the instructions [here](https://github.com/omarroth/invidious/wiki/Updating).
|
||||
|
||||
You can now configure default user preferences for your instance. This allows you to set default locale, player preferences, and more. See [#415](https://github.com/omarroth/invidious/issues/415) for more details and example usage.
|
||||
|
||||
## For Developers
|
||||
|
||||
The [fields](https://developers.google.com/youtube/v3/getting-started#fields) API has been added with [#429](https://github.com/omarroth/invidious/pull/429) and is now supported on all JSON endpoints, thanks [**@afrmtbl**](https://github.com/afrmtbl)! Synax is straight-forward and can be used to reduce data transfer and create a simpler response for debugging. You can see an example [here](https://invidio.us/api/v1/videos/CvFH_6DNRCY?pretty=1&fields=title,recommendedVideos/title). I've been quite happy using it and hope it is similarly useful for others.
|
||||
|
||||
An `/api/v1/annotations/:id` endpoint has been added for pulling legacy annotation data from [this](https://archive.org/details/youtubeannotations) archive, see below for more details. You can also access annotation data available on YouTube using `?source=youtube`, although this will only return card data as legacy annotations were deleted on January 15th.
|
||||
|
||||
A couple minor changes to existing endpoints:
|
||||
|
||||
- A `premiereTimestamp` field has been added to `/api/v1/videos/:id`
|
||||
- A `sort_by` param has been added to `/api/v1/comments/:id`, supports `new`, `top`.
|
||||
|
||||
More info is available in the [documentation](https://github.com/omarroth/invidious/wiki/API).
|
||||
|
||||
## Annotations
|
||||
|
||||
I'm pleased to announce that annotation data is finally available from the roughly 1.4 billion videos archived as part of [this](https://www.reddit.com/r/DataHoarder/comments/aa6czg/youtube_annotation_archive/) project. They are accessible from the Internet Archive [here](https://archive.org/details/youtubeannotations) or as a 355GB torrent, see [here](https://www.reddit.com/r/DataHoarder/comments/b7imx9/youtube_annotation_archive_annotation_data_from/) for more details. A corresponding `/api/v1/annotations/:id` endpoint has been added to Invidious which uses the collection from IA to provide legacy annotations.
|
||||
|
||||
Support for them in the player is possible thanks to [this](https://github.com/afrmtbl/videojs-youtube-annotations) plugin developed by [**@afrmtbl**](https://github.com/afrmtbl). A PR for adding support to the site is available as [#303](https://github.com/omarroth/invidious/pull/303). There's also an [extension](https://github.com/afrmtbl/AnnotationsRestored) for overlaying them on top of the YouTube player (again thanks to [**@afrmtbl**](https://github.com/afrmtbl)), and an [extension](https://tech234a.bitbucket.io/AnnotationsReloaded?src=invidious) for hooking into code still present in the YouTube player itself, developed by [**@tech234a**](https://github.com/tech234a).
|
||||
|
||||
I would recommend reading the [official announcement](https://www.reddit.com/r/DataHoarder/comments/b7imx9/youtube_annotation_archive_annotation_data_from/) for more details. I would like to again thank everyone that helped contribute to this project.
|
||||
|
||||
## Finances
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$42.42
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$70.11
|
||||
- Crypto : ~\$1.76 (converted from BCH, BTC, BSV)
|
||||
- Total : \$114.29
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node5 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$80.00
|
||||
|
||||
This past month the site saw a couple abnormal peaks in traffic, so an additional webserver has been added to match the increased load. The goal on Patreon has been updated to match the above expenses.
|
||||
|
||||
Thanks everyone!
|
||||
|
||||
# 0.15.0 (2019-03-06)
|
||||
|
||||
## Version 0.15.0: Preferences and Channel Playlists
|
||||
|
||||
The project has seen quite a bit of activity this past month. Large focus has been on fixing bugs, but there's still quite a few new features I'm happy to announce. There have been [133 commits](https://github.com/omarroth/invidious/compare/0.14.0...0.15.0) from 15 contributors this past month.
|
||||
|
||||
As a couple miscellaneous changes, a couple [nice screenshots](https://github.com/omarroth/invidious#screenshots) have been added to the README, so folks can see more of what the site has to offer without creating an account.
|
||||
|
||||
The footer has also been cleaned up quite a bit, and now displays the current version, so it's easier to know what features are available from the current instance.
|
||||
|
||||
## For Administrators
|
||||
|
||||
This past month there has been a minor release - `0.14.1` - which fixes a breaking change made by YouTube for their polymer redesign.
|
||||
|
||||
There have been several new features that unfortunately require a database migration. There are migration scripts provided in `config/migrate-scripts`, and the [wiki](https://github.com/omarroth/invidious/wiki/Updating) has instructions for automatically applying them. I'll do my best to keep those changes to a minimum, and expect to see a corresponding script to automatically apply any new changes.
|
||||
|
||||
Administrator preferences have been added with [#312](https://github.com/omarroth/invidious/issues/312), which allows administrators to customize their instance. Administrators can change the order of feed menus, change the default homepage, disable open registration, and several other options. There's a short 'how-to' [here](https://github.com/omarroth/invidious/issues/312#issuecomment-468831842), and the new options are documented [here](https://github.com/omarroth/invidious/wiki/Configuration).
|
||||
|
||||
An `/api/v1/stats` endpoint has been added with [#356](https://github.com/omarroth/invidious/issues/356), which reports the instance version and number of active users. Statistics are disabled by default, and can be enabled in administator preferences. Statistics for the official instance are available [here](https://invidio.us/api/v1/stats?pretty=1).
|
||||
|
||||
## For Developers
|
||||
|
||||
`/api/v1/channels/:ucid` now provides an `autoGenerated` tag, which returns true for topic channels, and larger genre channels generated by YouTube. These channels don't have any videos of their own, so `latestVideos` will be empty. It is recommended instead to display a list of playlists generated by YouTube.
|
||||
|
||||
You can now pull a list of playlists from a channel with `/api/v1/channels/playlists/:ucid`. Supported options are documented in the [wiki](https://github.com/omarroth/invidious/wiki/API#get-apiv1channelsplaylistsucid-apiv1channelsucidplaylists). Pagination is handled with a `continuation` token, which is generated on each call. Of note is that auto-generated channels currently have one page of results, and subsequent calls will be empty.
|
||||
|
||||
For quickly pulling the latest 30 videos from a channel, there is now `/api/v1/channels/latest/:ucid`. It is much faster than a call to `/api/v1/channels/:ucid`. It will not convert an author name to a valid ucid automatically, and will not return any extra data about a channel.
|
||||
|
||||
## Preferences
|
||||
|
||||
In addition to administrator preferences mentioned above, you can now change your preferences without an account (see [#42](https://github.com/omarroth/invidious/pull/42)). I think this is quite an improvement to the usability of the site, and is much friendlier to privacy-conscious folks that don't want to make an account. Preferences will be automatically imported to a newly created account.
|
||||
|
||||
Several issues with sorting subscriptions have been fixed, and `/manage_subscriptions` has been sped up significantly. The subscription feed has also seen a bump in performance. Delayed notifications have unfortunately started becoming a problem now that there are more users on the site. Some new changes are currently being tested which should mostly resolve the issue, so expect to see more in the next release.
|
||||
|
||||
## Channel Playlists
|
||||
|
||||
You can now view available playlists from a channel, and [auto-generated channels](https://invidio.us/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) are no longer empty. You can sort as you would on YouTube, and all the same functionality should be available. I'm quite pleased to finally have it implemented, since it's currently the only data available from the above mentioned auto-generated channels, and makes it much easier to consume music on the site.
|
||||
|
||||
There's also more discussion on improving Invidious for streaming music in [#304](https://github.com/omarroth/invidious/issues/304), and adding support for music.youtube.com. I would appreciate any thoughts on how to improve that experience, since it's a very large and useful part of YouTube.
|
||||
|
||||
## Finances
|
||||
|
||||
### Donations
|
||||
|
||||
- [Patreon](https://www.patreon.com/omarroth) : \$42.42
|
||||
- [Liberapay](https://liberapay.com/omarroth) : \$30.97
|
||||
- Crypto : ~\$0.00 (converted from BCH, BTC)
|
||||
- Total : \$73.39
|
||||
|
||||
### Expenses
|
||||
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$75.00
|
||||
|
||||
It's been very humbling to see how fast the project has grown, and I look forward to making the site even better. Thank you everyone.
|
||||
|
||||
# 0.14.0 (2019-02-06)
|
||||
|
||||
## Version 0.14.0: Community
|
||||
@@ -59,14 +503,14 @@ Organizing this project has unfortunately taken up quite a bit of my time, and I
|
||||
|
||||
### Expenses
|
||||
|
||||
invidious-load1 (nyc1) : $10.00 (load balancer)
|
||||
invidious-update1 (s-1vcpu-1gb) : $5.00 (updates feeds)
|
||||
invidious-node1 (s-1vcpu-1gb) : $5.00 (web server)
|
||||
invidious-node2 (s-1vcpu-1gb) : $5.00 (web server)
|
||||
invidious-node3 (s-1vcpu-1gb) : $5.00 (web server)
|
||||
invidious-node4 (s-1vcpu-1gb) : $5.00 (web server)
|
||||
invidious-db1 (s-4vcpu-8gb) : $40.00 (database)
|
||||
Total : $75.00
|
||||
- invidious-load1 (nyc1) : \$10.00 (load balancer)
|
||||
- invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
|
||||
- invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
|
||||
- invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
|
||||
- Total : \$75.00
|
||||
|
||||
As always I'm grateful for everyone's contributions and support. I'll see you all in March.
|
||||
|
||||
|
||||
119
Makefile
Normal file
119
Makefile
Normal file
@@ -0,0 +1,119 @@
|
||||
# -----------------------
|
||||
# Compilation options
|
||||
# -----------------------
|
||||
|
||||
RELEASE := 1
|
||||
STATIC := 0
|
||||
|
||||
DISABLE_QUIC := 0
|
||||
NO_DBG_SYMBOLS := 0
|
||||
|
||||
|
||||
FLAGS ?=
|
||||
|
||||
|
||||
ifeq ($(RELEASE), 1)
|
||||
FLAGS += --release
|
||||
endif
|
||||
|
||||
ifeq ($(STATIC), 1)
|
||||
FLAGS += --static
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(NO_DBG_SYMBOLS), 1)
|
||||
FLAGS += --no-debug
|
||||
else
|
||||
FLAGS += --debug
|
||||
endif
|
||||
|
||||
ifeq ($(DISABLE_QUIC), 1)
|
||||
FLAGS += -Ddisable_quic
|
||||
endif
|
||||
|
||||
|
||||
# -----------------------
|
||||
# Main
|
||||
# -----------------------
|
||||
|
||||
all: invidious
|
||||
|
||||
get-libs:
|
||||
shards install --production
|
||||
|
||||
# TODO: add support for ARM64 via cross-compilation
|
||||
invidious: get-libs
|
||||
crystal build src/invidious.cr $(FLAGS) --progress --stats --error-trace
|
||||
|
||||
|
||||
run: invidious
|
||||
./invidious
|
||||
|
||||
|
||||
# -----------------------
|
||||
# Development
|
||||
# -----------------------
|
||||
|
||||
|
||||
format:
|
||||
crystal tool format
|
||||
|
||||
test:
|
||||
crystal spec
|
||||
|
||||
verify:
|
||||
crystal build src/invidious.cr -Dskip_videojs_download \
|
||||
--no-codegen --progress --stats --error-trace
|
||||
|
||||
|
||||
# -----------------------
|
||||
# (Un)Install
|
||||
# -----------------------
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
# -----------------------
|
||||
# Cleaning
|
||||
# -----------------------
|
||||
|
||||
clean:
|
||||
rm invidious
|
||||
|
||||
distclean: clean
|
||||
rm -rf libs
|
||||
|
||||
|
||||
# -----------------------
|
||||
# Help page
|
||||
# -----------------------
|
||||
|
||||
help:
|
||||
@echo "Targets available in this Makefile:"
|
||||
@echo ""
|
||||
@echo " get-libs Fetch Crystal libraries"
|
||||
@echo " invidious Build Invidious"
|
||||
@echo " run Launch Invidious"
|
||||
@echo ""
|
||||
@echo " format Run the Crystal formatter"
|
||||
@echo " test Run tests"
|
||||
@echo " verify Just make sure that the code compiles, but without"
|
||||
@echo " generating any binaries. Useful to search for errors"
|
||||
@echo ""
|
||||
@echo " clean Remove build artifacts"
|
||||
@echo " distclean Remove build artifacts and libraries"
|
||||
@echo ""
|
||||
@echo ""
|
||||
@echo "Build options available for this Makefile:"
|
||||
@echo ""
|
||||
@echo " RELEASE Make a release build (Default: 1)"
|
||||
@echo " STATIC Link libraries statically (Default: 0)"
|
||||
@echo ""
|
||||
@echo " DISABLE_QUIC Disable support for QUIC (Default: 0)"
|
||||
@echo " NO_DBG_SYMBOLS Strip debug symbols (Default: 0)"
|
||||
|
||||
|
||||
|
||||
# No targets generates an output named after themselves
|
||||
.PHONY: all get-libs build amd64 run
|
||||
.PHONY: format test verify clean distclean help
|
||||
337
README.md
337
README.md
@@ -1,230 +1,179 @@
|
||||
# Invidious
|
||||
<div align="center">
|
||||
<img src="assets/invidious-colored-vector.svg" width="192" height="192" alt="Invidious logo">
|
||||
<h1>Invidious</h1>
|
||||
|
||||
## Invidious is an alternative front-end to YouTube
|
||||
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">
|
||||
<img alt="License: AGPLv3" src="https://shields.io/badge/License-AGPL%20v3-blue.svg">
|
||||
</a>
|
||||
<a href="https://github.com/iv-org/invidious/actions">
|
||||
<img alt="Build Status" src="https://github.com/iv-org/invidious/workflows/Invidious%20CI/badge.svg">
|
||||
</a>
|
||||
<a href="https://github.com/iv-org/invidious/commits/master">
|
||||
<img alt="GitHub commits" src="https://img.shields.io/github/commit-activity/y/iv-org/invidious?color=red&label=commits">
|
||||
</a>
|
||||
<a href="https://github.com/iv-org/invidious/issues">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/iv-org/invidious?color=important">
|
||||
</a>
|
||||
<a href="https://github.com/iv-org/invidious/pulls">
|
||||
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/iv-org/invidious?color=blueviolet">
|
||||
</a>
|
||||
<a href="https://hosted.weblate.org/engage/invidious/">
|
||||
<img alt="Translation Status" src="https://hosted.weblate.org/widgets/invidious/-/translations/svg-badge.svg">
|
||||
</a>
|
||||
|
||||
- Audio-only mode (and no need to keep window open on mobile)
|
||||
- [Open-source](https://github.com/omarroth/invidious) (AGPLv3 licensed)
|
||||
- No ads
|
||||
- No need to create a Google account to save subscriptions
|
||||
- Lightweight (homepage is ~4 KB compressed)
|
||||
- Tools for managing subscriptions:
|
||||
- Only show unseen videos
|
||||
- Only show latest (or latest unseen) video from each channel
|
||||
- Delivers notifications from all subscribed channels
|
||||
- Automatically redirect homepage to feed
|
||||
- Import subscriptions from YouTube
|
||||
- Dark mode
|
||||
- Embed support
|
||||
- Set default player options (speed, quality, autoplay, loop)
|
||||
- Does not require JS to play videos
|
||||
- Support for Reddit comments in place of YT comments
|
||||
- Import/Export subscriptions, watch history, preferences
|
||||
- Does not use any of the official YouTube APIs
|
||||
- Developer [API](https://github.com/omarroth/invidious/wiki/API)
|
||||
<a href="https://github.com/humanetech-community/awesome-humane-tech">
|
||||
<img alt="Awesome Humane Tech" src="https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true">
|
||||
</a>
|
||||
|
||||
Liberapay: https://liberapay.com/omarroth
|
||||
Patreon: https://patreon.com/omarroth
|
||||
BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
|
||||
BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
|
||||
<h3>An open source alternative front-end to YouTube</h3>
|
||||
|
||||
Onion links:
|
||||
<a href="https://invidious.io/">Website</a>
|
||||
•
|
||||
<a href="https://instances.invidious.io/">Instances list</a>
|
||||
•
|
||||
<a href="https://docs.invidious.io/faq/">FAQ</a>
|
||||
•
|
||||
<a href="https://docs.invidious.io/">Documentation</a>
|
||||
•
|
||||
<a href="#contribute">Contribute</a>
|
||||
•
|
||||
<a href="https://invidious.io/donate/">Donate</a>
|
||||
|
||||
- kgg2m7yk5aybusll.onion
|
||||
- axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4bzzsg2ii4fv2iid.onion
|
||||
<h5>Chat with us:</h5>
|
||||
<a href="https://matrix.to/#/#invidious:matrix.org">
|
||||
<img alt="Matrix" src="https://img.shields.io/matrix/invidious:matrix.org?label=Matrix&color=darkgreen">
|
||||
</a>
|
||||
<a href="https://web.libera.chat/?channel=#invidious">
|
||||
<img alt="Libera.chat (IRC)" src="https://img.shields.io/badge/IRC%20%28Libera.chat%29-%23invidious-darkgreen">
|
||||
</a>
|
||||
<br>
|
||||
<a rel="me" href="https://social.tchncs.de/@invidious">
|
||||
<img alt="Fediverse: @invidious@social.tchncs.de" src="https://img.shields.io/badge/Fediverse-%40invidious%40social.tchncs.de-darkgreen">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://invidious.io/contact/">
|
||||
<img alt="E-mail" src="https://img.shields.io/badge/E%2d%2dmail-darkgreen">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
[Alternative Invidious instances](https://github.com/omarroth/invidious/wiki/Invidious-Instances)
|
||||
|
||||
## Screenshots
|
||||
|
||||
| Player | Preferences | Subscriptions |
|
||||
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [<img src="screenshots/01_player.png?raw=true" height="140" width="280">](screenshots/01_player.png?raw=true) | [<img src="screenshots/02_preferences.png?raw=true" height="140" width="280">](screenshots/02_preferences.png?raw=true) | [<img src="screenshots/03_subscriptions.png?raw=true" height="140" width="280">](screenshots/03_subscriptions.png?raw=true) |
|
||||
| [<img src="screenshots/04_description.png?raw=true" height="140" width="280">](screenshots/04_description.png?raw=true) | [<img src="screenshots/05_preferences.png?raw=true" height="140" width="280">](screenshots/05_preferences.png?raw=true) | [<img src="screenshots/06_subscriptions.png?raw=true" height="140" width="280">](screenshots/06_subscriptions.png?raw=true) |
|
||||
| Player | Preferences | Subscriptions |
|
||||
|-------------------------------------|-------------------------------------|---------------------------------------|
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|
||||
## Installation
|
||||
|
||||
See [Invidious-Updater](https://github.com/tmiland/Invidious-Updater) for a self-contained script that can automatically install and update Invidious.
|
||||
## Features
|
||||
|
||||
### Docker:
|
||||
**User features**
|
||||
- Lightweight
|
||||
- No ads
|
||||
- No tracking
|
||||
- No JavaScript required
|
||||
- Light/Dark themes
|
||||
- Customizable homepage
|
||||
- Subscriptions independent from Google
|
||||
- Notifications for all subscribed channels
|
||||
- Audio-only mode (with background play on mobile)
|
||||
- Support for Reddit comments
|
||||
- [Available in many languages](locales/), thanks to [our translators](#contribute)
|
||||
|
||||
#### Build and start cluster:
|
||||
**Data import/export**
|
||||
- Import subscriptions from YouTube, NewPipe and Freetube
|
||||
- Import watch history from NewPipe
|
||||
- Export subscriptions to NewPipe and Freetube
|
||||
- Import/Export Invidious user data
|
||||
|
||||
```bash
|
||||
$ docker-compose up
|
||||
```
|
||||
**Technical features**
|
||||
- Embedded video support
|
||||
- [Developer API](https://docs.invidious.io/api/)
|
||||
- Does not use official YouTube APIs
|
||||
- No Contributor License Agreement (CLA)
|
||||
|
||||
And visit `localhost:3000` in your browser.
|
||||
|
||||
#### Rebuild cluster:
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
$ docker-compose build
|
||||
```
|
||||
**Using invidious:**
|
||||
|
||||
#### Delete data and rebuild:
|
||||
- [Select a public instance from the list](https://instances.invidious.io) and start watching videos right now!
|
||||
|
||||
```bash
|
||||
$ docker volume rm invidious_postgresdata
|
||||
$ docker-compose build
|
||||
```
|
||||
**Hosting invidious:**
|
||||
|
||||
### Linux:
|
||||
- [Follow the installation instructions](https://docs.invidious.io/installation/)
|
||||
|
||||
#### Install dependencies
|
||||
|
||||
```bash
|
||||
# Arch Linux
|
||||
$ sudo pacman -S shards crystal imagemagick librsvg postgresql
|
||||
|
||||
# Ubuntu or Debian
|
||||
# First you have to add the repository to your APT configuration. For easy setup just run in your command line:
|
||||
$ curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash
|
||||
# That will add the signing key and the repository configuration. If you prefer to do it manually, execute the following commands:
|
||||
$ curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add -
|
||||
$ echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list
|
||||
$ sudo apt-get update
|
||||
$ sudo apt install crystal libssl-dev libxml2-dev libyaml-dev libgmp-dev libreadline-dev librsvg2-dev postgresql imagemagick libsqlite3-dev
|
||||
```
|
||||
|
||||
#### Add invidious user and clone repository
|
||||
|
||||
```bash
|
||||
$ useradd -m invidious
|
||||
$ sudo -i -u invidious
|
||||
$ git clone https://github.com/omarroth/invidious
|
||||
$ exit
|
||||
```
|
||||
|
||||
#### Setup PostgresSQL
|
||||
|
||||
```bash
|
||||
$ sudo systemctl enable postgresql
|
||||
$ sudo systemctl start postgresql
|
||||
$ sudo -i -u postgres
|
||||
$ psql -c "CREATE USER kemal WITH PASSWORD 'kemal';"
|
||||
$ createdb -O kemal invidious
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/channels.sql
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/videos.sql
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/channel_videos.sql
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/users.sql
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/session_ids.sql
|
||||
$ psql invidious < /home/invidious/invidious/config/sql/nonces.sql
|
||||
$ exit
|
||||
```
|
||||
|
||||
#### Setup Invidious
|
||||
|
||||
```bash
|
||||
$ sudo -i -u invidious
|
||||
$ cd invidious
|
||||
$ shards update && shards install
|
||||
$ crystal build src/invidious.cr --release
|
||||
# test compiled binary
|
||||
$ ./invidious # stop with ctrl c
|
||||
$ exit
|
||||
```
|
||||
|
||||
#### systemd service
|
||||
|
||||
```bash
|
||||
$ sudo cp /home/invidious/invidious/invidious.service /etc/systemd/system/invidious.service
|
||||
$ sudo systemctl enable invidious.service
|
||||
$ sudo systemctl start invidious.service
|
||||
```
|
||||
|
||||
### OSX:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
$ brew update
|
||||
$ brew install shards crystal-lang postgres imagemagick librsvg
|
||||
|
||||
# Clone repository and setup postgres database
|
||||
$ git clone https://github.com/omarroth/invidious
|
||||
$ cd invidious
|
||||
$ brew services start postgresql
|
||||
$ psql -c "CREATE ROLE kemal WITH LOGIN PASSWORD 'kemal';"
|
||||
$ createdb invidious -U kemal
|
||||
$ psql invidious < config/sql/channels.sql
|
||||
$ psql invidious < config/sql/videos.sql
|
||||
$ psql invidious < config/sql/channel_videos.sql
|
||||
$ psql invidious < config/sql/users.sql
|
||||
$ psql invidious < config/sql/session_ids.sql
|
||||
$ psql invidious < config/sql/nonces.sql
|
||||
|
||||
# Setup Invidious
|
||||
$ shards update && shards install
|
||||
$ crystal build src/invidious.cr --release
|
||||
```
|
||||
|
||||
## Update Invidious
|
||||
|
||||
You can see how to update Invidious [here](https://github.com/omarroth/invidious/wiki/Updating).
|
||||
|
||||
## Usage:
|
||||
|
||||
```bash
|
||||
$ ./invidious -h
|
||||
Usage: invidious [arguments]
|
||||
-b HOST, --bind HOST Host to bind (defaults to 0.0.0.0)
|
||||
-p PORT, --port PORT Port to listen for connections (defaults to 3000)
|
||||
-s, --ssl Enables SSL
|
||||
--ssl-key-file FILE SSL key file
|
||||
--ssl-cert-file FILE SSL certificate file
|
||||
-h, --help Shows this help
|
||||
-t THREADS, --crawl-threads=THREADS
|
||||
Number of threads for crawling YouTube (default: 0)
|
||||
-c THREADS, --channel-threads=THREADS
|
||||
Number of threads for refreshing channels (default: 1)
|
||||
-f THREADS, --feed-threads=THREADS
|
||||
Number of threads for refreshing feeds (default: 1)
|
||||
-v THREADS, --video-threads=THREADS
|
||||
Number of threads for refreshing videos (default: 0)
|
||||
-o OUTPUT, --output=OUTPUT Redirect output (default: STDOUT)
|
||||
```
|
||||
|
||||
Or for development:
|
||||
|
||||
```bash
|
||||
$ curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/master/install.cr | crystal eval
|
||||
$ ./sentry
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
[Documentation](https://github.com/omarroth/invidious/wiki) can be found in the wiki.
|
||||
The full documentation can be accessed online at https://docs.invidious.io/
|
||||
|
||||
## Extensions
|
||||
The documentation's source code is available in this repository:
|
||||
https://github.com/iv-org/documentation
|
||||
|
||||
[Extensions](https://github.com/omarroth/invidious/wiki/Extensions) can be found in the wiki, as well as documentation for integrating it into other projects.
|
||||
### Extensions
|
||||
|
||||
## Made with Invidious
|
||||
We highly recommend the use of [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect#get),
|
||||
a browser extension that automatically redirects Youtube URLs to any Invidious instance and replaces
|
||||
embedded youtube videos on other websites with invidious.
|
||||
|
||||
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): An Open Source YouTube app for privacy.
|
||||
- [CloudTube](https://github.com/cloudrac3r/cadencegq): Website featuring pastebin, image host, and YouTube player
|
||||
- [PeerTubeify](https://gitlab.com/Ealhad/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
|
||||
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A materialistic music player that streams music from YouTube.
|
||||
The documentation contains a list of browser extensions that we recommended to use along with Invidious.
|
||||
|
||||
## Contributing
|
||||
You can read more here: https://docs.invidious.io/applications/
|
||||
|
||||
1. Fork it ( https://github.com/omarroth/invidious/fork )
|
||||
2. Create your feature branch (git checkout -b my-new-feature)
|
||||
3. Commit your changes (git commit -am 'Add some feature')
|
||||
4. Push to the branch (git push origin my-new-feature)
|
||||
5. Create a new Pull Request
|
||||
|
||||
## Contact
|
||||
## Contribute
|
||||
|
||||
Feel free to send an email to omarroth@protonmail.com or join our [Matrix Server](https://riot.im/app/#/room/#invidious:matrix.org), or #invidious on Freenode.
|
||||
### Code
|
||||
|
||||
You can also view release notes on the [releases](https://github.com/omarroth/invidious/releases) page or in the CHANGELOG.md included in the repository.
|
||||
1. Fork it ( https://github.com/iv-org/invidious/fork ).
|
||||
1. Create your feature branch (`git checkout -b my-new-feature`).
|
||||
1. Stage your files (`git add .`).
|
||||
1. Commit your changes (`git commit -am 'Add some feature'`).
|
||||
1. Push to the branch (`git push origin my-new-feature`).
|
||||
1. Create a new pull request ( https://github.com/iv-org/invidious/compare ).
|
||||
|
||||
## License
|
||||
### Translations
|
||||
|
||||
[](http://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
We use [Weblate](https://weblate.org) to manage Invidious translations.
|
||||
|
||||
Invidious is Free Software: You can use, study share and improve it at your
|
||||
will. Specifically you can redistribute and/or modify it under the terms of the
|
||||
[GNU Affero General Public License](https://www.gnu.org/licenses/agpl.html) as
|
||||
published by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
You can suggest new translations and/or correction here: https://hosted.weblate.org/engage/invidious/.
|
||||
|
||||
Creating an account is not required, but recommended, especially if you want to contribute regularly.
|
||||
Weblate also allows you to log-in with major SSO providers like Github, Gitlab, BitBucket, Google, ...
|
||||
|
||||
|
||||
## Projects using Invidious
|
||||
|
||||
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): A libre software YouTube app for privacy.
|
||||
- [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player.
|
||||
- [PeerTubeify](https://gitlab.com/Cha_deL/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
|
||||
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube.
|
||||
- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favorites.
|
||||
- [WatchTube](https://github.com/WatchTubeTeam/WatchTube): Powerful YouTube client for Apple Watch.
|
||||
- [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV.
|
||||
- [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client.
|
||||
- [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API)
|
||||
|
||||
|
||||
## Liability
|
||||
|
||||
We take no responsibility for the use of our tool, or external instances
|
||||
provided by third parties. We strongly recommend you abide by the valid
|
||||
official regulations in your country. Furthermore, we refuse liability
|
||||
for any inappropriate use of Invidious, such as illegal downloading.
|
||||
This tool is provided to you in the spirit of free, open software.
|
||||
|
||||
You may view the LICENSE in which this software is provided to you [here](./LICENSE).
|
||||
|
||||
> 16. Limitation of Liability.
|
||||
>
|
||||
> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
1
TRANSLATION
Normal file
1
TRANSLATION
Normal file
@@ -0,0 +1 @@
|
||||
https://hosted.weblate.org/projects/invidious/
|
||||
@@ -1,34 +0,0 @@
|
||||
a:hover,
|
||||
a:active {
|
||||
color: rgb(0, 182, 240);
|
||||
}
|
||||
|
||||
a {
|
||||
color: #a0a0a0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: rgba(35, 35, 35, 1);
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.pure-form legend {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.pure-menu-heading {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.pure-form > fieldset > input,
|
||||
.pure-control-group > input,
|
||||
.pure-form > fieldset > select,
|
||||
.pure-control-group > select {
|
||||
color: rgba(35, 35, 35, 1);
|
||||
}
|
||||
|
||||
.navbar > .searchbar input {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
@@ -1,4 +1,38 @@
|
||||
.channel-owner {
|
||||
html,
|
||||
body {
|
||||
font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica,
|
||||
Arial, sans-serif;
|
||||
}
|
||||
|
||||
#contents {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.deleted {
|
||||
background-color: rgb(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.underlined {
|
||||
border-bottom: 1px solid;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.channel-profile > * {
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.channel-profile > img {
|
||||
width: 48px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body a.channel-owner {
|
||||
background-color: #008bec;
|
||||
color: #fff;
|
||||
border-radius: 9px;
|
||||
@@ -38,6 +72,22 @@
|
||||
color: rgb(255, 0, 0);
|
||||
}
|
||||
|
||||
.feed-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.feed-menu-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.feed-menu-item {
|
||||
flex: 0 0 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.h-box {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
@@ -54,6 +104,7 @@ div {
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: inline-block;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@@ -62,27 +113,36 @@ div {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
body a.pure-button {
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
button.pure-button-primary,
|
||||
a.pure-button-primary,
|
||||
body a.pure-button-primary,
|
||||
.channel-owner:hover {
|
||||
background-color: #a0a0a0;
|
||||
color: rgba(35, 35, 35, 1);
|
||||
}
|
||||
|
||||
button.pure-button-primary:hover,
|
||||
a.pure-button-primary:hover {
|
||||
body a.pure-button-primary:hover {
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
div.thumbnail {
|
||||
padding: 28.125%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img.thumbnail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.length {
|
||||
@@ -93,9 +153,8 @@ img.thumbnail {
|
||||
border-radius: 2px;
|
||||
padding: 2px;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
right: 0.5em;
|
||||
bottom: -0.5em;
|
||||
right: 0.25em;
|
||||
bottom: -0.75em;
|
||||
}
|
||||
|
||||
.watched {
|
||||
@@ -106,7 +165,6 @@ img.thumbnail {
|
||||
border-radius: 2px;
|
||||
padding: 4px 8px 4px 8px;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
left: 0.2em;
|
||||
top: -0.7em;
|
||||
}
|
||||
@@ -126,7 +184,7 @@ img.thumbnail {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navbar > .searchbar {
|
||||
.searchbar {
|
||||
flex-grow: 2; /* take double the space of the other items */
|
||||
}
|
||||
|
||||
@@ -136,30 +194,38 @@ img.thumbnail {
|
||||
|
||||
.navbar .index-link {
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.navbar > .searchbar .pure-form input[type="search"] {
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-radius: 0;
|
||||
.searchbar .pure-form fieldset { padding: 0; }
|
||||
|
||||
padding: initial 0;
|
||||
.searchbar input[type="search"] {
|
||||
width: 100%;
|
||||
margin: 1px;
|
||||
|
||||
box-shadow: none;
|
||||
border: 1px solid;
|
||||
border-color: rgba(0,0,0,0);
|
||||
border-bottom-color: #CCC;
|
||||
border-radius: 0;
|
||||
|
||||
transition: 0.1s border-bottom;
|
||||
box-shadow: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.searchbar input[type="search"]:focus {
|
||||
margin: 0 0 0.5px 0;
|
||||
border: 2px solid;
|
||||
border-color: rgba(0,0,0,0);
|
||||
border-bottom-color: #FED;
|
||||
}
|
||||
|
||||
/* https://stackoverflow.com/a/55170420 */
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.navbar > .searchbar .pure-form fieldset {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* attract focus to the searchbar by adding a subtle transition */
|
||||
.navbar > .searchbar .pure-form input[type="search"]:focus {
|
||||
border-bottom: 2px solid #aaa;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAn0lEQVR42u3UMQrDMBBEUZ9WfQqDmm22EaTyjRMHAlM5K+Y7lb0wnUZPIKHlnutOa+25Z4D++MRBX98MD1V/trSppLKHqj9TTBWKcoUqffbUcbBBEhTjBOV4ja4l4OIAZThEOV6jHO8ARXD+gPPvKMABinGOrnu6gTNUawrcQKNCAQ7QeTxORzle3+sDfjJpPCqhJh7GixZq4rHcc9l5A9qZ+WeBhgEuAAAAAElFTkSuQmCC);
|
||||
background-size: 14px;
|
||||
}
|
||||
|
||||
.user-field {
|
||||
@@ -170,13 +236,23 @@ img.thumbnail {
|
||||
}
|
||||
|
||||
.user-field div {
|
||||
width: initial;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.user-field div:not(:last-child) {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
@media only screen and (max-aspect-ratio: 16/9) {
|
||||
.player-dimensions.vjs-fluid {
|
||||
padding-top: 46.86% !important;
|
||||
}
|
||||
|
||||
#player-container {
|
||||
padding-bottom: 46.86% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.navbar {
|
||||
flex-direction: column;
|
||||
@@ -194,6 +270,11 @@ img.thumbnail {
|
||||
.navbar > .searchbar > form {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25em;
|
||||
margin: 0.42em 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
@@ -203,21 +284,43 @@ img.thumbnail {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Video "cards" (results/playlist/channel videos)
|
||||
*/
|
||||
|
||||
.video-card-row { margin: 15px 0; }
|
||||
|
||||
.flexible { display: flex; }
|
||||
.flex-left { flex: 1 1 100%; flex-wrap: wrap; }
|
||||
.flex-right { flex: 1 0 auto; flex-wrap: nowrap; }
|
||||
|
||||
p.channel-name { margin: 0; }
|
||||
p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
|
||||
|
||||
|
||||
/*
|
||||
* Footer
|
||||
*/
|
||||
|
||||
.footer {
|
||||
color: #666666;
|
||||
margin: 2em 0;
|
||||
footer {
|
||||
color: #919191;
|
||||
margin-top: auto;
|
||||
padding: 1.5em 0;
|
||||
text-align: center;
|
||||
max-height: 30vh;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: inherit;
|
||||
footer a {
|
||||
color: #919191 !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
footer span {
|
||||
margin: 4px 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* keyframes */
|
||||
|
||||
@keyframes spin {
|
||||
@@ -229,113 +332,206 @@ img.thumbnail {
|
||||
}
|
||||
}
|
||||
|
||||
/* Control Bar */
|
||||
@media screen and (max-width: 480px) {
|
||||
.video-js .vjs-control-bar,
|
||||
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||
overflow: -webkit-paged-x;
|
||||
fieldset > select,
|
||||
span > select {
|
||||
color: rgba(49, 49, 51, 1);
|
||||
}
|
||||
|
||||
.pure-control-group label {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Light theme
|
||||
*/
|
||||
|
||||
.light-theme a:hover,
|
||||
.light-theme a:active,
|
||||
.light-theme summary:hover {
|
||||
color: #075A9E !important;
|
||||
}
|
||||
|
||||
.light-theme a.pure-button-primary:hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.light-theme a {
|
||||
color: #335d7a;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* All links that do not fit with the default color goes here */
|
||||
.light-theme a:not([data-id]) > .icon,
|
||||
.light-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"],
|
||||
.light-theme .playlist-restricted > ol > li > a {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
.light-theme .pure-menu-heading {
|
||||
color: #565d64;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.no-theme a:hover,
|
||||
.no-theme a:active,
|
||||
.no-theme summary:hover {
|
||||
color: #075A9E !important;
|
||||
}
|
||||
|
||||
.no-theme a.pure-button-primary:hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.no-theme a {
|
||||
color: #335d7a;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* All links that do not fit with the default color goes here */
|
||||
.no-theme a:not([data-id]) > .icon,
|
||||
.no-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"],
|
||||
.no-theme .playlist-restricted > ol > li > a {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
.light-theme .pure-menu-heading {
|
||||
color: #565d64;
|
||||
}
|
||||
}
|
||||
|
||||
.video-js .vjs-control-bar,
|
||||
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
/*
|
||||
* Dark theme
|
||||
*/
|
||||
|
||||
.dark-theme a:hover,
|
||||
.dark-theme a:active,
|
||||
.dark-theme summary:hover {
|
||||
color: rgb(0, 182, 240);
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-menu-item:focus,
|
||||
.vjs-menu li.vjs-menu-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
color: rgba(49, 49, 51, 0.75);
|
||||
.dark-theme a {
|
||||
color: #a0a0a0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-selected,
|
||||
.vjs-menu li.vjs-selected:focus,
|
||||
.vjs-menu li.vjs-selected:hover {
|
||||
background-color: rgba(0, 182, 240, 0.75);
|
||||
body.dark-theme {
|
||||
background-color: rgba(35, 35, 35, 1);
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.video-js .vjs-slider {
|
||||
background-color: rgba(15, 15, 15, 0.5);
|
||||
.dark-theme .pure-form legend {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.video-js .vjs-load-progress,
|
||||
.video-js .vjs-load-progress div {
|
||||
background: rgba(87, 87, 88, 1);
|
||||
.dark-theme .pure-menu-heading {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.video-js .vjs-slider:hover,
|
||||
.video-js button:hover {
|
||||
color: rgba(0, 182, 240, 1);
|
||||
}
|
||||
|
||||
.video-js .vjs-play-progress {
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
}
|
||||
|
||||
/* ProgressBar marker */
|
||||
.vjs-marker {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
/* Big "Play" Button */
|
||||
.video-js .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.5);
|
||||
}
|
||||
|
||||
.video-js:hover .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
}
|
||||
|
||||
.video-js .vjs-current-time,
|
||||
.video-js .vjs-time-divider,
|
||||
.video-js .vjs-duration {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-js .vjs-time-divider {
|
||||
min-width: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.video-js .vjs-poster {
|
||||
background-size: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#player {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.player-dimensions.vjs-fluid {
|
||||
padding-top: 46.86%;
|
||||
}
|
||||
|
||||
#player-container {
|
||||
position: relative;
|
||||
padding-bottom: 46.86%;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#progress-container {
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
background-color: #a0a0a0;
|
||||
.dark-theme input,
|
||||
.dark-theme select,
|
||||
.dark-theme textarea {
|
||||
color: rgba(35, 35, 35, 1);
|
||||
}
|
||||
|
||||
#download-progress {
|
||||
width: 0%;
|
||||
border-radius: 2px;
|
||||
height: 10px;
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
color: #fff;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
.dark-theme .pure-form input[type="file"] {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.dark-theme .searchbar input {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.no-theme a:hover,
|
||||
.no-theme a:active {
|
||||
color: rgb(0, 182, 240);
|
||||
}
|
||||
|
||||
.no-theme a {
|
||||
color: #a0a0a0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body.no-theme {
|
||||
background-color: rgba(35, 35, 35, 1);
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.no-theme .pure-form legend {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.no-theme .pure-menu-heading {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.no-theme input,
|
||||
.no-theme select,
|
||||
.no-theme textarea {
|
||||
color: rgba(35, 35, 35, 1);
|
||||
}
|
||||
|
||||
.no-theme .pure-form input[type="file"] {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.no-theme .searchbar input {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
/*With commit d9528f5 all contents of the page is now within a flexbox. However,
|
||||
the hr element is rendered improperly within one.
|
||||
See https://stackoverflow.com/a/34372979 for more info */
|
||||
hr {
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
/* Description Expansion Styling*/
|
||||
#descexpansionbutton {
|
||||
display: none
|
||||
}
|
||||
|
||||
#descexpansionbutton ~ div {
|
||||
overflow: hidden;
|
||||
height: 8.3em;
|
||||
}
|
||||
|
||||
#descexpansionbutton:checked ~ div {
|
||||
overflow: unset;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#descexpansionbutton ~ label {
|
||||
order: 1;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Bidi (bidirectional text) support */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
p,
|
||||
#descriptionWrapper,
|
||||
#description-box {
|
||||
unicode-bidi: plaintext;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
#descriptionWrapper {
|
||||
max-width: 600px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* Center the "invidious" logo on the search page */
|
||||
#logo > h1 { text-align: center; }
|
||||
|
||||
/* IE11 fixes */
|
||||
:-ms-input-placeholder { color: #888; }
|
||||
|
||||
/* Wider settings name to less word wrap */
|
||||
.pure-form-aligned .pure-control-group label { width: 19em; }
|
||||
|
||||
26
assets/css/embed.css
Normal file
26
assets/css/embed.css
Normal file
@@ -0,0 +1,26 @@
|
||||
#player {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
z-index: -100;
|
||||
}
|
||||
|
||||
.watch-on-invidious {
|
||||
font-size: 1.3em !important;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
margin: 0 1em 0 1em !important;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.watch-on-invidious > a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.watch-on-invidious > a:hover {
|
||||
color: rgba(0, 182, 240, 1);;
|
||||
}
|
||||
16
assets/css/empty.css
Normal file
16
assets/css/empty.css
Normal file
@@ -0,0 +1,16 @@
|
||||
#search-widget {
|
||||
text-align: center;
|
||||
margin: 20vh 0 50px 0;
|
||||
}
|
||||
|
||||
#logo > h1 {
|
||||
font-size: 3.5em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1500px) and (max-height: 1000px) {
|
||||
#logo > h1 {
|
||||
font-size: 10vmin;
|
||||
}
|
||||
}
|
||||
4
assets/css/grids-responsive-min.css
vendored
4
assets/css/grids-responsive-min.css
vendored
File diff suppressed because one or more lines are too long
4
assets/css/ionicons.min.css
vendored
4
assets/css/ionicons.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -1,16 +0,0 @@
|
||||
a:hover,
|
||||
a:active {
|
||||
color: #167ac6 !important;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #61809b;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* All links that do not fit with the default color goes here */
|
||||
a > .icon,
|
||||
.pure-u-md-1-5 > .h-box > a[href^="/watch?"],
|
||||
.playlist-restricted > ol > li > a {
|
||||
color: #303030;
|
||||
}
|
||||
261
assets/css/player.css
Normal file
261
assets/css/player.css
Normal file
@@ -0,0 +1,261 @@
|
||||
/* Youtube player style */
|
||||
.video-js.player-style-youtube .vjs-progress-control {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar {
|
||||
background: linear-gradient(rgba(0,0,0,0.1), rgba(0, 0, 0,0.5));
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-slider {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-load-progress > div {
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control:hover .vjs-progress-holder {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar > .vjs-spacer {
|
||||
flex: 1;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress::before {
|
||||
color: red;
|
||||
font-size: 0.85em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-holder:hover .vjs-play-progress::before {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-big-play-button {
|
||||
/*
|
||||
Styles copied from video-js.min.css, definition of
|
||||
.vjs-big-play-centered .vjs-big-play-button
|
||||
*/
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -0.81666em;
|
||||
margin-left: -1.5em;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px;
|
||||
margin-bottom: 10px;}
|
||||
|
||||
ul.vjs-menu-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-user-inactive {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.video-js .vjs-text-track-display > div > div > div {
|
||||
background-color: rgba(0, 0, 0, 0.75) !important;
|
||||
border-radius: 9px !important;
|
||||
padding: 5px !important;
|
||||
}
|
||||
|
||||
.vjs-play-control,
|
||||
.vjs-volume-panel,
|
||||
.vjs-current-time,
|
||||
.vjs-time-control,
|
||||
.vjs-duration,
|
||||
.vjs-progress-control,
|
||||
.vjs-remaining-time {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.vjs-captions-button {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.vjs-audio-button {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.vjs-quality-selector,
|
||||
.video-js .vjs-http-source-selector {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
.vjs-playback-rate {
|
||||
order: 5;
|
||||
}
|
||||
|
||||
.vjs-share-control {
|
||||
order: 6;
|
||||
}
|
||||
|
||||
.vjs-fullscreen-control {
|
||||
order: 7;
|
||||
}
|
||||
|
||||
.vjs-playback-rate > .vjs-menu {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.vjs-control-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.vjs-control-bar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js .vjs-icon-cog {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.video-js .vjs-control-bar,
|
||||
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-menu-item:focus,
|
||||
.vjs-menu li.vjs-menu-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
color: rgba(49, 49, 51, 0.75);
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-selected,
|
||||
.vjs-menu li.vjs-selected:focus,
|
||||
.vjs-menu li.vjs-selected:hover {
|
||||
background-color: rgba(0, 182, 240, 0.75);
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.video-js .vjs-slider {
|
||||
background-color: rgba(15, 15, 15, 0.5);
|
||||
}
|
||||
|
||||
.video-js .vjs-load-progress,
|
||||
.video-js .vjs-load-progress div {
|
||||
background: rgba(87, 87, 88, 1);
|
||||
}
|
||||
|
||||
.video-js .vjs-slider:hover,
|
||||
.video-js button:hover {
|
||||
color: rgba(0, 182, 240, 1);
|
||||
}
|
||||
|
||||
.video-js.player-style-invidious .vjs-play-progress {
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
}
|
||||
vjs-menu-content
|
||||
/* Overlay */
|
||||
.video-js .vjs-overlay {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
/* ProgressBar marker */
|
||||
.vjs-marker {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Big "Play" Button */
|
||||
.video-js .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.5);
|
||||
}
|
||||
|
||||
.video-js:hover .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
}
|
||||
|
||||
.video-js .vjs-current-time,
|
||||
.video-js .vjs-time-divider,
|
||||
.video-js .vjs-duration {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-js .vjs-time-divider {
|
||||
min-width: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.video-js .vjs-poster {
|
||||
background-size: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.player-dimensions.vjs-fluid {
|
||||
padding-top: 82vh;
|
||||
}
|
||||
|
||||
video.video-js {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#player-container {
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
padding-bottom: 82vh;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.mobile-operations-bar {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1px !important;
|
||||
left: initial !important;
|
||||
width: initial !important;
|
||||
}
|
||||
|
||||
.mobile-operations-bar ul {
|
||||
position: absolute !important;
|
||||
bottom: unset !important;
|
||||
top: 1.5em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.video-js .vjs-share {
|
||||
justify-content: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.vjs-modal-dialog-content {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
6
assets/css/pure-min.css
vendored
6
assets/css/pure-min.css
vendored
File diff suppressed because one or more lines are too long
121
assets/css/search.css
Normal file
121
assets/css/search.css
Normal file
@@ -0,0 +1,121 @@
|
||||
summary {
|
||||
/* This should hide the marker */
|
||||
display: block;
|
||||
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
margin: 0 auto 10px auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
summary::-webkit-details-marker,
|
||||
summary::marker { display: none; }
|
||||
|
||||
summary:before {
|
||||
border-radius: 5px;
|
||||
content: "[ + ]";
|
||||
margin: -2px 10px 0 10px;
|
||||
padding: 1px 0 3px 0;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
details[open] > summary:before { content: "[ − ]"; }
|
||||
|
||||
|
||||
#filters-box {
|
||||
padding: 10px 20px 20px 10px;
|
||||
margin: 10px 15px;
|
||||
}
|
||||
#filters-flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
|
||||
fieldset, legend {
|
||||
display: contents !important;
|
||||
border: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.filter-column {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
width: max-content;
|
||||
min-width: max-content;
|
||||
max-width: 16em;
|
||||
margin: 15px;
|
||||
flex-grow: 2;
|
||||
flex-basis: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
.filter-name, .filter-options {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
margin: 0;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.filter-options div { margin: 6px 0; }
|
||||
.filter-options div * { vertical-align: middle; }
|
||||
.filter-options label { margin: 0 10px; }
|
||||
|
||||
|
||||
#filters-apply {
|
||||
text-align: right; /* IE11 only */
|
||||
text-align: end; /* Override for compatible browsers */
|
||||
}
|
||||
|
||||
/* Error message */
|
||||
|
||||
.no-results-error {
|
||||
text-align: center;
|
||||
line-height: 180%;
|
||||
font-size: 110%;
|
||||
padding: 15px 15px 125px 15px;
|
||||
}
|
||||
|
||||
/* Responsive rules */
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
summary { font-size: 1.30em; }
|
||||
#filters-box {
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
#filters-apply {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
|
||||
.light-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.no-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
|
||||
.dark-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.no-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
}
|
||||
1
assets/css/video-js.min.css
vendored
1
assets/css/video-js.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* videojs-share
|
||||
* @version 2.0.1
|
||||
* @copyright 2018 Mikhail Khazov <mkhazov.work@gmail.com>
|
||||
* @license MIT
|
||||
*/
|
||||
.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-modal-dialog-content{display:flex;align-items:center;padding:0;background-image:linear-gradient(to bottom, rgba(0,0,0,0.77), rgba(0,0,0,0.75))}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{position:absolute;right:0;top:5px;width:30px;height:30px;color:#fff;cursor:pointer;opacity:0.9;transition:opacity 0.25s ease-out}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button:before{content:'×';font-size:20px;line-height:15px}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button:hover{opacity:1}.video-js .vjs-share{display:flex;flex-direction:column;justify-content:space-around;align-items:center;width:100%;height:100%;max-height:400px}.video-js .vjs-share__top,.video-js .vjs-share__middle,.video-js .vjs-share__bottom{display:flex}.video-js .vjs-share__top,.video-js .vjs-share__middle{flex-direction:column;justify-content:space-between}.video-js .vjs-share__middle{padding:0 25px}.video-js .vjs-share__title{align-self:center;font-size:22px;color:#fff}.video-js .vjs-share__subtitle{width:100%;margin:0 auto 12px;font-size:16px;color:#fff;opacity:0.7}.video-js .vjs-share__short-link-wrapper{position:relative;display:block;width:100%;height:40px;margin:0 auto;margin-bottom:15px;border:0;color:rgba(255,255,255,0.65);background-color:#363636;outline:none;overflow:hidden;flex-shrink:0}.video-js .vjs-share__short-link{display:block;width:100%;height:100%;padding:0 40px 0 15px;border:0;color:rgba(255,255,255,0.65);background-color:#363636;outline:none}.video-js .vjs-share__btn{position:absolute;right:0;bottom:0;height:40px;width:40px;display:flex;align-items:center;padding:0 11px;border:0;color:#fff;background-color:#2e2e2e;background-size:18px 19px;background-position:center;background-repeat:no-repeat;cursor:pointer;outline:none;transition:width 0.3s ease-out, padding 0.3s ease-out}.video-js .vjs-share__btn svg{flex-shrink:0}.video-js .vjs-share__btn span{position:relative;padding-left:10px;opacity:0;transition:opacity 0.3s ease-out}.video-js .vjs-share__btn:hover{justify-content:center;width:100%;padding:0 40px;background-image:none}.video-js .vjs-share__btn:hover span{opacity:1}.video-js .vjs-share__socials{display:flex;flex-wrap:wrap;justify-content:center;align-content:flex-start;transition:width 0.3s ease-out, height 0.3s ease-out}.video-js .vjs-share__social{display:flex;justify-content:center;align-items:center;flex-shrink:0;width:32px;height:32px;margin-right:6px;margin-bottom:6px;cursor:pointer;font-size:8px;transition:transform 0.3s ease-out, filter 0.2s ease-out;border:none;outline:none}.video-js .vjs-share__social:hover{filter:brightness(115%)}.video-js .vjs-share__social svg{width:100%;max-height:24px}.video-js .vjs-share__social_vk{background-color:#5d7294}.video-js .vjs-share__social_ok{background-color:#ed7c20}.video-js .vjs-share__social_mail{background-color:#134785}.video-js .vjs-share__social_tw{background-color:#76aaeb}.video-js .vjs-share__social_reddit{background-color:#ff4500}.video-js .vjs-share__social_fbFeed{background-color:#475995}.video-js .vjs-share__social_messenger{background-color:#0084ff}.video-js .vjs-share__social_gp{background-color:#d53f35}.video-js .vjs-share__social_linkedin{background-color:#0077b5}.video-js .vjs-share__social_viber{background-color:#766db5}.video-js .vjs-share__social_telegram{background-color:#4bb0e2}.video-js .vjs-share__social_whatsapp{background-color:#78c870}.video-js .vjs-share__bottom{justify-content:center}@media (max-height: 220px){.video-js .vjs-share .hidden-xs{display:none}}@media (max-height: 350px){.video-js .vjs-share .hidden-sm{display:none}}@media (min-height: 400px){.video-js .vjs-share__title{margin-bottom:15px}.video-js .vjs-share__short-link-wrapper{margin-bottom:30px}}@media (min-width: 320px){.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{right:5px;top:10px}}@media (min-width: 660px){.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{right:20px;top:20px}.video-js .vjs-share__social{width:40px;height:40px}}
|
||||
1
assets/css/videojs-youtube-annotations.min.css
vendored
Normal file
1
assets/css/videojs-youtube-annotations.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.__cxt-ar-annotations-container__{--annotation-close-size: 20px;position:absolute;width:100%;height:100%;top:0;left:0;pointer-events:none;overflow:hidden}.__cxt-ar-annotation__{position:absolute;box-sizing:border-box;font-family:Arial,sans-serif;color:#fff;z-index:20;pointer-events:auto}.__cxt-ar-annotation__ span{position:absolute;left:0;top:0;overflow:hidden;word-wrap:break-word;white-space:pre-wrap;pointer-events:none;box-sizing:border-box;padding:2%;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.__cxt-ar-annotation-close__{display:none;position:absolute;width:var(--annotation-close-size);height:var(--annotation-close-size);cursor:pointer;right:calc(var(--annotation-close-size)/-1.8);top:calc(var(--annotation-close-size)/-1.8);z-index:1}.__cxt-ar-annotation__:hover:not([hidden]):not([data-ar-closed]) .__cxt-ar-annotation-close__{display:block}.__cxt-ar-annotation__[hidden]{display:none!important}.__cxt-ar-annotation__[data-ar-type=highlight]{border:1px solid rgba(255,255,255,.1);background-color:transparent}.__cxt-ar-annotation__[data-ar-type=highlight]:hover{border:1px solid rgba(255,255,255,.5);background-color:transparent}.__cxt-ar-annotation__ svg{pointer-events:all}
|
||||
1
assets/css/videojs.markers.min.css
vendored
1
assets/css/videojs.markers.min.css
vendored
@@ -1 +0,0 @@
|
||||
.vjs-marker{position:absolute;left:0;bottom:0;opacity:1;height:100%;transition:opacity .2s ease;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;z-index:100}.vjs-marker:hover{cursor:pointer;-webkit-transform:scale(1.3,1.3);-moz-transform:scale(1.3,1.3);-o-transform:scale(1.3,1.3);-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.vjs-tip{visibility:hidden;display:block;opacity:.8;padding:5px;font-size:10px;position:absolute;bottom:14px;z-index:100000}.vjs-tip .vjs-tip-arrow{background:url(data:image/gif;base64,R0lGODlhCQAJAIABAAAAAAAAACH5BAEAAAEALAAAAAAJAAkAAAIRjAOnwIrcDJxvwkplPtchVQAAOw==) no-repeat top left;bottom:0;left:50%;margin-left:-4px;background-position:bottom left;position:absolute;width:9px;height:5px}.vjs-tip .vjs-tip-inner{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;padding:5px 8px 4px 8px;background-color:#000;color:#fff;max-width:200px;text-align:center}.vjs-break-overlay{visibility:hidden;position:absolute;z-index:100000;top:0}.vjs-break-overlay .vjs-break-overlay-text{padding:9px;text-align:center}
|
||||
Binary file not shown.
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2018-6-14: Created with FontForge (http://fontforge.org)
|
||||
2019-5-24: Created with FontForge (http://fontforge.org)
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata>
|
||||
Created by FontForge 20160407 at Thu Jun 14 08:50:34 2018
|
||||
Created by FontForge 20160407 at Fri May 24 15:45:40 2019
|
||||
By Adam Bradley
|
||||
Copyright (c) 2018, Adam Bradley
|
||||
Copyright (c) 2019, Adam Bradley
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="Ionicons" horiz-adv-x="416" >
|
||||
|
||||
|
Before Width: | Height: | Size: 305 KiB After Width: | Height: | Size: 305 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
assets/invidious-colored-vector.svg
Normal file
2
assets/invidious-colored-vector.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="512pt" height="512pt" version="1.0" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g><rect x="-.0072516" y=".00056299" width="512.01" height="512.02" fill="#575757" stroke-width=".063019"/><path d="m247.17 455.95c-19.792-0.78921-38.719-4.2564-57.154-10.47-60.968-20.55-108.68-68.579-127-127.86-7.8955-25.538-10.062-53.943-6.2586-82.067 3.7105-27.439 13.603-53.515 29.342-77.344 12.069-18.273 29.138-36.277 47.228-49.816 36.891-27.61 85.944-42.49 132.38-40.157 25.88 1.3001 49.939 6.765 73.106 16.606 8.1948 3.481 20.024 9.6845 27.696 14.525 14.15 8.9272 22.367 15.498 34.482 27.573 13.254 13.211 22.128 24.276 30.398 37.906 7.2081 11.879 14.099 27.15 18.229 40.397 1.5996 5.1305 4.442 16.456 5.6852 22.653 2.3908 11.917 2.6998 15.722 2.7049 33.312 6e-3 18.515-0.46256 24.413-2.9166 36.758-9.3274 46.92-35.58 88.167-74.872 117.64-22.814 17.112-50.027 29.535-78.547 35.858-16.714 3.7059-35.421 5.2453-54.498 4.4846zm-35.1-78.786c-5.3e-4 -0.52647-0.0741-2.0564-0.16311-3.3999l-0.16178-2.4427-4.7018-0.26271c-4.0477-0.22614-4.7968-0.33363-5.3847-0.77253-2.0235-1.5108-1.4679-6.0695 2.2494-18.457 0.8637-2.8781 3.3371-11.321 5.4966-18.762 2.1594-7.4409 5.2002-17.836 6.7573-23.101 1.5571-5.2648 4.1948-14.282 5.8615-20.038 1.6667-5.7562 3.6145-12.4 4.3284-14.764 0.71391-2.3641 3.2583-11.037 5.6542-19.272 4.9475-17.007 8.1626-27.723 8.9438-29.811 0.51852-1.3858 0.54785-1.4139 0.99761-0.95317 0.25486 0.26106 3.8462 7.3667 7.9807 15.79 4.1345 8.4236 13.089 26.573 19.898 40.331 17.188 34.73 37.849 76.578 43.261 87.622l4.5356 9.257 11.359-0.0895c6.2475-0.0492 11.615-0.19623 11.929-0.32672 0.5614-0.23385 0.54167-0.2959-1.3723-4.3176-1.068-2.2442-8.1436-16.601-15.724-31.904-48.687-98.293-61.22-123.86-67.889-138.48-4.7022-10.309-6.9031-14.807-7.7139-15.762-0.82931-0.97742-1.6319-1.0638-2.3704-0.25525-1.1993 1.313-4.1046 10.063-9.3869 28.27-2.0569 7.0899-6.5372 22.425-9.9562 34.077-6.6396 22.629-8.5182 29.037-14.33 48.883-2.0354 6.9495-4.7977 16.369-6.1385 20.931-1.3408 4.5628-4.033 13.81-5.9826 20.549-4.304 14.877-6.136 20.889-7.3886 24.25-2.1371 5.7334-2.5723 6.3292-4.9216 6.7384-0.88855 0.15472-2.4102 0.28196-3.3815 0.28275-2.1993 3e-3 -3.5494 0.36339-4.0558 1.0863-0.42176 0.60215-0.56421 4.8802-0.18251 5.4812 0.20573 0.32388 2.4672 0.37414 23.34 0.51873l8.6151 0.0597-7e-4 -0.95723zm36.751-205.59c4.3282-0.92335 8.4607-4.943 9.4374-9.1796 0.36569-1.5862 0.32543-4.9758-0.077-6.4799-0.85108-3.1813-3.2688-6.291-6.039-7.7675-3.8111-2.0313-9.456-2.0295-13.272 5e-3 -5.9828 3.1888-8.1556 11.089-4.7878 17.408 2.6995 5.0648 8.3611 7.3754 14.738 6.015z" fill="#f0f0f0" stroke-width=".025526"/></g><g transform="matrix(.069892 0 0 -.069892 44.236 474.48)"><path d="m2787 4669c-124-65-123-255 3-319 86-44 196-16 247 62 58 87 26 211-67 258-51 26-132 26-183-1z" fill="#00b6f0" stroke="#00b6f0" stroke-width="4.25"/><path d="m2882 4108c-12-16-63-166-102-303-30-104-101-350-165-565-20-69-58-199-85-290-26-91-64-221-85-290-20-69-58-199-85-290-26-91-64-221-85-290-20-69-57-195-81-280-59-207-93-299-115-310-10-6-35-10-56-10-73 0-84-8-81-54l3-41 228-3 228-2-3 47-3 48-73 3c-66 3-74 5-84 27-13 28 0 104 37 225 13 41 47 156 75 255s66 230 85 290c18 61 56 191 85 290 28 99 66 230 85 290 18 61 56 191 85 290 85 297 123 419 131 429 5 5 17-11 28-35 10-24 192-393 403-819s447-902 523-1058l139-282h168c92 0 168 4 168 8s-75 158-166 342c-588 1183-969 1958-1033 2100-29 63-69 151-89 195-44 95-58 110-80 83z" fill="#575757"/></g></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
249
assets/js/_helpers.js
Normal file
249
assets/js/_helpers.js
Normal file
@@ -0,0 +1,249 @@
|
||||
'use strict';
|
||||
// Contains only auxiliary methods
|
||||
// May be included and executed unlimited number of times without any consequences
|
||||
|
||||
// Polyfills for IE11
|
||||
Array.prototype.find = Array.prototype.find || function (condition) {
|
||||
return this.filter(condition)[0];
|
||||
};
|
||||
Array.from = Array.from || function (source) {
|
||||
return Array.prototype.slice.call(source);
|
||||
};
|
||||
NodeList.prototype.forEach = NodeList.prototype.forEach || function (callback) {
|
||||
Array.from(this).forEach(callback);
|
||||
};
|
||||
String.prototype.includes = String.prototype.includes || function (searchString) {
|
||||
return this.indexOf(searchString) >= 0;
|
||||
};
|
||||
String.prototype.startsWith = String.prototype.startsWith || function (prefix) {
|
||||
return this.substr(0, prefix.length) === prefix;
|
||||
};
|
||||
Math.sign = Math.sign || function(x) {
|
||||
x = +x;
|
||||
if (!x) return x; // 0 and NaN
|
||||
return x > 0 ? 1 : -1;
|
||||
};
|
||||
if (!window.hasOwnProperty('HTMLDetailsElement') && !window.hasOwnProperty('mockHTMLDetailsElement')) {
|
||||
window.mockHTMLDetailsElement = true;
|
||||
const style = 'details:not([open]) > :not(summary) {display: none}';
|
||||
document.head.appendChild(document.createElement('style')).textContent = style;
|
||||
|
||||
addEventListener('click', function (e) {
|
||||
if (e.target.nodeName !== 'SUMMARY') return;
|
||||
const details = e.target.parentElement;
|
||||
if (details.hasAttribute('open'))
|
||||
details.removeAttribute('open');
|
||||
else
|
||||
details.setAttribute('open', '');
|
||||
});
|
||||
}
|
||||
|
||||
// Monstrous global variable for handy code
|
||||
// Includes: clamp, xhr, storage.{get,set,remove}
|
||||
window.helpers = window.helpers || {
|
||||
/**
|
||||
* https://en.wikipedia.org/wiki/Clamping_(graphics)
|
||||
* @param {Number} num Source number
|
||||
* @param {Number} min Low border
|
||||
* @param {Number} max High border
|
||||
* @returns {Number} Clamped value
|
||||
*/
|
||||
clamp: function (num, min, max) {
|
||||
if (max < min) {
|
||||
var t = max; max = min; min = t; // swap max and min
|
||||
}
|
||||
|
||||
if (max < num)
|
||||
return max;
|
||||
if (min > num)
|
||||
return min;
|
||||
return num;
|
||||
},
|
||||
|
||||
/** @private */
|
||||
_xhr: function (method, url, options, callbacks) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url);
|
||||
|
||||
// Default options
|
||||
xhr.responseType = 'json';
|
||||
xhr.timeout = 10000;
|
||||
// Default options redefining
|
||||
if (options.responseType)
|
||||
xhr.responseType = options.responseType;
|
||||
if (options.timeout)
|
||||
xhr.timeout = options.timeout;
|
||||
|
||||
if (method === 'POST')
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
|
||||
// better than onreadystatechange because of 404 codes https://stackoverflow.com/a/36182963
|
||||
xhr.onloadend = function () {
|
||||
if (xhr.status === 200) {
|
||||
if (callbacks.on200) {
|
||||
// fix for IE11. It doesn't convert response to JSON
|
||||
if (xhr.responseType === '' && typeof(xhr.response) === 'string')
|
||||
callbacks.on200(JSON.parse(xhr.response));
|
||||
else
|
||||
callbacks.on200(xhr.response);
|
||||
}
|
||||
} else {
|
||||
// handled by onerror
|
||||
if (xhr.status === 0) return;
|
||||
|
||||
if (callbacks.onNon200)
|
||||
callbacks.onNon200(xhr);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
if (callbacks.onTimeout)
|
||||
callbacks.onTimeout(xhr);
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
if (callbacks.onError)
|
||||
callbacks.onError(xhr);
|
||||
};
|
||||
|
||||
if (options.payload)
|
||||
xhr.send(options.payload);
|
||||
else
|
||||
xhr.send();
|
||||
},
|
||||
/** @private */
|
||||
_xhrRetry: function(method, url, options, callbacks) {
|
||||
if (options.retries <= 0) {
|
||||
console.warn('Failed to pull', options.entity_name);
|
||||
if (callbacks.onTotalFail)
|
||||
callbacks.onTotalFail();
|
||||
return;
|
||||
}
|
||||
helpers._xhr(method, url, options, callbacks);
|
||||
},
|
||||
/**
|
||||
* @callback callbackXhrOn200
|
||||
* @param {Object} response - xhr.response
|
||||
*/
|
||||
/**
|
||||
* @callback callbackXhrError
|
||||
* @param {XMLHttpRequest} xhr
|
||||
*/
|
||||
/**
|
||||
* @param {'GET'|'POST'} method - 'GET' or 'POST'
|
||||
* @param {String} url - URL to send request to
|
||||
* @param {Object} options - other XHR options
|
||||
* @param {XMLHttpRequestBodyInit} [options.payload=null] - payload for POST-requests
|
||||
* @param {'arraybuffer'|'blob'|'document'|'json'|'text'} [options.responseType=json]
|
||||
* @param {Number} [options.timeout=10000]
|
||||
* @param {Number} [options.retries=1]
|
||||
* @param {String} [options.entity_name='unknown'] - string to log
|
||||
* @param {Number} [options.retry_timeout=1000]
|
||||
* @param {Object} callbacks - functions to execute on events fired
|
||||
* @param {callbackXhrOn200} [callbacks.on200]
|
||||
* @param {callbackXhrError} [callbacks.onNon200]
|
||||
* @param {callbackXhrError} [callbacks.onTimeout]
|
||||
* @param {callbackXhrError} [callbacks.onError]
|
||||
* @param {callbackXhrError} [callbacks.onTotalFail] - if failed after all retries
|
||||
*/
|
||||
xhr: function(method, url, options, callbacks) {
|
||||
if (!options.retries || options.retries <= 1) {
|
||||
helpers._xhr(method, url, options, callbacks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.entity_name) options.entity_name = 'unknown';
|
||||
if (!options.retry_timeout) options.retry_timeout = 1000;
|
||||
const retries_total = options.retries;
|
||||
let currentTry = 1;
|
||||
|
||||
const retry = function () {
|
||||
console.warn('Pulling ' + options.entity_name + ' failed... ' + (currentTry++) + '/' + retries_total);
|
||||
setTimeout(function () {
|
||||
options.retries--;
|
||||
helpers._xhrRetry(method, url, options, callbacks);
|
||||
}, options.retry_timeout);
|
||||
};
|
||||
|
||||
// Pack retry() call into error handlers
|
||||
callbacks._onError = callbacks.onError;
|
||||
callbacks.onError = function (xhr) {
|
||||
if (callbacks._onError)
|
||||
callbacks._onError(xhr);
|
||||
retry();
|
||||
};
|
||||
callbacks._onTimeout = callbacks.onTimeout;
|
||||
callbacks.onTimeout = function (xhr) {
|
||||
if (callbacks._onTimeout)
|
||||
callbacks._onTimeout(xhr);
|
||||
retry();
|
||||
};
|
||||
|
||||
helpers._xhrRetry(method, url, options, callbacks);
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {Object} invidiousStorage
|
||||
* @property {(key:String) => Object} get
|
||||
* @property {(key:String, value:Object)} set
|
||||
* @property {(key:String)} remove
|
||||
*/
|
||||
|
||||
/**
|
||||
* Universal storage, stores and returns JS objects. Uses inside localStorage or cookies
|
||||
* @type {invidiousStorage}
|
||||
*/
|
||||
storage: (function () {
|
||||
// access to localStorage throws exception in Tor Browser, so try is needed
|
||||
let localStorageIsUsable = false;
|
||||
try{localStorageIsUsable = !!localStorage.setItem;}catch(e){}
|
||||
|
||||
if (localStorageIsUsable) {
|
||||
return {
|
||||
get: function (key) {
|
||||
if (!localStorage[key]) return;
|
||||
try {
|
||||
return JSON.parse(decodeURIComponent(localStorage[key]));
|
||||
} catch(e) {
|
||||
// Erase non parsable value
|
||||
helpers.storage.remove(key);
|
||||
}
|
||||
},
|
||||
set: function (key, value) { localStorage[key] = encodeURIComponent(JSON.stringify(value)); },
|
||||
remove: function (key) { localStorage.removeItem(key); }
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: fire 'storage' event for cookies
|
||||
console.info('Storage: localStorage is disabled or unaccessible. Cookies used as fallback');
|
||||
return {
|
||||
get: function (key) {
|
||||
const cookiePrefix = key + '=';
|
||||
function findCallback(cookie) {return cookie.startsWith(cookiePrefix);}
|
||||
const matchedCookie = document.cookie.split('; ').find(findCallback);
|
||||
if (matchedCookie) {
|
||||
const cookieBody = matchedCookie.replace(cookiePrefix, '');
|
||||
if (cookieBody.length === 0) return;
|
||||
try {
|
||||
return JSON.parse(decodeURIComponent(cookieBody));
|
||||
} catch(e) {
|
||||
// Erase non parsable value
|
||||
helpers.storage.remove(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
set: function (key, value) {
|
||||
const cookie_data = encodeURIComponent(JSON.stringify(value));
|
||||
|
||||
// Set expiration in 2 year
|
||||
const date = new Date();
|
||||
date.setFullYear(date.getFullYear()+2);
|
||||
|
||||
document.cookie = key + '=' + cookie_data + '; expires=' + date.toGMTString();
|
||||
},
|
||||
remove: function (key) {
|
||||
document.cookie = key + '=; Max-Age=0';
|
||||
}
|
||||
};
|
||||
})()
|
||||
};
|
||||
82
assets/js/community.js
Normal file
82
assets/js/community.js
Normal file
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
var community_data = JSON.parse(document.getElementById('community_data').textContent);
|
||||
|
||||
function hide_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = 'none';
|
||||
|
||||
target.innerHTML = sub_text;
|
||||
target.onclick = show_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function show_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = '';
|
||||
|
||||
target.innerHTML = sub_text;
|
||||
target.onclick = hide_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function get_youtube_replies(target, load_more) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
var fallback = body.innerHTML;
|
||||
body.innerHTML =
|
||||
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
|
||||
|
||||
var url = '/api/v1/channels/comments/' + community_data.ucid +
|
||||
'?format=html' +
|
||||
'&hl=' + community_data.preferences.locale +
|
||||
'&thin_mode=' + community_data.preferences.thin_mode +
|
||||
'&continuation=' + continuation;
|
||||
|
||||
helpers.xhr('GET', url, {}, {
|
||||
on200: function (response) {
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.innerHTML += response.contentHtml;
|
||||
} else {
|
||||
body.removeChild(body.lastElementChild);
|
||||
|
||||
var p = document.createElement('p');
|
||||
var a = document.createElement('a');
|
||||
p.appendChild(a);
|
||||
|
||||
a.href = 'javascript:void(0)';
|
||||
a.onclick = hide_youtube_replies;
|
||||
a.setAttribute('data-sub-text', community_data.hide_replies_text);
|
||||
a.setAttribute('data-inner-text', community_data.show_replies_text);
|
||||
a.textContent = community_data.hide_replies_text;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = response.contentHtml;
|
||||
|
||||
body.appendChild(p);
|
||||
body.appendChild(div);
|
||||
}
|
||||
},
|
||||
onNon200: function (xhr) {
|
||||
body.innerHTML = fallback;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
console.warn('Pulling comments failed');
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
29
assets/js/dash.mediaplayer.min.js
vendored
29
assets/js/dash.mediaplayer.min.js
vendored
File diff suppressed because one or more lines are too long
64
assets/js/embed.js
Normal file
64
assets/js/embed.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
|
||||
function get_playlist(plid) {
|
||||
var plid_url;
|
||||
if (plid.startsWith('RD')) {
|
||||
plid_url = '/api/v1/mixes/' + plid +
|
||||
'?continuation=' + video_data.id +
|
||||
'&format=html&hl=' + video_data.preferences.locale;
|
||||
} else {
|
||||
plid_url = '/api/v1/playlists/' + plid +
|
||||
'?index=' + video_data.index +
|
||||
'&continuation' + video_data.id +
|
||||
'&format=html&hl=' + video_data.preferences.locale;
|
||||
}
|
||||
|
||||
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
|
||||
on200: function (response) {
|
||||
if (!response.nextVideo)
|
||||
return;
|
||||
|
||||
player.on('ended', function () {
|
||||
var url = new URL('https://example.com/embed/' + response.nextVideo);
|
||||
|
||||
url.searchParams.set('list', plid);
|
||||
if (!plid.startsWith('RD'))
|
||||
url.searchParams.set('index', response.index);
|
||||
if (video_data.params.autoplay || video_data.params.continue_autoplay)
|
||||
url.searchParams.set('autoplay', '1');
|
||||
if (video_data.params.listen !== video_data.preferences.listen)
|
||||
url.searchParams.set('listen', video_data.params.listen);
|
||||
if (video_data.params.speed !== video_data.preferences.speed)
|
||||
url.searchParams.set('speed', video_data.params.speed);
|
||||
if (video_data.params.local !== video_data.preferences.local)
|
||||
url.searchParams.set('local', video_data.params.local);
|
||||
|
||||
location.assign(url.pathname + url.search);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener('load', function (e) {
|
||||
if (video_data.plid) {
|
||||
get_playlist(video_data.plid);
|
||||
} else if (video_data.video_series) {
|
||||
player.on('ended', function () {
|
||||
var url = new URL('https://example.com/embed/' + video_data.video_series.shift());
|
||||
|
||||
if (video_data.params.autoplay || video_data.params.continue_autoplay)
|
||||
url.searchParams.set('autoplay', '1');
|
||||
if (video_data.params.listen !== video_data.preferences.listen)
|
||||
url.searchParams.set('listen', video_data.params.listen);
|
||||
if (video_data.params.speed !== video_data.preferences.speed)
|
||||
url.searchParams.set('speed', video_data.params.speed);
|
||||
if (video_data.params.local !== video_data.preferences.local)
|
||||
url.searchParams.set('local', video_data.params.local);
|
||||
if (video_data.video_series.length !== 0)
|
||||
url.searchParams.set('playlist', video_data.video_series.join(','));
|
||||
|
||||
location.assign(url.pathname + url.search);
|
||||
});
|
||||
}
|
||||
});
|
||||
149
assets/js/handlers.js
Normal file
149
assets/js/handlers.js
Normal file
@@ -0,0 +1,149 @@
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
var video_player = document.getElementById('player_html5_api');
|
||||
if (video_player) {
|
||||
video_player.onmouseenter = function () { video_player['data-title'] = video_player['title']; video_player['title'] = ''; };
|
||||
video_player.onmouseleave = function () { video_player['title'] = video_player['data-title']; video_player['data-title'] = ''; };
|
||||
video_player.oncontextmenu = function () { video_player['title'] = video_player['data-title']; };
|
||||
}
|
||||
|
||||
// For dynamically inserted elements
|
||||
addEventListener('click', function (e) {
|
||||
if (!e || !e.target) return;
|
||||
|
||||
var t = e.target;
|
||||
var handler_name = t.getAttribute('data-onclick');
|
||||
|
||||
switch (handler_name) {
|
||||
case 'jump_to_time':
|
||||
e.preventDefault();
|
||||
var time = t.getAttribute('data-jump-time');
|
||||
player.currentTime(time);
|
||||
break;
|
||||
case 'get_youtube_replies':
|
||||
var load_more = t.getAttribute('data-load-more') !== null;
|
||||
var load_replies = t.getAttribute('data-load-replies') !== null;
|
||||
get_youtube_replies(t, load_more, load_replies);
|
||||
break;
|
||||
case 'toggle_parent':
|
||||
e.preventDefault();
|
||||
toggle_parent(t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-mouse="switch_classes"]').forEach(function (el) {
|
||||
var classes = el.getAttribute('data-switch-classes').split(',');
|
||||
var classOnEnter = classes[0];
|
||||
var classOnLeave = classes[1];
|
||||
function toggle_classes(toAdd, toRemove) {
|
||||
el.classList.add(toAdd);
|
||||
el.classList.remove(toRemove);
|
||||
}
|
||||
el.onmouseenter = function () { toggle_classes(classOnEnter, classOnLeave); };
|
||||
el.onmouseleave = function () { toggle_classes(classOnLeave, classOnEnter); };
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-onsubmit="return_false"]').forEach(function (el) {
|
||||
el.onsubmit = function () { return false; };
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-onclick="mark_watched"]').forEach(function (el) {
|
||||
el.onclick = function () { mark_watched(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="mark_unwatched"]').forEach(function (el) {
|
||||
el.onclick = function () { mark_unwatched(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="add_playlist_video"]').forEach(function (el) {
|
||||
el.onclick = function () { add_playlist_video(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="add_playlist_item"]').forEach(function (el) {
|
||||
el.onclick = function () { add_playlist_item(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="remove_playlist_item"]').forEach(function (el) {
|
||||
el.onclick = function () { remove_playlist_item(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="revoke_token"]').forEach(function (el) {
|
||||
el.onclick = function () { revoke_token(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="remove_subscription"]').forEach(function (el) {
|
||||
el.onclick = function () { remove_subscription(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="notification_requestPermission"]').forEach(function (el) {
|
||||
el.onclick = function () { Notification.requestPermission(); };
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-onrange="update_volume_value"]').forEach(function (el) {
|
||||
function update_volume_value() {
|
||||
document.getElementById('volume-value').textContent = el.value;
|
||||
}
|
||||
el.oninput = update_volume_value;
|
||||
el.onchange = update_volume_value;
|
||||
});
|
||||
|
||||
|
||||
function revoke_token(target) {
|
||||
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
row.style.display = 'none';
|
||||
var count = document.getElementById('count');
|
||||
count.textContent--;
|
||||
|
||||
var url = '/token_ajax?action_revoke_token=1&redirect=false' +
|
||||
'&referer=' + encodeURIComponent(location.href) +
|
||||
'&session=' + target.getAttribute('data-session');
|
||||
|
||||
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
count.textContent++;
|
||||
row.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove_subscription(target) {
|
||||
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
row.style.display = 'none';
|
||||
var count = document.getElementById('count');
|
||||
count.textContent--;
|
||||
|
||||
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
|
||||
'&referer=' + encodeURIComponent(location.href) +
|
||||
'&c=' + target.getAttribute('data-ucid');
|
||||
|
||||
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
count.textContent++;
|
||||
row.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle keypresses
|
||||
addEventListener('keydown', function (event) {
|
||||
// Ignore modifier keys
|
||||
if (event.ctrlKey || event.metaKey) return;
|
||||
|
||||
// Ignore shortcuts if any text input is focused
|
||||
let focused_tag = document.activeElement.tagName.toLowerCase();
|
||||
const allowed = /^(button|checkbox|file|radio|submit)$/;
|
||||
|
||||
if (focused_tag === 'textarea') return;
|
||||
if (focused_tag === 'input') {
|
||||
let focused_type = document.activeElement.type.toLowerCase();
|
||||
if (!focused_type.match(allowed)) return;
|
||||
}
|
||||
|
||||
// Focus search bar on '/'
|
||||
if (event.key === '/') {
|
||||
document.getElementById('searchbox').focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
})();
|
||||
131
assets/js/notifications.js
Normal file
131
assets/js/notifications.js
Normal file
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
|
||||
|
||||
/** Boolean meaning 'some tab have stream' */
|
||||
const STORAGE_KEY_STREAM = 'stream';
|
||||
/** Number of notifications. May be increased or reset */
|
||||
const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
|
||||
|
||||
var notifications, delivered;
|
||||
var notifications_mock = { close: function () { } };
|
||||
|
||||
function get_subscriptions() {
|
||||
helpers.xhr('GET', '/api/v1/auth/subscriptions?fields=authorId', {
|
||||
retries: 5,
|
||||
entity_name: 'subscriptions'
|
||||
}, {
|
||||
on200: create_notification_stream
|
||||
});
|
||||
}
|
||||
|
||||
function create_notification_stream(subscriptions) {
|
||||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE(
|
||||
'/api/v1/auth/notifications?fields=videoId,title,author,authorId,publishedText,published,authorThumbnails,liveNow', {
|
||||
withCredentials: true,
|
||||
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
delivered = [];
|
||||
|
||||
var start_time = Math.round(new Date() / 1000);
|
||||
|
||||
notifications.onmessage = function (event) {
|
||||
if (!event.id) return;
|
||||
|
||||
var notification = JSON.parse(event.data);
|
||||
console.info('Got notification:', notification);
|
||||
|
||||
// Ignore not actual and delivered notifications
|
||||
if (start_time > notification.published || delivered.includes(notification.videoId)) return;
|
||||
|
||||
delivered.push(notification.videoId);
|
||||
|
||||
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
|
||||
notification_count++;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
update_ticker_count();
|
||||
|
||||
// permission for notifications handled on settings page. JS handler is in handlers.js
|
||||
if (window.Notification && Notification.permission === 'granted') {
|
||||
var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
|
||||
notification_text = notification_text.replace('`x`', notification.author);
|
||||
|
||||
var system_notification = new Notification(notification_text, {
|
||||
body: notification.title,
|
||||
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
|
||||
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
|
||||
});
|
||||
|
||||
system_notification.onclick = function (e) {
|
||||
open('/watch?v=' + notification.videoId, '_blank');
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
notifications.addEventListener('error', function (e) {
|
||||
console.warn('Something went wrong with notifications, trying to reconnect...');
|
||||
notifications = notifications_mock;
|
||||
setTimeout(get_subscriptions, 1000);
|
||||
});
|
||||
|
||||
notifications.stream();
|
||||
}
|
||||
|
||||
function update_ticker_count() {
|
||||
var notification_ticker = document.getElementById('notification_ticker');
|
||||
|
||||
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
|
||||
if (notification_count > 0) {
|
||||
notification_ticker.innerHTML =
|
||||
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
|
||||
} else {
|
||||
notification_ticker.innerHTML =
|
||||
'<i class="icon ion-ios-notifications-outline"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
function start_stream_if_needed() {
|
||||
// random wait for other tabs set 'stream' flag
|
||||
setTimeout(function () {
|
||||
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
// if no one set 'stream', set it by yourself and start stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
notifications = notifications_mock;
|
||||
get_subscriptions();
|
||||
}
|
||||
}, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
|
||||
}
|
||||
|
||||
|
||||
addEventListener('storage', function (e) {
|
||||
if (e.key === STORAGE_KEY_NOTIF_COUNT)
|
||||
update_ticker_count();
|
||||
|
||||
// if 'stream' key was removed
|
||||
if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
if (notifications) {
|
||||
// restore it if we have active stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
} else {
|
||||
start_stream_if_needed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener('load', function () {
|
||||
var notification_count_el = document.getElementById('notification_count');
|
||||
var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
if (helpers.storage.get(STORAGE_KEY_STREAM))
|
||||
helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
start_stream_if_needed();
|
||||
});
|
||||
|
||||
addEventListener('unload', function () {
|
||||
// let chance to other tabs to be a streamer via firing 'storage' event
|
||||
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
});
|
||||
727
assets/js/player.js
Normal file
727
assets/js/player.js
Normal file
@@ -0,0 +1,727 @@
|
||||
'use strict';
|
||||
var player_data = JSON.parse(document.getElementById('player_data').textContent);
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
|
||||
var options = {
|
||||
preload: 'auto',
|
||||
liveui: true,
|
||||
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
|
||||
controlBar: {
|
||||
children: [
|
||||
'playToggle',
|
||||
'volumePanel',
|
||||
'currentTimeDisplay',
|
||||
'timeDivider',
|
||||
'durationDisplay',
|
||||
'progressControl',
|
||||
'remainingTimeDisplay',
|
||||
'Spacer',
|
||||
'captionsButton',
|
||||
'audioTrackButton',
|
||||
'qualitySelector',
|
||||
'playbackRateMenuButton',
|
||||
'fullscreenToggle'
|
||||
]
|
||||
},
|
||||
html5: {
|
||||
preloadTextTracks: false,
|
||||
vhs: {
|
||||
overrideNative: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (player_data.aspect_ratio) {
|
||||
options.aspectRatio = player_data.aspect_ratio;
|
||||
}
|
||||
|
||||
var embed_url = new URL(location);
|
||||
embed_url.searchParams.delete('v');
|
||||
var short_url = location.origin + '/' + video_data.id + embed_url.search;
|
||||
embed_url = location.origin + '/embed/' + video_data.id + embed_url.search;
|
||||
|
||||
var save_player_pos_key = 'save_player_pos';
|
||||
|
||||
videojs.Vhs.xhr.beforeRequest = function(options) {
|
||||
// set local if requested not videoplayback
|
||||
if (!options.uri.includes('videoplayback')) {
|
||||
if (!options.uri.includes('local=true'))
|
||||
options.uri += '?local=true';
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
var player = videojs('player', options);
|
||||
|
||||
player.on('error', function () {
|
||||
if (video_data.params.quality === 'dash') return;
|
||||
|
||||
var localNotDisabled = (
|
||||
!player.currentSrc().includes('local=true') && !video_data.local_disabled
|
||||
);
|
||||
var reloadMakesSense = (
|
||||
player.error().code === MediaError.MEDIA_ERR_NETWORK ||
|
||||
player.error().code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
|
||||
);
|
||||
|
||||
if (localNotDisabled) {
|
||||
// add local=true to all current sources
|
||||
player.src(player.currentSources().map(function (source) {
|
||||
source.src += '&local=true';
|
||||
return source;
|
||||
}));
|
||||
} else if (reloadMakesSense) {
|
||||
setTimeout(function () {
|
||||
console.warn('An error occurred in the player, reloading...');
|
||||
|
||||
// After load() all parameters are reset. Save them
|
||||
var currentTime = player.currentTime();
|
||||
var playbackRate = player.playbackRate();
|
||||
var paused = player.paused();
|
||||
|
||||
player.load();
|
||||
|
||||
if (currentTime > 0.5) currentTime -= 0.5;
|
||||
|
||||
player.currentTime(currentTime);
|
||||
player.playbackRate(playbackRate);
|
||||
if (!paused) player.play();
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
if (video_data.params.quality === 'dash') {
|
||||
player.reloadSourceOnError({
|
||||
errorInterval: 10
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for add time argument to url
|
||||
* @param {String} url
|
||||
* @returns {URL} urlWithTimeArg
|
||||
*/
|
||||
function addCurrentTimeToURL(url) {
|
||||
var urlUsed = new URL(url);
|
||||
urlUsed.searchParams.delete('start');
|
||||
var currentTime = Math.ceil(player.currentTime());
|
||||
if (currentTime > 0)
|
||||
urlUsed.searchParams.set('t', currentTime);
|
||||
else if (urlUsed.searchParams.has('t'))
|
||||
urlUsed.searchParams.delete('t');
|
||||
return urlUsed;
|
||||
}
|
||||
|
||||
var shareOptions = {
|
||||
socials: ['fbFeed', 'tw', 'reddit', 'email'],
|
||||
|
||||
get url() {
|
||||
return addCurrentTimeToURL(short_url);
|
||||
},
|
||||
title: player_data.title,
|
||||
description: player_data.description,
|
||||
image: player_data.thumbnail,
|
||||
get embedCode() {
|
||||
// Single quotes inside here required. HTML inserted as is into value attribute of input
|
||||
return "<iframe id='ivplayer' width='640' height='360' src='" +
|
||||
addCurrentTimeToURL(embed_url) + "' style='border:none;'></iframe>";
|
||||
}
|
||||
};
|
||||
|
||||
if (location.pathname.startsWith('/embed/')) {
|
||||
var overlay_content = '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>';
|
||||
player.overlay({
|
||||
overlays: [
|
||||
{ start: 'loadstart', content: overlay_content, end: 'playing', align: 'top'},
|
||||
{ start: 'pause', content: overlay_content, end: 'playing', align: 'top'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Detect mobile users and initialize mobileUi for better UX
|
||||
// Detection code taken from https://stackoverflow.com/a/20293441
|
||||
|
||||
function isMobile() {
|
||||
try{ document.createEvent('TouchEvent'); return true; }
|
||||
catch(e){ return false; }
|
||||
}
|
||||
|
||||
if (isMobile()) {
|
||||
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
|
||||
|
||||
var buttons = ['playToggle', 'volumePanel', 'captionsButton'];
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') buttons.push('audioTrackButton');
|
||||
if (video_data.params.listen || video_data.params.quality !== 'dash') buttons.push('qualitySelector');
|
||||
|
||||
// Create new control bar object for operation buttons
|
||||
const ControlBar = videojs.getComponent('controlBar');
|
||||
let operations_bar = new ControlBar(player, {
|
||||
children: [],
|
||||
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
||||
});
|
||||
buttons.slice(1).forEach(function (child) {operations_bar.addChild(child);});
|
||||
|
||||
// Remove operation buttons from primary control bar
|
||||
var primary_control_bar = player.getChild('controlBar');
|
||||
buttons.forEach(function (child) {primary_control_bar.removeChild(child);});
|
||||
|
||||
var operations_bar_element = operations_bar.el();
|
||||
operations_bar_element.classList.add('mobile-operations-bar');
|
||||
player.addChild(operations_bar);
|
||||
|
||||
// Playback menu doesn't work when it's initialized outside of the primary control bar
|
||||
var playback_element = document.getElementsByClassName('vjs-playback-rate')[0];
|
||||
operations_bar_element.append(playback_element);
|
||||
|
||||
// The share and http source selector element can't be fetched till the players ready.
|
||||
player.one('playing', function () {
|
||||
var share_element = document.getElementsByClassName('vjs-share-control')[0];
|
||||
operations_bar_element.append(share_element);
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') {
|
||||
var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0];
|
||||
operations_bar_element.append(http_source_selector);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Enable VR video support
|
||||
if (!video_data.params.listen && video_data.vr && video_data.params.vr_mode) {
|
||||
player.crossOrigin('anonymous');
|
||||
switch (video_data.projection_type) {
|
||||
case 'EQUIRECTANGULAR':
|
||||
player.vr({projection: 'equirectangular'});
|
||||
default: // Should only be 'MESH' but we'll use this as a fallback.
|
||||
player.vr({projection: 'EAC'});
|
||||
}
|
||||
}
|
||||
|
||||
// Add markers
|
||||
if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
|
||||
var markers = [{ time: video_data.params.video_start, text: 'Start' }];
|
||||
|
||||
if (video_data.params.video_end < 0) {
|
||||
markers.push({ time: video_data.length_seconds - 0.5, text: 'End' });
|
||||
} else {
|
||||
markers.push({ time: video_data.params.video_end, text: 'End' });
|
||||
}
|
||||
|
||||
player.markers({
|
||||
onMarkerReached: function (marker) {
|
||||
if (marker.text === 'End')
|
||||
player.loop() ? player.markers.prev('Start') : player.pause();
|
||||
},
|
||||
markers: markers
|
||||
});
|
||||
|
||||
player.currentTime(video_data.params.video_start);
|
||||
}
|
||||
|
||||
player.volume(video_data.params.volume / 100);
|
||||
player.playbackRate(video_data.params.speed);
|
||||
|
||||
/**
|
||||
* Method for getting the contents of a cookie
|
||||
*
|
||||
* @param {String} name Name of cookie
|
||||
* @returns {String|null} cookieValue
|
||||
*/
|
||||
function getCookieValue(name) {
|
||||
var cookiePrefix = name + '=';
|
||||
var matchedCookie = document.cookie.split(';').find(function (item) {return item.includes(cookiePrefix);});
|
||||
if (matchedCookie)
|
||||
return matchedCookie.replace(cookiePrefix, '');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for updating the 'PREFS' cookie (or creating it if missing)
|
||||
*
|
||||
* @param {number} newVolume New volume defined (null if unchanged)
|
||||
* @param {number} newSpeed New speed defined (null if unchanged)
|
||||
*/
|
||||
function updateCookie(newVolume, newSpeed) {
|
||||
var volumeValue = newVolume !== null ? newVolume : video_data.params.volume;
|
||||
var speedValue = newSpeed !== null ? newSpeed : video_data.params.speed;
|
||||
|
||||
var cookieValue = getCookieValue('PREFS');
|
||||
var cookieData;
|
||||
|
||||
if (cookieValue !== null) {
|
||||
var cookieJson = JSON.parse(decodeURIComponent(cookieValue));
|
||||
cookieJson.volume = volumeValue;
|
||||
cookieJson.speed = speedValue;
|
||||
cookieData = encodeURIComponent(JSON.stringify(cookieJson));
|
||||
} else {
|
||||
cookieData = encodeURIComponent(JSON.stringify({ 'volume': volumeValue, 'speed': speedValue }));
|
||||
}
|
||||
|
||||
// Set expiration in 2 year
|
||||
var date = new Date();
|
||||
date.setFullYear(date.getFullYear() + 2);
|
||||
|
||||
var ipRegex = /^((\d+\.){3}\d+|[A-Fa-f0-9]*:[A-Fa-f0-9:]*:[A-Fa-f0-9:]+)$/;
|
||||
var domainUsed = location.hostname;
|
||||
|
||||
// Fix for a bug in FF where the leading dot in the FQDN is not ignored
|
||||
if (domainUsed.charAt(0) !== '.' && !ipRegex.test(domainUsed) && domainUsed !== 'localhost')
|
||||
domainUsed = '.' + location.hostname;
|
||||
|
||||
var secure = location.protocol.startsWith("https") ? " Secure;" : "";
|
||||
|
||||
document.cookie = 'PREFS=' + cookieData + '; SameSite=Lax; path=/; domain=' +
|
||||
domainUsed + '; expires=' + date.toGMTString() + ';' + secure;
|
||||
|
||||
video_data.params.volume = volumeValue;
|
||||
video_data.params.speed = speedValue;
|
||||
}
|
||||
|
||||
player.on('ratechange', function () {
|
||||
updateCookie(null, player.playbackRate());
|
||||
if (isMobile()) {
|
||||
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
|
||||
}
|
||||
});
|
||||
|
||||
player.on('volumechange', function () {
|
||||
updateCookie(Math.ceil(player.volume() * 100), null);
|
||||
});
|
||||
|
||||
player.on('waiting', function () {
|
||||
if (player.playbackRate() > 1 && player.liveTracker.isLive() && player.liveTracker.atLiveEdge()) {
|
||||
console.info('Player has caught up to source, resetting playbackRate');
|
||||
player.playbackRate(1);
|
||||
}
|
||||
});
|
||||
|
||||
if (video_data.premiere_timestamp && Math.round(new Date() / 1000) < video_data.premiere_timestamp) {
|
||||
player.getChild('bigPlayButton').hide();
|
||||
}
|
||||
|
||||
if (video_data.params.save_player_pos) {
|
||||
const url = new URL(location);
|
||||
const hasTimeParam = url.searchParams.has('t');
|
||||
const rememberedTime = get_video_time();
|
||||
let lastUpdated = 0;
|
||||
|
||||
if(!hasTimeParam) set_seconds_after_start(rememberedTime);
|
||||
|
||||
player.on('timeupdate', function () {
|
||||
const raw = player.currentTime();
|
||||
const time = Math.floor(raw);
|
||||
|
||||
if(lastUpdated !== time && raw <= video_data.length_seconds - 15) {
|
||||
save_video_time(time);
|
||||
lastUpdated = time;
|
||||
}
|
||||
});
|
||||
}
|
||||
else remove_all_video_times();
|
||||
|
||||
if (video_data.params.autoplay) {
|
||||
var bpb = player.getChild('bigPlayButton');
|
||||
bpb.hide();
|
||||
|
||||
player.ready(function () {
|
||||
new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {resolve(1);}, 1);
|
||||
}).then(function (result) {
|
||||
var promise = player.play();
|
||||
|
||||
if (promise !== undefined) {
|
||||
promise.then(function () {
|
||||
}).catch(function (error) {
|
||||
bpb.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') {
|
||||
player.httpSourceSelector();
|
||||
|
||||
if (video_data.params.quality_dash !== 'auto') {
|
||||
player.ready(function () {
|
||||
player.on('loadedmetadata', function () {
|
||||
const qualityLevels = Array.from(player.qualityLevels()).sort(function (a, b) {return a.height - b.height;});
|
||||
let targetQualityLevel;
|
||||
switch (video_data.params.quality_dash) {
|
||||
case 'best':
|
||||
targetQualityLevel = qualityLevels.length - 1;
|
||||
break;
|
||||
case 'worst':
|
||||
targetQualityLevel = 0;
|
||||
break;
|
||||
default:
|
||||
const targetHeight = parseInt(video_data.params.quality_dash);
|
||||
for (let i = 0; i < qualityLevels.length; i++) {
|
||||
if (qualityLevels[i].height <= targetHeight)
|
||||
targetQualityLevel = i;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
qualityLevels.forEach(function (level, index) {
|
||||
level.enabled = (index === targetQualityLevel);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
player.vttThumbnails({
|
||||
src: '/api/v1/storyboards/' + video_data.id + '?height=90',
|
||||
showTimestamp: true
|
||||
});
|
||||
|
||||
// Enable annotations
|
||||
if (!video_data.params.listen && video_data.params.annotations) {
|
||||
addEventListener('load', function (e) {
|
||||
addEventListener('__ar_annotation_click', function (e) {
|
||||
const url = e.detail.url,
|
||||
target = e.detail.target,
|
||||
seconds = e.detail.seconds;
|
||||
var path = new URL(url);
|
||||
|
||||
if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) {
|
||||
path.search += '&t=' + seconds;
|
||||
}
|
||||
|
||||
path = path.pathname + path.search;
|
||||
|
||||
if (target === 'current') {
|
||||
location.href = path;
|
||||
} else if (target === 'new') {
|
||||
open(path, '_blank');
|
||||
}
|
||||
});
|
||||
|
||||
helpers.xhr('GET', '/api/v1/annotations/' + video_data.id, {
|
||||
responseType: 'text',
|
||||
timeout: 60000
|
||||
}, {
|
||||
on200: function (response) {
|
||||
var video_container = document.getElementById('player');
|
||||
videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
|
||||
if (player.paused()) {
|
||||
player.one('play', function (event) {
|
||||
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
|
||||
});
|
||||
} else {
|
||||
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function change_volume(delta) {
|
||||
const curVolume = player.volume();
|
||||
let newVolume = curVolume + delta;
|
||||
newVolume = helpers.clamp(newVolume, 0, 1);
|
||||
player.volume(newVolume);
|
||||
}
|
||||
|
||||
function toggle_muted() {
|
||||
player.muted(!player.muted());
|
||||
}
|
||||
|
||||
function skip_seconds(delta) {
|
||||
const duration = player.duration();
|
||||
const curTime = player.currentTime();
|
||||
let newTime = curTime + delta;
|
||||
newTime = helpers.clamp(newTime, 0, duration);
|
||||
player.currentTime(newTime);
|
||||
}
|
||||
|
||||
function set_seconds_after_start(delta) {
|
||||
const start = video_data.params.video_start;
|
||||
player.currentTime(start + delta);
|
||||
}
|
||||
|
||||
function save_video_time(seconds) {
|
||||
const all_video_times = get_all_video_times();
|
||||
all_video_times[video_data.id] = seconds;
|
||||
helpers.storage.set(save_player_pos_key, all_video_times);
|
||||
}
|
||||
|
||||
function get_video_time() {
|
||||
return get_all_video_times()[video_data.id] || 0;
|
||||
}
|
||||
|
||||
function get_all_video_times() {
|
||||
return helpers.storage.get(save_player_pos_key) || {};
|
||||
}
|
||||
|
||||
function remove_all_video_times() {
|
||||
helpers.storage.remove(save_player_pos_key);
|
||||
}
|
||||
|
||||
function set_time_percent(percent) {
|
||||
const duration = player.duration();
|
||||
const newTime = duration * (percent / 100);
|
||||
player.currentTime(newTime);
|
||||
}
|
||||
|
||||
function play() { player.play(); }
|
||||
function pause() { player.pause(); }
|
||||
function stop() { player.pause(); player.currentTime(0); }
|
||||
function toggle_play() { player.paused() ? play() : pause(); }
|
||||
|
||||
const toggle_captions = (function () {
|
||||
let toggledTrack = null;
|
||||
|
||||
function bindChange(onOrOff) {
|
||||
player.textTracks()[onOrOff]('change', function (e) {
|
||||
toggledTrack = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Wrapper function to ignore our own emitted events and only listen
|
||||
// to events emitted by Video.js on click on the captions menu items.
|
||||
function setMode(track, mode) {
|
||||
bindChange('off');
|
||||
track.mode = mode;
|
||||
setTimeout(function () {
|
||||
bindChange('on');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
bindChange('on');
|
||||
return function () {
|
||||
if (toggledTrack !== null) {
|
||||
if (toggledTrack.mode !== 'showing') {
|
||||
setMode(toggledTrack, 'showing');
|
||||
} else {
|
||||
setMode(toggledTrack, 'disabled');
|
||||
}
|
||||
toggledTrack = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Used as a fallback if no captions are currently active.
|
||||
// TODO: Make this more intelligent by e.g. relying on browser language.
|
||||
let fallbackCaptionsTrack = null;
|
||||
|
||||
const tracks = player.textTracks();
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
if (track.kind !== 'captions') continue;
|
||||
|
||||
if (fallbackCaptionsTrack === null) {
|
||||
fallbackCaptionsTrack = track;
|
||||
}
|
||||
if (track.mode === 'showing') {
|
||||
setMode(track, 'disabled');
|
||||
toggledTrack = track;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if no captions are currently active.
|
||||
if (fallbackCaptionsTrack !== null) {
|
||||
setMode(fallbackCaptionsTrack, 'showing');
|
||||
toggledTrack = fallbackCaptionsTrack;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
function toggle_fullscreen() {
|
||||
player.isFullscreen() ? player.exitFullscreen() : player.requestFullscreen();
|
||||
}
|
||||
|
||||
function increase_playback_rate(steps) {
|
||||
const maxIndex = options.playbackRates.length - 1;
|
||||
const curIndex = options.playbackRates.indexOf(player.playbackRate());
|
||||
let newIndex = curIndex + steps;
|
||||
newIndex = helpers.clamp(newIndex, 0, maxIndex);
|
||||
player.playbackRate(options.playbackRates[newIndex]);
|
||||
}
|
||||
|
||||
addEventListener('keydown', function (e) {
|
||||
if (e.target.tagName.toLowerCase() === 'input') {
|
||||
// Ignore input when focus is on certain elements, e.g. form fields.
|
||||
return;
|
||||
}
|
||||
// See https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L310-L313
|
||||
const isPlayerFocused = false
|
||||
|| e.target === document.querySelector('.video-js')
|
||||
|| e.target === document.querySelector('.vjs-tech')
|
||||
|| e.target === document.querySelector('.iframeblocker')
|
||||
|| e.target === document.querySelector('.vjs-control-bar')
|
||||
;
|
||||
let action = null;
|
||||
|
||||
const code = e.keyCode;
|
||||
const decoratedKey =
|
||||
e.key
|
||||
+ (e.altKey ? '+alt' : '')
|
||||
+ (e.ctrlKey ? '+ctrl' : '')
|
||||
+ (e.metaKey ? '+meta' : '')
|
||||
;
|
||||
switch (decoratedKey) {
|
||||
case ' ':
|
||||
case 'k':
|
||||
case 'MediaPlayPause':
|
||||
action = toggle_play;
|
||||
break;
|
||||
|
||||
case 'MediaPlay': action = play; break;
|
||||
case 'MediaPause': action = pause; break;
|
||||
case 'MediaStop': action = stop; break;
|
||||
|
||||
case 'ArrowUp':
|
||||
if (isPlayerFocused) action = change_volume.bind(this, 0.1);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
if (isPlayerFocused) action = change_volume.bind(this, -0.1);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
action = toggle_muted;
|
||||
break;
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'MediaFastForward':
|
||||
action = skip_seconds.bind(this, 5 * player.playbackRate());
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
case 'MediaTrackPrevious':
|
||||
action = skip_seconds.bind(this, -5 * player.playbackRate());
|
||||
break;
|
||||
case 'l':
|
||||
action = skip_seconds.bind(this, 10 * player.playbackRate());
|
||||
break;
|
||||
case 'j':
|
||||
action = skip_seconds.bind(this, -10 * player.playbackRate());
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
// Ignore numpad numbers
|
||||
if (code > 57) break;
|
||||
|
||||
const percent = (code - 48) * 10;
|
||||
action = set_time_percent.bind(this, percent);
|
||||
break;
|
||||
|
||||
case 'c': action = toggle_captions; break;
|
||||
case 'f': action = toggle_fullscreen; break;
|
||||
|
||||
case 'N':
|
||||
case 'MediaTrackNext':
|
||||
action = next_video;
|
||||
break;
|
||||
case 'P':
|
||||
case 'MediaTrackPrevious':
|
||||
// TODO: Add support to play back previous video.
|
||||
break;
|
||||
|
||||
// TODO: More precise step. Now FPS is taken equal to 29.97
|
||||
// Common FPS: https://forum.videohelp.com/threads/81868#post323588
|
||||
// Possible solution is new HTMLVideoElement.requestVideoFrameCallback() https://wicg.github.io/video-rvfc/
|
||||
case ',': action = function () { pause(); skip_seconds(-1/29.97); }; break;
|
||||
case '.': action = function () { pause(); skip_seconds( 1/29.97); }; break;
|
||||
|
||||
case '>': action = increase_playback_rate.bind(this, 1); break;
|
||||
case '<': action = increase_playback_rate.bind(this, -1); break;
|
||||
|
||||
default:
|
||||
console.info('Unhandled key down event: %s:', decoratedKey, e);
|
||||
break;
|
||||
}
|
||||
|
||||
if (action) {
|
||||
e.preventDefault();
|
||||
action();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Add support for controlling the player volume by scrolling over it. Adapted from
|
||||
// https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L292-L328
|
||||
(function () {
|
||||
const pEl = document.getElementById('player');
|
||||
|
||||
var volumeHover = false;
|
||||
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
|
||||
if (volumeSelector !== null) {
|
||||
volumeSelector.onmouseover = function () { volumeHover = true; };
|
||||
volumeSelector.onmouseout = function () { volumeHover = false; };
|
||||
}
|
||||
|
||||
function mouseScroll(event) {
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (!player.controls() || !volumeHover) return;
|
||||
|
||||
event.preventDefault();
|
||||
var wheelMove = event.wheelDelta || -event.detail;
|
||||
var volumeSign = Math.sign(wheelMove);
|
||||
|
||||
change_volume(volumeSign * 0.05); // decrease/increase by 5%
|
||||
}
|
||||
|
||||
player.on('mousewheel', mouseScroll);
|
||||
player.on('DOMMouseScroll', mouseScroll);
|
||||
}());
|
||||
|
||||
// Since videojs-share can sometimes be blocked, we defer it until last
|
||||
if (player.share) player.share(shareOptions);
|
||||
|
||||
// show the preferred caption by default
|
||||
if (player_data.preferred_caption_found) {
|
||||
player.ready(function () {
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') {
|
||||
// play.textTracks()[0] on DASH mode is showing some debug messages
|
||||
player.textTracks()[1].mode = 'showing';
|
||||
} else {
|
||||
player.textTracks()[0].mode = 'showing';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Safari audio double duration fix
|
||||
if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
|
||||
player.on('loadedmetadata', function () {
|
||||
player.on('timeupdate', function () {
|
||||
if (player.remainingTime() < player.duration() / 2 && player.remainingTime() >= 2) {
|
||||
player.currentTime(player.duration() - 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Watch on Invidious link
|
||||
if (location.pathname.startsWith('/embed/')) {
|
||||
const Button = videojs.getComponent('Button');
|
||||
let watch_on_invidious_button = new Button(player);
|
||||
|
||||
// Create hyperlink for current instance
|
||||
var redirect_element = document.createElement('a');
|
||||
redirect_element.setAttribute('href', location.pathname.replace('/embed/', '/watch?v='));
|
||||
redirect_element.appendChild(document.createTextNode('Invidious'));
|
||||
|
||||
watch_on_invidious_button.el().appendChild(redirect_element);
|
||||
watch_on_invidious_button.addClass('watch-on-invidious');
|
||||
|
||||
var cb = player.getChild('ControlBar');
|
||||
cb.addChild(watch_on_invidious_button);
|
||||
}
|
||||
|
||||
addEventListener('DOMContentLoaded', function () {
|
||||
// Save time during redirection on another instance
|
||||
const changeInstanceLink = document.querySelector('#watch-on-another-invidious-instance > a');
|
||||
if (changeInstanceLink) changeInstanceLink.addEventListener('click', function () {
|
||||
changeInstanceLink.href = addCurrentTimeToURL(changeInstanceLink.href);
|
||||
});
|
||||
});
|
||||
48
assets/js/playlist_widget.js
Normal file
48
assets/js/playlist_widget.js
Normal file
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
var playlist_data = JSON.parse(document.getElementById('playlist_data').textContent);
|
||||
var payload = 'csrf_token=' + playlist_data.csrf_token;
|
||||
|
||||
function add_playlist_video(target) {
|
||||
var select = target.parentNode.children[0].children[1];
|
||||
var option = select.children[select.selectedIndex];
|
||||
|
||||
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
|
||||
'&video_id=' + target.getAttribute('data-id') +
|
||||
'&playlist_id=' + option.getAttribute('data-plid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
on200: function (response) {
|
||||
option.textContent = '✓' + option.textContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add_playlist_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
|
||||
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
|
||||
'&video_id=' + target.getAttribute('data-id') +
|
||||
'&playlist_id=' + target.getAttribute('data-plid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove_playlist_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
|
||||
var url = '/playlist_ajax?action_remove_video=1&redirect=false' +
|
||||
'&set_video_id=' + target.getAttribute('data-index') +
|
||||
'&playlist_id=' + target.getAttribute('data-plid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
200
assets/js/sse.js
Normal file
200
assets/js/sse.js
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Copyright (C) 2016 Maxime Petazzoni <maxime.petazzoni@bulix.org>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
var SSE = function (url, options) {
|
||||
if (!(this instanceof SSE)) {
|
||||
return new SSE(url, options);
|
||||
}
|
||||
|
||||
this.INITIALIZING = -1;
|
||||
this.CONNECTING = 0;
|
||||
this.OPEN = 1;
|
||||
this.CLOSED = 2;
|
||||
|
||||
this.url = url;
|
||||
|
||||
options = options || {};
|
||||
this.headers = options.headers || {};
|
||||
this.payload = options.payload !== undefined ? options.payload : '';
|
||||
this.method = options.method || (this.payload && 'POST' || 'GET');
|
||||
|
||||
this.FIELD_SEPARATOR = ':';
|
||||
this.listeners = {};
|
||||
|
||||
this.xhr = null;
|
||||
this.readyState = this.INITIALIZING;
|
||||
this.progress = 0;
|
||||
this.chunk = '';
|
||||
|
||||
this.addEventListener = function(type, listener) {
|
||||
if (this.listeners[type] === undefined) {
|
||||
this.listeners[type] = [];
|
||||
}
|
||||
|
||||
if (this.listeners[type].indexOf(listener) === -1) {
|
||||
this.listeners[type].push(listener);
|
||||
}
|
||||
};
|
||||
|
||||
this.removeEventListener = function(type, listener) {
|
||||
if (this.listeners[type] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var filtered = [];
|
||||
this.listeners[type].forEach(function(element) {
|
||||
if (element !== listener) {
|
||||
filtered.push(element);
|
||||
}
|
||||
});
|
||||
if (filtered.length === 0) {
|
||||
delete this.listeners[type];
|
||||
} else {
|
||||
this.listeners[type] = filtered;
|
||||
}
|
||||
};
|
||||
|
||||
this.dispatchEvent = function(e) {
|
||||
if (!e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
e.source = this;
|
||||
|
||||
var onHandler = 'on' + e.type;
|
||||
if (this.hasOwnProperty(onHandler)) {
|
||||
this[onHandler].call(this, e);
|
||||
if (e.defaultPrevented) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.listeners[e.type]) {
|
||||
return this.listeners[e.type].every(function(callback) {
|
||||
callback(e);
|
||||
return !e.defaultPrevented;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this._setReadyState = function (state) {
|
||||
var event = new CustomEvent('readystatechange');
|
||||
event.readyState = state;
|
||||
this.readyState = state;
|
||||
this.dispatchEvent(event);
|
||||
};
|
||||
|
||||
this._onStreamFailure = function(e) {
|
||||
this.dispatchEvent(new CustomEvent('error'));
|
||||
this.close();
|
||||
}
|
||||
|
||||
this._onStreamProgress = function(e) {
|
||||
if (this.xhr.status !== 200 && this.readyState !== this.CLOSED) {
|
||||
this._onStreamFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.readyState == this.CONNECTING) {
|
||||
this.dispatchEvent(new CustomEvent('open'));
|
||||
this._setReadyState(this.OPEN);
|
||||
}
|
||||
|
||||
var data = this.xhr.responseText.substring(this.progress);
|
||||
this.progress += data.length;
|
||||
data.split(/(\r\n|\r|\n){2}/g).forEach(function(part) {
|
||||
if (part.trim().length === 0) {
|
||||
this.dispatchEvent(this._parseEventChunk(this.chunk.trim()));
|
||||
this.chunk = '';
|
||||
} else {
|
||||
this.chunk += part;
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
this._onStreamLoaded = function(e) {
|
||||
this._onStreamProgress(e);
|
||||
|
||||
// Parse the last chunk.
|
||||
this.dispatchEvent(this._parseEventChunk(this.chunk));
|
||||
this.chunk = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a received SSE event chunk into a constructed event object.
|
||||
*/
|
||||
this._parseEventChunk = function(chunk) {
|
||||
if (!chunk || chunk.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var e = {'id': null, 'retry': null, 'data': '', 'event': 'message'};
|
||||
chunk.split(/\n|\r\n|\r/).forEach(function(line) {
|
||||
line = line.trimRight();
|
||||
var index = line.indexOf(this.FIELD_SEPARATOR);
|
||||
if (index <= 0) {
|
||||
// Line was either empty, or started with a separator and is a comment.
|
||||
// Either way, ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
var field = line.substring(0, index);
|
||||
if (!(field in e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var value = line.substring(index + 1).trimLeft();
|
||||
if (field === 'data') {
|
||||
e[field] += value;
|
||||
} else {
|
||||
e[field] = value;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
var event = new CustomEvent(e.event);
|
||||
event.data = e.data;
|
||||
event.id = e.id;
|
||||
return event;
|
||||
};
|
||||
|
||||
this._checkStreamClosed = function() {
|
||||
if (this.xhr.readyState === XMLHttpRequest.DONE) {
|
||||
this._setReadyState(this.CLOSED);
|
||||
}
|
||||
};
|
||||
|
||||
this.stream = function() {
|
||||
this._setReadyState(this.CONNECTING);
|
||||
|
||||
this.xhr = new XMLHttpRequest();
|
||||
this.xhr.addEventListener('progress', this._onStreamProgress.bind(this));
|
||||
this.xhr.addEventListener('load', this._onStreamLoaded.bind(this));
|
||||
this.xhr.addEventListener('readystatechange', this._checkStreamClosed.bind(this));
|
||||
this.xhr.addEventListener('error', this._onStreamFailure.bind(this));
|
||||
this.xhr.addEventListener('abort', this._onStreamFailure.bind(this));
|
||||
this.xhr.open(this.method, this.url);
|
||||
for (var header in this.headers) {
|
||||
this.xhr.setRequestHeader(header, this.headers[header]);
|
||||
}
|
||||
this.xhr.send(this.payload);
|
||||
};
|
||||
|
||||
this.close = function() {
|
||||
if (this.readyState === this.CLOSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.xhr.abort();
|
||||
this.xhr = null;
|
||||
this._setReadyState(this.CLOSED);
|
||||
};
|
||||
};
|
||||
|
||||
// Export our SSE module for npm.js
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.SSE = SSE;
|
||||
}
|
||||
44
assets/js/subscribe_widget.js
Normal file
44
assets/js/subscribe_widget.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
var subscribe_data = JSON.parse(document.getElementById('subscribe_data').textContent);
|
||||
var payload = 'csrf_token=' + subscribe_data.csrf_token;
|
||||
|
||||
var subscribe_button = document.getElementById('subscribe');
|
||||
subscribe_button.parentNode.action = 'javascript:void(0)';
|
||||
|
||||
if (subscribe_button.getAttribute('data-type') === 'subscribe') {
|
||||
subscribe_button.onclick = subscribe;
|
||||
} else {
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
}
|
||||
|
||||
function subscribe() {
|
||||
var fallback = subscribe_button.innerHTML;
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
|
||||
|
||||
var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
|
||||
'&c=' + subscribe_data.ucid;
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'subscribe request'}, {
|
||||
onNon200: function (xhr) {
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
var fallback = subscribe_button.innerHTML;
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
|
||||
|
||||
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
|
||||
'&c=' + subscribe_data.ucid;
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'unsubscribe request'}, {
|
||||
onNon200: function (xhr) {
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
44
assets/js/themes.js
Normal file
44
assets/js/themes.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
var toggle_theme = document.getElementById('toggle_theme');
|
||||
toggle_theme.href = 'javascript:void(0)';
|
||||
|
||||
const STORAGE_KEY_THEME = 'dark_mode';
|
||||
const THEME_DARK = 'dark';
|
||||
const THEME_LIGHT = 'light';
|
||||
|
||||
// TODO: theme state controlled by system
|
||||
toggle_theme.addEventListener('click', function () {
|
||||
const isDarkTheme = helpers.storage.get(STORAGE_KEY_THEME) === THEME_DARK;
|
||||
const newTheme = isDarkTheme ? THEME_LIGHT : THEME_DARK;
|
||||
setTheme(newTheme);
|
||||
helpers.storage.set(STORAGE_KEY_THEME, newTheme);
|
||||
helpers.xhr('GET', '/toggle_theme?redirect=false', {}, {});
|
||||
});
|
||||
|
||||
/** @param {THEME_DARK|THEME_LIGHT} theme */
|
||||
function setTheme(theme) {
|
||||
// By default body element has .no-theme class that uses OS theme via CSS @media rules
|
||||
// It rewrites using hard className below
|
||||
if (theme === THEME_DARK) {
|
||||
toggle_theme.children[0].className = 'icon ion-ios-sunny';
|
||||
document.body.className = 'dark-theme';
|
||||
} else {
|
||||
toggle_theme.children[0].className = 'icon ion-ios-moon';
|
||||
document.body.className = 'light-theme';
|
||||
}
|
||||
}
|
||||
|
||||
// Handles theme change event caused by other tab
|
||||
addEventListener('storage', function (e) {
|
||||
if (e.key === STORAGE_KEY_THEME)
|
||||
setTheme(helpers.storage.get(STORAGE_KEY_THEME));
|
||||
});
|
||||
|
||||
// Set theme from preferences on page load
|
||||
addEventListener('DOMContentLoaded', function () {
|
||||
const prefTheme = document.getElementById('dark_mode_pref').textContent;
|
||||
if (prefTheme) {
|
||||
setTheme(prefTheme);
|
||||
helpers.storage.set(STORAGE_KEY_THEME, prefTheme);
|
||||
}
|
||||
});
|
||||
7
assets/js/video.min.js
vendored
7
assets/js/video.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
/*! @name videojs-contrib-quality-levels @version 2.0.7 @license Apache-2.0 */
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js"),require("global/document")):"function"==typeof define&&define.amd?define(["video.js","global/document"],t):e.videojsContribQualityLevels=t(e.videojs,e.document)}(this,function(e,t){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;var n=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},r=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},i=function(i){function o(){n(this,o);var l=r(this,i.call(this)),s=l;if(e.browser.IS_IE8)for(var u in s=t.createElement("custom"),o.prototype)"constructor"!==u&&(s[u]=o.prototype[u]);return s.levels_=[],s.selectedIndex_=-1,Object.defineProperty(s,"selectedIndex",{get:function(){return s.selectedIndex_}}),Object.defineProperty(s,"length",{get:function(){return s.levels_.length}}),r(l,s)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(o,i),o.prototype.addQualityLevel=function(r){var i=this.getQualityLevelById(r.id);if(i)return i;var o=this.levels_.length;return i=new function r(i){n(this,r);var o=this;if(e.browser.IS_IE8)for(var l in o=t.createElement("custom"),r.prototype)"constructor"!==l&&(o[l]=r.prototype[l]);return o.id=i.id,o.label=o.id,o.width=i.width,o.height=i.height,o.bitrate=i.bandwidth,o.enabled_=i.enabled,Object.defineProperty(o,"enabled",{get:function(){return o.enabled_()},set:function(e){o.enabled_(e)}}),o}(r),""+o in this||Object.defineProperty(this,o,{get:function(){return this.levels_[o]}}),this.levels_.push(i),this.trigger({qualityLevel:i,type:"addqualitylevel"}),i},o.prototype.removeQualityLevel=function(e){for(var t=null,n=0,r=this.length;n<r;n++)if(this[n]===e){t=this.levels_.splice(n,1)[0],this.selectedIndex_===n?this.selectedIndex_=-1:this.selectedIndex_>n&&this.selectedIndex_--;break}return t&&this.trigger({qualityLevel:e,type:"removequalitylevel"}),t},o.prototype.getQualityLevelById=function(e){for(var t=0,n=this.length;t<n;t++){var r=this[t];if(r.id===e)return r}return null},o.prototype.dispose=function(){this.selectedIndex_=-1,this.levels_.length=0},o}(e.EventTarget);for(var o in i.prototype.allowedEvents_={change:"change",addqualitylevel:"addqualitylevel",removequalitylevel:"removequalitylevel"},i.prototype.allowedEvents_)i.prototype["on"+o]=null;var l=function(t){return n=this,e.mergeOptions({},t),r=n.qualityLevels,o=new i,n.on("dispose",function e(){o.dispose(),n.qualityLevels=r,n.off("dispose",e)}),n.qualityLevels=function(){return o},n.qualityLevels.VERSION="__VERSION__",o;var n,r,o};return(e.registerPlugin||e.plugin)("qualityLevels",l),l.VERSION="__VERSION__",l});
|
||||
3
assets/js/videojs-dash.min.js
vendored
3
assets/js/videojs-dash.min.js
vendored
File diff suppressed because one or more lines are too long
14
assets/js/videojs-http-streaming.min.js
vendored
14
assets/js/videojs-http-streaming.min.js
vendored
File diff suppressed because one or more lines are too long
4
assets/js/videojs-markers.min.js
vendored
4
assets/js/videojs-markers.min.js
vendored
File diff suppressed because one or more lines are too long
7
assets/js/videojs-share.min.js
vendored
7
assets/js/videojs-share.min.js
vendored
File diff suppressed because one or more lines are too long
1
assets/js/videojs-youtube-annotations.min.js
vendored
Normal file
1
assets/js/videojs-youtube-annotations.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,413 +0,0 @@
|
||||
/*
|
||||
* Video.js Hotkeys
|
||||
* https://github.com/ctd1500/videojs-hotkeys
|
||||
*
|
||||
* Copyright (c) 2015 Chris Dougherty
|
||||
* Licensed under the Apache-2.0 license.
|
||||
*/
|
||||
|
||||
;(function(root, factory) {
|
||||
if (typeof window !== 'undefined' && window.videojs) {
|
||||
factory(window.videojs);
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define('videojs-hotkeys', ['video.js'], function (module) {
|
||||
return factory(module.default || module);
|
||||
});
|
||||
} else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = factory(require('video.js'));
|
||||
}
|
||||
}(this, function (videojs) {
|
||||
"use strict";
|
||||
if (typeof window !== 'undefined') {
|
||||
window['videojs_hotkeys'] = { version: "0.2.22" };
|
||||
}
|
||||
|
||||
var hotkeys = function(options) {
|
||||
var player = this;
|
||||
var pEl = player.el();
|
||||
var doc = document;
|
||||
var def_options = {
|
||||
volumeStep: 0.1,
|
||||
seekStep: 5,
|
||||
enableMute: true,
|
||||
enableVolumeScroll: true,
|
||||
enableHoverScroll: true,
|
||||
enableFullscreen: true,
|
||||
enableNumbers: true,
|
||||
enableJogStyle: false,
|
||||
alwaysCaptureHotkeys: false,
|
||||
enableModifiersForNumbers: true,
|
||||
enableInactiveFocus: true,
|
||||
skipInitialFocus: false,
|
||||
playPauseKey: playPauseKey,
|
||||
rewindKey: rewindKey,
|
||||
forwardKey: forwardKey,
|
||||
volumeUpKey: volumeUpKey,
|
||||
volumeDownKey: volumeDownKey,
|
||||
muteKey: muteKey,
|
||||
fullscreenKey: fullscreenKey,
|
||||
customKeys: {}
|
||||
};
|
||||
|
||||
var cPlay = 1,
|
||||
cRewind = 2,
|
||||
cForward = 3,
|
||||
cVolumeUp = 4,
|
||||
cVolumeDown = 5,
|
||||
cMute = 6,
|
||||
cFullscreen = 7;
|
||||
|
||||
// Use built-in merge function from Video.js v5.0+ or v4.4.0+
|
||||
var mergeOptions = videojs.mergeOptions || videojs.util.mergeOptions;
|
||||
options = mergeOptions(def_options, options || {});
|
||||
|
||||
var volumeStep = options.volumeStep,
|
||||
seekStep = options.seekStep,
|
||||
enableMute = options.enableMute,
|
||||
enableVolumeScroll = options.enableVolumeScroll,
|
||||
enableHoverScroll = options.enableHoverScroll,
|
||||
enableFull = options.enableFullscreen,
|
||||
enableNumbers = options.enableNumbers,
|
||||
enableJogStyle = options.enableJogStyle,
|
||||
alwaysCaptureHotkeys = options.alwaysCaptureHotkeys,
|
||||
enableModifiersForNumbers = options.enableModifiersForNumbers,
|
||||
enableInactiveFocus = options.enableInactiveFocus,
|
||||
skipInitialFocus = options.skipInitialFocus;
|
||||
|
||||
// Set default player tabindex to handle keydown and doubleclick events
|
||||
if (!pEl.hasAttribute('tabIndex')) {
|
||||
pEl.setAttribute('tabIndex', '-1');
|
||||
}
|
||||
|
||||
// Remove player outline to fix video performance issue
|
||||
pEl.style.outline = "none";
|
||||
|
||||
if (alwaysCaptureHotkeys || !player.autoplay()) {
|
||||
if (!skipInitialFocus) {
|
||||
player.one('play', function() {
|
||||
pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (enableInactiveFocus) {
|
||||
player.on('userinactive', function() {
|
||||
// When the control bar fades, re-apply focus to the player if last focus was a control button
|
||||
var cancelFocusingPlayer = function() {
|
||||
clearTimeout(focusingPlayerTimeout);
|
||||
};
|
||||
var focusingPlayerTimeout = setTimeout(function() {
|
||||
player.off('useractive', cancelFocusingPlayer);
|
||||
var activeElement = doc.activeElement;
|
||||
var controlBar = pEl.querySelector('.vjs-control-bar');
|
||||
if (activeElement && activeElement.parentElement == controlBar) {
|
||||
pEl.focus();
|
||||
}
|
||||
}, 10);
|
||||
|
||||
player.one('useractive', cancelFocusingPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
player.on('play', function() {
|
||||
// Fix allowing the YouTube plugin to have hotkey support.
|
||||
var ifblocker = pEl.querySelector('.iframeblocker');
|
||||
if (ifblocker && ifblocker.style.display === '') {
|
||||
ifblocker.style.display = "block";
|
||||
ifblocker.style.bottom = "39px";
|
||||
}
|
||||
});
|
||||
|
||||
var keyDown = function keyDown(event) {
|
||||
var ewhich = event.which, wasPlaying, seekTime;
|
||||
var ePreventDefault = event.preventDefault;
|
||||
var duration = player.duration();
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
|
||||
// Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true
|
||||
var activeEl = doc.activeElement;
|
||||
if (alwaysCaptureHotkeys ||
|
||||
activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.vjs-control-bar') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker')) {
|
||||
|
||||
switch (checkKeys(event, player)) {
|
||||
// Spacebar toggles play/pause
|
||||
case cPlay:
|
||||
ePreventDefault();
|
||||
if (alwaysCaptureHotkeys) {
|
||||
// Prevent control activation with space
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (player.paused()) {
|
||||
player.play();
|
||||
} else {
|
||||
player.pause();
|
||||
}
|
||||
break;
|
||||
|
||||
// Seeking with the left/right arrow keys
|
||||
case cRewind: // Seek Backward
|
||||
wasPlaying = !player.paused();
|
||||
ePreventDefault();
|
||||
if (wasPlaying) {
|
||||
player.pause();
|
||||
}
|
||||
seekTime = player.currentTime() - seekStepD(event);
|
||||
// The flash player tech will allow you to seek into negative
|
||||
// numbers and break the seekbar, so try to prevent that.
|
||||
if (seekTime <= 0) {
|
||||
seekTime = 0;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
if (wasPlaying) {
|
||||
player.play();
|
||||
}
|
||||
break;
|
||||
case cForward: // Seek Forward
|
||||
wasPlaying = !player.paused();
|
||||
ePreventDefault();
|
||||
if (wasPlaying) {
|
||||
player.pause();
|
||||
}
|
||||
seekTime = player.currentTime() + seekStepD(event);
|
||||
// Fixes the player not sending the end event if you
|
||||
// try to seek past the duration on the seekbar.
|
||||
if (seekTime >= duration) {
|
||||
seekTime = wasPlaying ? duration - .001 : duration;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
if (wasPlaying) {
|
||||
player.play();
|
||||
}
|
||||
break;
|
||||
|
||||
// Volume control with the up/down arrow keys
|
||||
case cVolumeDown:
|
||||
ePreventDefault();
|
||||
if (!enableJogStyle) {
|
||||
player.volume(player.volume() - volumeStep);
|
||||
} else {
|
||||
seekTime = player.currentTime() - 1;
|
||||
if (player.currentTime() <= 1) {
|
||||
seekTime = 0;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
}
|
||||
break;
|
||||
case cVolumeUp:
|
||||
ePreventDefault();
|
||||
if (!enableJogStyle) {
|
||||
player.volume(player.volume() + volumeStep);
|
||||
} else {
|
||||
seekTime = player.currentTime() + 1;
|
||||
if (seekTime >= duration) {
|
||||
seekTime = duration;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Mute with the M key
|
||||
case cMute:
|
||||
if (enableMute) {
|
||||
player.muted(!player.muted());
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Fullscreen with the F key
|
||||
case cFullscreen:
|
||||
if (enableFull) {
|
||||
if (player.isFullscreen()) {
|
||||
player.exitFullscreen();
|
||||
} else {
|
||||
player.requestFullscreen();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Number keys from 0-9 skip to a percentage of the video. 0 is 0% and 9 is 90%
|
||||
if ((ewhich > 47 && ewhich < 59) || (ewhich > 95 && ewhich < 106)) {
|
||||
// Do not handle if enableModifiersForNumbers set to false and keys are Ctrl, Cmd or Alt
|
||||
if (enableModifiersForNumbers || !(event.metaKey || event.ctrlKey || event.altKey)) {
|
||||
if (enableNumbers) {
|
||||
var sub = 48;
|
||||
if (ewhich > 95) {
|
||||
sub = 96;
|
||||
}
|
||||
var number = ewhich - sub;
|
||||
ePreventDefault();
|
||||
player.currentTime(player.duration() * number * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any custom hotkeys
|
||||
for (var customKey in options.customKeys) {
|
||||
var customHotkey = options.customKeys[customKey];
|
||||
// Check for well formed custom keys
|
||||
if (customHotkey && customHotkey.key && customHotkey.handler) {
|
||||
// Check if the custom key's condition matches
|
||||
if (customHotkey.key(event)) {
|
||||
ePreventDefault();
|
||||
customHotkey.handler(player, options, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var doubleClick = function doubleClick(event) {
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
|
||||
// Don't catch clicks if any control buttons are focused
|
||||
var activeEl = event.relatedTarget || event.toElement || doc.activeElement;
|
||||
if (activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker')) {
|
||||
|
||||
if (enableFull) {
|
||||
if (player.isFullscreen()) {
|
||||
player.exitFullscreen();
|
||||
} else {
|
||||
player.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var volumeHover = false;
|
||||
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
|
||||
volumeSelector.onmouseover = function() { volumeHover = true; }
|
||||
volumeSelector.onmouseout = function() { volumeHover = false; }
|
||||
|
||||
var mouseScroll = function mouseScroll(event) {
|
||||
if (enableHoverScroll) {
|
||||
// If we leave this undefined then it can match non-existent elements below
|
||||
var activeEl = 0;
|
||||
} else {
|
||||
var activeEl = doc.activeElement;
|
||||
}
|
||||
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
if (alwaysCaptureHotkeys ||
|
||||
activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker') ||
|
||||
activeEl == pEl.querySelector('.vjs-control-bar') ||
|
||||
volumeHover) {
|
||||
|
||||
if (enableVolumeScroll) {
|
||||
event = window.event || event;
|
||||
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
|
||||
event.preventDefault();
|
||||
|
||||
if (delta == 1) {
|
||||
player.volume(player.volume() + volumeStep);
|
||||
} else if (delta == -1) {
|
||||
player.volume(player.volume() - volumeStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var checkKeys = function checkKeys(e, player) {
|
||||
// Allow some modularity in defining custom hotkeys
|
||||
|
||||
// Play/Pause check
|
||||
if (options.playPauseKey(e, player)) {
|
||||
return cPlay;
|
||||
}
|
||||
|
||||
// Seek Backward check
|
||||
if (options.rewindKey(e, player)) {
|
||||
return cRewind;
|
||||
}
|
||||
|
||||
// Seek Forward check
|
||||
if (options.forwardKey(e, player)) {
|
||||
return cForward;
|
||||
}
|
||||
|
||||
// Volume Up check
|
||||
if (options.volumeUpKey(e, player)) {
|
||||
return cVolumeUp;
|
||||
}
|
||||
|
||||
// Volume Down check
|
||||
if (options.volumeDownKey(e, player)) {
|
||||
return cVolumeDown;
|
||||
}
|
||||
|
||||
// Mute check
|
||||
if (options.muteKey(e, player)) {
|
||||
return cMute;
|
||||
}
|
||||
|
||||
// Fullscreen check
|
||||
if (options.fullscreenKey(e, player)) {
|
||||
return cFullscreen;
|
||||
}
|
||||
};
|
||||
|
||||
function playPauseKey(e) {
|
||||
// Space bar or MediaPlayPause
|
||||
return (e.which === 32 || e.which === 179);
|
||||
}
|
||||
|
||||
function rewindKey(e) {
|
||||
// Left Arrow or MediaRewind
|
||||
return (e.which === 37 || e.which === 177);
|
||||
}
|
||||
|
||||
function forwardKey(e) {
|
||||
// Right Arrow or MediaForward
|
||||
return (e.which === 39 || e.which === 176);
|
||||
}
|
||||
|
||||
function volumeUpKey(e) {
|
||||
// Up Arrow
|
||||
return (e.which === 38);
|
||||
}
|
||||
|
||||
function volumeDownKey(e) {
|
||||
// Down Arrow
|
||||
return (e.which === 40);
|
||||
}
|
||||
|
||||
function muteKey(e) {
|
||||
// M key
|
||||
return (e.which === 77);
|
||||
}
|
||||
|
||||
function fullscreenKey(e) {
|
||||
// F key
|
||||
return (e.which === 70);
|
||||
}
|
||||
|
||||
function seekStepD(e) {
|
||||
// SeekStep caller, returns an int, or a function returning an int
|
||||
return (typeof seekStep === "function" ? seekStep(e) : seekStep);
|
||||
}
|
||||
|
||||
player.on('keydown', keyDown);
|
||||
player.on('dblclick', doubleClick);
|
||||
player.on('mousewheel', mouseScroll);
|
||||
player.on("DOMMouseScroll", mouseScroll);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
registerPlugin('hotkeys', hotkeys);
|
||||
}));
|
||||
2
assets/js/videojs.hotkeys.min.js
vendored
2
assets/js/videojs.hotkeys.min.js
vendored
@@ -1,2 +0,0 @@
|
||||
/* videojs-hotkeys v0.2.22 - https://github.com/ctd1500/videojs-hotkeys */
|
||||
!function(e,t){"undefined"!=typeof window&&window.videojs?t(window.videojs):"function"==typeof define&&define.amd?define("videojs-hotkeys",["video.js"],function(e){return t(e.default||e)}):"undefined"!=typeof module&&module.exports&&(module.exports=t(require("video.js")))}(0,function(s){"use strict";"undefined"!=typeof window&&(window.videojs_hotkeys={version:"0.2.22"});(s.registerPlugin||s.plugin)("hotkeys",function(m){var y=this,v=y.el(),f=document,e={volumeStep:.1,seekStep:5,enableMute:!0,enableVolumeScroll:!0,enableHoverScroll:!0,enableFullscreen:!0,enableNumbers:!0,enableJogStyle:!1,alwaysCaptureHotkeys:!1,enableModifiersForNumbers:!0,enableInactiveFocus:!0,skipInitialFocus:!1,playPauseKey:function(e){return 32===e.which||179===e.which},rewindKey:function(e){return 37===e.which||177===e.which},forwardKey:function(e){return 39===e.which||176===e.which},volumeUpKey:function(e){return 38===e.which},volumeDownKey:function(e){return 40===e.which},muteKey:function(e){return 77===e.which},fullscreenKey:function(e){return 70===e.which},customKeys:{}},t=s.mergeOptions||s.util.mergeOptions,d=(m=t(e,m||{})).volumeStep,n=m.seekStep,p=m.enableMute,r=m.enableVolumeScroll,o=m.enableHoverScroll,b=m.enableFullscreen,h=m.enableNumbers,w=m.enableJogStyle,k=m.alwaysCaptureHotkeys,S=m.enableModifiersForNumbers,u=m.enableInactiveFocus,l=m.skipInitialFocus;v.hasAttribute("tabIndex")||v.setAttribute("tabIndex","-1"),v.style.outline="none",!k&&y.autoplay()||l||y.one("play",function(){v.focus()}),u&&y.on("userinactive",function(){var n=function(){clearTimeout(e)},e=setTimeout(function(){y.off("useractive",n);var e=f.activeElement,t=v.querySelector(".vjs-control-bar");e&&e.parentElement==t&&v.focus()},10);y.one("useractive",n)}),y.on("play",function(){var e=v.querySelector(".iframeblocker");e&&""===e.style.display&&(e.style.display="block",e.style.bottom="39px")});var i=!1,c=v.querySelector(".vjs-volume-menu-button")||v.querySelector(".vjs-volume-panel");c.onmouseover=function(){i=!0},c.onmouseout=function(){i=!1};var a=function(e){if(o)var t=0;else t=f.activeElement;if(y.controls()&&(k||t==v||t==v.querySelector(".vjs-tech")||t==v.querySelector(".iframeblocker")||t==v.querySelector(".vjs-control-bar")||i)&&r){e=window.event||e;var n=Math.max(-1,Math.min(1,e.wheelDelta||-e.detail));e.preventDefault(),1==n?y.volume(y.volume()+d):-1==n&&y.volume(y.volume()-d)}},K=function(e,t){return m.playPauseKey(e,t)?1:m.rewindKey(e,t)?2:m.forwardKey(e,t)?3:m.volumeUpKey(e,t)?4:m.volumeDownKey(e,t)?5:m.muteKey(e,t)?6:m.fullscreenKey(e,t)?7:void 0};function q(e){return"function"==typeof n?n(e):n}return y.on("keydown",function(e){var t,n,r=e.which,o=e.preventDefault,u=y.duration();if(y.controls()){var l=f.activeElement;if(k||l==v||l==v.querySelector(".vjs-tech")||l==v.querySelector(".vjs-control-bar")||l==v.querySelector(".iframeblocker"))switch(K(e,y)){case 1:o(),k&&e.stopPropagation(),y.paused()?y.play():y.pause();break;case 2:t=!y.paused(),o(),t&&y.pause(),(n=y.currentTime()-q(e))<=0&&(n=0),y.currentTime(n),t&&y.play();break;case 3:t=!y.paused(),o(),t&&y.pause(),u<=(n=y.currentTime()+q(e))&&(n=t?u-.001:u),y.currentTime(n),t&&y.play();break;case 5:o(),w?(n=y.currentTime()-1,y.currentTime()<=1&&(n=0),y.currentTime(n)):y.volume(y.volume()-d);break;case 4:o(),w?(u<=(n=y.currentTime()+1)&&(n=u),y.currentTime(n)):y.volume(y.volume()+d);break;case 6:p&&y.muted(!y.muted());break;case 7:b&&(y.isFullscreen()?y.exitFullscreen():y.requestFullscreen());break;default:if((47<r&&r<59||95<r&&r<106)&&(S||!(e.metaKey||e.ctrlKey||e.altKey))&&h){var i=48;95<r&&(i=96);var c=r-i;o(),y.currentTime(y.duration()*c*.1)}for(var a in m.customKeys){var s=m.customKeys[a];s&&s.key&&s.handler&&s.key(e)&&(o(),s.handler(y,m,e))}}}}),y.on("dblclick",function(e){if(y.controls()){var t=e.relatedTarget||e.toElement||f.activeElement;t!=v&&t!=v.querySelector(".vjs-tech")&&t!=v.querySelector(".iframeblocker")||b&&(y.isFullscreen()?y.exitFullscreen():y.requestFullscreen())}}),y.on("mousewheel",a),y.on("DOMMouseScroll",a),this})});
|
||||
@@ -1,52 +1,350 @@
|
||||
function toggle_parent(target) {
|
||||
body = target.parentNode.parentNode.children[1];
|
||||
if (body.style.display === null || body.style.display === "") {
|
||||
target.innerHTML = "[ + ]";
|
||||
body.style.display = "none";
|
||||
} else {
|
||||
target.innerHTML = "[ - ]";
|
||||
body.style.display = "";
|
||||
}
|
||||
}
|
||||
'use strict';
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
var spinnerHTML = '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
|
||||
var spinnerHTMLwithHR = spinnerHTML + '<hr>';
|
||||
|
||||
function toggle_comments(target) {
|
||||
body = target.parentNode.parentNode.parentNode.children[1];
|
||||
if (body.style.display === null || body.style.display === "") {
|
||||
target.innerHTML = "[ + ]";
|
||||
body.style.display = "none";
|
||||
} else {
|
||||
target.innerHTML = "[ - ]";
|
||||
body.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
function swap_comments(source) {
|
||||
if (source == "youtube") {
|
||||
get_youtube_comments();
|
||||
} else if (source == "reddit") {
|
||||
get_reddit_comments();
|
||||
}
|
||||
}
|
||||
|
||||
String.prototype.supplant = function(o) {
|
||||
return this.replace(/{([^{}]*)}/g, function(a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === "string" || typeof r === "number" ? r : a;
|
||||
});
|
||||
String.prototype.supplant = function (o) {
|
||||
return this.replace(/{([^{}]*)}/g, function (a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||
});
|
||||
};
|
||||
|
||||
function show_youtube_replies(target, inner_text, sub_text) {
|
||||
body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = "";
|
||||
|
||||
target.innerHTML = inner_text;
|
||||
target.setAttribute("onclick", "hide_youtube_replies(this, \'" + inner_text + "\', \'" + sub_text + "\')");
|
||||
function toggle_parent(target) {
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
if (body.style.display === 'none') {
|
||||
target.textContent = '[ − ]';
|
||||
body.style.display = '';
|
||||
} else {
|
||||
target.textContent = '[ + ]';
|
||||
body.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function hide_youtube_replies(target, inner_text, sub_text) {
|
||||
body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = "none";
|
||||
|
||||
target.innerHTML = sub_text;
|
||||
target.setAttribute("onclick", "show_youtube_replies(this, \'" + inner_text + "\', \'" + sub_text + "\')");
|
||||
function toggle_comments(event) {
|
||||
var target = event.target;
|
||||
var body = target.parentNode.parentNode.parentNode.children[1];
|
||||
if (body.style.display === 'none') {
|
||||
target.textContent = '[ − ]';
|
||||
body.style.display = '';
|
||||
} else {
|
||||
target.textContent = '[ + ]';
|
||||
body.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function swap_comments(event) {
|
||||
var source = event.target.getAttribute('data-comments');
|
||||
|
||||
if (source === 'youtube') {
|
||||
get_youtube_comments();
|
||||
} else if (source === 'reddit') {
|
||||
get_reddit_comments();
|
||||
}
|
||||
}
|
||||
|
||||
function hide_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = 'none';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = show_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function show_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = '';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = hide_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
var continue_button = document.getElementById('continue');
|
||||
if (continue_button) {
|
||||
continue_button.onclick = continue_autoplay;
|
||||
}
|
||||
|
||||
function next_video() {
|
||||
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
|
||||
|
||||
if (video_data.params.autoplay || video_data.params.continue_autoplay)
|
||||
url.searchParams.set('autoplay', '1');
|
||||
if (video_data.params.listen !== video_data.preferences.listen)
|
||||
url.searchParams.set('listen', video_data.params.listen);
|
||||
if (video_data.params.speed !== video_data.preferences.speed)
|
||||
url.searchParams.set('speed', video_data.params.speed);
|
||||
if (video_data.params.local !== video_data.preferences.local)
|
||||
url.searchParams.set('local', video_data.params.local);
|
||||
url.searchParams.set('continue', '1');
|
||||
|
||||
location.assign(url.pathname + url.search);
|
||||
}
|
||||
|
||||
function continue_autoplay(event) {
|
||||
if (event.target.checked) {
|
||||
player.on('ended', next_video);
|
||||
} else {
|
||||
player.off('ended');
|
||||
}
|
||||
}
|
||||
|
||||
function get_playlist(plid) {
|
||||
var playlist = document.getElementById('playlist');
|
||||
|
||||
playlist.innerHTML = spinnerHTMLwithHR;
|
||||
|
||||
var plid_url;
|
||||
if (plid.startsWith('RD')) {
|
||||
plid_url = '/api/v1/mixes/' + plid +
|
||||
'?continuation=' + video_data.id +
|
||||
'&format=html&hl=' + video_data.preferences.locale;
|
||||
} else {
|
||||
plid_url = '/api/v1/playlists/' + plid +
|
||||
'?index=' + video_data.index +
|
||||
'&continuation=' + video_data.id +
|
||||
'&format=html&hl=' + video_data.preferences.locale;
|
||||
}
|
||||
|
||||
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
|
||||
on200: function (response) {
|
||||
playlist.innerHTML = response.playlistHtml;
|
||||
|
||||
if (!response.nextVideo) return;
|
||||
|
||||
var nextVideo = document.getElementById(response.nextVideo);
|
||||
nextVideo.parentNode.parentNode.scrollTop = nextVideo.offsetTop;
|
||||
|
||||
player.on('ended', function () {
|
||||
var url = new URL('https://example.com/watch?v=' + response.nextVideo);
|
||||
|
||||
url.searchParams.set('list', plid);
|
||||
if (!plid.startsWith('RD'))
|
||||
url.searchParams.set('index', response.index);
|
||||
if (video_data.params.autoplay || video_data.params.continue_autoplay)
|
||||
url.searchParams.set('autoplay', '1');
|
||||
if (video_data.params.listen !== video_data.preferences.listen)
|
||||
url.searchParams.set('listen', video_data.params.listen);
|
||||
if (video_data.params.speed !== video_data.preferences.speed)
|
||||
url.searchParams.set('speed', video_data.params.speed);
|
||||
if (video_data.params.local !== video_data.preferences.local)
|
||||
url.searchParams.set('local', video_data.params.local);
|
||||
|
||||
location.assign(url.pathname + url.search);
|
||||
});
|
||||
},
|
||||
onNon200: function (xhr) {
|
||||
playlist.innerHTML = '';
|
||||
document.getElementById('continue').style.display = '';
|
||||
},
|
||||
onError: function (xhr) {
|
||||
playlist.innerHTML = spinnerHTMLwithHR;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
playlist.innerHTML = spinnerHTMLwithHR;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_reddit_comments() {
|
||||
var comments = document.getElementById('comments');
|
||||
|
||||
var fallback = comments.innerHTML;
|
||||
comments.innerHTML = spinnerHTML;
|
||||
|
||||
var url = '/api/v1/comments/' + video_data.id +
|
||||
'?source=reddit&format=html' +
|
||||
'&hl=' + video_data.preferences.locale;
|
||||
|
||||
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
|
||||
if (video_data.params.comments[1] === 'youtube')
|
||||
onNon200 = function (xhr) {};
|
||||
|
||||
helpers.xhr('GET', url, {retries: 5, entity_name: ''}, {
|
||||
on200: function (response) {
|
||||
comments.innerHTML = ' \
|
||||
<div> \
|
||||
<h3> \
|
||||
<a href="javascript:void(0)">[ − ]</a> \
|
||||
{title} \
|
||||
</h3> \
|
||||
<p> \
|
||||
<b> \
|
||||
<a href="javascript:void(0)" data-comments="youtube"> \
|
||||
{youtubeCommentsText} \
|
||||
</a> \
|
||||
</b> \
|
||||
</p> \
|
||||
<b> \
|
||||
<a rel="noopener" target="_blank" href="https://reddit.com{permalink}">{redditPermalinkText}</a> \
|
||||
</b> \
|
||||
</div> \
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'.supplant({
|
||||
title: response.title,
|
||||
youtubeCommentsText: video_data.youtube_comments_text,
|
||||
redditPermalinkText: video_data.reddit_permalink_text,
|
||||
permalink: response.permalink,
|
||||
contentHtml: response.contentHtml
|
||||
});
|
||||
|
||||
comments.children[0].children[0].children[0].onclick = toggle_comments;
|
||||
comments.children[0].children[1].children[0].onclick = swap_comments;
|
||||
},
|
||||
onNon200: onNon200, // declared above
|
||||
});
|
||||
}
|
||||
|
||||
function get_youtube_comments() {
|
||||
var comments = document.getElementById('comments');
|
||||
|
||||
var fallback = comments.innerHTML;
|
||||
comments.innerHTML = spinnerHTML;
|
||||
|
||||
var url = '/api/v1/comments/' + video_data.id +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode;
|
||||
|
||||
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
|
||||
if (video_data.params.comments[1] === 'youtube')
|
||||
onNon200 = function (xhr) {};
|
||||
|
||||
helpers.xhr('GET', url, {retries: 5, entity_name: 'comments'}, {
|
||||
on200: function (response) {
|
||||
comments.innerHTML = ' \
|
||||
<div> \
|
||||
<h3> \
|
||||
<a href="javascript:void(0)">[ − ]</a> \
|
||||
{commentsText} \
|
||||
</h3> \
|
||||
<b> \
|
||||
<a href="javascript:void(0)" data-comments="reddit"> \
|
||||
{redditComments} \
|
||||
</a> \
|
||||
</b> \
|
||||
</div> \
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'.supplant({
|
||||
contentHtml: response.contentHtml,
|
||||
redditComments: video_data.reddit_comments_text,
|
||||
commentsText: video_data.comments_text.supplant({
|
||||
// toLocaleString correctly splits number with local thousands separator. e.g.:
|
||||
// '1,234,567.89' for user with English locale
|
||||
// '1 234 567,89' for user with Russian locale
|
||||
// '1.234.567,89' for user with Portuguese locale
|
||||
commentCount: response.commentCount.toLocaleString()
|
||||
})
|
||||
});
|
||||
|
||||
comments.children[0].children[0].children[0].onclick = toggle_comments;
|
||||
comments.children[0].children[1].children[0].onclick = swap_comments;
|
||||
},
|
||||
onNon200: onNon200, // declared above
|
||||
onError: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_youtube_replies(target, load_more, load_replies) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
var fallback = body.innerHTML;
|
||||
body.innerHTML = spinnerHTML;
|
||||
|
||||
var url = '/api/v1/comments/' + video_data.id +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode +
|
||||
'&continuation=' + continuation;
|
||||
if (load_replies) url += '&action=action_get_comment_replies';
|
||||
|
||||
helpers.xhr('GET', url, {}, {
|
||||
on200: function (response) {
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.innerHTML += response.contentHtml;
|
||||
} else {
|
||||
body.removeChild(body.lastElementChild);
|
||||
|
||||
var p = document.createElement('p');
|
||||
var a = document.createElement('a');
|
||||
p.appendChild(a);
|
||||
|
||||
a.href = 'javascript:void(0)';
|
||||
a.onclick = hide_youtube_replies;
|
||||
a.setAttribute('data-sub-text', video_data.hide_replies_text);
|
||||
a.setAttribute('data-inner-text', video_data.show_replies_text);
|
||||
a.textContent = video_data.hide_replies_text;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = response.contentHtml;
|
||||
|
||||
body.appendChild(p);
|
||||
body.appendChild(div);
|
||||
}
|
||||
},
|
||||
onNon200: function (xhr) {
|
||||
body.innerHTML = fallback;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
console.warn('Pulling comments failed');
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (video_data.play_next) {
|
||||
player.on('ended', function () {
|
||||
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
|
||||
|
||||
if (video_data.params.autoplay || video_data.params.continue_autoplay)
|
||||
url.searchParams.set('autoplay', '1');
|
||||
if (video_data.params.listen !== video_data.preferences.listen)
|
||||
url.searchParams.set('listen', video_data.params.listen);
|
||||
if (video_data.params.speed !== video_data.preferences.speed)
|
||||
url.searchParams.set('speed', video_data.params.speed);
|
||||
if (video_data.params.local !== video_data.preferences.local)
|
||||
url.searchParams.set('local', video_data.params.local);
|
||||
url.searchParams.set('continue', '1');
|
||||
|
||||
location.assign(url.pathname + url.search);
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener('load', function (e) {
|
||||
if (video_data.plid)
|
||||
get_playlist(video_data.plid);
|
||||
|
||||
if (video_data.params.comments[0] === 'youtube') {
|
||||
get_youtube_comments();
|
||||
} else if (video_data.params.comments[0] === 'reddit') {
|
||||
get_reddit_comments();
|
||||
} else if (video_data.params.comments[1] === 'youtube') {
|
||||
get_youtube_comments();
|
||||
} else if (video_data.params.comments[1] === 'reddit') {
|
||||
get_reddit_comments();
|
||||
} else {
|
||||
var comments = document.getElementById('comments');
|
||||
comments.innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
34
assets/js/watched_widget.js
Normal file
34
assets/js/watched_widget.js
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
var watched_data = JSON.parse(document.getElementById('watched_data').textContent);
|
||||
var payload = 'csrf_token=' + watched_data.csrf_token;
|
||||
|
||||
function mark_watched(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
|
||||
var url = '/watch_ajax?action_mark_watched=1&redirect=false' +
|
||||
'&id=' + target.getAttribute('data-id');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mark_unwatched(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
var count = document.getElementById('count');
|
||||
count.textContent--;
|
||||
|
||||
var url = '/watch_ajax?action_mark_unwatched=1&redirect=false' +
|
||||
'&id=' + target.getAttribute('data-id');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
count.textContent++;
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /search
|
||||
Disallow: /login
|
||||
Disallow: /
|
||||
|
||||
4
assets/videojs/.gitignore
vendored
Normal file
4
assets/videojs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
871
config/config.example.yml
Normal file
871
config/config.example.yml
Normal file
@@ -0,0 +1,871 @@
|
||||
#########################################
|
||||
#
|
||||
# Database configuration
|
||||
#
|
||||
#########################################
|
||||
|
||||
##
|
||||
## Database configuration with separate parameters.
|
||||
## This setting is MANDATORY, unless 'database_url' is used.
|
||||
##
|
||||
db:
|
||||
user: kemal
|
||||
password: kemal
|
||||
host: localhost
|
||||
port: 5432
|
||||
dbname: invidious
|
||||
|
||||
##
|
||||
## Database configuration using a single URI. This is an
|
||||
## alternative to the 'db' parameter above. If both forms
|
||||
## are used, then only database_url is used.
|
||||
## This setting is MANDATORY, unless 'db' is used.
|
||||
##
|
||||
## Note: The 'database_url' setting allows the use of UNIX
|
||||
## sockets. To do so, remove the IP address (or FQDN) and port
|
||||
## and append the 'host' parameter. E.g:
|
||||
## postgres://kemal:kemal@/invidious?host=/var/run/postgresql
|
||||
##
|
||||
## Accepted values: a postgres:// URI
|
||||
## Default: postgres://kemal:kemal@localhost:5432/invidious
|
||||
##
|
||||
#database_url: postgres://kemal:kemal@localhost:5432/invidious
|
||||
|
||||
##
|
||||
## Enable automatic table integrity check. This will create
|
||||
## the required tables and columns if anything is missing.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#check_tables: false
|
||||
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Server config
|
||||
#
|
||||
#########################################
|
||||
|
||||
# -----------------------------
|
||||
# Network (inbound)
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Port to listen on for incoming connections.
|
||||
##
|
||||
## Note: Ports lower than 1024 requires either root privileges
|
||||
## (not recommended) or the "CAP_NET_BIND_SERVICE" capability
|
||||
## (See https://stackoverflow.com/a/414258 and `man capabilities`)
|
||||
##
|
||||
## Accepted values: 1-65535
|
||||
## Default: 3000
|
||||
##
|
||||
#port: 3000
|
||||
|
||||
##
|
||||
## When the invidious instance is behind a proxy, and the proxy
|
||||
## listens on a different port than the instance does, this lets
|
||||
## invidious know about it. This is used to craft absolute URLs
|
||||
## to the instance (e.g in the API).
|
||||
##
|
||||
## Note: This setting is MANDATORY if invidious is behind a
|
||||
## reverse proxy.
|
||||
##
|
||||
## Accepted values: 1-65535
|
||||
## Default: <none>
|
||||
##
|
||||
#external_port:
|
||||
|
||||
##
|
||||
## Interface address to listen on for incoming connections.
|
||||
##
|
||||
## Accepted values: a valid IPv4 or IPv6 address.
|
||||
## default: 0.0.0.0 (listen on all interfaces)
|
||||
##
|
||||
#host_binding: 0.0.0.0
|
||||
|
||||
##
|
||||
## Domain name under which this instance is hosted. This is
|
||||
## used to craft absolute URLs to the instance (e.g in the API).
|
||||
## The domain MUST be defined if your instance is accessed from
|
||||
## a domain name (like 'example.com').
|
||||
##
|
||||
## Accepted values: a fully qualified domain name (FQDN)
|
||||
## Default: <none>
|
||||
##
|
||||
domain:
|
||||
|
||||
##
|
||||
## Tell Invidious that it is behind a proxy that provides only
|
||||
## HTTPS, so all links must use the https:// scheme. This
|
||||
## setting MUST be set to true if invidious is behind a
|
||||
## reverse proxy serving HTTPs.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
https_only: false
|
||||
|
||||
##
|
||||
## Enable/Disable 'Strict-Transport-Security'. Make sure that
|
||||
## the domain specified under 'domain' is served securely.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#hsts: true
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Network (outbound)
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Disable proxying server-wide. Can be disable as a whole, or
|
||||
## only for a single function.
|
||||
##
|
||||
## Accepted values: true, false, dash, livestreams, downloads, local
|
||||
## Default: false
|
||||
##
|
||||
#disable_proxy: false
|
||||
|
||||
##
|
||||
## Size of the HTTP pool used to connect to youtube. Each
|
||||
## domain ('youtube.com', 'ytimg.com', ...) has its own pool.
|
||||
##
|
||||
## Accepted values: a positive integer
|
||||
## Default: 100
|
||||
##
|
||||
#pool_size: 100
|
||||
|
||||
##
|
||||
## Enable/Disable the use of QUIC (HTTP/3) when connecting
|
||||
## to the youtube API and websites ('youtube.com', 'ytimg.com').
|
||||
## QUIC's main advantages are its lower latency and lower bandwidth
|
||||
## use, compared to its predecessors. However, the current version
|
||||
## of QUIC used in invidious is still based on the IETF draft 31,
|
||||
## meaning that the underlying library may still not be fully
|
||||
## optimized. You can read more about QUIC at the link below:
|
||||
## https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport-31
|
||||
##
|
||||
## Note: you should try both options and see what is the best for your
|
||||
## instance. In general QUIC is recommended for public instances. Your
|
||||
## mileage may vary.
|
||||
##
|
||||
## Note 2: Using QUIC prevents some captcha challenges from appearing.
|
||||
## See: https://github.com/iv-org/invidious/issues/957#issuecomment-576424042
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#use_quic: false
|
||||
|
||||
##
|
||||
## Additional cookies to be sent when requesting the youtube API.
|
||||
##
|
||||
## Accepted values: a string in the format "name1=value1; name2=value2..."
|
||||
## Default: <none>
|
||||
##
|
||||
#cookies:
|
||||
|
||||
##
|
||||
## Force connection to youtube over a specific IP family.
|
||||
##
|
||||
## Note: This may sometimes resolve issues involving rate-limiting.
|
||||
## See https://github.com/ytdl-org/youtube-dl/issues/21729.
|
||||
##
|
||||
## Accepted values: ipv4, ipv6
|
||||
## Default: <none>
|
||||
##
|
||||
#force_resolve:
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Logging
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Path to log file. Can be absolute or relative to the invidious
|
||||
## binary. This is overridden if "-o OUTPUT" or "--output=OUTPUT"
|
||||
## are passed on the command line.
|
||||
##
|
||||
## Accepted values: a filesystem path or 'STDOUT'
|
||||
## Default: STDOUT
|
||||
##
|
||||
#output: STDOUT
|
||||
|
||||
##
|
||||
## Logging Verbosity. This is overridden if "-l LEVEL" or
|
||||
## "--log-level=LEVEL" are passed on the command line.
|
||||
##
|
||||
## Accepted values: All, Trace, Debug, Info, Warn, Error, Fatal, Off
|
||||
## Default: Info
|
||||
##
|
||||
#log_level: Info
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Features
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Enable/Disable the "Popular" tab on the main page.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#popular_enabled: true
|
||||
|
||||
##
|
||||
## Enable/Disable statstics (available at /api/v1/stats).
|
||||
## The following data is available:
|
||||
## - Software name ("invidious") and version+branch (same data as
|
||||
## displayed in the footer, e.g: "2021.05.13-75e5b49" / "master")
|
||||
## - The value of the 'registration_enabled' config (true/false)
|
||||
## - Number of currently registered users
|
||||
## - Number of registered users who connected in the last month
|
||||
## - Number of registered users who connected in the last 6 months
|
||||
## - Timestamp of the last server restart
|
||||
## - Timestamp of the last "Channel Refresh" job execution
|
||||
##
|
||||
## Warning: This setting MUST be set to true if you plan to run
|
||||
## a public instance. It is used by api.invidious.io to refresh
|
||||
## your instance's status.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#statistics_enabled: false
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Users and accounts
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Allow/Forbid Invidious (local) account creation. Invidious
|
||||
## accounts allow users to subscribe to channels and to create
|
||||
## playlists without a Google account.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#registration_enabled: true
|
||||
|
||||
##
|
||||
## Allow/Forbid users to log-in. This setting affects the ability
|
||||
## to connect with BOTH Google and Invidious (local) accounts.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#login_enabled: true
|
||||
|
||||
##
|
||||
## Enable/Disable the captcha challenge on the login page.
|
||||
##
|
||||
## Note: this is a basic captcha challenge that doesn't
|
||||
## depend on any third parties.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#captcha_enabled: true
|
||||
|
||||
##
|
||||
## List of usernames that will be granted administrator rights.
|
||||
## A user with administrator rights will be able to change the
|
||||
## server configuration options listed below in /preferences,
|
||||
## in addition to the usual user preferences.
|
||||
##
|
||||
## Server-wide settings:
|
||||
## - popular_enabled
|
||||
## - captcha_enabled
|
||||
## - login_enabled
|
||||
## - registration_enabled
|
||||
## - statistics_enabled
|
||||
## Default user preferences:
|
||||
## - default_home
|
||||
## - feed_menu
|
||||
##
|
||||
## Accepted values: an array of strings
|
||||
## Default: [""]
|
||||
##
|
||||
#admins: [""]
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Background jobs
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Number of threads to use when crawling channel videos (during
|
||||
## subscriptions update).
|
||||
##
|
||||
## Notes:
|
||||
## - Setting this to 0 will disable the channel videos crawl job.
|
||||
## - This setting is overridden if "-c THREADS" or
|
||||
## "--channel-threads=THREADS" are passed on the command line.
|
||||
##
|
||||
## Accepted values: a positive integer
|
||||
## Default: 1
|
||||
##
|
||||
channel_threads: 1
|
||||
|
||||
##
|
||||
## Time interval between two executions of the job that crawls
|
||||
## channel videos (subscriptions update).
|
||||
##
|
||||
## Accepted values: a valid time interval (like 1h30m or 90m)
|
||||
## Default: 30m
|
||||
##
|
||||
#channel_refresh_interval: 30m
|
||||
|
||||
##
|
||||
## Forcefully dump and re-download the entire list of uploaded
|
||||
## videos when crawling channel (during subscriptions update).
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
full_refresh: false
|
||||
|
||||
##
|
||||
## Number of threads to use when updating RSS feeds.
|
||||
##
|
||||
## Notes:
|
||||
## - Setting this to 0 will disable the channel videos crawl job.
|
||||
## - This setting is overridden if "-f THREADS" or
|
||||
## "--feed-threads=THREADS" are passed on the command line.
|
||||
##
|
||||
## Accepted values: a positive integer
|
||||
## Default: 1
|
||||
##
|
||||
feed_threads: 1
|
||||
|
||||
##
|
||||
## Enable/Disable the polling job that keeps the decryption
|
||||
## function (for "secured" videos) up to date.
|
||||
##
|
||||
## Note: This part of the code generate a small amount of data every minute.
|
||||
## This may not be desired if you have bandwidth limits set by your ISP.
|
||||
##
|
||||
## Note 2: This part of the code is currently broken, so changing
|
||||
## this setting has no impact.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#decrypt_polling: false
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Captcha API
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## URL of the captcha solving service.
|
||||
##
|
||||
## Accepted values: any URL
|
||||
## Default: https://api.anti-captcha.com
|
||||
##
|
||||
#captcha_api_url: https://api.anti-captcha.com
|
||||
|
||||
##
|
||||
## API key for the captcha solving service.
|
||||
##
|
||||
## Accepted values: a string
|
||||
## Default: <none>
|
||||
##
|
||||
#captcha_key:
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Miscellaneous
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## custom banner displayed at the top of every page. This can
|
||||
## used for instance announcements, e.g.
|
||||
##
|
||||
## Accepted values: any string. HTML is accepted.
|
||||
## Default: <none>
|
||||
##
|
||||
#banner:
|
||||
|
||||
##
|
||||
## Subscribe to channels using PubSubHub (Google PubSubHubbub service).
|
||||
## PubSubHub allows Invidious to be instantly notified when a new video
|
||||
## is published on any subscribed channels. When PubSubHub is not used,
|
||||
## Invidious will check for new videos every minute.
|
||||
##
|
||||
## Note: This setting is recommended for public instances.
|
||||
##
|
||||
## Note 2:
|
||||
## - Requires a public instance (it uses /feed/webhook/v1)
|
||||
## - Requires 'domain' and 'hmac_key' to be set.
|
||||
## - Setting this parameter to any number greater than zero will
|
||||
## enable channel subscriptions via PubSubHub, but will limit the
|
||||
## amount of concurrent subscriptions.
|
||||
##
|
||||
## Accepted values: true, false, a positive integer
|
||||
## Default: false
|
||||
##
|
||||
#use_pubsub_feeds: false
|
||||
|
||||
##
|
||||
## HMAC signing key used for CSRF tokens and pubsub
|
||||
## subscriptions verification.
|
||||
##
|
||||
## Accepted values: a string
|
||||
## Default: <none>
|
||||
##
|
||||
#hmac_key:
|
||||
|
||||
##
|
||||
## List of video IDs where the "download" widget must be
|
||||
## disabled, in order to comply with DMCA requests.
|
||||
##
|
||||
## Accepted values: an array of string
|
||||
## Default: <none>
|
||||
##
|
||||
#dmca_content:
|
||||
|
||||
##
|
||||
## Cache video annotations in the database.
|
||||
##
|
||||
## Warning: empty annotations or annotations that only contain
|
||||
## cards won't be cached.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#cache_annotations: false
|
||||
|
||||
##
|
||||
## Source code URL. If your instance is running a modified source
|
||||
## code, you MUST publish it somewhere and set this option.
|
||||
##
|
||||
## Accepted values: a string
|
||||
## Default: <none>
|
||||
##
|
||||
#modified_source_code_url: ""
|
||||
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Default user preferences
|
||||
#
|
||||
#########################################
|
||||
|
||||
##
|
||||
## NOTE: All the settings below define the default user
|
||||
## preferences. They will apply to ALL users connecting
|
||||
## without a preferences cookie (so either on the first
|
||||
## connection to the instance or after clearing the
|
||||
## browser's cookies).
|
||||
##
|
||||
|
||||
default_user_preferences:
|
||||
|
||||
# -----------------------------
|
||||
# Internationalization
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Default user interface language (locale).
|
||||
##
|
||||
## Note: When hosting a public instance, overriding the
|
||||
## default (english) is not recommended, as it may
|
||||
## people using other languages.
|
||||
##
|
||||
## Accepted values:
|
||||
## ar (Arabic)
|
||||
## da (Danish)
|
||||
## de (German)
|
||||
## en-US (english, US)
|
||||
## el (Greek)
|
||||
## eo (Esperanto)
|
||||
## es (Spanish)
|
||||
## fa (Persian)
|
||||
## fi (Finnish)
|
||||
## fr (French)
|
||||
## he (Hebrew)
|
||||
## hr (Hungarian)
|
||||
## id (Indonesian)
|
||||
## is (Icelandic)
|
||||
## it (Italian)
|
||||
## ja (Japanese)
|
||||
## nb-NO (Norwegian, Bokmål)
|
||||
## nl (Dutch)
|
||||
## pl (Polish)
|
||||
## pt-BR (Portuguese, Brazil)
|
||||
## pt-PT (Portuguese, Portugal)
|
||||
## ro (Romanian)
|
||||
## ru (Russian)
|
||||
## sv (Swedish)
|
||||
## tr (Turkish)
|
||||
## uk (Ukrainian)
|
||||
## zh-CN (Chinese, China) (a.k.a "Simplified Chinese")
|
||||
## zh-TW (Chinese, Taiwan) (a.k.a "Traditional Chinese")
|
||||
##
|
||||
## Default: en-US
|
||||
##
|
||||
#locale: en-US
|
||||
|
||||
##
|
||||
## Default geographical location for content.
|
||||
##
|
||||
## Accepted values:
|
||||
## AE, AR, AT, AU, AZ, BA, BD, BE, BG, BH, BO, BR, BY, CA, CH, CL, CO, CR,
|
||||
## CY, CZ, DE, DK, DO, DZ, EC, EE, EG, ES, FI, FR, GB, GE, GH, GR, GT, HK,
|
||||
## HN, HR, HU, ID, IE, IL, IN, IQ, IS, IT, JM, JO, JP, KE, KR, KW, KZ, LB,
|
||||
## LI, LK, LT, LU, LV, LY, MA, ME, MK, MT, MX, MY, NG, NI, NL, NO, NP, NZ,
|
||||
## OM, PA, PE, PG, PH, PK, PL, PR, PT, PY, QA, RO, RS, RU, SA, SE, SG, SI,
|
||||
## SK, SN, SV, TH, TN, TR, TW, TZ, UA, UG, US, UY, VE, VN, YE, ZA, ZW
|
||||
##
|
||||
## Default: US
|
||||
##
|
||||
#region: US
|
||||
|
||||
##
|
||||
## Top 3 preferred languages for video captions.
|
||||
##
|
||||
## Note: overriding the default (no preferred
|
||||
## caption language) is not recommended, in order
|
||||
## to not penalize people using other languages.
|
||||
##
|
||||
## Accepted values: a three-entries array.
|
||||
## Each entry can be one of:
|
||||
## "English", "English (auto-generated)",
|
||||
## "Afrikaans", "Albanian", "Amharic", "Arabic",
|
||||
## "Armenian", "Azerbaijani", "Bangla", "Basque",
|
||||
## "Belarusian", "Bosnian", "Bulgarian", "Burmese",
|
||||
## "Catalan", "Cebuano", "Chinese (Simplified)",
|
||||
## "Chinese (Traditional)", "Corsican", "Croatian",
|
||||
## "Czech", "Danish", "Dutch", "Esperanto", "Estonian",
|
||||
## "Filipino", "Finnish", "French", "Galician", "Georgian",
|
||||
## "German", "Greek", "Gujarati", "Haitian Creole", "Hausa",
|
||||
## "Hawaiian", "Hebrew", "Hindi", "Hmong", "Hungarian",
|
||||
## "Icelandic", "Igbo", "Indonesian", "Irish", "Italian",
|
||||
## "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer",
|
||||
## "Korean", "Kurdish", "Kyrgyz", "Lao", "Latin", "Latvian",
|
||||
## "Lithuanian", "Luxembourgish", "Macedonian",
|
||||
## "Malagasy", "Malay", "Malayalam", "Maltese", "Maori",
|
||||
## "Marathi", "Mongolian", "Nepali", "Norwegian Bokmål",
|
||||
## "Nyanja", "Pashto", "Persian", "Polish", "Portuguese",
|
||||
## "Punjabi", "Romanian", "Russian", "Samoan",
|
||||
## "Scottish Gaelic", "Serbian", "Shona", "Sindhi",
|
||||
## "Sinhala", "Slovak", "Slovenian", "Somali",
|
||||
## "Southern Sotho", "Spanish", "Spanish (Latin America)",
|
||||
## "Sundanese", "Swahili", "Swedish", "Tajik", "Tamil",
|
||||
## "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
|
||||
## "Uzbek", "Vietnamese", "Welsh", "Western Frisian",
|
||||
## "Xhosa", "Yiddish", "Yoruba", "Zulu"
|
||||
##
|
||||
## Default: ["", "", ""]
|
||||
##
|
||||
#captions: ["", "", ""]
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Interface
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Enable/Disable dark mode.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: <none>
|
||||
##
|
||||
#dark_mode:
|
||||
|
||||
##
|
||||
## Enable/Disable thin mode (no video thumbnails).
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#thin_mode: false
|
||||
|
||||
##
|
||||
## List of feeds available on the home page.
|
||||
##
|
||||
## Note: "Subscriptions" and "Playlists" are only visible
|
||||
## when the user is logged in.
|
||||
##
|
||||
## Accepted values: A list of strings
|
||||
## Each entry can be one of: "Popular", "Trending",
|
||||
## "Subscriptions", "Playlists"
|
||||
##
|
||||
## Default: ["Popular", "Trending", "Subscriptions", "Playlists"] (show all feeds)
|
||||
##
|
||||
#feed_menu: ["Popular", "Trending", "Subscriptions", "Playlists"]
|
||||
|
||||
##
|
||||
## Default feed to display on the home page.
|
||||
##
|
||||
## Note: setting this option to "Popular" has no
|
||||
## effect when 'popular_enabled' is set to false.
|
||||
##
|
||||
## Accepted values: Popular, Trending, Subscriptions, Playlists, <none>
|
||||
## Default: Popular
|
||||
##
|
||||
#default_home: Popular
|
||||
|
||||
##
|
||||
## Default number of results to display per page.
|
||||
##
|
||||
## Note: this affects invidious-generated pages only, such
|
||||
## as watch history and subscription feeds. Playlists, search
|
||||
## results and channel videos depend on the data returned by
|
||||
## the Youtube API.
|
||||
##
|
||||
## Accepted values: any positive integer
|
||||
## Default: 40
|
||||
##
|
||||
#max_results: 40
|
||||
|
||||
##
|
||||
## Show/hide annotations.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#annotations: false
|
||||
|
||||
##
|
||||
## Show/hide annotation.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#annotations_subscribed: false
|
||||
|
||||
##
|
||||
## Type of comments to display below video.
|
||||
##
|
||||
## Accepted values: a two-entries array.
|
||||
## Each entry can be one of: "youtube", "reddit", ""
|
||||
##
|
||||
## Default: ["youtube", ""]
|
||||
##
|
||||
#comments: ["youtube", ""]
|
||||
|
||||
##
|
||||
## Default player style.
|
||||
##
|
||||
## Accepted values: invidious, youtube
|
||||
## Default: invidious
|
||||
##
|
||||
#player_style: invidious
|
||||
|
||||
##
|
||||
## Show/Hide the "related videos" sidebar when
|
||||
## watching a video.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#related_videos: true
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Video player behavior
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Automatically play videos on page load.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#autoplay: false
|
||||
|
||||
##
|
||||
## Automatically load the "next" video (either next in
|
||||
## playlist or proposed) when the current video ends.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#continue: false
|
||||
|
||||
##
|
||||
## Autoplay next video by default.
|
||||
##
|
||||
## Note: Only effective if 'continue' is set to true.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#continue_autoplay: true
|
||||
|
||||
##
|
||||
## Play videos in Audio-only mode by default.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#listen: false
|
||||
|
||||
##
|
||||
## Loop videos automatically.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#video_loop: false
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Video playback settings
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Default video quality.
|
||||
##
|
||||
## Accepted values: dash, hd720, medium, small
|
||||
## Default: hd720
|
||||
##
|
||||
#quality: hd720
|
||||
|
||||
##
|
||||
## Default dash video quality.
|
||||
##
|
||||
## Note: this setting only takes effet if the
|
||||
## 'quality' parameter is set to "dash".
|
||||
##
|
||||
## Accepted values:
|
||||
## auto, best, 4320p, 2160p, 1440p, 1080p,
|
||||
## 720p, 480p, 360p, 240p, 144p, worst
|
||||
## Default: auto
|
||||
##
|
||||
#quality_dash: auto
|
||||
|
||||
##
|
||||
## Default video playback speed.
|
||||
##
|
||||
## Accepted values: 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0
|
||||
## Default: 1.0
|
||||
##
|
||||
#speed: 1.0
|
||||
|
||||
##
|
||||
## Default volume.
|
||||
##
|
||||
## Accepted values: 0-100
|
||||
## Default: 100
|
||||
##
|
||||
#volume: 100
|
||||
|
||||
##
|
||||
## Allow 360° videos to be played.
|
||||
##
|
||||
## Note: This feature requires a WebGL-enabled browser.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#vr_mode: true
|
||||
|
||||
# -----------------------------
|
||||
# Subscription feed
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## In the "Subscription" feed, only show the latest video
|
||||
## of each channel the user is subscribed to.
|
||||
##
|
||||
## Note: when combined with 'unseen_only', the latest unseen
|
||||
## video of each channel will be displayed instead of the
|
||||
## latest by date.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#latest_only: false
|
||||
|
||||
##
|
||||
## Enable/Disable user subscriptions desktop notifications.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#notifications_only: false
|
||||
|
||||
##
|
||||
## In the "Subscription" feed, Only show the videos that the
|
||||
## user haven't watched yet (i.e which are not in their watch
|
||||
## history).
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#unseen_only: false
|
||||
|
||||
##
|
||||
## Default sorting parameter for subscription feeds.
|
||||
##
|
||||
## Accepted values:
|
||||
## 'alphabetically'
|
||||
## 'alphabetically - reverse'
|
||||
## 'channel name'
|
||||
## 'channel name - reverse'
|
||||
## 'published'
|
||||
## 'published - reverse'
|
||||
##
|
||||
## Default: published
|
||||
##
|
||||
#sort: published
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Miscellaneous
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## Proxy videos through instance by default.
|
||||
##
|
||||
## Warning: As most users won't change this setting in their
|
||||
## preferences, defaulting to true will significantly
|
||||
## increase the instance's network usage, so make sure that
|
||||
## your server's connection can handle it.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#local: false
|
||||
|
||||
##
|
||||
## Show the connected user's nick at the top right.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: true
|
||||
##
|
||||
#show_nick: true
|
||||
|
||||
##
|
||||
## Automatically redirect to a random instance when the user uses
|
||||
## any "switch invidious instance" link (For videos, it's the plane
|
||||
## icon, next to "watch on youtube" and "listen"). When set to false,
|
||||
## the user is sent to https://redirect.invidious.io instead, where
|
||||
## they can manually select an instance.
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#automatic_instance_redirect: false
|
||||
|
||||
##
|
||||
## Show the entire video description by default (when set to 'false',
|
||||
## only the first few lines of the description are shown and a
|
||||
## "show more" button allows to expand it).
|
||||
##
|
||||
## Accepted values: true, false
|
||||
## Default: false
|
||||
##
|
||||
#extend_desc: false
|
||||
@@ -1,13 +0,0 @@
|
||||
video_threads: 0
|
||||
crawl_threads: 0
|
||||
channel_threads: 1
|
||||
feed_threads: 1
|
||||
db:
|
||||
user: kemal
|
||||
password: kemal
|
||||
host: localhost
|
||||
port: 5432
|
||||
dbname: invidious
|
||||
full_refresh: false
|
||||
https_only: false
|
||||
domain:
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
psql invidious -c "ALTER TABLE channels ADD COLUMN subscribed bool;"
|
||||
psql invidious -c "UPDATE channels SET subscribed = false;"
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed bool;"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = false;"
|
||||
|
||||
10
config/migrate-scripts/migrate-db-1c8075c.sh
Executable file
10
config/migrate-scripts/migrate-db-1c8075c.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN live_now CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN premiere_timestamp CASCADE"
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz"
|
||||
22
config/migrate-scripts/migrate-db-1eca969.sh
Executable file
22
config/migrate-scripts/migrate-db-1eca969.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN title CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN views CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN likes CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN dislikes CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN wilson_score CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN published CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN description CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN language CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN ucid CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN allowed_regions CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN is_family_friendly CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre_url CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN license CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN sub_count_text CASCADE"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author_thumbnail CASCADE"
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
psql invidious -c "ALTER TABLE channels ADD COLUMN deleted bool;"
|
||||
psql invidious -c "UPDATE channels SET deleted = false;"
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN deleted bool;"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET deleted = false;"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
psql invidious < config/sql/session_ids.sql
|
||||
psql invidious -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING"
|
||||
psql invidious -c "ALTER TABLE users DROP COLUMN id"
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/session_ids.sql
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users DROP COLUMN id"
|
||||
|
||||
6
config/migrate-scripts/migrate-db-3bcb98e.sh
Executable file
6
config/migrate-scripts/migrate-db-3bcb98e.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/annotations.sql
|
||||
6
config/migrate-scripts/migrate-db-52cb239.sh
Executable file
6
config/migrate-scripts/migrate-db-52cb239.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN views bigint;"
|
||||
7
config/migrate-scripts/migrate-db-6e51189.sh
Executable file
7
config/migrate-scripts/migrate-db-6e51189.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool;"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channel_videos SET live_now = false;"
|
||||
6
config/migrate-scripts/migrate-db-701b5ea.sh
Executable file
6
config/migrate-scripts/migrate-db-701b5ea.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users ADD COLUMN feed_needs_update boolean"
|
||||
6
config/migrate-scripts/migrate-db-88b7097.sh
Executable file
6
config/migrate-scripts/migrate-db-88b7097.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz;"
|
||||
8
config/migrate-scripts/migrate-db-8e884fe.sh
Executable file
8
config/migrate-scripts/migrate-db-8e884fe.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
|
||||
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
|
||||
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels DROP COLUMN subscribed"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz"
|
||||
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'"
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
psql invidious -c "ALTER TABLE channels DROP COLUMN subscribed"
|
||||
psql invidious -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz"
|
||||
psql invidious -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'"
|
||||
12
config/sql/annotations.sql
Normal file
12
config/sql/annotations.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- Table: public.annotations
|
||||
|
||||
-- DROP TABLE public.annotations;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.annotations
|
||||
(
|
||||
id text NOT NULL,
|
||||
annotations xml,
|
||||
CONSTRAINT annotations_id_key UNIQUE (id)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.annotations TO current_user;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
-- DROP TABLE public.channel_videos;
|
||||
|
||||
CREATE TABLE public.channel_videos
|
||||
CREATE TABLE IF NOT EXISTS public.channel_videos
|
||||
(
|
||||
id text NOT NULL,
|
||||
title text,
|
||||
@@ -11,25 +11,19 @@ CREATE TABLE public.channel_videos
|
||||
ucid text,
|
||||
author text,
|
||||
length_seconds integer,
|
||||
live_now boolean,
|
||||
premiere_timestamp timestamp with time zone,
|
||||
views bigint,
|
||||
CONSTRAINT channel_videos_id_key UNIQUE (id)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.channel_videos TO kemal;
|
||||
|
||||
-- Index: public.channel_videos_published_idx
|
||||
|
||||
-- DROP INDEX public.channel_videos_published_idx;
|
||||
|
||||
CREATE INDEX channel_videos_published_idx
|
||||
ON public.channel_videos
|
||||
USING btree
|
||||
(published);
|
||||
GRANT ALL ON TABLE public.channel_videos TO current_user;
|
||||
|
||||
-- Index: public.channel_videos_ucid_idx
|
||||
|
||||
-- DROP INDEX public.channel_videos_ucid_idx;
|
||||
|
||||
CREATE INDEX channel_videos_ucid_idx
|
||||
CREATE INDEX IF NOT EXISTS channel_videos_ucid_idx
|
||||
ON public.channel_videos
|
||||
USING btree
|
||||
(ucid COLLATE pg_catalog."default");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
-- DROP TABLE public.channels;
|
||||
|
||||
CREATE TABLE public.channels
|
||||
CREATE TABLE IF NOT EXISTS public.channels
|
||||
(
|
||||
id text NOT NULL,
|
||||
author text,
|
||||
@@ -12,13 +12,13 @@ CREATE TABLE public.channels
|
||||
CONSTRAINT channels_id_key UNIQUE (id)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.channels TO kemal;
|
||||
GRANT ALL ON TABLE public.channels TO current_user;
|
||||
|
||||
-- Index: public.channels_id_idx
|
||||
|
||||
-- DROP INDEX public.channels_id_idx;
|
||||
|
||||
CREATE INDEX channels_id_idx
|
||||
CREATE INDEX IF NOT EXISTS channels_id_idx
|
||||
ON public.channels
|
||||
USING btree
|
||||
(id COLLATE pg_catalog."default");
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
|
||||
-- DROP TABLE public.nonces;
|
||||
|
||||
CREATE TABLE public.nonces
|
||||
CREATE TABLE IF NOT EXISTS public.nonces
|
||||
(
|
||||
nonce text,
|
||||
expire timestamp with time zone,
|
||||
CONSTRAINT nonces_id_key UNIQUE (nonce)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.nonces TO kemal;
|
||||
GRANT ALL ON TABLE public.nonces TO current_user;
|
||||
|
||||
-- Index: public.nonces_nonce_idx
|
||||
|
||||
-- DROP INDEX public.nonces_nonce_idx;
|
||||
|
||||
CREATE INDEX nonces_nonce_idx
|
||||
CREATE INDEX IF NOT EXISTS nonces_nonce_idx
|
||||
ON public.nonces
|
||||
USING btree
|
||||
(nonce COLLATE pg_catalog."default");
|
||||
|
||||
19
config/sql/playlist_videos.sql
Normal file
19
config/sql/playlist_videos.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- Table: public.playlist_videos
|
||||
|
||||
-- DROP TABLE public.playlist_videos;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.playlist_videos
|
||||
(
|
||||
title text,
|
||||
id text,
|
||||
author text,
|
||||
ucid text,
|
||||
length_seconds integer,
|
||||
published timestamptz,
|
||||
plid text references playlists(id),
|
||||
index int8,
|
||||
live_now boolean,
|
||||
PRIMARY KEY (index,plid)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.playlist_videos TO current_user;
|
||||
29
config/sql/playlists.sql
Normal file
29
config/sql/playlists.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Type: public.privacy
|
||||
|
||||
-- DROP TYPE public.privacy;
|
||||
|
||||
CREATE TYPE public.privacy AS ENUM
|
||||
(
|
||||
'Public',
|
||||
'Unlisted',
|
||||
'Private'
|
||||
);
|
||||
|
||||
-- Table: public.playlists
|
||||
|
||||
-- DROP TABLE public.playlists;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.playlists
|
||||
(
|
||||
title text,
|
||||
id text primary key,
|
||||
author text,
|
||||
description text,
|
||||
video_count integer,
|
||||
created timestamptz,
|
||||
updated timestamptz,
|
||||
privacy privacy,
|
||||
index int8[]
|
||||
);
|
||||
|
||||
GRANT ALL ON public.playlists TO current_user;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
-- DROP TABLE public.session_ids;
|
||||
|
||||
CREATE TABLE public.session_ids
|
||||
CREATE TABLE IF NOT EXISTS public.session_ids
|
||||
(
|
||||
id text NOT NULL,
|
||||
email text,
|
||||
@@ -10,13 +10,13 @@ CREATE TABLE public.session_ids
|
||||
CONSTRAINT session_ids_pkey PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.session_ids TO kemal;
|
||||
GRANT ALL ON TABLE public.session_ids TO current_user;
|
||||
|
||||
-- Index: public.session_ids_id_idx
|
||||
|
||||
-- DROP INDEX public.session_ids_id_idx;
|
||||
|
||||
CREATE INDEX session_ids_id_idx
|
||||
CREATE INDEX IF NOT EXISTS session_ids_id_idx
|
||||
ON public.session_ids
|
||||
USING btree
|
||||
(id COLLATE pg_catalog."default");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
-- DROP TABLE public.users;
|
||||
|
||||
CREATE TABLE public.users
|
||||
CREATE TABLE IF NOT EXISTS public.users
|
||||
(
|
||||
updated timestamp with time zone,
|
||||
notifications text[],
|
||||
@@ -12,16 +12,17 @@ CREATE TABLE public.users
|
||||
password text,
|
||||
token text,
|
||||
watched text[],
|
||||
feed_needs_update boolean,
|
||||
CONSTRAINT users_email_key UNIQUE (email)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.users TO kemal;
|
||||
GRANT ALL ON TABLE public.users TO current_user;
|
||||
|
||||
-- Index: public.email_unique_idx
|
||||
|
||||
-- DROP INDEX public.email_unique_idx;
|
||||
|
||||
CREATE UNIQUE INDEX email_unique_idx
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS email_unique_idx
|
||||
ON public.users
|
||||
USING btree
|
||||
(lower(email) COLLATE pg_catalog."default");
|
||||
|
||||
@@ -2,38 +2,21 @@
|
||||
|
||||
-- DROP TABLE public.videos;
|
||||
|
||||
CREATE TABLE public.videos
|
||||
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
|
||||
(
|
||||
id text NOT NULL,
|
||||
info text,
|
||||
updated timestamp with time zone,
|
||||
title text,
|
||||
views bigint,
|
||||
likes integer,
|
||||
dislikes integer,
|
||||
wilson_score double precision,
|
||||
published timestamp with time zone,
|
||||
description text,
|
||||
language text,
|
||||
author text,
|
||||
ucid text,
|
||||
allowed_regions text[],
|
||||
is_family_friendly boolean,
|
||||
genre text,
|
||||
genre_url text,
|
||||
license text,
|
||||
sub_count_text text,
|
||||
author_thumbnail text,
|
||||
CONSTRAINT videos_pkey PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE public.videos TO kemal;
|
||||
GRANT ALL ON TABLE public.videos TO current_user;
|
||||
|
||||
-- Index: public.id_idx
|
||||
|
||||
-- DROP INDEX public.id_idx;
|
||||
|
||||
CREATE UNIQUE INDEX id_idx
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS id_idx
|
||||
ON public.videos
|
||||
USING btree
|
||||
(id COLLATE pg_catalog."default");
|
||||
|
||||
@@ -1,21 +1,56 @@
|
||||
version: '3'
|
||||
# Warning: This docker-compose file is made for development purposes.
|
||||
# Using it will build an image from the locally cloned repository.
|
||||
#
|
||||
# If you want to use Invidious in production, see the docker-compose.yml file provided
|
||||
# in the installation documentation: https://docs.invidious.io/installation/
|
||||
|
||||
version: "3"
|
||||
services:
|
||||
postgres:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.postgres
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgresdata:/var/lib/postgresql/data
|
||||
|
||||
invidious:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "127.0.0.1:3000:3000"
|
||||
environment:
|
||||
# Please read the following file for a comprehensive list of all available
|
||||
# configuration options and their associated syntax:
|
||||
# https://github.com/iv-org/invidious/blob/master/config/config.example.yml
|
||||
INVIDIOUS_CONFIG: |
|
||||
db:
|
||||
dbname: invidious
|
||||
user: kemal
|
||||
password: kemal
|
||||
host: invidious-db
|
||||
port: 5432
|
||||
check_tables: true
|
||||
# external_port:
|
||||
# domain:
|
||||
# https_only: false
|
||||
# statistics_enabled: false
|
||||
healthcheck:
|
||||
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
depends_on:
|
||||
- postgres
|
||||
- invidious-db
|
||||
|
||||
invidious-db:
|
||||
image: docker.io/library/postgres:13
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgresdata:/var/lib/postgresql/data
|
||||
- ./config/sql:/config/sql
|
||||
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
|
||||
environment:
|
||||
POSTGRES_DB: invidious
|
||||
POSTGRES_USER: kemal
|
||||
POSTGRES_PASSWORD: kemal
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
|
||||
volumes:
|
||||
postgresdata:
|
||||
|
||||
@@ -1,15 +1,54 @@
|
||||
FROM archlinux/base
|
||||
FROM crystallang/crystal:1.4.1-alpine AS builder
|
||||
RUN apk add --no-cache sqlite-static yaml-static
|
||||
|
||||
RUN pacman -Sy --noconfirm shards crystal imagemagick librsvg \
|
||||
which pkgconf gcc ttf-liberation
|
||||
# base-devel contains many other basic packages, that are normally assumed to already exist on a clean arch system
|
||||
|
||||
ADD . /invidious
|
||||
ARG release
|
||||
|
||||
WORKDIR /invidious
|
||||
COPY ./shard.yml ./shard.yml
|
||||
COPY ./shard.lock ./shard.lock
|
||||
RUN shards install --production
|
||||
|
||||
RUN sed -i 's/host: localhost/host: postgres/' config/config.yml && \
|
||||
shards update && shards install && \
|
||||
crystal build src/invidious.cr
|
||||
COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a
|
||||
|
||||
COPY ./src/ ./src/
|
||||
# TODO: .git folder is required for building – this is destructive.
|
||||
# See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION.
|
||||
COPY ./.git/ ./.git/
|
||||
|
||||
# Required for fetching player dependencies
|
||||
COPY ./scripts/ ./scripts/
|
||||
COPY ./assets/ ./assets/
|
||||
COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
|
||||
|
||||
RUN crystal spec --warnings all \
|
||||
--link-flags "-lxml2 -llzma"
|
||||
|
||||
RUN if [ "${release}" == 1 ] ; then \
|
||||
crystal build ./src/invidious.cr \
|
||||
--release \
|
||||
--static --warnings all \
|
||||
--link-flags "-lxml2 -llzma"; \
|
||||
else \
|
||||
crystal build ./src/invidious.cr \
|
||||
--static --warnings all \
|
||||
--link-flags "-lxml2 -llzma"; \
|
||||
fi
|
||||
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache librsvg ttf-opensans
|
||||
WORKDIR /invidious
|
||||
RUN addgroup -g 1000 -S invidious && \
|
||||
adduser -u 1000 -S invidious -G invidious
|
||||
COPY --chown=invidious ./config/config.* ./config/
|
||||
RUN mv -n config/config.example.yml config/config.yml
|
||||
RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: invidious-db/' config/config.yml
|
||||
COPY ./config/sql/ ./config/sql/
|
||||
COPY ./locales/ ./locales/
|
||||
COPY --from=builder /invidious/assets ./assets/
|
||||
COPY --from=builder /invidious/invidious .
|
||||
RUN chmod o+rX -R ./assets ./config ./locales
|
||||
|
||||
EXPOSE 3000
|
||||
USER invidious
|
||||
CMD [ "/invidious/invidious" ]
|
||||
|
||||
53
docker/Dockerfile.arm64
Normal file
53
docker/Dockerfile.arm64
Normal file
@@ -0,0 +1,53 @@
|
||||
FROM alpine:3.16 AS builder
|
||||
RUN apk add --no-cache 'crystal=1.4.1-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev
|
||||
|
||||
ARG release
|
||||
|
||||
WORKDIR /invidious
|
||||
COPY ./shard.yml ./shard.yml
|
||||
COPY ./shard.lock ./shard.lock
|
||||
RUN shards install --production
|
||||
|
||||
COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a
|
||||
|
||||
COPY ./src/ ./src/
|
||||
# TODO: .git folder is required for building – this is destructive.
|
||||
# See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION.
|
||||
COPY ./.git/ ./.git/
|
||||
|
||||
# Required for fetching player dependencies
|
||||
COPY ./scripts/ ./scripts/
|
||||
COPY ./assets/ ./assets/
|
||||
COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
|
||||
|
||||
RUN crystal spec --warnings all \
|
||||
--link-flags "-lxml2 -llzma"
|
||||
|
||||
RUN if [ ${release} == 1 ] ; then \
|
||||
crystal build ./src/invidious.cr \
|
||||
--release \
|
||||
--static --warnings all \
|
||||
--link-flags "-lxml2 -llzma"; \
|
||||
else \
|
||||
crystal build ./src/invidious.cr \
|
||||
--static --warnings all \
|
||||
--link-flags "-lxml2 -llzma"; \
|
||||
fi
|
||||
|
||||
FROM alpine:3.16
|
||||
RUN apk add --no-cache librsvg ttf-opensans
|
||||
WORKDIR /invidious
|
||||
RUN addgroup -g 1000 -S invidious && \
|
||||
adduser -u 1000 -S invidious -G invidious
|
||||
COPY --chown=invidious ./config/config.* ./config/
|
||||
RUN mv -n config/config.example.yml config/config.yml
|
||||
RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: invidious-db/' config/config.yml
|
||||
COPY ./config/sql/ ./config/sql/
|
||||
COPY ./locales/ ./locales/
|
||||
COPY --from=builder /invidious/assets ./assets/
|
||||
COPY --from=builder /invidious/invidious .
|
||||
RUN chmod o+rX -R ./assets ./config ./locales
|
||||
|
||||
EXPOSE 3000
|
||||
USER invidious
|
||||
CMD [ "/invidious/invidious" ]
|
||||
@@ -1,9 +0,0 @@
|
||||
FROM postgres:10
|
||||
|
||||
ENV POSTGRES_USER postgres
|
||||
|
||||
ADD ./config/sql /config/sql
|
||||
ADD ./docker/entrypoint.postgres.sh /entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
CMD [ "postgres" ]
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CMD="$@"
|
||||
if [ ! -f /var/lib/postgresql/data/setupFinished ]; then
|
||||
echo "### first run - setting up invidious database"
|
||||
/usr/local/bin/docker-entrypoint.sh postgres &
|
||||
sleep 10
|
||||
until runuser -l postgres -c 'pg_isready' 2>/dev/null; do
|
||||
>&2 echo "### Postgres is unavailable - waiting"
|
||||
sleep 5
|
||||
done
|
||||
>&2 echo "### importing table schemas"
|
||||
su postgres -c 'createdb invidious'
|
||||
su postgres -c 'psql -c "CREATE USER kemal WITH PASSWORD '"'kemal'"'"'
|
||||
su postgres -c 'psql invidious < config/sql/channels.sql'
|
||||
su postgres -c 'psql invidious < config/sql/videos.sql'
|
||||
su postgres -c 'psql invidious < config/sql/channel_videos.sql'
|
||||
su postgres -c 'psql invidious < config/sql/users.sql'
|
||||
su postgres -c 'psql invidious < config/sql/session_ids.sql'
|
||||
su postgres -c 'psql invidious < config/sql/nonces.sql'
|
||||
touch /var/lib/postgresql/data/setupFinished
|
||||
echo "### invidious database setup finished"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "running postgres /usr/local/bin/docker-entrypoint.sh $CMD"
|
||||
exec /usr/local/bin/docker-entrypoint.sh $CMD
|
||||
12
docker/init-invidious-db.sh
Executable file
12
docker/init-invidious-db.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channels.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/videos.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channel_videos.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/users.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/session_ids.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/nonces.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/annotations.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlists.sql
|
||||
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlist_videos.sql
|
||||
1
kubernetes/.gitignore
vendored
Normal file
1
kubernetes/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/charts/*.tgz
|
||||
6
kubernetes/Chart.lock
Normal file
6
kubernetes/Chart.lock
Normal file
@@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://charts.bitnami.com/bitnami/
|
||||
version: 11.1.3
|
||||
digest: sha256:79061645472b6fb342d45e8e5b3aacd018ef5067193e46a060bccdc99fe7f6e1
|
||||
generated: "2022-03-02T05:57:20.081432389+13:00"
|
||||
22
kubernetes/Chart.yaml
Normal file
22
kubernetes/Chart.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
apiVersion: v2
|
||||
name: invidious
|
||||
description: Invidious is an alternative front-end to YouTube
|
||||
version: 1.1.1
|
||||
appVersion: 0.20.1
|
||||
keywords:
|
||||
- youtube
|
||||
- proxy
|
||||
- video
|
||||
- privacy
|
||||
home: https://invidio.us/
|
||||
icon: https://raw.githubusercontent.com/iv-org/invidious/05988c1c49851b7d0094fca16aeaf6382a7f64ab/assets/favicon-32x32.png
|
||||
sources:
|
||||
- https://github.com/iv-org/invidious
|
||||
maintainers:
|
||||
- name: Leon Klingele
|
||||
email: mail@leonklingele.de
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: ~11.1.3
|
||||
repository: "https://charts.bitnami.com/bitnami/"
|
||||
engine: gotpl
|
||||
41
kubernetes/README.md
Normal file
41
kubernetes/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Invidious Helm chart
|
||||
|
||||
Easily deploy Invidious to Kubernetes.
|
||||
|
||||
## Installing Helm chart
|
||||
|
||||
```sh
|
||||
# Build Helm dependencies
|
||||
$ helm dep build
|
||||
|
||||
# Add PostgreSQL init scripts
|
||||
$ kubectl create configmap invidious-postgresql-init \
|
||||
--from-file=../config/sql/channels.sql \
|
||||
--from-file=../config/sql/videos.sql \
|
||||
--from-file=../config/sql/channel_videos.sql \
|
||||
--from-file=../config/sql/users.sql \
|
||||
--from-file=../config/sql/session_ids.sql \
|
||||
--from-file=../config/sql/nonces.sql \
|
||||
--from-file=../config/sql/annotations.sql \
|
||||
--from-file=../config/sql/playlists.sql \
|
||||
--from-file=../config/sql/playlist_videos.sql
|
||||
|
||||
# Install Helm app to your Kubernetes cluster
|
||||
$ helm install invidious ./
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
```sh
|
||||
# Upgrading is easy, too!
|
||||
$ helm upgrade invidious ./
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```sh
|
||||
# Get rid of everything (except database)
|
||||
$ helm delete invidious
|
||||
|
||||
# To also delete the database, remove all invidious-postgresql PVCs
|
||||
```
|
||||
16
kubernetes/templates/_helpers.tpl
Normal file
16
kubernetes/templates/_helpers.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "invidious.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
*/}}
|
||||
{{- define "invidious.fullname" -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
11
kubernetes/templates/configmap.yaml
Normal file
11
kubernetes/templates/configmap.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
labels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
release: {{ .Release.Name }}
|
||||
data:
|
||||
INVIDIOUS_CONFIG: |
|
||||
{{ toYaml .Values.config | indent 4 }}
|
||||
61
kubernetes/templates/deployment.yaml
Normal file
61
kubernetes/templates/deployment.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
labels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: {{ .Values.securityContext.runAsUser }}
|
||||
runAsGroup: {{ .Values.securityContext.runAsGroup }}
|
||||
fsGroup: {{ .Values.securityContext.fsGroup }}
|
||||
initContainers:
|
||||
- name: wait-for-postgresql
|
||||
image: postgres
|
||||
args:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- until pg_isready -h {{ .Values.config.db.host }} -p {{ .Values.config.db.port }} -U {{ .Values.config.db.user }}; do echo waiting for database; sleep 2; done;
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: INVIDIOUS_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: INVIDIOUS_CONFIG
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: {{ .Values.securityContext.allowPrivilegeEscalation }}
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 10 }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 3000
|
||||
path: /
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 3000
|
||||
path: /
|
||||
initialDelaySeconds: 15
|
||||
restartPolicy: Always
|
||||
18
kubernetes/templates/hpa.yaml
Normal file
18
kubernetes/templates/hpa.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
labels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
targetCPUUtilizationPercentage: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
20
kubernetes/templates/service.yaml
Normal file
20
kubernetes/templates/service.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "invidious.fullname" . }}
|
||||
labels:
|
||||
app: {{ template "invidious.name" . }}
|
||||
chart: {{ .Chart.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: {{ template "invidious.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- if .Values.service.loadBalancerIP }}
|
||||
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
|
||||
{{- end }}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user