mirror of
https://codeberg.org/ashley/poke
synced 2026-05-01 23:47:57 +00:00
Compare commits
628 Commits
videojs
...
c2aa651b68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2aa651b68 | ||
|
|
33cb84b340 | ||
|
|
23cf962230 | ||
|
|
2be8727949 | ||
|
|
7677d27698 | ||
|
|
0fdb869af7 | ||
|
|
7ee79d8013 | ||
|
|
79804c063c | ||
|
|
98d5df7a96 | ||
|
|
d451401186 | ||
|
|
046c35119b | ||
|
|
11faafeaff | ||
|
|
ec4d53cbed | ||
|
|
35b83a5d3c | ||
|
|
64b0529e1b | ||
|
|
bbdb3be59d | ||
|
|
10eee1e4b7 | ||
|
|
d564e49d09 | ||
|
|
43b8b641a7 | ||
|
|
a3b44a58ee | ||
|
|
b75dc35a6b | ||
|
|
f7831ad85e | ||
|
|
07490ec727 | ||
|
|
0654c381c7 | ||
|
|
26558507db | ||
|
|
daae5abff1 | ||
|
|
50597f59a7 | ||
|
|
10a18905ec | ||
|
|
f661ee76a9 | ||
|
|
024bea2b33 | ||
|
|
488d98d1f1 | ||
|
|
9ec1a3a718 | ||
|
|
bb28ed0de6 | ||
|
|
43c2b1111e | ||
|
|
3f471059f8 | ||
|
|
46dc456562 | ||
|
|
0c346781b8 | ||
|
|
6824df229b | ||
|
|
00749f907d | ||
|
|
e50e7b730a | ||
|
|
dcf3b20fac | ||
|
|
b91128ff11 | ||
|
|
46abf16976 | ||
|
|
f9d434903d | ||
|
|
dd5aea5304 | ||
|
|
84b92ae3ac | ||
|
|
f05f3e5b3a | ||
|
|
b4d7b3da11 | ||
|
|
bb1d09ea20 | ||
|
|
d708f964eb | ||
|
|
e4da5d48d4 | ||
|
|
ac979c3bdc | ||
|
|
f31ff98a0b | ||
|
|
cc3922ad0e | ||
|
|
6e1aecaeb4 | ||
|
|
c9e8b8f85d | ||
|
|
99f4d03bbb | ||
|
|
efd3c59df7 | ||
|
|
a5a18ba383 | ||
|
|
c531678a18 | ||
|
|
c9bd527527 | ||
|
|
495367eace | ||
|
|
547f68c882 | ||
|
|
6268bdebfb | ||
|
|
7527e49c9c | ||
|
|
7125ecc36e | ||
|
|
4058e8ef11 | ||
|
|
0527a9e52f | ||
|
|
230d371ced | ||
|
|
4a8070e65c | ||
|
|
556285a8d1 | ||
|
|
3076fb9393 | ||
|
|
777e8522b2 | ||
|
|
efe0f7d758 | ||
|
|
38094732ba | ||
|
|
b8ce39698a | ||
|
|
4519933c9e | ||
|
|
18635eca02 | ||
|
|
2202193d23 | ||
|
|
b1de0358a7 | ||
|
|
739d64f684 | ||
|
|
d539547fd3 | ||
|
|
c5e67b7f05 | ||
|
|
d31253551c | ||
|
|
0f40a4a152 | ||
|
|
6e627b48a7 | ||
|
|
704b7bc806 | ||
|
|
f77d5e0260 | ||
|
|
5ab4cc30a5 | ||
|
|
afa738f8c2 | ||
|
|
3f19854798 | ||
|
|
6801889f64 | ||
|
|
ce5428eccb | ||
|
|
3f66e88dae | ||
|
|
7ef352f3cb | ||
|
|
38871bba36 | ||
|
|
e755ef3f4c | ||
|
|
64433797f3 | ||
|
|
3f69953875 | ||
|
|
b69b2a590e | ||
|
|
3558b47301 | ||
|
|
acef288683 | ||
|
|
27becd05a6 | ||
|
|
0347a9ab94 | ||
|
|
e775e617ed | ||
|
|
59dd95fa39 | ||
|
|
fd1f3ea864 | ||
|
|
310ffb643c | ||
|
|
4bf687b4a3 | ||
|
|
4acf840d80 | ||
|
|
4b33b38e1f | ||
|
|
7188bfdf03 | ||
|
|
440431abbd | ||
|
|
172a7175ec | ||
|
|
b5e09f60eb | ||
|
|
906f7b237e | ||
|
|
28e4ffb4eb | ||
|
|
9747dc7f47 | ||
|
|
9f1a52d840 | ||
|
|
461cec77b2 | ||
|
|
73278c70ee | ||
|
|
953ee0daaf | ||
|
|
e5e55ce26e | ||
|
|
5104cbc3c7 | ||
|
|
de0afd6266 | ||
|
|
4c6f21c5ee | ||
|
|
b42ad79716 | ||
|
|
a9339ff5ae | ||
|
|
1ff9b80fdd | ||
|
|
07bf8bc42d | ||
|
|
64e258bb5b | ||
|
|
ad3ca4635e | ||
|
|
1331fb4c43 | ||
|
|
f9c22c8bf8 | ||
|
|
46a2763802 | ||
|
|
49d399e388 | ||
|
|
0fd7ea8849 | ||
|
|
16dabbd087 | ||
|
|
a638c930cc | ||
|
|
22e804850b | ||
|
|
a28f8e2536 | ||
|
|
d477b56af0 | ||
|
|
fce3396c30 | ||
|
|
85cee23ab0 | ||
|
|
96b01e74b9 | ||
|
|
84fdbb5c40 | ||
|
|
7cdfdb9359 | ||
|
|
f50386a94f | ||
|
|
9e7240ecdb | ||
|
|
bc7ffd16e3 | ||
|
|
663cbe3cde | ||
|
|
b4264b7bf3 | ||
|
|
6f96374fa4 | ||
|
|
8ca00ef881 | ||
|
|
cad92a9ad2 | ||
|
|
dfe5608681 | ||
|
|
a931643afc | ||
|
|
fef0cc3edc | ||
|
|
faad2b4d80 | ||
|
|
001a22e325 | ||
|
|
9d62bb5a88 | ||
|
|
e9d140876c | ||
|
|
36b447ed3a | ||
|
|
c06c1ddb0e | ||
|
|
78b42e1de4 | ||
|
|
b997f4a9ba | ||
|
|
6fcc6e0b40 | ||
|
|
aff56b1e10 | ||
|
|
610d58390d | ||
|
|
5b9da606fd | ||
|
|
bbc27dc37f | ||
|
|
d622ed4f38 | ||
|
|
ae1ef1f057 | ||
|
|
4adf09b4c9 | ||
|
|
38a7d7c3af | ||
|
|
b601d2354e | ||
|
|
7ed5e35be8 | ||
|
|
60f4c2adfa | ||
|
|
edb1f1a4c8 | ||
|
|
3872e7cb64 | ||
|
|
1cde1c23ef | ||
|
|
de98d86101 | ||
|
|
a0191ae39a | ||
|
|
9a9d00e73f | ||
|
|
2fd11f9679 | ||
|
|
fca41df817 | ||
|
|
35d175e2d5 | ||
|
|
5a476089b4 | ||
|
|
bcd634e683 | ||
|
|
e3b68cb658 | ||
|
|
4827e9c626 | ||
|
|
9b4660da5c | ||
|
|
002ca6a4b4 | ||
|
|
f2c9a99245 | ||
|
|
8a10da5a24 | ||
|
|
d94933dd25 | ||
|
|
69decd0db9 | ||
|
|
f871564712 | ||
|
|
981d23bc4a | ||
|
|
42feec4d78 | ||
|
|
373b915a79 | ||
|
|
b94a57d1fa | ||
|
|
3f323d5d40 | ||
|
|
c0d847250a | ||
|
|
df4d228861 | ||
|
|
8228907bfe | ||
|
|
1462834ad2 | ||
|
|
82b141717d | ||
|
|
53b75a10fb | ||
|
|
65219997c9 | ||
|
|
1fae26c530 | ||
|
|
2c4f30a813 | ||
|
|
4fc34f1a4f | ||
|
|
d8e56cf530 | ||
|
|
4314ae9b01 | ||
|
|
cd6eac25a8 | ||
|
|
3796095cbb | ||
|
|
d8f93dedd1 | ||
|
|
aeddb5e612 | ||
|
|
a298fd5e4a | ||
|
|
2702071e11 | ||
|
|
ebb1bf369b | ||
|
|
53cb8665ff | ||
|
|
4e4dee2879 | ||
|
|
7e98929101 | ||
|
|
19f43779b1 | ||
|
|
ffab316b42 | ||
|
|
e0e0200568 | ||
|
|
c393cd3ca7 | ||
|
|
2e763de66b | ||
|
|
b0e760325f | ||
|
|
d2bcde4944 | ||
|
|
d6e35c5aa3 | ||
|
|
c21f028786 | ||
|
|
fec30d4d92 | ||
|
|
737da26f6f | ||
|
|
5ad8f63563 | ||
|
|
0b0aa6326c | ||
|
|
b742f91764 | ||
|
|
67a98c9d5d | ||
|
|
f207ea4998 | ||
|
|
8b5d3fa0cc | ||
|
|
e7d3b07130 | ||
|
|
0cd5c523f9 | ||
|
|
28dd44c91e | ||
|
|
93f3efdb3e | ||
|
|
df6bfafedb | ||
|
|
8b0134bb11 | ||
|
|
af49f2c4ae | ||
|
|
f9199b1d5d | ||
|
|
b783cfaab1 | ||
|
|
2be1cf2d95 | ||
|
|
c930e55c46 | ||
|
|
72fe45cb9e | ||
|
|
3635d37245 | ||
|
|
c91b1423a4 | ||
|
|
7f9933ae16 | ||
|
|
e4f33378bd | ||
|
|
d4c24d7241 | ||
|
|
1403c2ab24 | ||
|
|
7d8b4945d3 | ||
|
|
6a9f35dae1 | ||
|
|
2aa943f141 | ||
|
|
06d1770c0e | ||
|
|
3ddc1e072d | ||
|
|
0a35ab0cf0 | ||
|
|
9cdc85ca91 | ||
|
|
3c2564f170 | ||
|
|
786704bcde | ||
|
|
27ae6acc87 | ||
|
|
d5dd9303b8 | ||
|
|
3e75a28a12 | ||
|
|
5d9880304a | ||
|
|
ed06fcb92c | ||
|
|
2d78a1a7f6 | ||
|
|
4e8af0337a | ||
|
|
ddbb533e6d | ||
|
|
c1faf2bbcd | ||
|
|
8e6cade155 | ||
|
|
59b746fd1c | ||
|
|
4b1765757f | ||
|
|
5bb5dacd30 | ||
|
|
f5f88fe072 | ||
|
|
b4d566f41f | ||
|
|
038b44cf98 | ||
|
|
8d3d4961e5 | ||
|
|
4a821d700d | ||
|
|
1df1b8af2d | ||
|
|
fa8038f001 | ||
|
|
9de47441af | ||
|
|
76a6f4b101 | ||
|
|
1a19f51072 | ||
|
|
4bb8d742da | ||
|
|
c92caf80c5 | ||
|
|
f5e98c8fe2 | ||
|
|
4a6c62f68b | ||
|
|
e1be2a6979 | ||
|
|
98f80c3ced | ||
|
|
4a55d5c92c | ||
|
|
adc02ed151 | ||
|
|
00a7738a1d | ||
|
|
5bf3da0dc0 | ||
|
|
4c2001ceee | ||
|
|
0082765bf6 | ||
|
|
2008aabb74 | ||
|
|
c33916b5db | ||
|
|
3cee9ae5b5 | ||
|
|
cf6420cde1 | ||
|
|
dc66c4582f | ||
|
|
48c00ce59f | ||
|
|
e9c9cac4fd | ||
|
|
75d05d3c85 | ||
|
|
2941fa7b86 | ||
|
|
308518f6cd | ||
|
|
2e02fd3e23 | ||
|
|
8bd284f67e | ||
|
|
5887f05fe6 | ||
|
|
0183286bb2 | ||
|
|
0f54d186d1 | ||
|
|
6cb5ea0ba1 | ||
|
|
ae06025b0e | ||
|
|
8b596806a5 | ||
|
|
bda8921b34 | ||
|
|
e5bf497462 | ||
|
|
3612e5e837 | ||
|
|
96ece4f570 | ||
|
|
727a6a6727 | ||
|
|
db26363c95 | ||
|
|
50eaf51c5f | ||
|
|
5727480778 | ||
|
|
4d1080fcd2 | ||
|
|
1f457865df | ||
|
|
a2177f2c3d | ||
|
|
6692cc525b | ||
|
|
5a486263e1 | ||
|
|
0c628a9cd0 | ||
|
|
809a0d57c9 | ||
|
|
750dc09ef6 | ||
|
|
56fa4b8993 | ||
|
|
aea2181389 | ||
|
|
bb8c50df8e | ||
|
|
1ce1ae5837 | ||
|
|
4e2f1b052e | ||
|
|
f5c31e24a9 | ||
|
|
9d8de3d898 | ||
|
|
5dc6a9e435 | ||
|
|
a771300e84 | ||
|
|
c2a693abb2 | ||
|
|
ba73e43725 | ||
|
|
2f0da0e6cc | ||
|
|
0c033002d4 | ||
|
|
0956a9b66e | ||
|
|
bf2af710c3 | ||
|
|
e59d1c0e1c | ||
|
|
878cca2760 | ||
|
|
6ffcbb1683 | ||
|
|
49de19ca5f | ||
|
|
c2682cdeac | ||
|
|
feb3242638 | ||
|
|
b25841a667 | ||
|
|
fdb2c1d12e | ||
|
|
d477c04ce4 | ||
|
|
9f1cbd77d8 | ||
|
|
aa3de021c7 | ||
|
|
54b92b1e29 | ||
|
|
93898da514 | ||
|
|
f3a5c690e3 | ||
|
|
75affcf7c7 | ||
|
|
4000c2d5ad | ||
|
|
43581eaa59 | ||
|
|
442ae884a3 | ||
|
|
a4223a2772 | ||
|
|
670343d2ff | ||
|
|
38dec52081 | ||
|
|
f792fe9172 | ||
|
|
d66d6fc0ff | ||
|
|
d7ca2fd473 | ||
|
|
9665d8c79f | ||
|
|
79ce4dc835 | ||
|
|
102ba2b9be | ||
|
|
1a6accc21b | ||
|
|
b86af7ec97 | ||
|
|
8d59e75054 | ||
|
|
2f56a1cc9a | ||
|
|
9602febccb | ||
|
|
ae7ad7bb31 | ||
|
|
a6961fb849 | ||
|
|
4a0513ff53 | ||
|
|
6cc78e33d3 | ||
|
|
09ddd96d95 | ||
|
|
12be559b2f | ||
|
|
9d5a3db53a | ||
|
|
bdca2bc4c6 | ||
|
|
e2388890a8 | ||
|
|
70f02e3162 | ||
|
|
f30f724bac | ||
|
|
93da9b7078 | ||
|
|
5f7321e96d | ||
|
|
134a9d9e0e | ||
|
|
61428f2c5d | ||
|
|
524715b393 | ||
|
|
eccd7ded4c | ||
|
|
3956897066 | ||
|
|
8f5d7de44d | ||
|
|
0d08532765 | ||
|
|
e90976a2a4 | ||
|
|
62702ef128 | ||
|
|
deedb165e7 | ||
|
|
be66d1aafc | ||
|
|
a2fd10382b | ||
|
|
77be7688e5 | ||
|
|
58405cfeaf | ||
|
|
0898586a70 | ||
|
|
bb95c21a78 | ||
|
|
690f6eaf94 | ||
|
|
4a9f666624 | ||
|
|
eb7f0e6019 | ||
|
|
79ba510788 | ||
|
|
f97b582260 | ||
|
|
2bfdb92de1 | ||
|
|
211d0f6305 | ||
|
|
bfa34ebe74 | ||
|
|
d74c9cb2e0 | ||
|
|
d1aca04dfb | ||
|
|
9183634701 | ||
|
|
2d6ccb5cf6 | ||
|
|
c0bb5d7179 | ||
|
|
c3f1f485e8 | ||
|
|
8069c21f94 | ||
|
|
291f4eed43 | ||
|
|
5aca984206 | ||
|
|
97d706f60b | ||
|
|
df50735df7 | ||
|
|
7df849531e | ||
|
|
9b9f42837a | ||
|
|
2c9b109c74 | ||
|
|
fbbf9a46db | ||
|
|
450b3900eb | ||
|
|
9cfdfde9a9 | ||
|
|
03fb903de7 | ||
|
|
f20efd8b16 | ||
|
|
055e64ba5d | ||
|
|
513e028a73 | ||
|
|
8d3e3ff5fc | ||
|
|
d5e5c49f38 | ||
|
|
ac408b3dfa | ||
|
|
97eee7f1e3 | ||
|
|
73d728650b | ||
|
|
23ab483a7d | ||
|
|
d66de07e20 | ||
|
|
6a73db8ab3 | ||
|
|
7b61bb3b4e | ||
|
|
fb9f5a5cd4 | ||
|
|
b828caa86e | ||
|
|
d01a34f9b5 | ||
|
|
1370c899fa | ||
|
|
9e3eeb277e | ||
|
|
cb67431323 | ||
|
|
c3b0209489 | ||
|
|
9e5dd4f5f1 | ||
|
|
1642b99e1b | ||
|
|
bf7073597c | ||
|
|
ab61946366 | ||
|
|
587132d85a | ||
|
|
8d62e93d50 | ||
|
|
4d0a66bcbd | ||
|
|
13b34adc6b | ||
|
|
3e0af4d807 | ||
|
|
86be21db2c | ||
|
|
76e90c109e | ||
|
|
5b7ed14da8 | ||
|
|
78802224c5 | ||
|
|
9278f62c3b | ||
|
|
31933b9199 | ||
|
|
780963cbbe | ||
|
|
f124787087 | ||
|
|
d648f1242b | ||
|
|
8c0332cbd5 | ||
|
|
dcaa51a303 | ||
|
|
6b1a1008ea | ||
|
|
a215c19082 | ||
|
|
3b9cff1c24 | ||
|
|
c2866de248 | ||
|
|
5e982b7301 | ||
|
|
9901c091bc | ||
|
|
28767cb850 | ||
|
|
599b5cac54 | ||
|
|
acb2cd0db3 | ||
|
|
37745ccd05 | ||
|
|
24810b4cfe | ||
|
|
6bf345e94f | ||
|
|
8a67797707 | ||
|
|
1ce1bfa7aa | ||
|
|
df04819b26 | ||
|
|
49cf22446c | ||
|
|
36f750fd11 | ||
|
|
11c3e561e9 | ||
|
|
4f901e602a | ||
|
|
ad28847a67 | ||
|
|
db151c1ec2 | ||
|
|
18785ff8c7 | ||
|
|
adecc668f1 | ||
|
|
8d43e65e5b | ||
|
|
826e4dfc1a | ||
|
|
7be4184ac0 | ||
|
|
3dad202d86 | ||
|
|
2ad1df61a8 | ||
|
|
2fd231f1d9 | ||
|
|
47009054da | ||
|
|
1db888809c | ||
|
|
0b8f307632 | ||
|
|
f74efd2db0 | ||
|
|
c53d1a57c9 | ||
|
|
9c77e01076 | ||
|
|
05b753c210 | ||
|
|
e9fbc65755 | ||
|
|
b17f7f9548 | ||
|
|
70254d8965 | ||
|
|
d3d6f6a91c | ||
|
|
83f24de9dd | ||
|
|
7849af14f6 | ||
|
|
872ba191b3 | ||
|
|
93243ed7db | ||
|
|
79b209fabe | ||
|
|
ffa846d96d | ||
|
|
6ca3d6a716 | ||
|
|
3eba5ec6f5 | ||
|
|
455d93b15b | ||
|
|
0e1d3b2961 | ||
|
|
be142c3695 | ||
|
|
55cdded315 | ||
|
|
50f0345f91 | ||
|
|
20d6128206 | ||
|
|
a60ce8a9d9 | ||
|
|
90f5f08160 | ||
|
|
13b28550e9 | ||
|
|
819533f0a2 | ||
|
|
4c00425f66 | ||
|
|
ed2620e44f | ||
|
|
4006cf9c93 | ||
|
|
4a94d4b290 | ||
|
|
5f5fe6d555 | ||
|
|
e410bdc95a | ||
|
|
8badc1dab5 | ||
|
|
2eb634d12e | ||
|
|
3b6c9789f9 | ||
|
|
cd811ba381 | ||
|
|
f183ee0c18 | ||
|
|
7019791f44 | ||
|
|
12604c4a46 | ||
|
|
430d27d303 | ||
|
|
0a415383a9 | ||
|
|
0a29a4de21 | ||
|
|
4f3ae7ca61 | ||
|
|
b2d5f7ef05 | ||
|
|
4fc92bb6bd | ||
|
|
d8bf3d83df | ||
|
|
aba0ca0986 | ||
|
|
9bd369b553 | ||
|
|
5fe05620cc | ||
|
|
9275d6cfe8 | ||
|
|
7dc7180870 | ||
|
|
5fc2a56b17 | ||
|
|
73ad994113 | ||
|
|
e0d9a74907 | ||
|
|
0f6f9d4ee8 | ||
|
|
92fd9c8f0f | ||
|
|
84057e8649 | ||
|
|
00e8afb72e | ||
|
|
50475cc654 | ||
|
|
c1a77f2076 | ||
|
|
47fa155aa7 | ||
|
|
ee73dd6770 | ||
|
|
8da7fd6030 | ||
|
|
653de060c5 | ||
|
|
f204c94415 | ||
|
|
d1d83c2dea | ||
|
|
cfd9e0636f | ||
|
|
54ad94ed13 | ||
|
|
eeafcef5c9 | ||
|
|
0be7e45bf8 | ||
|
|
29c16b386c | ||
|
|
a05d27f34f | ||
|
|
8147eb8a00 | ||
|
|
a617d8e887 | ||
|
|
8dfaf532d8 | ||
|
|
adbf2784fc | ||
|
|
56e66dbf10 | ||
|
|
15e1d7e237 | ||
|
|
7f8edf6b55 | ||
|
|
e38f3dea87 | ||
|
|
1d0d76832d | ||
|
|
fa6a64c5f3 | ||
|
|
2950cfb808 | ||
|
|
890aec6a30 | ||
|
|
87eeac58a3 | ||
|
|
c7b01a6af3 | ||
|
|
eda2b4f887 | ||
|
|
5a1555f93e | ||
|
|
e4c3aab9ee | ||
|
|
c40c1de959 | ||
|
|
9d1a5c3e66 | ||
|
|
eaacf84ea9 | ||
|
|
4bc351de27 | ||
|
|
1a9278bb49 | ||
|
|
2720bd1c23 | ||
|
|
def048716a | ||
|
|
0f26c1904b | ||
|
|
7e702d93c5 | ||
|
|
7ea1d89495 | ||
|
|
4ac19500e3 | ||
|
|
c264af305d | ||
|
|
d383c1ec2f | ||
|
|
a43fbf5dfc | ||
|
|
fb8b797891 | ||
|
|
92b398809f | ||
|
|
7452a2c18e | ||
|
|
cef4b4c97d | ||
|
|
33838c0b1d | ||
|
|
84c49a0007 | ||
|
|
063d83d851 | ||
|
|
e0aa8cd3d5 | ||
|
|
538222e1a1 | ||
|
|
8166cf6f3c | ||
|
|
b0d58a61cb | ||
|
|
99a59740ae | ||
|
|
2496c9efc3 | ||
|
|
6c0a84fc99 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ yarn.lock
|
||||
package-lock.json
|
||||
.env
|
||||
json.sqlite
|
||||
config.json
|
||||
13
LICENSE-APPSTORE.md
Normal file
13
LICENSE-APPSTORE.md
Normal file
@@ -0,0 +1,13 @@
|
||||
The developers are aware that the terms of service that apply to apps distributed via Apple's App Store services and similar app stores may conflict
|
||||
with rights granted under the Poke license, the GNU General
|
||||
Public License, version 3.
|
||||
|
||||
The copyright holders of the Poke project do not wish this conflict to prevent the otherwise-compliant distribution of derived apps via the App Store and similar app stores.
|
||||
|
||||
Therefore, we have committed not to pursue any license
|
||||
violation that results solely from the conflict between the GNU GPLv3
|
||||
and the Apple App Store terms of service or similar app stores. In
|
||||
other words, as long as you comply with the GPL in all other respects,
|
||||
including its requirements to provide users with source code and the
|
||||
text of the license, we will not object to your distribution of the
|
||||
Poke project through the App Store.
|
||||
52
README.md
52
README.md
@@ -6,7 +6,7 @@
|
||||
<img src="https://static.fsf.org/dbd/label/DRM-free%20label%20120.en.png"
|
||||
alt="DRM Free" width="65" height="65" border="0" align="middle" />
|
||||
</a>
|
||||
<p>PRIVACY APP OF YOUR DREAMS! :3</p>
|
||||
<p>THE PRIVACY APP OF YOUR DREAMS! :3</p>
|
||||
</h1>
|
||||
|
||||
<div align="center">
|
||||
@@ -16,12 +16,9 @@
|
||||
<div align="center">
|
||||
<a href="#welcome">Welcome!</a> | <a href="#features">Features</a> | <a href="#no-non-free-codec-needed">No Non-Free Codec</a> | <a href="#hosting-poke">Hosting</a> | <a href="#poke-community">Community</a> | <a href="#legal">Legal</a>
|
||||
<br><br>
|
||||
<a href="https://status.poketube.fun" target="_blank">
|
||||
<img width="170" src="https://api.netweak.com/status-badges/K2LY9" alt="Status Badge"/>
|
||||
</a>
|
||||
<img src="https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg" alt="Stand with Ukraine">
|
||||
<a href="./LICENSE">
|
||||
<img src="https://img.shields.io/badge/License-GPL--3-FF6666" alt="GPL-3 License">
|
||||
<img src="https://img.shields.io/badge/License-GPL--3-FF6666" alt="GPL-3-or-later License">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +26,7 @@
|
||||
|
||||
## Welcome!
|
||||
|
||||
Welcome to Poke (formerly PokeTube), the privacy-friendly YouTube front-end built with the InnerTube API! Imagine paying for YouTube Premium just to download videos - couldn't be us (literally).
|
||||
Welcome to Poke (formerly PokeTube), the privacy-friendly YouTube front-end built with the invidious API! Imagine paying for YouTube Premium just to download videos - couldn't be us (literally).
|
||||
|
||||
## Features
|
||||
|
||||
@@ -48,12 +45,23 @@ Poke uses OpenH264, which is free software! No non-free components included :3 V
|
||||
### NodeJS
|
||||
|
||||
1. **Install Packages**
|
||||
- Fedora/RHEL: `$ sudo dnf install git make gcc libcurl nodejs python libcurl4 g++`
|
||||
- Debian/Ubuntu: `$ sudo apt install git make gcc libcurl4-openssl-dev nodejs npm python g++`
|
||||
- Fedora/RHEL GNU/linux: `$ sudo dnf install git make gcc libcurl nodejs python libcurl g++ curl-config`
|
||||
- Debian/Ubuntu GNU/linux: `$ sudo apt install git make gcc libcurl4-openssl-dev nodejs npm python g++`
|
||||
- Alpine Linux (non-gnu): `$ apk add git nodejs npm python make gcc g++ libcurl curl-dev`
|
||||
|
||||
(we dont know if it works on non-gnu systems)
|
||||
|
||||
2. **Clone Repo**
|
||||
- Codeberg: `$ git clone https://codeberg.org/ashley/poke.git`
|
||||
- GitHub: `$ git clone https://github.com/ashley0143/poke.git`
|
||||
|
||||
reccomended unoffical mirrors:
|
||||
- code.lgbt: `$ git clone https://code.lgbt/mirror/poke.git` [sync every 10mins]
|
||||
- git.lgbt: `$ git clone https://git.lgbt/mirror/poke.git` [sync every 10mins]
|
||||
- nin0git :`$ git clone https://git.nin0.dev/mirrors/poke.git` [sync every 10mins]
|
||||
|
||||
not reccomended, unstable
|
||||
- SudoVanilla: `$ git clone https://ark.sudovanilla.org/Korbs/poke.git` [sync every week]
|
||||
|
||||
3. **Install Dependencies**
|
||||
- `$ cd poke`
|
||||
@@ -62,32 +70,21 @@ Poke uses OpenH264, which is free software! No non-free components included :3 V
|
||||
4. **Start Server**
|
||||
- `$ node server.js`
|
||||
|
||||
Congrats! PokeTube should now be running on `localhost:6003`! 🎉
|
||||
|
||||
### Docker
|
||||
|
||||
1. **Create Directory**
|
||||
- `$ mkdir poketube && cd poketube`
|
||||
|
||||
2. **Download Config**
|
||||
- `$ curl -O https://codeberg.org/Ashley/poke/raw/branch/main/docker-compose.yml`
|
||||
|
||||
3. **Run PokeTube**
|
||||
- `$ docker compose up -d`
|
||||
|
||||
Congrats! PokeTube should now be running on `localhost:6003`! 🎉
|
||||
|
||||
Congrats! Poke should now be running on `localhost:6003`! 🎉
|
||||
|
||||
## Poke Community
|
||||
|
||||
Join us on [Revolt](https://rvlt.gg/poketube) or [Matrix](https://matrix.to/#/#poke:vern.cc)! I promise we're cool! <3
|
||||
Join us on [Discord](https://discord.poketube.fun/) or [Matrix](https://matrix.to/#/#poke:vern.cc)! I promise we're cool! <3
|
||||
|
||||
|
||||
or if u like fedi, we host [PokeSocial](https://social.poketube.fun) as well :3
|
||||
|
||||
## The Legal Stuff (boring tbh)
|
||||
the main parts of the project is Under GPL-3.0-OR-LATER :3
|
||||
|
||||
see the each sections LICENSE tho!!
|
||||
Copyleft 2021-202x Poke Project
|
||||
see the each sections LICENSE tho!!
|
||||
|
||||
Copyleft 2021-202x Poke Project, mostly ashley0143 - poke does not support the ["source first"](https://sourcefirst.com/) or ["open source"](https://opensource.org) movement :3 - we support the free software movement (fsf.org)
|
||||
|
||||
|
||||
[Code Of conduct](https://codeberg.org/Ashley/poke/src/branch/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
@@ -104,6 +101,7 @@ We use the GNU Coding Standard Thingy, see [this link.](https://www.gnu.org/prep
|
||||
<img src="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/glitch-fastly-lock-up.svg">
|
||||
</a>
|
||||
<br><hr>
|
||||
<p> this software does not support <a href="https://opensource.org">"OSI"</a> - <a href="https://rationalwiki.org/wiki/Eric_S._Raymond#Open_Source">they are terrible people </a> - pls support the FSF/GNU (<a href="https://fsf.org">fsf.org</a> or <a href="https://gnu.org">gnu.org</a>) instead :3 </p>
|
||||
<a href="https://gnu.org/not-open-source">
|
||||
<img width="200" src="https://autumn.revolt.chat/attachments/eNpfwV2C1_wudONe43YCvWr-4vbvLpG78HbuXgOYfO">
|
||||
</a>
|
||||
|
||||
1
backend-services/README.md
Normal file
1
backend-services/README.md
Normal file
@@ -0,0 +1 @@
|
||||
haii!! these files are made for the poke's server - if u wanna use them on ur server u might have to change the directories :p
|
||||
40
backend-services/scripts/inv-refresh-token.sh
Normal file
40
backend-services/scripts/inv-refresh-token.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Copyright (C) 2024-20xx Poke! (https://codeberg.org/ashley/poke)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
scriptDir=$(dirname "$(readlink -f "$0")")
|
||||
|
||||
output=$(docker run quay.io/invidious/youtube-trusted-session-generator)
|
||||
|
||||
visitor_data=$(echo "$output" | grep -oP '(?<=visitor_data: )[^ ]+')
|
||||
po_token=$(echo "$output" | grep -oP '(?<=po_token: )[^ ]+')
|
||||
|
||||
if [ -z "$visitor_data" ] || [ -z "$po_token" ]; then
|
||||
echo "Error: Could not generate visitor_data or po_token."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -i "s/visitor_data: .*/visitor_data: $visitor_data/g" $scriptDir/../services/invidious/docker-compose.yml
|
||||
sed -i "s/po_token: .*/po_token: $po_token/g" $scriptDir/../services/invidious/docker-compose.yml
|
||||
|
||||
cd $scriptDir/../services/invidious
|
||||
|
||||
docker compose up -d
|
||||
|
||||
echo "Successfully updated visitor_data and po_token on Invidious."
|
||||
|
||||
164
backend-services/scripts/inv-restart-docker.sh
Normal file
164
backend-services/scripts/inv-restart-docker.sh
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Copyright (C) 2024-20xx Poke! (https://codeberg.org/ashley/poke)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Function to generate a random Chrome version
|
||||
generate_random_chrome_version() {
|
||||
major=$((RANDOM % 100 + 1)) # Major version 1-99
|
||||
minor=$((RANDOM % 100)) # Minor version 0-99
|
||||
build=$((RANDOM % 10000)) # Build version 0-9999
|
||||
patch=$((RANDOM % 100)) # Patch version 0-99
|
||||
echo "$major.$minor.$build.$patch"
|
||||
}
|
||||
|
||||
restart_services() {
|
||||
echo "Restarting services..."
|
||||
|
||||
# Navigate to the script directory
|
||||
scriptDir=$(dirname "$(readlink -f "$0")")
|
||||
|
||||
cd "$scriptDir/../services/invidious" || { echo "Error: Failed to navigate to $scriptDir/../services/invidious"; exit 1; }
|
||||
|
||||
docker compose down
|
||||
echo "Services stopped. Restarting..."
|
||||
|
||||
docker compose up -d
|
||||
echo "Services restarted successfully."
|
||||
|
||||
/home/qt/globe/scripts/inv-update-token.sh
|
||||
}
|
||||
|
||||
fetch_playlist() {
|
||||
local playlist_id="$1"
|
||||
response=$(curl -s -w "%{http_code}" -o /tmp/playlist_data.json "https://invid-api.poketube.fun/api/v1/playlists/${playlist_id}")
|
||||
|
||||
if [ "$response" -eq 502 ] || [ "$response" -eq 500 ] || [ "$response" -eq 403 ]; then
|
||||
echo "Error: Failed to fetch playlist data. HTTP Status: $response"
|
||||
restart_services
|
||||
return 1
|
||||
elif [ "$response" -ne 200 ]; then
|
||||
echo "Error: Failed to fetch playlist data. HTTP Status: $response"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_video_ids() {
|
||||
local json_data="$1"
|
||||
video_ids=$(jq -r '.videos[].videoId' "$json_data")
|
||||
if [ -z "$video_ids" ]; then
|
||||
echo "Error: Failed to extract video IDs from the playlist data."
|
||||
return 1
|
||||
fi
|
||||
echo "$video_ids"
|
||||
}
|
||||
|
||||
# Playlist IDs to fetch
|
||||
playlist_ids=("PLMws9SCqJ1JCeVMVPsdamuUM0HK0MbA6g")
|
||||
|
||||
# Base URL for the API
|
||||
base_url="http://localhost:54301/latest_version?id="
|
||||
|
||||
# Pick a random playlist (without using invalid options in shuf)
|
||||
random_playlist_id="PLMC9KNkIncKvYin_USF1qoJQnIyMAfRxl"
|
||||
echo "Randomly selected playlist: $random_playlist_id"
|
||||
|
||||
# Fetch playlist JSON data
|
||||
fetch_playlist "$random_playlist_id"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Playlist fetch failed. Restarting services..."
|
||||
restart_services # Restart services before exiting
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract video IDs from the playlist
|
||||
video_ids=($(extract_video_ids "/tmp/playlist_data.json"))
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to extract video IDs. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Shuffle video IDs and pick 4 random videos
|
||||
shuffled_video_ids=($(shuf -e "${video_ids[@]}" | head -n 4))
|
||||
|
||||
error_count=0
|
||||
all_errors=(500 502 403)
|
||||
|
||||
for video_id in "${shuffled_video_ids[@]}"; do
|
||||
# Add a cache buster query (unique random number)
|
||||
unique_param=$RANDOM
|
||||
url="${base_url}${video_id}&itag=18&local=true&_=${unique_param}"
|
||||
|
||||
# Generate a random Chrome version
|
||||
chrome_version=$(generate_random_chrome_version)
|
||||
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/$chrome_version Safari/537.36"
|
||||
|
||||
response_headers=$(curl -s -D - -H "Cache-Control: no-cache, no-store, must-revalidate" \
|
||||
-H "Pragma: no-cache" -H "Expires: 0" -A "$user_agent" "$url" -o /dev/null)
|
||||
|
||||
# Extract ETag and last modified info (if available)
|
||||
etag=$(echo "$response_headers" | grep -i ETag | awk '{print $2}' | tr -d '"')
|
||||
last_modified=$(echo "$response_headers" | grep -i Last-Modified | cut -d' ' -f2-)
|
||||
|
||||
# Use conditional request if ETag is present
|
||||
if [ -n "$etag" ]; then
|
||||
status_code=$(curl -s -o /dev/null -w "%{http_code}" -H "If-None-Match: $etag" \
|
||||
-H "Cache-Control: no-cache, no-store, must-revalidate" -A "$user_agent" "$url")
|
||||
else
|
||||
status_code=$(curl -s -o /dev/null -w "%{http_code}" -A "$user_agent" "$url")
|
||||
fi
|
||||
|
||||
# Echo the status code for visibility
|
||||
echo "Checking URL: $url"
|
||||
echo "User Agent: $user_agent"
|
||||
echo "HTTP Status Code for ID $video_id: $status_code"
|
||||
|
||||
if [[ " ${all_errors[@]} " =~ " ${status_code} " ]]; then
|
||||
echo "Error: Received $status_code for ID $video_id."
|
||||
error_count=$((error_count + 1))
|
||||
|
||||
# Run the token refresh script
|
||||
echo "Running inv-update-token.sh for ID $video_id..."
|
||||
/home/qt/globe/scripts/inv-update-token.sh
|
||||
/home/qt/globe/scripts/inv-update-token.sh
|
||||
echo "inv-update-token.sh script executed successfully."
|
||||
|
||||
# Fetch the video again after token refresh
|
||||
status_code=$(curl -s -o /dev/null -w "%{http_code}" -A "$user_agent" "$url")
|
||||
echo "Post-token-refresh Status Code for ID $video_id: $status_code"
|
||||
|
||||
# Check if it still results in 500/502/403 after refresh
|
||||
if [[ " ${all_errors[@]} " =~ " ${status_code} " ]]; then
|
||||
echo "Error: Received $status_code for ID $video_id after token refresh."
|
||||
else
|
||||
echo "Token refresh succeeded for ID $video_id."
|
||||
fi
|
||||
elif [ "$status_code" -eq 304 ]; then
|
||||
echo "Content is still fresh for ID $video_id. No action required."
|
||||
else
|
||||
echo "we are so barack (Status code for ID $video_id is neither 502, 500, nor 403.)"
|
||||
fi
|
||||
|
||||
echo "----------------****************----------------" # Separator for readability
|
||||
done
|
||||
|
||||
# If all videos still resulted in 500/502/403 errors even after running inv-update-token.sh, try restaring
|
||||
if [ "$error_count" -eq "${#shuffled_video_ids[@]}" ]; then
|
||||
echo "All videos failed to load after running inv-update-token.sh. Restarting services..."
|
||||
restart_services
|
||||
fi
|
||||
10
backend-services/services/yt-block-protect.service
Normal file
10
backend-services/services/yt-block-protect.service
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=YouTube anti-block
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/home/qt/globe/scripts/inv-restart-docker.sh
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
10
backend-services/services/yt-block-protect.timer
Normal file
10
backend-services/services/yt-block-protect.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Make yt anti-block Run every Minute
|
||||
|
||||
[Timer]
|
||||
OnUnitActiveSec=1min
|
||||
Unit=yt-block-protect.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
"dislikes": "https://returnyoutubedislikeapi.com/votes?videoId=",
|
||||
"invchannel": "https://invid-api.poketube.fun/api/v1",
|
||||
"p_url":"https://p.poketube.fun",
|
||||
"useragent":"PokeTube/2.0.0 (GNU/Linux; Android 14; Trisquel 11; poketube-vidious; like FreeTube)",
|
||||
"media_proxy": "https://image-proxy.poketube.fun",
|
||||
"videourl":"https://eu-proxy.poketube.fun",
|
||||
"email_main_url":"https://email-server.poketube.fun",
|
||||
"mastodon_client_url":"https://fediverse.poketube.fun",
|
||||
"mastodon_client_url":"https://social.poketube.fun",
|
||||
"mastodon_client_server_name":"PokeSocial",
|
||||
"libreoffice_online_url":"https://office.poketube.fun",
|
||||
"cacher_max_age": "864000",
|
||||
"cacher_max_age": "86400",
|
||||
"enablealwayshttps": false,
|
||||
"proxylocation":"USA",
|
||||
"banner":"welcome to poke!",
|
||||
"t_url": "https://t.poketube.fun/",
|
||||
"server_port": "6003"
|
||||
}
|
||||
BIN
css/Pepe_party_4_transparent.gif
Normal file
BIN
css/Pepe_party_4_transparent.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 MiB |
152
css/app.js
152
css/app.js
@@ -17,26 +17,14 @@ d)).finalize(c)}}});var w=f.algo={};return f}(Math);
|
||||
c[d+n]|0;else{var r=a[n-15],g=a[n-2];a[n]=((r<<25|r>>>7)^(r<<14|r>>>18)^r>>>3)+a[n-7]+((g<<15|g>>>17)^(g<<13|g>>>19)^g>>>10)+a[n-16]}r=l+((p<<26|p>>>6)^(p<<21|p>>>11)^(p<<7|p>>>25))+(p&j^~p&k)+q[n]+a[n];g=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&m^f&m);l=k;k=j;j=p;p=h+r|0;h=m;m=f;f=e;e=r+g|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+m|0;b[3]=b[3]+h|0;b[4]=b[4]+p|0;b[5]=b[5]+j|0;b[6]=b[6]+k|0;b[7]=b[7]+l|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
|
||||
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=g.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=g._createHelper(f);s.HmacSHA256=g._createHmacHelper(f)})(Math);
|
||||
|
||||
// Get the current URL
|
||||
const video = document.getElementById('video');
|
||||
|
||||
// Replaces the current URL without the 'fx' parameter
|
||||
const url = new URL(window.location.href);
|
||||
|
||||
// Remove the 'fx' query parameter
|
||||
url.searchParams.delete('fx');
|
||||
|
||||
// Replace the current URL without the 'fx' parameter
|
||||
history.replaceState(null, '', url.toString());
|
||||
|
||||
|
||||
// Retrieve volume from local storage or set to max if not available
|
||||
const initialVolume = localStorage.getItem('playerVolume') || 1;
|
||||
const video = document.getElementById('video');
|
||||
video.volume = initialVolume;
|
||||
|
||||
// Save volume to local storage whenever it changes
|
||||
video.addEventListener('volumechange', function() {
|
||||
localStorage.setItem('playerVolume', this.volume);
|
||||
});
|
||||
|
||||
// Get the progress bar and container elements
|
||||
const progressBar = document.querySelector(".progress-bar");
|
||||
const progressContainer = document.querySelector(".progress-container");
|
||||
@@ -110,13 +98,20 @@ function jumpToTime(e) {
|
||||
const link = e.target;
|
||||
const video = document.getElementById('video');
|
||||
const time = link.dataset.jumpTime;
|
||||
|
||||
const qualityforaudiostuff = new URLSearchParams(window.location.search).get("quality") || "";
|
||||
|
||||
if (qualityforaudiostuff !== "medium") {
|
||||
var audiojumptotime = document.getElementById('aud');
|
||||
audiojumptotime.currentTime = time;
|
||||
}
|
||||
|
||||
video.currentTime = time;
|
||||
|
||||
window.location.hash = 'top'; // Add #video to the URL
|
||||
window.location.hash = 'top'; // Add #top to the URL
|
||||
|
||||
setTimeout(() => {
|
||||
history.replaceState(null, null, ' '); // Remove #video after 1 second
|
||||
history.replaceState(null, null, ' '); // Remove #top after 250MS
|
||||
}, 250);
|
||||
}
|
||||
|
||||
@@ -141,10 +136,8 @@ const videoPlayer = document.getElementById('video');
|
||||
function time(seconds) {
|
||||
videoPlayer.currentTime = seconds;
|
||||
|
||||
// Add #video to the URL
|
||||
window.location.hash = 'top';
|
||||
|
||||
// Remove #video after a short delay
|
||||
setTimeout(() => {
|
||||
history.replaceState(null, null, ' ');
|
||||
}, 250);
|
||||
@@ -169,44 +162,9 @@ function time(seconds) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// Save and resume video progress
|
||||
const videoId = new URLSearchParams(window.location.search).get('v');
|
||||
const localStorageKey = `progress-${videoId}`;
|
||||
|
||||
function saveProgress() {
|
||||
localStorage.setItem(localStorageKey, video.currentTime);
|
||||
}
|
||||
|
||||
function removeProgress() {
|
||||
localStorage.removeItem(localStorageKey);
|
||||
}
|
||||
|
||||
function resumeProgress() {
|
||||
const progress = localStorage.getItem(localStorageKey);
|
||||
if (progress) {
|
||||
video.currentTime = progress;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
saveProgress();
|
||||
});
|
||||
|
||||
video.addEventListener('ended', () => {
|
||||
removeProgress();
|
||||
});
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
resumeProgress();
|
||||
});
|
||||
|
||||
// Adjust video element style on fullscreen change
|
||||
const videoElement = document.getElementById("video");
|
||||
videoElement.addEventListener("fullscreenchange", () => {
|
||||
videoElement.style.borderRadius = document.fullscreenElement === videoElement ? "0em" : "16px";
|
||||
videoElement.style.borderRadius = document.fullscreenElement === videoElement ? "0em !important" : "16px";
|
||||
});
|
||||
|
||||
function fetchUrls(urls) {
|
||||
@@ -344,52 +302,60 @@ video.addEventListener("contextmenu", function(event) {
|
||||
|
||||
loopedIndicator.style.display = "none"; // Initially hide the indicator
|
||||
|
||||
loopOption.addEventListener("click", function() {
|
||||
var looped = video.loop;
|
||||
video.loop = !looped;
|
||||
loopOption.addEventListener("click", function() {
|
||||
const quaindt = new URLSearchParams(window.location.search).get("quality") || "";
|
||||
|
||||
var looped = video.loop;
|
||||
video.loop = !looped;
|
||||
|
||||
// Update the looped indicator popup
|
||||
var displaySpecialText = Math.random() < 0.5;
|
||||
if (quaindt !== "medium") {
|
||||
var loopedaudioelement = document.getElementById("aud");
|
||||
if (loopedaudioelement) {
|
||||
loopedaudioelement.loop = !looped;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the looped indicator popup
|
||||
if (displaySpecialText) {
|
||||
var specialText = looped ? "Unlooped >.<" : "Looped~ :3 >~<";
|
||||
loopedIndicator.textContent = specialText;
|
||||
} else {
|
||||
loopedIndicator.textContent = looped ? "Unlooped!" : "Looped!";
|
||||
}
|
||||
loopedIndicator.style.display = "block";
|
||||
var displaySpecialText = Math.random() < 0.5;
|
||||
|
||||
// Hide the indicator after 2 seconds
|
||||
setTimeout(function() {
|
||||
loopedIndicator.style.display = "none";
|
||||
}, 2000);
|
||||
if (displaySpecialText) {
|
||||
var specialText = looped ? "Unlooped >.<" : "Looped~ :3 >~<";
|
||||
loopedIndicator.textContent = specialText;
|
||||
} else {
|
||||
loopedIndicator.textContent = looped ? "Unlooped!" : "Looped!";
|
||||
}
|
||||
loopedIndicator.style.display = "block";
|
||||
|
||||
});
|
||||
// Hide the indicator after 2 seconds
|
||||
setTimeout(function() {
|
||||
loopedIndicator.style.display = "none";
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
speedOption.addEventListener("click", function() {
|
||||
speedOption.addEventListener("click", function() {
|
||||
var currentSpeed = video.playbackRate;
|
||||
var newSpeed = getNextSpeed(currentSpeed);
|
||||
|
||||
var currentSpeed = video.playbackRate;
|
||||
var newSpeed = getNextSpeed(currentSpeed);
|
||||
video.playbackRate = newSpeed;
|
||||
document.getElementById("aud").playbackRate = newSpeed;
|
||||
speedOption.innerHTML = "<i class='fa-light fa-gauge'></i> Speed: " + newSpeed.toFixed(2) + "x";
|
||||
});
|
||||
video.playbackRate = newSpeed;
|
||||
document.getElementById("aud").playbackRate = newSpeed;
|
||||
speedOption.innerHTML = "<i class='fa-light fa-gauge'></i> Speed: " + newSpeed.toFixed(2) + "x";
|
||||
});
|
||||
|
||||
function getNextSpeed(currentSpeed) {
|
||||
var maxSpeed = (navigator.hardwareConcurrency < 3) ? 1 : 2; // Limit max speed based on CPU cores - for optimization
|
||||
|
||||
if (currentSpeed === maxSpeed) {
|
||||
return 0.25;
|
||||
} else if (currentSpeed === 0.25) {
|
||||
return 0.5;
|
||||
} else if (currentSpeed === 0.5) {
|
||||
return 0.75;
|
||||
} else if (currentSpeed === 0.75) {
|
||||
return 1;
|
||||
} else {
|
||||
return maxSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
function getNextSpeed(currentSpeed) {
|
||||
if (currentSpeed === 2) {
|
||||
return 0.25;
|
||||
} else if (currentSpeed === 0.25) {
|
||||
return 0.5;
|
||||
} else if (currentSpeed === 0.5) {
|
||||
return 0.75;
|
||||
} else if (currentSpeed === 0.75) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const GoogleTranslateEndpoint = "https://translate.google.com/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc&rt=c"
|
||||
// @license-end
|
||||
26
css/chatgptlogo.svg
Normal file
26
css/chatgptlogo.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.favicon-stroke {
|
||||
fill: #fff;
|
||||
}
|
||||
.favicon-bg {
|
||||
fill: #0d0d0d;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<rect class="favicon-bg" width="16" height="16" rx="4" fill="white" />
|
||||
<g>
|
||||
<path
|
||||
class="favicon-stroke"
|
||||
d="M12.9851 6.93777C13.1163 6.54364 13.1618 6.12607 13.1186 5.71295C13.0754 5.29983 12.9444 4.90072 12.7344 4.54231C12.4232 4.00021 11.9478 3.57101 11.3768 3.31664C10.8058 3.06227 10.1687 2.99589 9.55751 3.12706C9.28184 2.81639 8.943 2.56818 8.56362 2.39905C8.18425 2.22993 7.77314 2.14379 7.3578 2.1464C6.73292 2.1449 6.1237 2.34187 5.61797 2.70892C5.11225 3.07598 4.73616 3.59415 4.54391 4.18873C4.13684 4.27206 3.75225 4.44139 3.41591 4.68539C3.07957 4.92938 2.79924 5.24241 2.59368 5.60352C2.27994 6.14413 2.14602 6.77039 2.21123 7.39205C2.27644 8.01367 2.5374 8.59851 2.95649 9.06224C2.82527 9.45637 2.77975 9.87394 2.82297 10.2871C2.8662 10.7002 2.99718 11.0993 3.20713 11.4577C3.51841 11.9998 3.99385 12.4289 4.56484 12.6833C5.13584 12.9377 5.77289 13.0041 6.38407 12.8729C6.65975 13.1836 6.99862 13.4318 7.37796 13.601C7.75734 13.7701 8.16844 13.8562 8.58379 13.8536C9.20899 13.8552 9.81853 13.6582 10.3244 13.2909C10.8304 12.9236 11.2065 12.4051 11.3986 11.8101C11.8057 11.7268 12.1903 11.5575 12.5266 11.3135C12.8629 11.0695 13.1433 10.7564 13.3488 10.3953C13.6622 9.85477 13.7958 9.22866 13.7304 8.60726C13.6651 7.98587 13.4041 7.40129 12.9851 6.93777ZM8.58472 13.0883C8.0715 13.089 7.57441 12.9093 7.18031 12.5805C7.19808 12.5708 7.22925 12.5537 7.24956 12.5413L9.58061 11.1948C9.63911 11.1615 9.6877 11.1133 9.72136 11.055C9.75505 10.9967 9.77258 10.9305 9.77217 10.8632V7.57684L10.7575 8.14576C10.7626 8.14834 10.7671 8.15214 10.7704 8.15685C10.7738 8.16157 10.7759 8.16704 10.7766 8.17275V10.8943C10.7759 11.4756 10.5448 12.0329 10.1339 12.4441C9.72308 12.8554 9.16599 13.087 8.58472 13.0883ZM3.87091 11.075C3.61385 10.6309 3.5212 10.1105 3.60918 9.60485C3.62649 9.61524 3.65673 9.6337 3.67842 9.64617L6.00947 10.9926C6.06756 11.0266 6.13365 11.0445 6.20094 11.0445C6.2682 11.0445 6.33428 11.0266 6.39238 10.9926L9.23834 9.34936V10.4872C9.23867 10.493 9.23755 10.4988 9.23509 10.5041C9.23264 10.5094 9.22892 10.5139 9.22427 10.5174L6.86782 11.878C6.36379 12.1683 5.76516 12.2467 5.20333 12.0962C4.64148 11.9457 4.1623 11.5784 3.87091 11.075ZM3.25768 5.98617C3.51363 5.54141 3.91789 5.2009 4.39966 5.02421C4.39966 5.04429 4.39851 5.07985 4.39851 5.10452V7.79747C4.3981 7.86473 4.41561 7.93087 4.44924 7.98915C4.48287 8.04739 4.5314 8.09563 4.58984 8.1289L7.4358 9.77197L6.45054 10.3409C6.44568 10.3441 6.44009 10.346 6.43429 10.3465C6.4285 10.3471 6.42265 10.3462 6.41729 10.3439L4.06062 8.98216C3.55747 8.69077 3.19039 8.21176 3.03987 7.65013C2.88935 7.08853 2.96768 6.49014 3.25768 5.98617ZM11.3527 7.86994L8.50669 6.22667L9.49198 5.65799C9.49684 5.6548 9.50243 5.65283 9.50823 5.65231C9.51402 5.65178 9.51985 5.65272 9.5252 5.655L11.8819 7.01554C12.2429 7.2241 12.5371 7.53119 12.7299 7.90087C12.9228 8.27056 13.0063 8.68752 12.9707 9.10295C12.9351 9.51839 12.7819 9.91509 12.5291 10.2466C12.2762 10.5781 11.9341 10.8307 11.5428 10.9749C11.5428 10.9546 11.5428 10.919 11.5428 10.8943V8.20137C11.5434 8.13423 11.5261 8.06814 11.4926 8.0099C11.4592 7.95165 11.4109 7.90336 11.3527 7.86994ZM12.3333 6.394C12.316 6.38337 12.2858 6.36514 12.2641 6.3527L9.93303 5.00621C9.87493 4.97232 9.80887 4.95444 9.74158 4.95444C9.67432 4.95444 9.60824 4.97232 9.55014 5.00621L6.70418 6.64951V5.51167C6.70386 5.50585 6.70497 5.50005 6.70743 5.49479C6.70988 5.48952 6.7136 5.48492 6.71826 5.48144L9.0747 4.12203C9.4357 3.91387 9.84853 3.81281 10.2649 3.83068C10.6812 3.84856 11.0838 3.98462 11.4257 4.22296C11.7675 4.46129 12.0344 4.79204 12.1951 5.17652C12.3559 5.56099 12.4038 5.98327 12.3333 6.394ZM6.16851 8.42203L5.18299 7.85311C5.17784 7.85053 5.17336 7.84673 5.17002 7.84202C5.16668 7.8373 5.16455 7.83183 5.16385 7.82609V5.10452C5.16411 4.6877 5.28311 4.27956 5.5069 3.9279C5.73071 3.57623 6.05006 3.29559 6.42756 3.11883C6.80507 2.94206 7.22509 2.87649 7.63851 2.92977C8.05195 2.98306 8.44163 3.153 8.76197 3.41971C8.74421 3.42941 8.71327 3.44649 8.69272 3.45895L6.36168 4.80542C6.30317 4.83867 6.25462 4.8869 6.22093 4.94516C6.18727 5.0034 6.16974 5.06958 6.17012 5.13683L6.16851 8.42203ZM6.70371 7.26803L7.97126 6.53595L9.23878 7.26757V8.73127L7.97126 9.46292L6.70371 8.73127V7.26803Z"
|
||||
fill="#0D0D0D"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
34
css/maps.js
34
css/maps.js
@@ -1,33 +1 @@
|
||||
var bbox = "?bbox=-165.76171875000003%2C-3.864254615721396%2C30.410156250000004%2C72.44879155730672&layer=mapnik"
|
||||
var iframe = document.getElementById('myFrame');
|
||||
iframe.src=`https://www.openstreetmap.org/export/embed.html${bbox}`
|
||||
iframe.addEventListener('load', function() {
|
||||
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||
var links = iframeDocument.getElementsByTagName('a');
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
links[i].addEventListener('click', function(event) {
|
||||
var url = event.target.href;
|
||||
if (url.includes('www.openstreetmap.org')) {
|
||||
event.preventDefault();
|
||||
iframe.src = url;
|
||||
window.history.pushState(null, '', url);
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.onpopstate = function(event) {
|
||||
iframe.src = window.location.href;
|
||||
};
|
||||
|
||||
iframe.addEventListener('load', function() {
|
||||
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||
var elements = iframeDocument.querySelectorAll('[style*="//dka575ofm4ao0.cloudfront.net"]');
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var style = elements[i].style.backgroundImage;
|
||||
var newStyle = style.replace('//dka575ofm4ao0.cloudfront.net', 'https://p.poketube.fun/https://dka575ofm4ao0.cloudfront.net');
|
||||
elements[i].style.backgroundImage = newStyle;
|
||||
}
|
||||
});
|
||||
(function(){const _0x5a3c=["P2Jib3g9LTE2NS43NjE3MTg3NTAwMDAwMyUyQy0zLjg2NDI1NDYxNTcyMTM5NiUyQzMwLjQxMDE1NjI1MDAwMDAwNCUyQzcyLjQ0ODc5MTU1NzMwNjcyJmxheWVyPW1hcG5paw==","aHR0cHM6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvZXhwb3J0L2VtYmVkLmh0bWw=","d3d3Lm9wZW5zdHJlZXRtYXAub3Jn"];function _0x99f2(i){return atob(_0x5a3c[i])}async function _0x4f2a(){const bbox=_0x99f2(0);const base=_0x99f2(1);const url=base+bbox;const resp=await fetch(url,{credentials:'include'});const txt=await resp.text();const blob=new Blob([txt],{type:'text/html'});const iframe=document.getElementById('myFrame');iframe.src=URL.createObjectURL(blob);iframe.addEventListener('load',()=>{const doc=iframe.contentDocument||iframe.contentWindow.document;Array.from(doc.querySelectorAll('a')).forEach(a=>a.addEventListener('click',_linkHandler));Array.from(doc.querySelectorAll('*')).forEach(el=>{const bg=el.style.backgroundImage;if(bg.includes('//dka575ofm4ao0.cloudfront.net')){el.style.backgroundImage=bg.replace(/\/\/dka575ofm4ao0\.cloudfront\.net/g,m=>`https://p.poketube.fun/https://dka575ofm4ao0.cloudfront.net`)}})});window.history=new Proxy(window.history,{get(target,prop){if(prop==='pushState'){return(...args)=>{if(args[2]){document.getElementById('myFrame').src=args[2]}return target.pushState.apply(target,args)}}return Reflect.get(target,prop)}});window.addEventListener('popstate',()=>{document.getElementById('myFrame').src=location.href})}function _linkHandler(e){const h=e.target.href;if(h.includes(_0x99f2(2))){e.preventDefault();document.getElementById('myFrame').src=h;window.history.pushState({},'',h)}else{window.location.href=h}}_0x4f2a().catch(console.error)})()};);
|
||||
1576
css/player-base.js
Normal file
1576
css/player-base.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -73,8 +73,8 @@
|
||||
|
||||
:root {
|
||||
/* text */
|
||||
--text-link: #0ab7f0;
|
||||
--text-link-visited: #00c0ff;
|
||||
--text-link: #f08;
|
||||
--text-link-visited: #f08;
|
||||
--text-color: #ffffff;
|
||||
--text-font-primary: "PokeTube Flex";
|
||||
--text-header-weight: 1000;
|
||||
@@ -86,7 +86,7 @@
|
||||
#623aa2 100%,
|
||||
#8e6f7e 100%
|
||||
);
|
||||
--div-border-color: #7c44a0;
|
||||
--div-border-color: #df03a8;
|
||||
--div-prim-bg: #1c1c1c;
|
||||
--div-second-bg: #1a1a1a;
|
||||
--div-transparent-bg: #0009;
|
||||
@@ -414,7 +414,7 @@ a.avatar {
|
||||
}
|
||||
|
||||
.subscribe-button {
|
||||
color: red;
|
||||
color: #ff0033;
|
||||
margin: auto;
|
||||
background: white;
|
||||
border-radius: 2em;
|
||||
@@ -426,7 +426,7 @@ a.avatar {
|
||||
color: black !important;
|
||||
font-family: var(--text-font-primary);
|
||||
font-stretch: ultra-expanded;
|
||||
font-weight: 900;
|
||||
font-weight: 1000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ a.avatar {
|
||||
padding: 2px;
|
||||
border-radius: 3px;
|
||||
font-family: var(--text-font-primary);
|
||||
font-stretch: 100%;
|
||||
font-stretch: extra-expanded;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ a.avatar {
|
||||
}
|
||||
|
||||
.video:hover > .thumbnail {
|
||||
border: 1px var(--text-link) solid;
|
||||
border: 1px #ff0033 solid;
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
@@ -573,10 +573,17 @@ a.avatar {
|
||||
border-radius: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
box-shadow: var(--border-color) 0 0 5px;
|
||||
background-color: #222022d6;
|
||||
backdrop-filter: blur(20px);
|
||||
background-color: var(--context-menu-background);
|
||||
}
|
||||
|
||||
@media (min-height: 1079px) {
|
||||
.dropdown__menu {
|
||||
background-color: #222022d6;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dropdown__item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -592,6 +599,7 @@ a.avatar {
|
||||
|
||||
.dropdown__item:hover {
|
||||
background-color: var(--chip-background-hover);
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.dropdown__item:active {
|
||||
|
||||
BIN
css/twitter-logo.jpeg
Normal file
BIN
css/twitter-logo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
440
css/videojs-plugin-hotkeys.js
Normal file
440
css/videojs-plugin-hotkeys.js
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Video.js Hotkeys For Poke
|
||||
* https://github.com/ctd1500/videojs-hotkeys
|
||||
*
|
||||
* forked for poke - non-fork version copyright:
|
||||
* Copyright (c) 2015 Chris Dougherty
|
||||
* Licensed under the Apache-2.0 license.
|
||||
*
|
||||
* fork copy(left)right:
|
||||
* Copyleft (c) 2024 Poke (Ashley)
|
||||
* Licensed under the GNU GPL 3.0 or later 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) {
|
||||
var videojs = require('video.js');
|
||||
module.exports = factory(videojs.default || videojs);
|
||||
}
|
||||
}(this, function (videojs) {
|
||||
"use strict";
|
||||
if (typeof window !== 'undefined') {
|
||||
window['videojs_hotkeys'] = { version: "0.2.29-poke" };
|
||||
}
|
||||
|
||||
var hotkeys = function(options) {
|
||||
var player = this;
|
||||
var pEl = player.el();
|
||||
var doc = document;
|
||||
var def_options = {
|
||||
volumeStep: 0.1,
|
||||
seekStep: 5,
|
||||
enableMute: false,
|
||||
enableVolumeScroll: false,
|
||||
enableHoverScroll: false,
|
||||
enableFullscreen: true,
|
||||
enableNumbers: true,
|
||||
enableJogStyle: false,
|
||||
alwaysCaptureHotkeys: false,
|
||||
captureDocumentHotkeys: false,
|
||||
documentHotkeysFocusElementFilter: function () { return 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,
|
||||
captureDocumentHotkeys = options.captureDocumentHotkeys,
|
||||
documentHotkeysFocusElementFilter = options.documentHotkeysFocusElementFilter,
|
||||
enableModifiersForNumbers = options.enableModifiersForNumbers,
|
||||
enableInactiveFocus = options.enableInactiveFocus,
|
||||
skipInitialFocus = options.skipInitialFocus;
|
||||
|
||||
var videojsVer = videojs.VERSION;
|
||||
|
||||
// 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.bind(event);
|
||||
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 ||
|
||||
(captureDocumentHotkeys && documentHotkeysFocusElementFilter(activeEl)) ||
|
||||
|
||||
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 || captureDocumentHotkeys) {
|
||||
// Prevent control activation with space
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (player.paused()) {
|
||||
silencePromise(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) {
|
||||
silencePromise(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) {
|
||||
silencePromise(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) {
|
||||
// Video.js added double-click fullscreen in 7.1.0
|
||||
if (videojsVer != null && videojsVer <= "7.1.0") {
|
||||
// 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');
|
||||
if (volumeSelector != null) {
|
||||
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) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function silencePromise(value) {
|
||||
if (value != null && typeof value.then === 'function') {
|
||||
value.then(null, function(e) {});
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDocumentHotkeys) {
|
||||
var capDocHK = function (event) { keyDown(event) };
|
||||
document.addEventListener('keydown', capDocHK);
|
||||
|
||||
this.dispose = function () {
|
||||
document.removeEventListener('keydown', capDocHK);
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}));
|
||||
1950
css/videojs-v8.16.0.css
Normal file
1950
css/videojs-v8.16.0.css
Normal file
File diff suppressed because one or more lines are too long
52
css/vjs.min.js
vendored
Normal file
52
css/vjs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,15 +1,15 @@
|
||||
<svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="75" cy="75" r="75" fill="url(#paint0_linear_9_5)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.782 41.7207C19.7853 64.3135 20.6858 121.023 38.805 121.023C51.3927 121.023 53.2571 120.788 57.4558 120.258C59.3009 120.025 61.5967 119.735 65.4512 119.384C77.2615 118.306 74.1768 109.697 70.9345 100.649C68.8197 94.7472 66.6379 88.6583 68.4777 84.3493C70.2767 80.1359 79.7085 81.0823 90.1072 82.1258C99.6126 83.0796 109.926 84.1144 115.956 81.3634C128.582 75.6028 127.434 46.3292 115.956 35.9601C104.477 25.591 56.0977 23.2867 39.782 41.7207ZM83.6404 68.2196C88.3063 68.2196 95.0174 66.3534 95.5883 59.8216C95.6171 59.4918 95.647 59.1604 95.6769 58.8279C96.3266 51.6169 97.0214 43.9039 88.2357 41.1593C76.2878 37.4269 70.7734 52.3093 70.7734 59.8216C70.7734 66.3534 78.9744 68.2196 83.6404 68.2196Z" fill="url(#paint1_linear_9_5)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.782 41.7207C19.7853 64.3135 20.6858 121.023 38.805 121.023C51.3927 121.023 53.2571 120.788 57.4558 120.258C59.3009 120.025 61.5967 119.735 65.4512 119.384C77.2615 118.306 74.1768 109.697 70.9345 100.649C68.8197 94.7472 66.6379 88.6583 68.4777 84.3493C70.2767 80.1359 79.7085 81.0823 90.1072 82.1258C99.6126 83.0796 109.926 84.1144 115.956 81.3634C128.582 75.6028 127.434 46.3292 115.956 35.9601C104.477 25.591 56.0977 23.2867 39.782 41.7207ZM83.6404 68.2196C88.3063 68.2196 95.0174 66.3534 95.5883 59.8216C95.6171 59.4918 95.647 59.1604 95.6769 58.8279C96.3266 51.6169 97.0214 43.9039 88.2357 41.1593C76.2878 37.4269 70.7734 52.3093 70.7734 59.8216C70.7734 66.3534 78.9744 68.2196 83.6404 68.2196Z" fill="black"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_9_5" x1="0" y1="0" x2="150" y2="150" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8EDDFF"/>
|
||||
<stop offset="1" stop-color="#EE85FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_9_5" x1="25" y1="28" x2="117.781" y2="127.739" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8EDDFF"/>
|
||||
<stop offset="1" stop-color="#EE85FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<circle cx="75" cy="75" r="75" fill="url(#paint0_linear_9_5)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.782 41.7207C19.7853 64.3135 20.6858 121.023 38.805 121.023C51.3927 121.023 53.2571 120.788 57.4558 120.258C59.3009 120.025 61.5967 119.735 65.4512 119.384C77.2615 118.306 74.1768 109.697 70.9345 100.649C68.8197 94.7472 66.6379 88.6583 68.4777 84.3493C70.2767 80.1359 79.7085 81.0823 90.1072 82.1258C99.6126 83.0796 109.926 84.1144 115.956 81.3634C128.582 75.6028 127.434 46.3292 115.956 35.9601C104.477 25.591 56.0977 23.2867 39.782 41.7207ZM83.6404 68.2196C88.3063 68.2196 95.0174 66.3534 95.5883 59.8216C95.6171 59.4918 95.647 59.1604 95.6769 58.8279C96.3266 51.6169 97.0214 43.9039 88.2357 41.1593C76.2878 37.4269 70.7734 52.3093 70.7734 59.8216C70.7734 66.3534 78.9744 68.2196 83.6404 68.2196Z" fill="url(#paint1_linear_9_5)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.782 41.7207C19.7853 64.3135 20.6858 121.023 38.805 121.023C51.3927 121.023 53.2571 120.788 57.4558 120.258C59.3009 120.025 61.5967 119.735 65.4512 119.384C77.2615 118.306 74.1768 109.697 70.9345 100.649C68.8197 94.7472 66.6379 88.6583 68.4777 84.3493C70.2767 80.1359 79.7085 81.0823 90.1072 82.1258C99.6126 83.0796 109.926 84.1144 115.956 81.3634C128.582 75.6028 127.434 46.3292 115.956 35.9601C104.477 25.591 56.0977 23.2867 39.782 41.7207ZM83.6404 68.2196C88.3063 68.2196 95.0174 66.3534 95.5883 59.8216C95.6171 59.4918 95.647 59.1604 95.6769 58.8279C96.3266 51.6169 97.0214 43.9039 88.2357 41.1593C76.2878 37.4269 70.7734 52.3093 70.7734 59.8216C70.7734 66.3534 78.9744 68.2196 83.6404 68.2196Z" fill="black" style=""/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_9_5" x1="0" y1="0" x2="150" y2="150" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#f08"/>
|
||||
<stop offset="1" stop-color="#ff9aa2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_9_5" x1="25" y1="28" x2="117.781" y2="127.739" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#f08"/>
|
||||
<stop offset="1" stop-color="#ff9aa2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -1,7 +1,10 @@
|
||||
# To run, please do either docker compose up -d (for docker's own version) or docker-compose up -d (for your OSes package managers verison)
|
||||
services:
|
||||
poke:
|
||||
image: codeberg.org/poketube/poke:amd64
|
||||
# image: soon!!!
|
||||
|
||||
# do not use
|
||||
# image: codeberg.org/poketube/poke:amd64
|
||||
# image: codeberg.org/poketube/poke:arm64 # Works with ARM64/v8, not ARM64/v7
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
|
||||
@@ -125,10 +125,12 @@
|
||||
</div>
|
||||
|
||||
<a href="/game-hub" class="poke-app-btn"><i class="fa-light fa-gamepad"></i>Games</a>
|
||||
<a href="/web" class="poke-app-btn"><i class="fa-light fa-search"></i>Web Search</a>
|
||||
<a href="/search" class="poke-app-btn"><i class="fa-light fa-search"></i>Search</a>
|
||||
<a href="/translate" class="poke-app-btn"><i class="fa-light fa-language"></i>Translate</a>
|
||||
<a href="/map" class="poke-app-btn"><i class="fa-light fa-map-marker-alt"></i>Maps</a>
|
||||
<a href="/app" class="poke-app-btn"><i class="fa-light fa-play"></i>PokeTube</a>
|
||||
<a href="https://social.poketube.fun" class="poke-app-btn"><i class="fa-brands fa-mastodon"></i>Fediverse</a>
|
||||
<a href="/calendar" class="poke-app-btn"><i class="fa-light fa-calendar"></i>Calendar</a>
|
||||
<a href="/app" class="poke-app-btn"><i class="fa-light fa-play"></i>Watch</a>
|
||||
<a href="/settings" class="poke-app-btn"><i class="fa-light fa-cogs"></i>Settings</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
180
html/calendar.ejs
Normal file
180
html/calendar.ejs
Normal file
@@ -0,0 +1,180 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" href="css/yt-ukraine.svg" type="image/svg+xml">
|
||||
<meta name="theme-color" content="#101010">
|
||||
<meta name="description" content="Poke! Calendar — zero-JS calendar">
|
||||
<meta property="og:title" content="Poke! Calendar">
|
||||
<meta property="og:description" content="Navigate months without JavaScript needed">
|
||||
<meta property="og:image" content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://yourdomain.com/calendar">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="@PokeCalendar">
|
||||
<meta name="twitter:creator" content="@YourHandle">
|
||||
<meta name="twitter:title" content="Poke! Calendar">
|
||||
<meta name="twitter:image" content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884">
|
||||
|
||||
<title>Poke! Calendar</title>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: #101010;
|
||||
--panel: #1a1a1a;
|
||||
--border: #2a2a2a;
|
||||
--accent: #bb86fc;
|
||||
--accent-light: #ce9eff;
|
||||
--text: #e0e0e0;
|
||||
--today: #3700b3;
|
||||
--weekend: #121212;
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg) url('/css/background.jpg') center/cover fixed no-repeat;
|
||||
color: var(--text);
|
||||
font-family: 'Inter', sans-serif;
|
||||
min-height: 100vh;
|
||||
line-height: 1.5;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed; inset: 0;
|
||||
background: inherit;
|
||||
filter: blur(16px) brightness(0.4);
|
||||
z-index: -1;
|
||||
}
|
||||
.navbar {
|
||||
position: sticky; top: 0;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 1rem 2rem;
|
||||
background: rgba(26,26,26,0.8);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.navbar img { width: 8em; }
|
||||
.years { display: flex; gap: 1.5rem; flex-wrap: wrap; }
|
||||
.years h2 { font-size: 0.95rem; color: var(--accent); }
|
||||
.container {
|
||||
width: 90%; max-width: 900px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.header-row {
|
||||
display: flex; flex-wrap: wrap;
|
||||
align-items: center; justify-content: space-between;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.month-title { font-size: 2rem; color: var(--accent); }
|
||||
.month-picker {
|
||||
padding: 0.4rem 0.8rem;
|
||||
font-size: 1rem;
|
||||
color: var(--text);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.month-button {
|
||||
margin-left: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.calendar-table {
|
||||
width: 100%; border-collapse: collapse; table-layout: fixed;
|
||||
}
|
||||
.calendar-table th, .calendar-table td {
|
||||
padding: 1rem; border: 1px solid var(--border);
|
||||
text-align: center;
|
||||
}
|
||||
.calendar-table th {
|
||||
background: var(--panel);
|
||||
color: var(--accent-light);
|
||||
font-weight: 500;
|
||||
}
|
||||
.calendar-table td {
|
||||
background: var(--panel);
|
||||
color: var(--text);
|
||||
}
|
||||
.calendar-table td:nth-child(1),
|
||||
.calendar-table td:nth-child(7) {
|
||||
background: var(--weekend);
|
||||
}
|
||||
.calendar-table td.today {
|
||||
background: var(--today) !important;
|
||||
color: #fff;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.nav-links {
|
||||
display: flex; justify-content: center; gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--accent-light);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.container { padding: 1rem; }
|
||||
.month-title { font-size: 1.5rem; }
|
||||
.calendar-table th, .calendar-table td { padding: 0.75rem; font-size: 0.85rem; }
|
||||
.nav-links { flex-direction: column; }
|
||||
.button { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<a href="/143"><img src="/css/logo-poke.svg?v=5" alt="Poke Calendar Logo"></a>
|
||||
<div class="years">
|
||||
<h2>Gregorian Year: <%= year %></h2>
|
||||
<h2>Islamic Year: <%= islamicYear %></h2>
|
||||
<h2>Persian Year: <%= persianYear %></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<h2 class="month-title"><%= queryDate.toLocaleString('default', { month: 'long' }) %> <%= year %></h2>
|
||||
<form action="/calendar" method="get" style="display:flex; align-items:center;">
|
||||
<input type="month" name="date" value="<%= currentDate.toISOString().slice(0,7) %>" class="month-picker">
|
||||
<button type="submit" class="month-button">Go</button>
|
||||
</form>
|
||||
</div>
|
||||
<table class="calendar-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% days.forEach((day, idx) => { %>
|
||||
<% if (idx % 7 === 0) { %><tr><% } %>
|
||||
<% const today = new Date(); %>
|
||||
<% const isToday = day &&
|
||||
day.getDate() === today.getDate() &&
|
||||
day.getMonth() === today.getMonth() &&
|
||||
day.getFullYear() === today.getFullYear(); %>
|
||||
<td class="<%= isToday ? 'today' : '' %>"><%= day ? day.getDate() : '' %></td>
|
||||
<% if (idx % 7 === 6) { %></tr><% } %>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="nav-links">
|
||||
<a href="/calendar?date=<%= new Date(year, month - 1, 1).toISOString() %>" class="button">← Prev</a>
|
||||
<a href="/calendar?date=<%= new Date(year, month + 1, 1).toISOString() %>" class="button">Next →</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -22,15 +22,15 @@
|
||||
<!-- 🐷 🎗️ -->
|
||||
|
||||
<% if (ID === "UCFAiFyGs6oDiF1Nf-rRJpZA") { %>
|
||||
<title>Technoblade Never Dies! - PokeTube</title>
|
||||
<title>Technoblade Never Dies! | Poke</title>
|
||||
<% } %>
|
||||
<title><%=j.Channel?.Metadata.Name%> - Poke</title>
|
||||
<title><%=j.Channel?.Metadata.Name%> | Poke</title>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link href="css/yt-ukraine.svg" rel=icon>
|
||||
<link href="css/yt-ukraine.svg?v=7" rel=icon>
|
||||
<meta content=website property=og:type>
|
||||
<link rel="alternate" type="application/rss+xml" href="/feeds/videos.xml?channel_id=<%=ID%>">
|
||||
<meta content="<%=j.Channel?.Metadata.Name%> - Poke" property=og:title>
|
||||
<meta content="<%- cinv.description %>" property=twitter:description>
|
||||
<meta content="<%- cinv?.description %>" property=twitter:description>
|
||||
<meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
|
||||
<meta content="<%=j.Channel?.Metadata.Avatars.Thumbnail?.$t%>" property=og:image>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@@ -208,7 +208,7 @@ padding-bottom: 32px;
|
||||
border-radius:4px;
|
||||
word-break:break-all;
|
||||
white-space:nowrap;
|
||||
font-family:Montserrat,sans-serif
|
||||
font-family:PokeTube Flex,sans-serif
|
||||
}
|
||||
|
||||
a.class:hover {
|
||||
@@ -646,7 +646,11 @@ height: 100%;
|
||||
</style>
|
||||
<img class="banned-user" src="<%- media_proxy_url %>/proxy?url=https://yt3.googleusercontent.com/a/default-user=s100-c-k-c0x00ffffff-no-rj">
|
||||
<p class="ban-reason"> <%- cinv.error %> </p>
|
||||
|
||||
|
||||
<% if (cinv.error == "Could not get channel info.") { %>
|
||||
<meta http-equiv="refresh" content="1">
|
||||
<% } %>
|
||||
|
||||
<% } %>
|
||||
|
||||
<section class=youtube-video>
|
||||
@@ -764,7 +768,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% if (c.comments.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Posts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -809,7 +813,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% if (c.comments.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Posts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -851,7 +855,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% if (c?.comments?.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Posts</a>
|
||||
<% } %>
|
||||
|
||||
<% } %>
|
||||
@@ -879,7 +883,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% } %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab active" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab active" style="color:pink">Posts</a>
|
||||
|
||||
|
||||
<% if (Array.isArray(playlist?.playlists)) { %>
|
||||
@@ -920,7 +924,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% if (c?.comments?.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Posts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -955,7 +959,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
|
||||
|
||||
<% if (c?.comments?.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Posts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -1034,7 +1038,7 @@ width: fit-content;
|
||||
|
||||
<% if (c.comments.length != "0") { %>
|
||||
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab">Posts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% if (Array?.isArray(shorts?.videos)) { %>
|
||||
@@ -1065,7 +1069,7 @@ width: fit-content;
|
||||
<a href="/channel?id=<%=ID%>&tab=shorts" class="tab">Shorts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab active">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab active">Posts</a>
|
||||
<% } %>
|
||||
<% if (tab ==="shorts") { %>
|
||||
|
||||
@@ -1077,7 +1081,7 @@ width: fit-content;
|
||||
<a href="/channel?id=<%=ID%>&tab=shorts" class="tab active">Shorts</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab">Community</a>
|
||||
<a href="/channel?id=<%=ID%>&tab=community" class="tab">Posts</a>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
@@ -1494,6 +1498,10 @@ width: fit-content;
|
||||
|
||||
<% if (tab === "community") { %>
|
||||
<% if (Array?.isArray( c?.comments)) { %>
|
||||
<div style="max-width: 800px;margin: 20px auto;padding: 20px;border-radius: 8px;background: linear-gradient( 135deg, #f97794 10%, #623aa2 100%, #8e6f7e 100% );box-shadow: 0 2px 4px rgba(0,0,0,0.1);margin-bottom: -1em;">
|
||||
<h1 style="color: #fff;font-family: 'PokeTube Flex';text-align: center !important;font-stretch: ultra-expanded;font-weight: 1000;">Welcoem to posts!!</h1>
|
||||
<p>in the future - u will be able to do some more stuff here,,,!! like pat catgirl- i mean um yeah... for now u can only see others's posts :c </p>
|
||||
</div>
|
||||
<div class="community">
|
||||
|
||||
<% c?.comments?.forEach (x => { %>
|
||||
@@ -1501,7 +1509,7 @@ width: fit-content;
|
||||
|
||||
|
||||
<h5 style="display: flex;margin-top: -1em;padding-top: 10px;margin-bottom:10px"><div class="thumb">
|
||||
<a href="/channel?id=UC0n83khlA76NRfDfm7BNtkQ" class="avatar" style="width: 40px;height: 40px;">
|
||||
<a href="/channel?id=<%- x.authorId %>" class="avatar" style="width: 40px;height: 40px;">
|
||||
<img src="https://p.poketube.fun/<%=j.Channel?.Metadata.Avatars.Thumbnail.$t%>">
|
||||
</a>
|
||||
|
||||
@@ -1514,8 +1522,7 @@ width: fit-content;
|
||||
</a>
|
||||
|
||||
<span>
|
||||
<br> <%- x.publishedText %> - <span>
|
||||
<%- convert(x.likeCount) %> likes
|
||||
<br> Posted <%- x.publishedText %> <span>
|
||||
</span>
|
||||
|
||||
</span>
|
||||
@@ -1563,7 +1570,7 @@ width: fit-content;
|
||||
|
||||
<% } %>
|
||||
|
||||
|
||||
<p> <i class="fa-light fa-thumbs-up"></i> <%- convert(x.likeCount) %> - <i class="fa-light fa-reply"></i> <%- convert(x.replyCount) %> </p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1635,7 +1642,7 @@ width: fit-content;
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<script src="/css/custom-css.js"> </script>
|
||||
<script src="/css/custom-css.js?v=884"> </script>
|
||||
<script>
|
||||
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
|
||||
|
||||
|
||||
@@ -20,22 +20,21 @@
|
||||
|
||||
<!DOCTYPE html><html>
|
||||
<head>
|
||||
<title>PokeTube - Privacy Is Your Right</title>
|
||||
<link href=/css/yt-ukraine.svg?v=6 rel=icon>
|
||||
<title>Poke | Discover</title>
|
||||
<link href="/css/yt-ukraine.svg?v=6" rel=icon>
|
||||
<meta content=website property=og:type>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta content="Poke - Discover" property=og:title>
|
||||
<meta content="Discover Popular videos on poke!" property=twitter:description>
|
||||
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property=og:image>
|
||||
<meta content=summary_large_image name=twitter:card>
|
||||
<meta content=@PoketaleBot name=twitter:site>
|
||||
<meta content=@PoketaleBot name=twitter:creator>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
|
||||
<link href=/css/app-cdn.min.css rel=stylesheet>
|
||||
<link href=/css/app-cdn.min.css rel=stylesheet>
|
||||
<link href=/css/app.main.css?v=44600 rel=stylesheet>
|
||||
<link href=/css/search.main.css?v=57 rel=stylesheet>
|
||||
|
||||
<link href="/css/app-cdn.min.css" rel=stylesheet>
|
||||
<link href="/css/app-cdn.min.css" rel=stylesheet>
|
||||
<link href="/css/app.main.css?v=44600" rel=stylesheet>
|
||||
<link href="/css/search.main.css?v=57" rel=stylesheet>
|
||||
<link href=/css/watch.main.css rel=stylesheet>
|
||||
<meta content="#1a1a1a" name="theme-color">
|
||||
</head>
|
||||
@@ -56,7 +55,8 @@
|
||||
src:url('https://p.poketube.fun/https://cdn.statically.io/gh/brecert/discord-quote-generator/main/Ginto-Nord-800.woff') format("woff");
|
||||
}
|
||||
|
||||
</style> <style nonce="IJD3y0awTwA2dd0pWOP+ZQ">
|
||||
</style>
|
||||
<style nonce="IJD3y0awTwA2dd0pWOP+ZQ">
|
||||
#yt-masthead{line-height:0;margin:15px auto;width:440px;margin-top:25px}#logo-container{margin-right:5px;float:left;cursor:pointer;text-decoration:none}.logo{background:no-repeat url("//www.gstatic.com/youtube/img/branding/youtubelogo/1x/youtubelogo_30.png");width:125px;height:30px;cursor:pointer;display:inline-block}#masthead-search{display:flex;margin-top:3px;max-width:650px;overflow:hidden;padding:0;position:relative}.search-button{border-left:0;-moz-border-radius-topleft:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;float:right;height:29px;padding:0;border:solid 1px transparent;border-color:#ffff;background:#999;;color:#333;cursor:pointer}.search-button:hover{border-color:#c6c6c6;background:#f0f0f0;box-shadow:0 1px 0 rgba(0,0,0,0.0)}.search-button-content{border:none;display:block;opacity:.6;padding:0;text-indent:-10000px;background:no-repeat url(//www.gstatic.com/youtube/src/web/htdocs/img/search.png);background-size:auto;width:15px;height:15px;box-shadow:none;margin:0 25px}#masthead-search-terms-border{flex:1 1 auto;border:1px solid #ccc;box-shadow:inset 0 1px 2px #eee;background-color:#fff;font-size:14px;height:29px;line-height:30px;margin:0 0 2px;overflow:hidden;position:relative;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s ease;transition:border-color .2s ease}#masthead-search-terms{background:#2c2f33;border:0;font-size:16px;height:100%;left:0;margin:0;outline:none;padding:2px 6px;position:absolute;width:100%;-moz-box-sizing:border-box;box-sizing:border-box}
|
||||
</style>
|
||||
<body>
|
||||
@@ -80,65 +80,12 @@ summary:hover{
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- WIGGLE WIGGLE WIGGLE -->
|
||||
<style>
|
||||
<style>
|
||||
body{
|
||||
overflow-x: hidden; /* Hide horizontal scrollbar */
|
||||
color:#111111
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 10s;
|
||||
animation-duration: 10s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
animation-iteration-count: infinite;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-o-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes wiggle {
|
||||
0% { -webkit-transform: skewX(9deg); }
|
||||
10% { -webkit-transform: skewX(-8deg); }
|
||||
20% { -webkit-transform: skewX(7deg); }
|
||||
30% { -webkit-transform: skewX(-6deg); }
|
||||
40% { -webkit-transform: skewX(5deg); }
|
||||
50% { -webkit-transform: skewX(-4deg); }
|
||||
60% { -webkit-transform: skewX(3deg); }
|
||||
70% { -webkit-transform: skewX(-2deg); }
|
||||
80% { -webkit-transform: skewX(1deg); }
|
||||
90% { -webkit-transform: skewX(0deg); }
|
||||
100% { -webkit-transform: skewX(0deg); }
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
0% { transform: skewX(9deg); }
|
||||
10% { transform: skewX(-8deg); }
|
||||
20% { transform: skewX(7deg); }
|
||||
30% { transform: skewX(-6deg); }
|
||||
40% { transform: skewX(5deg); }
|
||||
50% { transform: skewX(-4deg); }
|
||||
60% { transform: skewX(3deg); }
|
||||
70% { transform: skewX(-2deg); }
|
||||
80% { transform: skewX(1deg); }
|
||||
90% { transform: skewX(0deg); }
|
||||
100% { transform: skewX(0deg); }
|
||||
}
|
||||
|
||||
.wiggle {
|
||||
-webkit-animation-name: wiggle;
|
||||
animation-name: wiggle;
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
.animated.wiggle {
|
||||
-webkit-animation-duration: 0.75s;
|
||||
animation-duration: 0.75s;
|
||||
}
|
||||
|
||||
:root {
|
||||
--text-primary: #fff;
|
||||
--text-secondary: #fff;
|
||||
@@ -187,14 +134,19 @@ summary:hover{
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: purple;
|
||||
background-image: linear-gradient(to right,
|
||||
#ff0045,
|
||||
#ff0e55,
|
||||
#ff1d79
|
||||
);
|
||||
transition: width 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.video > .info > .title {
|
||||
color: var(--text-primary) !important;
|
||||
font-weight: bold;
|
||||
@@ -234,8 +186,7 @@ margin-right: auto;
|
||||
color: black;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style>section p {
|
||||
section p {
|
||||
font-family:Whitney, Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@@ -327,12 +278,8 @@ margin-left: auto;
|
||||
background: var(--not-quite-black);
|
||||
text-decoration: none;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
}
|
||||
|
||||
<style>
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
@@ -354,8 +301,9 @@ margin-left: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.video-grid > .video:hover{
|
||||
border:solid;
|
||||
border:solid #ff0033;
|
||||
}
|
||||
|
||||
.video-grid > .video {
|
||||
border-radius: 16px;
|
||||
background: black;
|
||||
@@ -492,7 +440,7 @@ Popular Videos On Poke :3 </h1>
|
||||
|
||||
|
||||
|
||||
<div class="video-grid" style="border-top-left-radius:2em;width: 80em;border-top-right-radius: 2em;border: solid 1px purple;">
|
||||
<div class="video-grid" style="border-top-left-radius:2em;width: 80em;border-top-right-radius: 2em;border: solid 1px #df03a8;">
|
||||
<% inv.forEach(x => { %>
|
||||
<a href="/watch?v=<%- x.videoId %>" class="video canloadhd" data-author="<%- x.author %>" >
|
||||
<div class="thumbnail" style="background-image: url('/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw');border-radius: 10px;"><span class="video-length"><%- turntomins(x.lengthSeconds) %></span></div>
|
||||
@@ -506,8 +454,7 @@ Popular Videos On Poke :3 </h1>
|
||||
|
||||
|
||||
<% }) %>
|
||||
<p>u reached the end.... :sob: </p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -518,9 +465,6 @@ Popular Videos On Poke :3 </h1>
|
||||
<script>
|
||||
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('service-worker.js');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let bgs = document.querySelectorAll('[data-bg]');
|
||||
@@ -614,7 +558,6 @@ element.addEventListener('mouseleave', () => {
|
||||
});
|
||||
|
||||
element.addEventListener('wheel', (e) => {
|
||||
// You can also handle mouse wheel events for kinetic scrolling
|
||||
// Adjust the scrollTop based on e.deltaY
|
||||
element.scrollTop += e.deltaY;
|
||||
});
|
||||
@@ -1063,7 +1006,7 @@ margin-left: auto;
|
||||
<div class="app" >
|
||||
<nav>
|
||||
|
||||
<div class=left><a class="class" href="/" style=font-family:Inter,sans-serif;color:#fff> <img style="width: 8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo-mobile.svg"> </a>
|
||||
<div class=left><a class="class" href="/" style=font-family:Inter,sans-serif;color:#fff> <img style="width: 8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo-poke.svg"> </a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1097,7 +1040,7 @@ margin-left: auto;
|
||||
|
||||
<div class="tabs tabs-center" style="margin: 0px;">
|
||||
|
||||
<a href="?tab=popular" class="tab">Popular</a>
|
||||
<a href="?tab=popular" style="display:none" class="tab">Popular</a>
|
||||
|
||||
<a class="tab active">Trends</a>
|
||||
|
||||
@@ -1111,7 +1054,7 @@ margin-left: auto;
|
||||
|
||||
<a class="tab active">Popular</a>
|
||||
|
||||
<a href="/app" class="tab Activw">Trends</a>
|
||||
<a href="/app" class="tab active">Trends</a>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (!tab) { %>
|
||||
@@ -1195,19 +1138,19 @@ Privacy
|
||||
|
||||
|
||||
<% if (tab == "search") { %>
|
||||
<a href="/app"><-- </a><br>
|
||||
<span>
|
||||
Search morbillion amount of videos from poketube !!
|
||||
<a href="/app"><-- go to home </a>
|
||||
<span style="display:none">
|
||||
Search videos on poke! poke is a youtube front end so all of the videos on youtube should workm!!
|
||||
</span>
|
||||
<div class=search>
|
||||
|
||||
<form action="/app">
|
||||
|
||||
|
||||
|
||||
<input class="search-bar" autocomplete="on" id="fname" name="mobilesearch" placeholder="Search some videos lol "style="color:#fff;font-family:Inter,sans-serif;border-radius: 8px;">
|
||||
<input class="search-bar" autocomplete="on" id="fname" name="mobilesearch" placeholder="Search! "style="background: linear-gradient(90deg, hsla(235, 21%, 21%, 1) 0%, hsla(194, 41%, 22%, 1) 50%, hsla(174, 48%, 20%, 1) 100%);color:#fff;font-family:Inter,sans-serif;border-radius: 9999px;">
|
||||
|
||||
|
||||
<button class="btn btn-success" type=submit>
|
||||
<button class="btn btn-success" style="border-radius:1em;display:none;" type=submit>
|
||||
<i class="fa-light fa-search" style="margin: auto;"></i></button></form>
|
||||
<img src="https://t.poketube.fun/t/rep.gif" style="border:0;width: 0;visibility: hidden;">
|
||||
|
||||
@@ -1232,7 +1175,7 @@ Privacy
|
||||
|
||||
<div class="video-grid" style="background-color: var(--app-background);margin-top: -4em;">
|
||||
<span style="margin-bottom: -8em;margin-top: 8px;">
|
||||
<%=j.Search.estimatedResults.toLocaleString()%> Results (estimated)
|
||||
<%=j.Search.estimatedResults.toLocaleString()%> Results
|
||||
</span>
|
||||
<% j.Search.Results.Video.forEach(x => { %>
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
--><!DOCTYPE html><html>
|
||||
<head>
|
||||
<title>PokeTube | Download Video </title>
|
||||
<title>Poke | Download Video </title>
|
||||
<meta content="<%=color%>" name="theme-color">
|
||||
<link href=/css/yt-ukraine.svg?v=6 rel=icon>
|
||||
<link href=/css/yt-ukraine.svg?v=7 rel=icon>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta content=website property=og:type>
|
||||
<meta name="viewport" content="width=device-1200px, initial-scale=1.0, shrink-to-fit=yes, viewport-fit=cover">
|
||||
@@ -173,7 +173,9 @@ font-family:Ubuntu
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background: #333;
|
||||
height: 56em;
|
||||
height: fit-content;
|
||||
height: -moz-fit-content;
|
||||
|
||||
border-radius: 10px;
|
||||
margin-top: 2em;
|
||||
}
|
||||
@@ -183,9 +185,9 @@ font-family:Ubuntu
|
||||
<body>
|
||||
<% if (!isMobile) { %>
|
||||
|
||||
<div class="app" style="background:linear-gradient(135deg, #820622 10%, #4e2e82 100%, #725965 100%);height: 67em;">
|
||||
<div class="app" style="background:linear-gradient(135deg, #820622 10%, #4e2e82 100%, #725965 100%);height: 162em;">
|
||||
<nav>
|
||||
<div class=left><a class="class" href="/143" style=font-family:Inter,sans-serif;color:#fff><img style="transform: scale(1.3);padding-left:0.9em;width: 8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo.svg?v=5"> </a> </div>
|
||||
<div class=left><a class="class" href="/143" style=font-family:Inter,sans-serif;color:#fff><img style="transform: scale(1.3);padding-left:0.9em;width: 8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo-poke.svg?v=5"> </a> </div>
|
||||
<div class="middle">
|
||||
<form action="/search"><input class="search-bar" autocomplete="on" id="fname" name="query" style="color:var(--text-color);font-family:poketube flex,sans-serif;border-radius: 2em;font-weight: 850;font-stretch: extra-expanded;" data-ddg-inputtype="identities.firstName">
|
||||
<button class="btn btn-success" type="submit" style="transform: translate(21em, -1.25em);"><i class="fa-light fa-search"></i></button>
|
||||
@@ -237,17 +239,8 @@ font-family:Ubuntu
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"><div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;">
|
||||
<% } %>
|
||||
3GPP
|
||||
</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=17">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"><div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;">
|
||||
MP4 (480p)
|
||||
|
||||
MP4 - 480p
|
||||
</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=18">
|
||||
<i class="fa-light fa-download"></i>
|
||||
@@ -255,26 +248,35 @@ font-family:Ubuntu
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"> <div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;"> MP4 (720p)
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format-list" style="margin-top: 2em;">
|
||||
|
||||
<h2>Audio-Only formats</h2>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"> <div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;"> m4a (low)
|
||||
</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>">
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=139&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<!-- ignore this -->
|
||||
<p style="visibility: hidden;">
|
||||
we dont see the videos that you are downloading :P
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="format-list" style="margin-top: -2em;">
|
||||
|
||||
<h2>Audio-Only formats</h2>
|
||||
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"> <div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;"> m4a (high)
|
||||
</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=140&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;/*! text-align: center; */"> <div style="font-family:"PokeTube flex";font-stretch: ultra-expanded;font-weight: 700;"> webm (low)
|
||||
</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=249&f=webm">
|
||||
@@ -292,7 +294,118 @@ font-family:Ubuntu
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="format-list" style="margin-top: 2em;">
|
||||
<h2>Video-Only formats</h2>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">144p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=160&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">144p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=278&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">240p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=133&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">240p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=242&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">360p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=134&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">360p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=243&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">480p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=135&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">480p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=244&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">720p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=136&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">720p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=247&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">1080p - MP4</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=137&f=mp4">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="download-format" style="background: black;margin: 6px;border-radius: 14px;">
|
||||
<div style="font-family:'PokeTube flex';font-stretch: ultra-expanded;font-weight: 700;">1080p - WEBM</div>
|
||||
<a style="color:#fff;font-family:Ubuntu" href="/api/video/download?v=<%=v%>&q=248&f=webm">
|
||||
<i class="fa-light fa-download"></i>
|
||||
Download
|
||||
</a>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<!-- ignore this -->
|
||||
<p style="visibility: hidden;">
|
||||
gfhefdhgrdfhdfshfgddfsfdgdfsds gfhefdhgrdfhdfshfgddfsfdgdfsds
|
||||
@@ -305,7 +418,7 @@ font-family:Ubuntu
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/css/custom-css.js"> </script><script src="/css/data-mobile.js"> </script>
|
||||
<script src="/css/custom-css.js?v=54"> </script><script src="/css/data-mobile.js?v=549"> </script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
2596
html/gamehub.ejs
2596
html/gamehub.ejs
File diff suppressed because it is too large
Load Diff
122
html/landing.ejs
122
html/landing.ejs
@@ -2,7 +2,7 @@
|
||||
|
||||
This Source Code Form is subject to the terms of the GNU General Public License:
|
||||
|
||||
Copyright (C) 2021-2024 Poke (https://codeberg.org/Ashley/poke)
|
||||
Copyright (C) 2021-2025 Poke (https://codeberg.org/ashley/poke)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -30,17 +30,17 @@
|
||||
<link rel="stylesheet" href="/css/landing.css?v=2544">
|
||||
<link rel="stylesheet" href="/css/snow.css">
|
||||
<link rel="stylesheet" href="/css/app.main.css">
|
||||
<link href=/css/yt-ukraine.svg?v=6 rel=icon>
|
||||
<link href=/css/yt-ukraine.svg?v=7 rel=icon>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
|
||||
<meta content="▶▶ Poke - The privacy app of your dreams!" property=og:title>
|
||||
<meta content="▶▶ Poke - The only (good) front-end in the world!" property=og:title>
|
||||
<% if(embedtype === "woke") { %>
|
||||
<meta content="Poke is a 𝔀𝓸𝓴𝓮 software YouTube front-end, search engine, translator, 𝔀𝓸𝓴𝓮 app, and even 𝔀𝓸𝓴𝓮!! Watch 𝔀𝓸𝓴𝓮 videos, search the internet, and do all of that and more 𝔀𝓸𝓴𝓮 in this all-in-one 𝔀𝓸𝓴𝓮 app!!!"
|
||||
<meta content="Poke is a 𝔀𝓸𝓴𝓮 software YouTube front-end, translator, 𝔀𝓸𝓴𝓮 app, and even 𝔀𝓸𝓴𝓮!! Watch 𝔀𝓸𝓴𝓮 videos, search the internet, and do all of that and more 𝔀𝓸𝓴𝓮 in this all-in-one 𝔀𝓸𝓴𝓮 app!!!"
|
||||
property="twitter:description">
|
||||
<meta content="https://cdn.glitch.global/302c6ee0-629f-453b-9024-bad1f8d7be36/9fhFiXJ.png?v=1717357642758"
|
||||
property="og:image">
|
||||
<% } else { %>
|
||||
<meta content="Poke is a free software YouTube front-end, search engine, translator, map app, and more!! Watch silly videos, search the internet, and do all of that and more anonymously in this all-in-one privacy app!!!"
|
||||
<meta content="Poke is a free software YouTube front-end, translator, map app, and more!! Watch silly videos, search the internet, and do all of that and more anonymously in this all-in-one privacy app!!!"
|
||||
property="twitter:description">
|
||||
<meta content="https://cdn.glitch.global/302c6ee0-629f-453b-9024-bad1f8d7be36/poke.png?v=1716216428745"
|
||||
property="og:image">
|
||||
@@ -113,65 +113,70 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="margin-top: 6em;margin-left: auto;margin-right: auto;margin-bottom: -7em;background: #000;padding: 1em;border-radius: 14px;font-family: ubuntu;"> <%- banner %></div>
|
||||
<%- include('./partials/header.ejs') %>
|
||||
<video playsinline autoplay muted loop><source src="/bg-480.webm" type="video/webm"/></video>
|
||||
<video playsinline muted paused><source src="/bg-480.webm" type="video/webm"/></video>
|
||||
<div class="landing">
|
||||
<h1>PRIVACY APP OF YOUR DREAMS</h1>
|
||||
<p style="max-width: 800px;">Poke is a free software youtube front-end, search engine, translator, map app and even more!!1!! Watch silly videos, search stuff on the internet and do all of that and more anonymously in this all-in-one privacy app!!!1! :3</p><br>
|
||||
<a style="color: white; text-decoration: underline;" href="https://buildpalestine.com/2021/05/15/trusted-organizations-to-donate-to-palestine">Help & Support Palestine</a> <a style="color: white; text-decoration: underline;" href="https://gazaesims.com">(or donate esims to gaza!)</a> - <a style="color: white; text-decoration: underline;" href="https://war.ukraine.ua/donate"> Donate to Ukraine </a>
|
||||
<h1 style="text-align: center;">WELCOME TO POKE!</h1>
|
||||
<p style="max-width: 800px;text-align: center;margin: auto;margin-bottom: 3em;">Poke is a free software AD-FREE ! YouTube front-end, translator, map app, and more!!1! Watch videos and explore without a trace in this all-in-one privacy app!!1! :3</p>
|
||||
<div style="text-align: center; padding: 10px; border-radius: 8px;margin-left: -1em;">
|
||||
<details>
|
||||
<summary style="cursor: pointer; color: white; font-size: 18px; text-decoration: underline;">
|
||||
Support Humanitarian Efforts
|
||||
</summary>
|
||||
<div style="margin-top: 10px;">
|
||||
<a style="color: white; text-decoration: underline; margin: 0 5px;" href="https://buildpalestine.com/2021/05/15/trusted-organizations-to-donate-to-palestine">
|
||||
Support Palestine
|
||||
</a>
|
||||
<a style="color: white; text-decoration: underline; margin: 0px -6px;" href="https://gazaesims.com">
|
||||
(or u can donate esims to Gaza!)
|
||||
</a>
|
||||
<span style="color: white; margin: 0 5px;"> - </span>
|
||||
<a style="color: white; text-decoration: underline; margin: 0 5px;" href="https://war.ukraine.ua/donate">
|
||||
Donate to 🇺🇦
|
||||
</a>
|
||||
<span style="color: white; margin: 0 5px;"> - </span>
|
||||
<a style="color: white; text-decoration: underline; margin: 0 5px;" href="https://donate.unhcr.org/int/en/general">
|
||||
Donate to other areas of military conflict
|
||||
</a>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<% if(!DisablePokeChan) { %>
|
||||
<img src="/static/poke-chan-outfit-a.png" title="Poke-chan sitting :3">
|
||||
<% } %>
|
||||
|
||||
<div style="display: flex; gap: 12px;">
|
||||
<img src="/static/poke-screnshot-v2.png" />
|
||||
<img src="/static/Poke-Mobile.jpg" />
|
||||
</div>
|
||||
|
||||
<!-- Icons are from Iconoir -->
|
||||
<h1 style="margin-left: auto;margin-right: auto;text-align: center;margin-bottom: -1em;margin-top: 1em;"> Why Choose Poke Lol? </h1>
|
||||
<div class="new-feature-set">
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #ea6d6d;" width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" style="--darkreader-inline-color: #e8e6e3;" data-darkreader-inline-color=""><path d="M19.5 16L17.0248 12.6038" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M12 17.5V14" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M4.5 16L6.96895 12.6124" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M3 8C6.6 16 17.4 16 21 8" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path></svg>
|
||||
<h2>No Tracking and Ads</h2>
|
||||
</div>
|
||||
<p> Poke Has no Trackers or ads - we dont and we wont see the vids ur watching :3</p>
|
||||
</div>
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #6d8cea;" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" style="--darkreader-inline-color: #e8e6e3;" data-darkreader-inline-color=""><path d="M15 7C16.1046 7 17 6.10457 17 5C17 3.89543 16.1046 3 15 3C13.8954 3 13 3.89543 13 5C13 6.10457 13.8954 7 15 7Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M12.6133 8.26691L9.30505 12.4021L13.4403 16.5374L11.3727 21.0861" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M6.4104 9.5075L9.79728 6.19931L12.6132 8.26692L15.508 11.5752H19.2297" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M8.89152 15.7103L7.65095 16.5374H4.34277" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path></svg>
|
||||
<h2>Very Fast</h2>
|
||||
</div>
|
||||
<p> Poke is really ligthweight (both on server and client :3) so you can still use it on poor connections :3</p>
|
||||
</div>
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #519355;" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" style="--darkreader-inline-color: #e8e6e3;" data-darkreader-inline-color=""><path d="M12 8V16M12 16L15.5 12.5M12 16L8.5 12.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path></svg>
|
||||
<h2>Downloader</h2>
|
||||
</div>
|
||||
<p>You wouldnt download a car - welp i would :D u can download videos from poke for 0$!</p>
|
||||
</div>
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #ea6ddc;" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" style="--darkreader-inline-color: #e8e6e3;" data-darkreader-inline-color=""><path d="M21 12V10C21 7.23858 18.7614 5 16 5H8C5.23858 5 3 7.23858 3 10V10C3 12.7614 5.23858 15 8 15H12" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M20.1241 19.1185C20.6654 18.5758 21 17.827 21 17C21 15.3431 19.6569 14 18 14C16.3431 14 15 15.3431 15 17C15 18.6569 16.3431 20 18 20C18.8299 20 19.581 19.663 20.1241 19.1185ZM20.1241 19.1185L22 21" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path></svg>
|
||||
<h2>Web Search</h2>
|
||||
</div>
|
||||
<p>u can search the interwebs for anything that u wan!! (ik wha ur gonna search :p) </p>
|
||||
</div>
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #b7a358;" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" style="--darkreader-inline-color: #e8e6e3;" data-darkreader-inline-color=""><path d="M4.62323 5.24841C2.99408 7.02743 2 9.39765 2 12C2 17.5229 6.47715 22 12 22C14.5361 22 16.8517 21.0559 18.6146 19.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M21.3021 15.6775C21.7525 14.5392 22 13.2985 22 12C22 6.47715 17.5228 2 12 2C10.7687 2 9.58934 2.22255 8.5 2.62961" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M9 15C9.64448 15.8593 10.8428 16.3494 12 16.391C13.1141 16.431 14.1901 16.0554 14.6973 15.1933" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M12 16.391V18.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M9.5 9.5C9.5 10.6811 10.3525 11.1647 11.3862 11.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M15 8.5C14.315 7.81501 13.1087 7.33855 12 7.30872V5.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path><path d="M3 3L21 21" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="--darkreader-inline-stroke: #ffffff;" data-darkreader-inline-stroke=""></path></svg>
|
||||
<h2>DRM Free</h2>
|
||||
</div>
|
||||
<p> Poke comes without digital restrictions management - poke is free software :3</p>
|
||||
</div>
|
||||
<div class="feature-set">
|
||||
<div class="feature-set-title">
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg style="background: #886dea;" width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff"><path d="M17.5 17.5C20 21 23.9486 18.4151 23 15C21.5753 9.87113 20.8001 7.01556 20.3969 5.50793C20.1597 4.62136 19.3562 4 18.4384 4L5.56155 4C4.64382 4 3.844 4.62481 3.62085 5.515C2.7815 8.86349 2.0326 11.8016 1.14415 15C0.195501 18.4151 4.14415 21 6.64415 17.5" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16 4V6C16 7.10457 15.1046 8 14 8H10C8.89543 8 8 7.10457 8 6L8 4" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 16C9.10457 16 10 15.1046 10 14C10 12.8954 9.10457 12 8 12C6.89543 12 6 12.8954 6 14C6 15.1046 6.89543 16 8 16Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16 16C17.1046 16 18 15.1046 18 14C18 12.8954 17.1046 12 16 12C14.8954 12 14 12.8954 14 14C14 15.1046 14.8954 16 16 16Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
<h2>Games Included</h2>
|
||||
</div>
|
||||
<p> U can play funnie games on poke! </p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 style="margin-left: auto;margin-right: auto;text-align: center;margin-bottom: -1em;margin-top: 1em;">TOP 3 REASONS WHY POKE IZ COOL!!</h1>
|
||||
</div>
|
||||
<%
|
||||
const features = [
|
||||
{ title: "No Tracking and Ads", description: "Poke Has no Trackers or ads - we dont and we wont see the vids ur watching :3", icon: "<svg style='background: #ea6d6d;' width='24px' height='24px' viewBox='0 0 24 24' stroke-width='1.5' fill='none' xmlns='http://www.w3.org/2000/svg' color='#ffffff'><path d='M19.5 16L17.0248 12.6038' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M12 17.5V14' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M4.5 16L6.96895 12.6124' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M3 8C6.6 16 17.4 16 21 8' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>" },
|
||||
{ title: "Speedy", description: "Poke is really ligthweight (both on server and client :3) so you can still use it on poor connections :3", icon: "<svg style='background: #6d8cea;' width='24px' height='24px' stroke-width='1.5' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg' color='#ffffff'><path d='M15 7C16.1046 7 17 6.10457 17 5C17 3.89543 16.1046 3 15 3C13.8954 3 13 3.89543 13 5C13 6.10457 13.8954 7 15 7Z' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M12.6133 8.26691L9.30505 12.4021L13.4403 16.5374L11.3727 21.0861' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M6.4104 9.5075L9.79728 6.19931L12.6132 8.26692L15.508 11.5752H19.2297' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M8.89152 15.7103L7.65095 16.5374H4.34277' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>" },
|
||||
{ title: "Downloader", description: "You wouldnt download a car - welp i would :D u can download videos from poke for 0$!", icon: "<svg style='background: #519355;' width='24px' height='24px' stroke-width='1.5' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg' color='#ffffff'><path d='M12 8V16M12 16L15.5 12.5M12 16L8.5 12.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>" },
|
||||
{ title: "DRM Free", description: "Poke comes without digital restrictions management - poke is free software :3", icon: "<svg style='background: #b7a358;' width='24px' height='24px' stroke-width='1.5' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg' color='#ffffff'><path d='M4.62323 5.24841C2.99408 7.02743 2 9.39765 2 12C2 17.5229 6.47715 22 12 22C14.5361 22 16.8517 21.0559 18.6146 19.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M21.3021 15.6775C21.7525 14.5392 22 13.2985 22 12C22 6.47715 17.5228 2 12 2C10.7687 2 9.58934 2.22255 8.5 2.62961' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M9 15C9.64448 15.8593 10.8428 16.3494 12 16.391C13.1141 16.431 14.1901 16.0554 14.6973 15.1933' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M12 16.391V18.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M9.5 9.5C9.5 10.6811 10.3525 11.1647 11.3862 11.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M15 8.5C14.315 7.81501 13.1087 7.33855 12 7.30872V5.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M3 3L21 21' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>" },
|
||||
{ title: "Games Included", description: "U can play funnie games on poke!", icon: "<svg style='background: #886dea;' width='24px' height='24px' viewBox='0 0 24 24' stroke-width='1.5' fill='none' xmlns='http://www.w3.org/2000/svg' color='#ffffff'><path d='M17.5 17.5C20 21 23.9486 18.4151 23 15C21.5753 9.87113 20.8001 7.01556 20.3969 5.50793C20.1597 4.62136 19.3562 4 18.4384 4L5.56155 4C4.64382 4 3.844 4.62481 3.62085 5.515C2.7815 8.86349 2.0326 11.8016 1.14415 15C0.195501 18.4151 4.14415 21 6.64415 17.5' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M16 4V6C16 7.10457 15.1046 8 14 8H10C8.89543 8 8 7.10457 8 6L8 4' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M8 16C9.10457 16 10 15.1046 10 14C10 12.8954 9.10457 12 8 12C6.89543 12 6 12.8954 6 14C6 15.1046 6.89543 16 8 16Z' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/><path d='M16 16C17.1046 16 18 15.1046 18 14C18 12.8954 17.1046 12 16 12C14.8954 12 14 12.8954 14 14C14 15.1046 14.8954 16 16 16Z' stroke='#ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>" }
|
||||
];
|
||||
|
||||
const randomFeatures = features.sort(() => 0.5 - Math.random()).slice(0, 3);
|
||||
%>
|
||||
<div class="new-feature-set">
|
||||
<% randomFeatures.forEach(feature => { %>
|
||||
<div class="feature-set">
|
||||
|
||||
<div class="feature-set-title"><%- feature.icon %>
|
||||
<h2><%- feature.title %></h2>
|
||||
</div>
|
||||
<p><%- feature.description %></p></div>
|
||||
|
||||
<% }) %>
|
||||
</div>
|
||||
<div class="adaptive-grid">
|
||||
<%- include('./partials/card',
|
||||
icon_background='transparent',
|
||||
@@ -184,10 +189,7 @@
|
||||
secondary_icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.5 337.6 417.9 337.6z"/></svg>',
|
||||
secondary_text='Discord',
|
||||
secondary_link='https://discord.poketube.fun',
|
||||
|
||||
primary_icon='<svg version="1.1" viewBox="0 0 27.9 32" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="translate(-.095 .005)" fill="#040404"><path d="m27.1 31.2v-30.5h-2.19v-0.732h3.04v32h-3.04v-0.732z"/><path d="m8.23 10.4v1.54h0.044c0.385-0.564 0.893-1.03 1.49-1.37 0.58-0.323 1.25-0.485 1.99-0.485 0.72 0 1.38 0.14 1.97 0.42 0.595 0.279 1.05 0.771 1.36 1.48 0.338-0.5 0.796-0.941 1.38-1.32 0.58-0.383 1.27-0.574 2.06-0.574 0.602 0 1.16 0.074 1.67 0.22 0.514 0.148 0.954 0.383 .32 0.707 0.366 0.323 0.653 0.746 0.859 1.27 0.205 0.522 0.308 1.15 0.308 1.89v7.63h-3.13v-6.46c0-0.383-0.015-0.743-0.044-1.08-0.0209-0.307-0.103-0.607-0.242-0.882-0.133-0.251-0.336-0.458-0.584-0.596-0.257-0.146-0.606-0.22-1.05-0.22-0.44 0-0.796 0.085-1.07 0.253-0.272 0.17-0.485 0.39-0.639 0.662-0.159 0.287-0.264 0.602-0.308 0.927-0.052 0.347-0.078 0.697-0.078 1.05v6.35h-3.13v-6.4c0-0.338-7e-3 -0.673-0.021-1-0.0114-0.314-0.0749-0.623-0.188-0.916-0.108-0.277-0.3-0.512-0.55-0.673-0.258-0.168-0.636-0.253-1.14-0.253-0.198 0.0083-0.394 0.042-0.584 0.1-0.258 0.0745-0.498 0.202-0.705 0.374-0.228 0.184-0.422 0.449-0.584 0.794-0.161 0.346-0.242 0.798-0.242 1.36v6.62h-3.13v-11.4z"/><path d="m0.936 0.732v30.5h2.19v0.732h-3.04v-32h3.03v0.732z"/></g></svg>',
|
||||
primary_text='Matrix',
|
||||
primary_link='https://matrix.to/#/%23poke:vern.cc'
|
||||
|
||||
)%>
|
||||
|
||||
<%- include('./partials/card',
|
||||
@@ -232,7 +234,7 @@
|
||||
}
|
||||
// @license-end
|
||||
</script>
|
||||
<script src="/static/data-mobile.js"></script>
|
||||
<script src="/static/data-mobile.js?v=454545"></script>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
146
html/layouts/error-video.ejs
Normal file
146
html/layouts/error-video.ejs
Normal file
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Poke - <%= error %> !!!</title>
|
||||
<meta content="#111111" name="theme-color" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover" />
|
||||
<meta name="darkreader-lock" />
|
||||
<link href="/css/yt-ukraine.svg?v=3" rel="icon" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<style>
|
||||
body {
|
||||
margin: auto;
|
||||
background: #111111;
|
||||
color: white;
|
||||
font-family: arial;
|
||||
max-width: 600px;
|
||||
}
|
||||
body::before {
|
||||
background: linear-gradient(0deg, rgba(115, 32, 67, 1), rgba(17, 17, 17, 1));
|
||||
background-repeat: no-repeat;
|
||||
content: "";
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
transition: 1s height;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
p#abstract {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 500px;
|
||||
margin: 0px;
|
||||
font-weight: bold;
|
||||
opacity: 0.005;
|
||||
pointer-events: none;
|
||||
transition: 1s transform;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 790px) {
|
||||
p#abstract {
|
||||
transform: translate(-50%, -50%) rotate(90deg) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.error-footer {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
.error-footer a {
|
||||
color: #ac8fa5;
|
||||
text-decoration: none;
|
||||
margin: 0px 12px;
|
||||
}
|
||||
.error-footer a:hover {
|
||||
color: #d69cc8;
|
||||
}
|
||||
.countdown {
|
||||
margin-top: 16px;
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<% const RESTART_MSG = "Poke is currently restarting - please wait 1-2 minutes.."; %>
|
||||
<% if (description === RESTART_MSG) { %>
|
||||
<script>
|
||||
// Client-side reload logic
|
||||
let reloadCount = parseInt(localStorage.getItem('reloadCount') || '0', 10);
|
||||
reloadCount++;
|
||||
localStorage.setItem('reloadCount', reloadCount);
|
||||
|
||||
let seconds = reloadCount > 5 ? 30 : 10;
|
||||
|
||||
function updateCountdown() {
|
||||
const el = document.getElementById('countdown');
|
||||
if (el) {
|
||||
if (reloadCount > 5) {
|
||||
document.querySelector('.error p').textContent = "This is taking so long...";
|
||||
}
|
||||
el.textContent = "Trying again in " + seconds + " seconds...";
|
||||
}
|
||||
if (seconds <= 0) {
|
||||
location.reload();
|
||||
} else {
|
||||
seconds--;
|
||||
setTimeout(updateCountdown, 1000);
|
||||
}
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', updateCountdown);
|
||||
</script>
|
||||
<% } else { %>
|
||||
<script>
|
||||
// Clear reload count on non-restart errors
|
||||
localStorage.removeItem('reloadCount');
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<% if (description === RESTART_MSG) { %>
|
||||
<p id="abstract">502</p>
|
||||
<% } else { %>
|
||||
<p id="abstract">404</p>
|
||||
<% } %>
|
||||
|
||||
<div class="error">
|
||||
<h2><%= error %></h2>
|
||||
<p><%= description %></p>
|
||||
<% if (description === RESTART_MSG) { %>
|
||||
<div class="countdown" id="countdown"></div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="error-footer">
|
||||
<a href="https://codeberg.org/ashley/poke/issues/new/choose">Create issue</a>
|
||||
<a href="https://discord.poketube.fun">Report on our Discord</a>
|
||||
<% if (description === RESTART_MSG) { %>
|
||||
<a href="https://github.com/iv-org/invidious/issues">See Invidious issues</a>
|
||||
<a href="">Refresh Page</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
176
html/map.ejs
176
html/map.ejs
@@ -22,7 +22,7 @@
|
||||
* @licstart The following is the entire license notice for the JavaScript
|
||||
* code in this page.
|
||||
*
|
||||
* Copyright (C) 2021-2023 POKETUBE (https://github.com/iamashley0/poketube)
|
||||
* Copyright (C) 2021-2025 POKETUBE (https://github.com/iamashley0/poketube)
|
||||
*
|
||||
* The JavaScript code in this page is free software: you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License
|
||||
@@ -44,6 +44,178 @@
|
||||
|
||||
//--><!]]>
|
||||
</script>
|
||||
<script src="/static/maps.js"></script><script src="/static/data-mobile.js"></script>
|
||||
<script>(function(){
|
||||
const _0x5a3c=[
|
||||
"P2Jib3g9LTE2NS43NjE3MTg3NTAwMDAwMyUyQy0zLjg2NDI1NDYxNTcyMTM5NiUyQzMwLjQxMDE1NjI1MDAwMDAwNCUyQzcyLjQ0ODc5MTU1NzMwNjcyJmxheWVyPW1hcG5paw==",
|
||||
"aHR0cHM6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvZXhwb3J0L2VtYmVkLmh0bWw=",
|
||||
"d3d3Lm9wZW5zdHJlZXRtYXAub3Jn"
|
||||
];
|
||||
|
||||
function _0x99f2(i){ return atob(_0x5a3c[i]); }
|
||||
|
||||
function updateMap(lat, lon) {
|
||||
const delta = 0.25;
|
||||
const bbox = `?bbox=${lon-delta},${lat-delta},${lon+delta},${lat+delta}&layer=mapnik`;
|
||||
const newURL = _0x99f2(1) + bbox;
|
||||
const iframe = document.querySelector('iframe');
|
||||
if (iframe) {
|
||||
iframe.src = newURL;
|
||||
window.history.pushState({}, '', newURL);
|
||||
}
|
||||
const marker = document.getElementById('map-marker');
|
||||
if (marker) marker.remove();
|
||||
const newMarker = document.createElement('div');
|
||||
newMarker.id = 'map-marker';
|
||||
newMarker.style = 'position:absolute;width:20px;height:20px;background:red;border-radius:50%;transform:translate(-50%,-50%);z-index:9998;pointer-events:none;left:50%;top:50%';
|
||||
document.body.appendChild(newMarker);
|
||||
}
|
||||
|
||||
function copyCoordinates() {
|
||||
const marker = document.getElementById('map-marker');
|
||||
if (!marker) return alert('No coordinates to copy.');
|
||||
navigator.clipboard.writeText(window.location.href).then(() => {
|
||||
alert('Current map link copied to clipboard!');
|
||||
});
|
||||
}
|
||||
|
||||
function locateAndUpdate() {
|
||||
if (!navigator.geolocation) {
|
||||
alert('Geolocation is not supported by your browser.');
|
||||
return;
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
pos => {
|
||||
updateMap(pos.coords.latitude, pos.coords.longitude);
|
||||
},
|
||||
err => {
|
||||
alert('Unable to retrieve location: ' + err.message);
|
||||
},
|
||||
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
function _0x4f2a(){
|
||||
const bbox = _0x99f2(0);
|
||||
const base = _0x99f2(1);
|
||||
const url = base + bbox;
|
||||
const iframe = document.querySelector('iframe');
|
||||
if (!iframe) return setTimeout(_0x4f2a, 100);
|
||||
iframe.src = url;
|
||||
|
||||
iframe.addEventListener('load',()=>{
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
Array.from(doc.querySelectorAll('a')).forEach(a=>a.addEventListener('click',_linkHandler));
|
||||
Array.from(doc.querySelectorAll('*')).forEach(el=>{
|
||||
const bg = el.style.backgroundImage;
|
||||
if(bg.includes('//dka575ofm4ao0.cloudfront.net')){
|
||||
el.style.backgroundImage = bg.replace(/\/\/dka575ofm4ao0\.cloudfront\.net/g,
|
||||
m=>`https://p.poketube.fun/https://dka575ofm4ao0.cloudfront.net`);
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
console.warn('Cross-origin access denied, skipping DOM manipulation.');
|
||||
}
|
||||
});
|
||||
|
||||
window.history = new Proxy(window.history,{
|
||||
get(target, prop){
|
||||
if(prop === 'pushState') return (...args)=>{
|
||||
const iframe = document.querySelector('iframe');
|
||||
if(iframe && args[2]) iframe.src = args[2];
|
||||
return target.pushState.apply(target, args);
|
||||
};
|
||||
return Reflect.get(target, prop);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('popstate',()=>{
|
||||
const iframe = document.querySelector('iframe');
|
||||
if (iframe) iframe.src = location.href;
|
||||
});
|
||||
}
|
||||
|
||||
function _linkHandler(e){
|
||||
const h = e.target.href;
|
||||
const iframe = document.querySelector('iframe');
|
||||
if(!iframe) return;
|
||||
if(h.includes(_0x99f2(2))){
|
||||
e.preventDefault();
|
||||
iframe.src = h;
|
||||
window.history.pushState({}, '', h);
|
||||
} else {
|
||||
window.location.href = h;
|
||||
}
|
||||
}
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.style = 'position:absolute;top:10px;right:10px;z-index:9999;background:rgba(0,0,0,0.5);backdrop-filter:blur(12px);padding:10px 12px;border-radius:12px;box-shadow:0 4px 10px rgba(0,0,0,0.4);font-family:sans-serif;min-width:220px;';
|
||||
form.innerHTML = `
|
||||
<input id="searchBox" type="text" placeholder="Search..." style="padding:6px 10px;width:180px;font-size:14px;border:1px solid #444;border-radius:6px;background:#222;color:#fff">
|
||||
<ul id="suggestions" style="list-style:none;margin:6px 0 0;padding:0;max-height:180px;overflow:auto;background:#111;border:1px solid #333;border-radius:6px;display:none;position:relative;z-index:10000;color:#fff;"></ul>
|
||||
<div style="margin-top:10px;display:flex;gap:6px;flex-wrap:wrap">
|
||||
<button id="locate-btn" type="button" style="flex:1;padding:4px 6px;font-size:12px;background:#333;color:#fff;border:none;border-radius:6px">📍 Locate</button>
|
||||
<button type="button" style="flex:1;padding:4px 6px;font-size:12px;background:#333;color:#fff;border:none;border-radius:6px" onclick="copyCoordinates()">📋 Copy</button>
|
||||
<button type="button" style="flex:1;padding:4px 6px;font-size:12px;background:#333;color:#fff;border:none;border-radius:6px" onclick="location.reload()">🔁 Reset</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(form);
|
||||
document.getElementById('locate-btn').addEventListener('click', locateAndUpdate);
|
||||
|
||||
const input = form.querySelector('#searchBox');
|
||||
const suggestions = form.querySelector('#suggestions');
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
const query = input.value.trim();
|
||||
if (!query) return suggestions.style.display = 'none';
|
||||
fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(query)}&format=json&limit=5`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
suggestions.innerHTML = '';
|
||||
data.forEach(place => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = place.display_name;
|
||||
li.style = 'padding:6px 10px;cursor:pointer;border-bottom:1px solid #222;font-size:13px;background:#111';
|
||||
li.addEventListener('click', () => {
|
||||
input.value = place.display_name;
|
||||
suggestions.style.display = 'none';
|
||||
updateMap(parseFloat(place.lat), parseFloat(place.lon));
|
||||
});
|
||||
suggestions.appendChild(li);
|
||||
});
|
||||
suggestions.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
form.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
const q = input.value.trim();
|
||||
if(!q) return;
|
||||
fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q)}&format=json&limit=1`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(data[0]){
|
||||
updateMap(parseFloat(data[0].lat), parseFloat(data[0].lon));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const fab = document.createElement('button');
|
||||
fab.textContent = '+';
|
||||
fab.style = 'position:fixed;bottom:20px;right:20px;width:48px;height:48px;font-size:24px;background:#111;color:#fff;border:none;border-radius:50%;box-shadow:0 2px 10px rgba(0,0,0,0.4);cursor:pointer;z-index:9999';
|
||||
fab.title = 'More Tools';
|
||||
fab.onclick = () => alert('More features coming soon!');
|
||||
document.body.appendChild(fab);
|
||||
|
||||
const branding = document.createElement('div');
|
||||
branding.textContent = 'PokeMaps';
|
||||
branding.style = 'position: absolute; bottom: 10px; left: 10px; padding: 6px 10px; font-size: 31px; font-weight: 500; background: rgba(0, 0, 0, 0.6); color: white; border-radius: 6px; font-family: sans-serif; backdrop-filter: blur(6px); z-index: 9999; pointer-events: none;display: block;';
|
||||
document.body.appendChild(branding);
|
||||
|
||||
_0x4f2a();
|
||||
})();
|
||||
|
||||
</script><script src="/static/data-mobile.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
<% if (has_secondary_action=="true") { %>
|
||||
<a class="card-secondary" href="<%= secondary_link %>"><%- secondary_icon %> <%= secondary_text %></a>
|
||||
<% } %>
|
||||
<a class="card-primary" href="<%= primary_link %>"><%- primary_icon %> <%= primary_text %></a>
|
||||
</div>
|
||||
</div>
|
||||
<% }%>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<form action="/search">
|
||||
<input autocomplete="off" type="search" class="search-bar" id="fname" name="query">
|
||||
<input type="search" class="search-bar" id="fname" name="query">
|
||||
<button><?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff"><path d="M17 17L21 21" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 11C3 15.4183 6.58172 19 11 19C13.213 19 15.2161 18.1015 16.6644 16.6493C18.1077 15.2022 19 13.2053 19 11C19 6.58172 15.4183 3 11 3C6.58172 3 3 6.58172 3 11Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg></button>
|
||||
</form>
|
||||
<div style="opacity: 0;" class="suggestions"></div>
|
||||
@@ -28,36 +28,8 @@ through which recipients can access the Corresponding Source.
|
||||
@licend The above is the entire license notice
|
||||
for the JavaScript code in this page.
|
||||
*/
|
||||
|
||||
// Dismiss when the end-user clicks else where
|
||||
document.body.addEventListener("click", function (evt) {
|
||||
document.querySelector('.suggestions').style.opacity = '0'
|
||||
});
|
||||
|
||||
// When the end-user starts typing, trigget the fetch function
|
||||
document.querySelector('input[type="search"]').addEventListener('input', function(e) {
|
||||
if (e.target.value !== '') {
|
||||
document.querySelector('.suggestions').style.opacity = '1'
|
||||
GetResults()
|
||||
}
|
||||
else {null}
|
||||
});
|
||||
|
||||
// Fetch
|
||||
function GetResults() {
|
||||
var SearchValue = document.querySelector('input[type="search"]').value
|
||||
var YouTubeSuggestions = document.querySelector('.suggestions')
|
||||
fetch(window.location.origin + `/api/improving-poke/getsugesstions?q=${SearchValue}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {YouTubeSuggestions.innerHTML = ListOfSuggestionsYT(data)})
|
||||
}
|
||||
|
||||
// Create List
|
||||
function ListOfSuggestionsYT(data) {
|
||||
const text = data.suggestions.map(data => `<a href="/search?query=${data}">${data}</a>`).join("\n")
|
||||
return `${text}`
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
804
html/pokepad.ejs
Normal file
804
html/pokepad.ejs
Normal file
@@ -0,0 +1,804 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Pokepad</title>
|
||||
<meta property="og:title" content="PokePad">
|
||||
<meta property="og:description" content="E2EE notepad">
|
||||
<meta property="og:image" content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884">
|
||||
<meta property="og:type" content="website">
|
||||
<style>
|
||||
/* Root colors */
|
||||
:root {
|
||||
--bg: #0f0f17;
|
||||
--container-bg: rgba(30, 30, 47, 0.6);
|
||||
--surface: rgba(20, 20, 35, 0.4);
|
||||
--text: #e0e0e0;
|
||||
--accent: #8a2be2;
|
||||
--border: #444458;
|
||||
--btn-bg: rgba(60, 60, 80, 0.7);
|
||||
--btn-hover: rgba(70, 70, 100, 0.8);
|
||||
--error: #f66;
|
||||
--success: #8f8;
|
||||
--tab-hover: rgba(100, 100, 120, 0.8);
|
||||
--tab-active-bg: rgba(10, 10, 15, 0.9);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
#container {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
background: var(--container-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
backdrop-filter: blur(12px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
background: var(--surface);
|
||||
backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
header .title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
header .title svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: var(--accent);
|
||||
}
|
||||
header .e2ee {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#e2eePopup {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: var(--container-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
backdrop-filter: blur(12px);
|
||||
padding: 1rem;
|
||||
max-width: 300px;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
#e2eePopup p {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
#e2eePopup button {
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#tabBar {
|
||||
display: flex;
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 0.95rem;
|
||||
border-right: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
background: var(--surface);
|
||||
transition: background 0.2s, color 0.2s;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tab:hover {
|
||||
background: var(--tab-hover);
|
||||
}
|
||||
.tab.active {
|
||||
background: var(--tab-active-bg);
|
||||
color: var(--accent);
|
||||
border-bottom: 2px solid var(--accent);
|
||||
}
|
||||
.tab .title-text {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.tab .icon-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-left: 0.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tab .icon-btn:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
.tab[data-dragging="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#editor {
|
||||
flex: 1;
|
||||
background: rgba(20, 20, 35, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
min-height: 300px;
|
||||
color: var(--text);
|
||||
}
|
||||
#editor:empty:before {
|
||||
content: attr(data-placeholder);
|
||||
color: #888;
|
||||
}
|
||||
#editor:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
}
|
||||
|
||||
#toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.tool-btn {
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: background 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tool-btn svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: var(--text);
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
.tool-btn:hover {
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
#controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--surface);
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
#controls input[type="password"] {
|
||||
flex: 1;
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 20, 35, 0.6);
|
||||
color: var(--text);
|
||||
}
|
||||
#controls .action-btn {
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
#controls .action-btn:hover {
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
#message {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
min-height: 1.2em;
|
||||
}
|
||||
#message.error {
|
||||
color: var(--error);
|
||||
}
|
||||
#message.success {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
#fileInput {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<!-- Header with icon and E2EE label -->
|
||||
<header>
|
||||
<div class="title">
|
||||
<!-- Lock SVG -->
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2C9.238 2 7 4.238 7 7v3H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-1V7c0-2.762-2.238-5-5-5zm-3 5c0-1.654 1.346-3 3-3s3 1.346 3 3v3H9V7zm-1 5h8v7H8v-7z"/>
|
||||
</svg>
|
||||
Pokepad
|
||||
</div>
|
||||
<div class="e2ee" id="e2eeLabel">End-to-End Encrypted</div>
|
||||
</header>
|
||||
|
||||
<!-- E2EE Info Popup -->
|
||||
<div id="e2eePopup">
|
||||
<p><strong>End-to-End Encryption (E2EE)</strong></p>
|
||||
<p>All notes are encrypted locally before being saved. Only you (and anyone you share your password with) can decrypt and read your notes. No unencrypted data ever leaves your browser.</p>
|
||||
<button id="e2eeCloseBtn">Close</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab bar -->
|
||||
<div id="tabBar"></div>
|
||||
|
||||
<!-- Formatting toolbar -->
|
||||
<div id="toolbar">
|
||||
<!-- Bold -->
|
||||
<button class="tool-btn" data-cmd="bold" title="Bold (Ctrl+B)">
|
||||
<svg viewBox="0 0 24 24"><path d="M15.6 10.79c.75-.54 1.4-1.31 1.85-2.18.46-.87.7-1.86.7-2.93 0-1.07-.24-2.06-.7-2.93-.46-.87-1.1-1.64-1.85-2.18-.81-.59-1.77-.89-2.77-.89h-5v16h5c1 0 1.96-.3 2.77-.89.75-.54 1.4-1.31 1.85-2.18.46-.87.7-1.86.7-2.93 0-1.07-.24-2.06-.7-2.93zM11 4h1.5c.74 0 1.44.26 2 .72.56.46 1.01 1.11 1.28 1.8.27.69.42 1.45.42 2.22 0 .77-.15 1.53-.42 2.22-.27.69-.72 1.34-1.28 1.8-.56.46-1.26.72-2 .72h-1.5V4zm1.5 12H11c-.74 0-1.44-.26-2-.72-.56-.46-1.01-1.11-1.28-1.8-.27-.69-.42-1.45-.42-2.22 0-.77.15-1.53.42-2.22.27-.69.72-1.34 1.28-1.8.56-.46 1.26-.72 2-.72h1.5v9z"/></svg>
|
||||
<span>Bold</span>
|
||||
</button>
|
||||
<!-- Italic -->
|
||||
<button class="tool-btn" data-cmd="italic" title="Italic (Ctrl+I)">
|
||||
<svg viewBox="0 0 24 24"><path d="M10 4v3h2.21l-3.42 10H6v3h8v-3h-2.21l3.42-10H18V4z"/></svg>
|
||||
<span>Italic</span>
|
||||
</button>
|
||||
<!-- Underline -->
|
||||
<button class="tool-btn" data-cmd="underline" title="Underline (Ctrl+U)">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 17c3.31 0 6-2.69 6-6V4h-2v7c0 2.21-1.79 4-4 4s-4-1.79-4-4V4H6v7c0 3.31 2.69 6 6 6zm-5 2v2h10v-2H7z"/></svg>
|
||||
<span>Underline</span>
|
||||
</button>
|
||||
<!-- Strikethrough -->
|
||||
<button class="tool-btn" data-cmd="strikeThrough" title="Strikethrough">
|
||||
<svg viewBox="0 0 24 24"><path d="M10 19v-2H5.41L9 13.41 7.59 12 2 17.59V19h8zm4 0h8v-2h-5.59L15 13.41 13.59 12 8 17.59V19h6zM21 11h-8V9h8v2z"/></svg>
|
||||
<span>Strike</span>
|
||||
</button>
|
||||
<!-- Unordered List -->
|
||||
<button class="tool-btn" data-cmd="insertUnorderedList" title="Bullet List">
|
||||
<svg viewBox="0 0 24 24"><path d="M4 10.5c.83 0 1.5-.67 1.5-1.5S4.83 7.5 4 7.5 2.5 8.17 2.5 9 3.17 10.5 4 10.5zm0 5c.83 0 1.5-.67 1.5-1.5S4.83 12.5 4 12.5 2.5 13.17 2.5 14 3.17 15.5 4 15.5zm0 5c.83 0 1.5-.67 1.5-1.5S4.83 17.5 4 17.5 2.5 18.17 2.5 19 3.17 20.5 4 20.5zM7 9h14v2H7V9zm0 5h14v2H7v-2zm0 5h14v2H7v-2z"/></svg>
|
||||
<span>Bullets</span>
|
||||
</button>
|
||||
<!-- Ordered List -->
|
||||
<button class="tool-btn" data-cmd="insertOrderedList" title="Numbered List">
|
||||
<svg viewBox="0 0 24 24"><path d="M4 10h2v1H4v2h2v1H4v2h4v-1H6v-2h2v-1H4v-2zm0-4h4v1H6v2h2v1H4v2h4v1H4v2h6v-1H6v-2h2v-1H4v-2zm0 10h12v-1H4v-2h2v-1H4v-2h4v-1H4V6h6V5H4v2h4v1H4v2h4v1H4v2z"/></svg>
|
||||
<span>Numbers</span>
|
||||
</button>
|
||||
<!-- Align Left -->
|
||||
<button class="tool-btn" data-cmd="justifyLeft" title="Align Left">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm0 4h12v2H3V9zm0 4h18v2H3v-2zm0 4h12v2H3v-2z"/></svg>
|
||||
<span>Left</span>
|
||||
</button>
|
||||
<!-- Align Center -->
|
||||
<button class="tool-btn" data-cmd="justifyCenter" title="Align Center">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm3 4h12v2H6V9zm3 4h18v2H9v-2zm3 4h12v2H12v-2z"/></svg>
|
||||
<span>Center</span>
|
||||
</button>
|
||||
<!-- Align Right -->
|
||||
<button class="tool-btn" data-cmd="justifyRight" title="Align Right">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm6 4h12v2H9V9zm6 4h18v2H15v-2zm6 4h12v2h-12v-2z"/></svg>
|
||||
<span>Right</span>
|
||||
</button>
|
||||
<!-- Text Color -->
|
||||
<button class="tool-btn" id="colorPickerBtn" title="Text Color">
|
||||
<svg viewBox="0 0 24 24"><path d="M15.55 14.52L9.48 4.5H7.03l6.07 10.02c.18.3.28.64.28 1 0 1.1-.9 2-2 2s-2-.9-2-2H7c0 1.66 1.34 3 3 3s3-1.34 3-3c0-.61-.22-1.17-.58-1.6zM12 2C8.13 2 5 5.13 5 9c0 1.66.58 3.18 1.55 4.38L12 22l5.45-8.62C18.42 12.18 19 10.66 19 9c0-3.87-3.13-7-7-7z"/></svg>
|
||||
<span>Color</span>
|
||||
</button>
|
||||
<input type="color" id="colorPicker" style="display:none" />
|
||||
<!-- Insert Link -->
|
||||
<button class="tool-btn" data-cmd="createLink" title="Insert Link">
|
||||
<svg viewBox="0 0 24 24"><path d="M3.9 12c0-1.17.44-2.27 1.24-3.06l3.34-3.34c1.65-1.65 4.33-1.65 5.98 0 1.65 1.65 1.65 4.33 0 5.98l-1.06 1.06-1.41-1.41 1.06-1.06c.88-.88.88-2.31 0-3.19-.88-.88-2.31-.88-3.19 0l-3.34 3.34c-.88.88-.88 2.31 0 3.19.88.88 2.31.88 3.19 0l1.06-1.06 1.41 1.41-1.06 1.06c-1.65 1.65-4.33 1.65-5.98 0C4.34 14.27 3.9 13.17 3.9 12zm16.2 0c0 1.17-.44 2.27-1.24 3.06l-3.34 3.34c-1.65 1.65-4.33 1.65-5.98 0-1.65-1.65-1.65-4.33 0-5.98l1.06-1.06 1.41 1.41-1.06 1.06c-.88.88-.88 2.31 0 3.19.88.88 2.31.88 3.19 0l3.34-3.34c.88-.88.88-2.31 0-3.19-.88-.88-2.31-.88-3.19 0l-1.06 1.06-1.41-1.41 1.06-1.06c1.65-1.65 4.33-1.65 5.98 0 1.65 1.65 1.65 4.33 0 5.98z"/></svg>
|
||||
<span>Link</span>
|
||||
</button>
|
||||
<!-- Unlink -->
|
||||
<button class="tool-btn" data-cmd="unlink" title="Remove Link">
|
||||
<svg viewBox="0 0 24 24"><path d="M12.71 11.29l-1.42 1.42L10 11.42l-1.29 1.29-1.42-1.42L8.58 10 7.29 8.71l1.42-1.42L10 8.58l1.29-1.29 1.42 1.42L11.42 10l1.29 1.29zM17.65 6.35l-1.41 1.41 1.41 1.41L19.06 7.76l-1.41-1.41zm-11.3 11.3l-1.41 1.41 1.41 1.41 1.41-1.41-1.41-1.41z"/></svg>
|
||||
<span>Unlink</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="editor" contenteditable="true" spellcheck="false" data-placeholder="Type your notes here..."></div>
|
||||
|
||||
<div id="controls">
|
||||
<input type="password" id="password" placeholder="Password" />
|
||||
<button class="action-btn" id="saveBtn">Download Encrypted</button>
|
||||
<button class="action-btn" id="loadBtn">Decrypt & Load</button>
|
||||
<button class="action-btn" id="uploadBtn">Upload Encrypted File</button>
|
||||
<input type="file" id="fileInput" accept=".json" />
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let notes = [];
|
||||
let currentTabId = null;
|
||||
let tabCounter = 0;
|
||||
let dragSrcId = null;
|
||||
|
||||
const STORAGE_KEY = 'pokepadEncrypted';
|
||||
const PLAIN_KEY = 'pokepadPlain';
|
||||
const tabBar = document.getElementById('tabBar');
|
||||
const editor = document.getElementById('editor');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const saveBtn = document.getElementById('saveBtn');
|
||||
const loadBtn = document.getElementById('loadBtn');
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const messageDiv = document.getElementById('message');
|
||||
const toolbarButtons = document.querySelectorAll('#toolbar .tool-btn');
|
||||
const e2eeLabel = document.getElementById('e2eeLabel');
|
||||
const e2eePopup = document.getElementById('e2eePopup');
|
||||
const e2eeCloseBtn = document.getElementById('e2eeCloseBtn');
|
||||
const colorPicker = document.getElementById('colorPicker');
|
||||
const colorPickerBtn = document.getElementById('colorPickerBtn');
|
||||
|
||||
// On load: attempt to load plain data so tabs persist
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const storedPlain = localStorage.getItem(PLAIN_KEY);
|
||||
if (storedPlain) {
|
||||
try {
|
||||
const parsedPlain = JSON.parse(storedPlain);
|
||||
if (parsedPlain.notes && Array.isArray(parsedPlain.notes)) {
|
||||
notes = parsedPlain.notes;
|
||||
tabCounter = notes.length ? Math.max(...notes.map(n => n.id)) + 1 : 0;
|
||||
currentTabId = parsedPlain.currentTabId != null
|
||||
? parsedPlain.currentTabId
|
||||
: notes[0]?.id;
|
||||
renderTabs();
|
||||
if (currentTabId != null) {
|
||||
const curr = notes.find(n => n.id === currentTabId);
|
||||
editor.innerHTML = curr ? curr.content : '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// If no plain data, create default tab
|
||||
createNewTab();
|
||||
setInterval(savePlain, 10000);
|
||||
});
|
||||
|
||||
function createNewTab(name = null, content = '') {
|
||||
const id = tabCounter++;
|
||||
const defaultName = name || `Note ${id + 1}`;
|
||||
notes.push({ id, name: defaultName, content });
|
||||
switchToTab(id);
|
||||
renderTabs();
|
||||
savePlain();
|
||||
}
|
||||
|
||||
function renderTabs() {
|
||||
tabBar.innerHTML = '';
|
||||
notes.forEach((note) => {
|
||||
const tabEl = document.createElement('div');
|
||||
tabEl.className = 'tab' + (note.id === currentTabId ? ' active' : '');
|
||||
tabEl.setAttribute('draggable', 'true');
|
||||
tabEl.dataset.id = note.id;
|
||||
|
||||
const titleSpan = document.createElement('span');
|
||||
titleSpan.className = 'title-text';
|
||||
titleSpan.textContent = note.name;
|
||||
tabEl.appendChild(titleSpan);
|
||||
|
||||
const renameBtn = document.createElement('span');
|
||||
renameBtn.className = 'icon-btn';
|
||||
renameBtn.textContent = '✎';
|
||||
renameBtn.title = 'Rename tab';
|
||||
renameBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
promptRename(note.id, titleSpan);
|
||||
});
|
||||
tabEl.appendChild(renameBtn);
|
||||
|
||||
const closeBtn = document.createElement('span');
|
||||
closeBtn.className = 'icon-btn';
|
||||
closeBtn.textContent = '×';
|
||||
closeBtn.title = 'Close tab';
|
||||
closeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
closeTab(note.id);
|
||||
});
|
||||
tabEl.appendChild(closeBtn);
|
||||
|
||||
tabEl.addEventListener('click', () => switchToTab(note.id));
|
||||
|
||||
tabEl.addEventListener('dragstart', tabDragStart);
|
||||
tabEl.addEventListener('dragover', tabDragOver);
|
||||
tabEl.addEventListener('drop', tabDrop);
|
||||
tabEl.addEventListener('dragend', tabDragEnd);
|
||||
|
||||
tabBar.appendChild(tabEl);
|
||||
});
|
||||
|
||||
const newTabEl = document.createElement('div');
|
||||
newTabEl.className = 'tab';
|
||||
newTabEl.textContent = '+';
|
||||
newTabEl.title = 'Add new tab';
|
||||
newTabEl.addEventListener('click', () => createNewTab());
|
||||
tabBar.appendChild(newTabEl);
|
||||
}
|
||||
|
||||
function switchToTab(id) {
|
||||
if (currentTabId !== null) {
|
||||
const currentNote = notes.find(n => n.id === currentTabId);
|
||||
if (currentNote) currentNote.content = editor.innerHTML;
|
||||
}
|
||||
currentTabId = id;
|
||||
const nextNote = notes.find(n => n.id === id);
|
||||
editor.innerHTML = nextNote ? nextNote.content : '';
|
||||
renderTabs();
|
||||
clearMessage();
|
||||
editor.focus();
|
||||
savePlain();
|
||||
}
|
||||
|
||||
function promptRename(id, titleSpan) {
|
||||
const note = notes.find(n => n.id === id);
|
||||
if (!note) return;
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = note.name;
|
||||
input.style.fontSize = '0.95rem';
|
||||
input.style.background = 'rgba(20,20,35,0.6)';
|
||||
input.style.color = 'var(--text)';
|
||||
input.style.border = '1px solid var(--border)';
|
||||
input.style.borderRadius = '4px';
|
||||
input.style.padding = '2px 4px';
|
||||
titleSpan.replaceWith(input);
|
||||
input.focus();
|
||||
input.select();
|
||||
input.addEventListener('blur', () => {
|
||||
const newName = input.value.trim();
|
||||
if (newName) note.name = newName;
|
||||
renderTabs();
|
||||
savePlain();
|
||||
});
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') input.blur();
|
||||
});
|
||||
}
|
||||
|
||||
function closeTab(id) {
|
||||
const idx = notes.findIndex(n => n.id === id);
|
||||
if (idx === -1) return;
|
||||
const wasActive = (id === currentTabId);
|
||||
notes.splice(idx, 1);
|
||||
if (notes.length === 0) {
|
||||
tabCounter = 0;
|
||||
notes = [];
|
||||
createNewTab();
|
||||
return;
|
||||
}
|
||||
if (wasActive) {
|
||||
const newIdx = idx > 0 ? idx - 1 : 0;
|
||||
switchToTab(notes[newIdx].id);
|
||||
} else {
|
||||
renderTabs();
|
||||
savePlain();
|
||||
}
|
||||
}
|
||||
|
||||
function tabDragStart(e) {
|
||||
dragSrcId = Number(e.currentTarget.dataset.id);
|
||||
e.currentTarget.dataset.dragging = 'true';
|
||||
}
|
||||
function tabDragOver(e) {
|
||||
e.preventDefault();
|
||||
const targetId = Number(e.currentTarget.dataset.id);
|
||||
if (dragSrcId === targetId) return;
|
||||
const srcIndex = notes.findIndex(n => n.id === dragSrcId);
|
||||
const tgtIndex = notes.findIndex(n => n.id === targetId);
|
||||
notes.splice(tgtIndex, 0, notes.splice(srcIndex, 1)[0]);
|
||||
renderTabs();
|
||||
}
|
||||
function tabDrop(e) {
|
||||
e.stopPropagation();
|
||||
savePlain();
|
||||
}
|
||||
function tabDragEnd(e) {
|
||||
delete e.currentTarget.dataset.dragging;
|
||||
}
|
||||
|
||||
// Save editor changes
|
||||
editor.addEventListener('input', () => {
|
||||
if (currentTabId !== null) {
|
||||
const note = notes.find(n => n.id === currentTabId);
|
||||
if (note) note.content = editor.innerHTML;
|
||||
}
|
||||
savePlain();
|
||||
});
|
||||
|
||||
function savePlain() {
|
||||
if (currentTabId !== null) {
|
||||
const currentNote = notes.find(n => n.id === currentTabId);
|
||||
if (currentNote) currentNote.content = editor.innerHTML;
|
||||
}
|
||||
const payload = { notes, currentTabId };
|
||||
localStorage.setItem(PLAIN_KEY, JSON.stringify(payload));
|
||||
}
|
||||
|
||||
async function getKeyMaterial(password) {
|
||||
const encoder = new TextEncoder();
|
||||
return crypto.subtle.importKey(
|
||||
'raw',
|
||||
encoder.encode(password),
|
||||
'PBKDF2',
|
||||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
}
|
||||
|
||||
async function deriveKey(password, salt) {
|
||||
const keyMaterial = await getKeyMaterial(password);
|
||||
return crypto.subtle.deriveKey(
|
||||
{ name: 'PBKDF2', salt: salt, iterations: 150000, hash: 'SHA-256' },
|
||||
keyMaterial,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
false,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
}
|
||||
|
||||
function arrayBufferToBase64(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = '';
|
||||
for (let b of bytes) {
|
||||
binary += String.fromCharCode(b);
|
||||
}
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
function base64ToArrayBuffer(base64) {
|
||||
const binary = atob(base64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
async function encryptData(plainText, password) {
|
||||
const encoder = new TextEncoder();
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const key = await deriveKey(password, salt);
|
||||
const cipherBuffer = await crypto.subtle.encrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
encoder.encode(plainText)
|
||||
);
|
||||
return {
|
||||
salt: arrayBufferToBase64(salt.buffer),
|
||||
iv: arrayBufferToBase64(iv.buffer),
|
||||
data: arrayBufferToBase64(cipherBuffer)
|
||||
};
|
||||
}
|
||||
|
||||
async function decryptData(encryptedObj, password) {
|
||||
const salt = base64ToArrayBuffer(encryptedObj.salt);
|
||||
const iv = base64ToArrayBuffer(encryptedObj.iv);
|
||||
const cipherBuffer = base64ToArrayBuffer(encryptedObj.data);
|
||||
const key = await deriveKey(password, salt);
|
||||
try {
|
||||
const decryptedBuffer = await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
cipherBuffer
|
||||
);
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decryptedBuffer);
|
||||
} catch {
|
||||
throw new Error('Decryption failed. Wrong password or corrupted data.');
|
||||
}
|
||||
}
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
clearMessage();
|
||||
const pw = passwordInput.value;
|
||||
if (!pw) {
|
||||
showMessage('Please enter a password before downloading.', 'error');
|
||||
return;
|
||||
}
|
||||
if (currentTabId !== null) {
|
||||
const currentNote = notes.find(n => n.id === currentTabId);
|
||||
if (currentNote) currentNote.content = editor.innerHTML;
|
||||
}
|
||||
savePlain();
|
||||
const payload = { notes, currentTabId };
|
||||
const jsonString = JSON.stringify(payload);
|
||||
try {
|
||||
const encrypted = await encryptData(jsonString, pw);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(encrypted));
|
||||
const downloadObj = JSON.stringify(encrypted);
|
||||
const blob = new Blob([downloadObj], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
const timestamp = new Date().toISOString().slice(0,19).replace(/[:T]/g, '-');
|
||||
a.download = `pokepad_${timestamp}.json`;
|
||||
a.href = url;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
showMessage('Encrypted file ready to download.', 'success');
|
||||
} catch {
|
||||
showMessage('Error during encryption. Try again.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
uploadBtn.addEventListener('click', () => { clearMessage(); fileInput.click(); });
|
||||
|
||||
fileInput.addEventListener('change', () => {
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (e) => {
|
||||
clearMessage();
|
||||
const pw = passwordInput.value;
|
||||
if (!pw) {
|
||||
showMessage('Enter password to decrypt uploaded file.', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const encryptedObj = JSON.parse(e.target.result);
|
||||
const decryptedText = await decryptData(encryptedObj, pw);
|
||||
const parsed = JSON.parse(decryptedText);
|
||||
if (!parsed.notes || !Array.isArray(parsed.notes)) {
|
||||
showMessage('Invalid file format.', 'error');
|
||||
return;
|
||||
}
|
||||
notes = parsed.notes.map(n => ({ id: n.id, name: n.name, content: n.content }));
|
||||
tabCounter = notes.length ? Math.max(...notes.map(n => n.id)) + 1 : 0;
|
||||
currentTabId = parsed.currentTabId != null ? parsed.currentTabId : (notes[0]?.id);
|
||||
renderTabs();
|
||||
if (currentTabId != null) {
|
||||
const curr = notes.find(n => n.id === currentTabId);
|
||||
editor.innerHTML = curr ? curr.content : '';
|
||||
} else if (notes[0]) {
|
||||
currentTabId = notes[0].id;
|
||||
editor.innerHTML = notes[0].content;
|
||||
}
|
||||
savePlain();
|
||||
showMessage('Decryption successful! Notes loaded.', 'success');
|
||||
} catch {
|
||||
showMessage('Failed to decrypt or parse file.', 'error');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
fileInput.value = '';
|
||||
});
|
||||
|
||||
loadBtn.addEventListener('click', async () => {
|
||||
clearMessage();
|
||||
const pw = passwordInput.value;
|
||||
if (!pw) {
|
||||
showMessage('Enter password to decrypt stored data.', 'error');
|
||||
return;
|
||||
}
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (!stored) {
|
||||
showMessage('No locally stored data found.', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const encryptedObj = JSON.parse(stored);
|
||||
const decryptedText = await decryptData(encryptedObj, pw);
|
||||
const parsed = JSON.parse(decryptedText);
|
||||
if (!parsed.notes || !Array.isArray(parsed.notes)) {
|
||||
showMessage('Invalid local data format.', 'error');
|
||||
return;
|
||||
}
|
||||
notes = parsed.notes.map(n => ({ id: n.id, name: n.name, content: n.content }));
|
||||
tabCounter = notes.length ? Math.max(...notes.map(n => n.id)) + 1 : 0;
|
||||
currentTabId = parsed.currentTabId != null ? parsed.currentTabId : (notes[0]?.id);
|
||||
renderTabs();
|
||||
if (currentTabId != null) {
|
||||
const curr = notes.find(n => n.id === currentTabId);
|
||||
editor.innerHTML = curr ? curr.content : '';
|
||||
} else if (notes[0]) {
|
||||
currentTabId = notes[0].id;
|
||||
editor.innerHTML = notes[0].content;
|
||||
}
|
||||
showMessage('Decryption successful! Notes loaded from localStorage.', 'success');
|
||||
savePlain();
|
||||
} catch {
|
||||
showMessage('Decryption failed or data corrupted.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') {
|
||||
e.preventDefault();
|
||||
showMessage('PokePad autosaves :3 ', 'success');
|
||||
}
|
||||
});
|
||||
|
||||
toolbarButtons.forEach(btn => {
|
||||
const cmd = btn.getAttribute('data-cmd');
|
||||
btn.addEventListener('click', () => {
|
||||
if (cmd === 'createLink') {
|
||||
const url = prompt('Enter the URL:', 'https://');
|
||||
if (url) document.execCommand(cmd, false, url);
|
||||
} else {
|
||||
document.execCommand(cmd, false, null);
|
||||
}
|
||||
editor.focus();
|
||||
});
|
||||
});
|
||||
|
||||
colorPickerBtn.addEventListener('click', () => {
|
||||
colorPicker.click();
|
||||
});
|
||||
colorPicker.addEventListener('input', () => {
|
||||
document.execCommand('foreColor', false, colorPicker.value);
|
||||
editor.focus();
|
||||
});
|
||||
|
||||
e2eeLabel.addEventListener('click', () => {
|
||||
e2eePopup.style.display = 'block';
|
||||
});
|
||||
e2eeCloseBtn.addEventListener('click', () => {
|
||||
e2eePopup.style.display = 'none';
|
||||
});
|
||||
|
||||
function showMessage(text, type) {
|
||||
messageDiv.textContent = text;
|
||||
messageDiv.className = type;
|
||||
setTimeout(() => {
|
||||
if (messageDiv.textContent === text) clearMessage();
|
||||
}, 4000);
|
||||
}
|
||||
function clearMessage() {
|
||||
messageDiv.textContent = '';
|
||||
messageDiv.className = '';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
931
html/search.ejs
931
html/search.ejs
File diff suppressed because it is too large
Load Diff
@@ -16,235 +16,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
-->
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>PokeTranslate</title>
|
||||
<link rel="icon" href="/static/yt-ukraine.svg">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta content="PokeTranslate" property=og:title>
|
||||
<meta content="Translate text - Anonymously!" property=twitter:description>
|
||||
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
|
||||
<meta content=summary_large_image name=twitter:card>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<style>
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wrap.languages {
|
||||
flex-wrap: nowrap;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#could_not_switch_languages_text {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
border-radius:1em;
|
||||
}
|
||||
|
||||
.item-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
width: 450px;
|
||||
margin: 5px 10px;
|
||||
}
|
||||
|
||||
|
||||
.language,
|
||||
.switch_languages {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.language {
|
||||
margin: 0px 10px;
|
||||
}
|
||||
|
||||
.switch_languages {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
#switchbutton {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1rem;
|
||||
padding: 4px 10px;
|
||||
border: 2px solid #888888;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
padding: 4px;
|
||||
border: 2px solid #888888;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
height: 5rem;
|
||||
font-family: sans-serif;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus,
|
||||
button:focus {
|
||||
border-color: #478061;
|
||||
outline: 1px solid #478061;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
justify-content: center;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#definitions_and_translations {
|
||||
display: grid;
|
||||
margin: auto;
|
||||
width: 1100px;
|
||||
gap: 10px;
|
||||
grid-template-areas: "definitions translations";
|
||||
|
||||
}
|
||||
|
||||
.def_type {
|
||||
color: #007979;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.syn {
|
||||
color: #804700;
|
||||
}
|
||||
|
||||
.syn_type {
|
||||
color: #007979;
|
||||
}
|
||||
|
||||
.use_in_sentence {
|
||||
color: #009902;
|
||||
}
|
||||
|
||||
.definitions li:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
#definitions_and_translations {
|
||||
display: grid;
|
||||
width: 90vw;
|
||||
grid-template-areas:
|
||||
"definitions definitions"
|
||||
"translations translations";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
div.definitions {
|
||||
grid-area: definitions;
|
||||
}
|
||||
|
||||
div.translations {
|
||||
grid-area: translations;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #212529;
|
||||
color: #f8f9fa;
|
||||
}
|
||||
|
||||
#could_not_switch_languages_text {
|
||||
color: #F13333;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #9759f6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #599bf6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
button,
|
||||
textarea {
|
||||
background-color: #131618;
|
||||
border-color: #495057;
|
||||
color: #f8f9fa;
|
||||
}
|
||||
|
||||
.def_type {
|
||||
color: cyan;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.syn {
|
||||
color: burlywood;
|
||||
}
|
||||
|
||||
.syn_type {
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
.use_in_sentence {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<% if (isMobile) { %>
|
||||
<style>
|
||||
body {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
<% } %>
|
||||
|
||||
<% if (!isMobile) { %>
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<% } %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="border-radius: 3em;background: #1a1a1a;padding: 1em;display: flex;flex-direction: column;height: fit-content;align-self: center;margin-top: 6em;">
|
||||
|
||||
<header class="center"><h1>PokeTranslate</h1></header>
|
||||
|
||||
<form action="/translate" method="GET" id="translation-form">
|
||||
|
||||
<!-- from and to language -->
|
||||
<div class="wrap languages">
|
||||
<div class="language">
|
||||
|
||||
<% const languageOptions = [
|
||||
<% const languageOptions = [
|
||||
{ code: 'autodetect', name: 'Autodetect' },
|
||||
{ code: 'af', name: 'Afrikaans' },
|
||||
{ code: 'sq', name: 'Albanian' },
|
||||
@@ -307,7 +80,7 @@
|
||||
{ code: 'km', name: 'Khmer' },
|
||||
{ code: 'rw', name: 'Kinyarwanda' },
|
||||
{ code: 'kok', name: 'Konkani' },
|
||||
{ code: 'ko', name: 'Korean' },
|
||||
{ code: 'ko', name: 'Korean (PROK)' },
|
||||
{ code: 'kri', name: 'Krio' },
|
||||
{ code: 'ku', name: 'Kurdish (Kurmanji)' },
|
||||
{ code: 'sd', name: 'Sindhi' },
|
||||
@@ -341,91 +114,283 @@
|
||||
{ code: 'zu', name: 'Zulu' }
|
||||
]; %>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>PokeTranslate</title>
|
||||
<link rel="icon" href="/static/yt-ukraine.svg">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta content="PokeTranslate" property=og:title>
|
||||
<meta content="Translate text - Anonymously!" property=twitter:description>
|
||||
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
|
||||
<meta content=summary_large_image name=twitter:card>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link href="https://fonts.bunny.net/css?family=poppins:400,500,600" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--accent-1: #ff6b6b;
|
||||
--accent-2: #f7d794;
|
||||
--bg-light: #fafafa;
|
||||
--bg-dark: #202027;
|
||||
--card-light: rgba(255,255,255,0.55);
|
||||
--card-dark: rgba(18,18,24,0.55);
|
||||
--radius: 20px;
|
||||
--transition: 0.3s ease;
|
||||
}
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
body {
|
||||
font-family:'Poppins',sans-serif;
|
||||
background: var(--bg-light);
|
||||
color: #fff;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
min-height:100vh; padding:20px;
|
||||
transition: background var(--transition), color var(--transition);
|
||||
overflow:hidden;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: var(--bg-dark); }
|
||||
}
|
||||
|
||||
<!-- Source language select -->
|
||||
<select name="from_language" id="from_language" style="margin-right: 1em;border-radius: 1em;padding: 7px;" aria-label="Source language">
|
||||
<% languageOptions.forEach(language => { %>
|
||||
<option value="<%= language.code %>" <%= language.code === (from_language || 'autodetect') ? 'selected' : '' %>><%= language.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
.glass {
|
||||
position: relative;
|
||||
background: var(--card-light);
|
||||
backdrop-filter: blur(30px) saturate(200%);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
||||
width:100%; max-width:840px; overflow:hidden;
|
||||
transition: background var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.glass {
|
||||
background: var(--card-dark);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.7);
|
||||
}
|
||||
}
|
||||
|
||||
.blob {
|
||||
position:absolute; width:300px; height:300px;
|
||||
background: linear-gradient(45deg,var(--accent-1),var(--accent-2));
|
||||
border-radius: 45% 55% 70% 30%/30% 60% 40% 70%;
|
||||
animation: blobMove 8s infinite; opacity:0.3; z-index:0;
|
||||
}
|
||||
.blob:nth-child(1){ top:-80px; left:-60px; }
|
||||
.blob:nth-child(2){ bottom:-100px; right:-80px; animation-duration:10s; }
|
||||
@keyframes blobMove {
|
||||
0%,100% { transform:translate(0,0) scale(1); }
|
||||
50% { transform:translate(20px,20px) scale(1.1); }
|
||||
}
|
||||
|
||||
<!-- Target language select -->
|
||||
<select name="to_language" id="to_language" style="margin-right: 1em;border-radius: 1em;padding: 7px;" aria-label="Target language">
|
||||
<% languageOptions.slice(1).forEach(language => { %>
|
||||
<option value="<%= language.code %>" <%= language.code === to_language ? 'selected' : '' %>><%= language.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
header {
|
||||
position:relative; padding:28px; text-align:center;
|
||||
background: linear-gradient(60deg,var(--accent-1),var(--accent-2));
|
||||
background-size:200% 200%; animation:gradientAnimation 6s ease infinite;
|
||||
z-index:1;
|
||||
}
|
||||
@keyframes gradientAnimation {
|
||||
0%{ background-position:0% 50%; }
|
||||
50%{ background-position:100% 50%; }
|
||||
100%{ background-position:0% 50%; }
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
/* Rainbow title unless user prefers reduced motion */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
header h1 {
|
||||
font-size:2.4rem; font-weight:600; letter-spacing:1.5px;
|
||||
background: conic-gradient(red,orange,yellow,green,blue,indigo,violet,red);
|
||||
background-size:300%;
|
||||
-webkit-background-clip:text;
|
||||
-webkit-text-fill-color:transparent;
|
||||
animation: rainbow 4s linear infinite;
|
||||
}
|
||||
@keyframes rainbow {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
header h1 {
|
||||
font-size:2.4rem; font-weight:600; letter-spacing:1.5px;
|
||||
color:#fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
form { position:relative; z-index:2; }
|
||||
.language-bar { display:flex; align-items:center; gap:12px; padding:16px 24px; }
|
||||
.language-select { flex:1; }
|
||||
.language-select select {
|
||||
width:100%; padding:14px 18px; border-radius:var(--radius);
|
||||
border:1px solid rgba(255,255,255,0.5); background:transparent;
|
||||
font-size:1rem; appearance:none; cursor:pointer; color:#fff;
|
||||
transition:border-color var(--transition);
|
||||
}
|
||||
.language-select select:focus { border-color:var(--accent-1); outline:none; }
|
||||
.swap-button {
|
||||
font-size:2rem; color:var(--accent-1); text-decoration:none;
|
||||
transition:transform var(--transition),color var(--transition);
|
||||
}
|
||||
.swap-button:hover {
|
||||
transform:rotate(180deg) scale(1.1);
|
||||
color:var(--accent-2);
|
||||
}
|
||||
|
||||
<!-- text boxes -->
|
||||
<div class="wrap">
|
||||
<div class="item-wrapper">
|
||||
<textarea autofocus class="item" id="input" name="input" dir="auto" placeholder="<%- text %>"><%- text %>
|
||||
</textarea>
|
||||
</div>
|
||||
.panels {
|
||||
display:grid; grid-template-columns:1fr 1fr; gap:20px;
|
||||
padding:0 24px 24px;
|
||||
}
|
||||
.panel { position:relative; }
|
||||
.panel label {
|
||||
display:block; margin-bottom:8px; font-weight:500; font-size:0.95rem;
|
||||
color:#fff;
|
||||
}
|
||||
.panel textarea {
|
||||
width:100%; min-height:160px; padding:16px; border-radius:var(--radius);
|
||||
border:1px solid rgba(255,255,255,0.5); resize:vertical; font-size:1rem;
|
||||
line-height:1.5; background:rgba(255,255,255,0.2); color:#fff;
|
||||
transition:border-color var(--transition),box-shadow var(--transition);
|
||||
}
|
||||
.panel textarea::placeholder { color:rgba(255,255,255,0.7); }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.panel textarea { background:rgba(20,20,25,0.5); }
|
||||
}
|
||||
.panel textarea:focus {
|
||||
border-color:var(--accent-1);
|
||||
box-shadow:0 0 8px var(--accent-1);
|
||||
outline:none;
|
||||
}
|
||||
|
||||
.actions { display:flex; justify-content:center; padding-bottom:24px; }
|
||||
.actions button {
|
||||
padding:16px 36px; font-size:1.1rem; font-weight:600; border:none;
|
||||
border-radius:var(--radius); cursor:pointer;
|
||||
background:linear-gradient(60deg,var(--accent-1),var(--accent-2));
|
||||
background-size:200% 200%; animation:gradientAnimation 6s ease infinite;
|
||||
color:#fff; box-shadow:0 4px 14px rgba(0,0,0,0.2);
|
||||
transition:transform var(--transition),opacity var(--transition);
|
||||
}
|
||||
.actions button:hover { transform:scale(1.03); opacity:0.9; }
|
||||
|
||||
@media (max-width:768px) { .panels { grid-template-columns:1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="glass">
|
||||
<div class="blob"></div>
|
||||
<div class="blob"></div>
|
||||
|
||||
<div class="item-wrapper">
|
||||
<textarea id="output" class="translation item" dir="auto" placeholder="Translation" readonly> <%- translation %> </textarea>
|
||||
</div>
|
||||
</div>
|
||||
<header><h1>PokeTranslate</h1></header>
|
||||
|
||||
<br>
|
||||
<form action="/translate" method="GET">
|
||||
<div class="language-bar">
|
||||
<div class="language-select">
|
||||
<select name="from_language" id="from_language">
|
||||
<% languageOptions.forEach(lang => { %>
|
||||
<option value="<%= lang.code %>" <%= lang.code === (from_language||'autodetect')?'selected':''%>>
|
||||
<%= lang.name %>
|
||||
</option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="center">
|
||||
<!-- translate button -->
|
||||
<button type="submit" style="border-radius: 1em;padding: 7px;">Translate :3</button>
|
||||
</div>
|
||||
<a
|
||||
href="?from_language=<%= to_language %>&to_language=<%= from_language %>&input=<%= encodeURIComponent(text) %>"
|
||||
id="swapBtn"
|
||||
class="material-icons swap-button"
|
||||
>swap_horiz</a>
|
||||
|
||||
<br>
|
||||
<div class="language-select">
|
||||
<select name="to_language" id="to_language">
|
||||
<% languageOptions.slice(1).forEach(lang => { %>
|
||||
<option value="<%= lang.code %>" <%= lang.code===to_language?'selected':''%>>
|
||||
<%= lang.name %>
|
||||
</option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panels">
|
||||
<div class="panel input">
|
||||
<label for="input">Input</label>
|
||||
<textarea id="input" name="input" placeholder="Enter text…"><%= text %></textarea>
|
||||
</div>
|
||||
<div class="panel output">
|
||||
<label for="output">Translation</label>
|
||||
<textarea id="output" readonly placeholder="Translated text…"><%= translation %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit">Translate :3</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
// swap button
|
||||
const swapBtn = document.getElementById('swapBtn');
|
||||
if(swapBtn){
|
||||
swapBtn.addEventListener('click', function(e){
|
||||
e.preventDefault();
|
||||
const from = document.getElementById('from_language');
|
||||
const to = document.getElementById('to_language');
|
||||
const inp = document.getElementById('input');
|
||||
const out = document.getElementById('output');
|
||||
[from.value, to.value] = [to.value, from.value];
|
||||
[inp.value, out.value] = [out.value, inp.value];
|
||||
});
|
||||
}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
// auto-resize all textareas
|
||||
document.querySelectorAll('textarea').forEach(el=>{
|
||||
const resize = ()=>{ el.style.height='auto'; el.style.height=el.scrollHeight+'px'; };
|
||||
el.addEventListener('input', resize);
|
||||
resize();
|
||||
});
|
||||
|
||||
// live-translate if JS available
|
||||
if(window.fetch){
|
||||
const from = document.getElementById('from_language');
|
||||
const to = document.getElementById('to_language');
|
||||
const input = document.getElementById('input');
|
||||
const output= document.getElementById('output');
|
||||
let timer;
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
function translateNow(){
|
||||
const q = new URLSearchParams({
|
||||
from_language: from.value,
|
||||
to_language: to.value,
|
||||
input: input.value
|
||||
});
|
||||
fetch(`/translate?${q}`)
|
||||
.then(r => r.text())
|
||||
.then(html => {
|
||||
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
const newOut = doc.getElementById('output');
|
||||
if(newOut){
|
||||
output.value = newOut.value;
|
||||
output.dispatchEvent(new Event('input'));
|
||||
}
|
||||
})
|
||||
.catch(() => {/* ignore */});
|
||||
}
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||
// this code submits the translation form when pressing Ctrl/Meta+Enter while focussed on the input text field
|
||||
document.getElementById("input").addEventListener("keydown", function(event) {
|
||||
if (event.keyCode === 13 && (event.metaKey || event.ctrlKey)) {
|
||||
document.getElementById("translation-form").submit();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto resize textarea to fit words inside it without need to scroll -- Thanks to: https://stackoverflow.com/a/25621277
|
||||
var input = document.getElementById("input");
|
||||
var output = document.getElementById("output");
|
||||
input.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
|
||||
output.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
|
||||
input.addEventListener("input", function(e) {
|
||||
this.style.height = 150 + "px";
|
||||
this.style.height = this.scrollHeight + "px";
|
||||
});
|
||||
|
||||
// @license-end
|
||||
</script>
|
||||
<script src="/static/custom-css.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
// debounce on typing
|
||||
input.addEventListener('input', ()=>{
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(translateNow, 500);
|
||||
});
|
||||
// re-translate on language change
|
||||
from.addEventListener('change', translateNow);
|
||||
to.addEventListener('change', translateNow);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
24
html/video-error.ejs
Normal file
24
html/video-error.ejs
Normal file
@@ -0,0 +1,24 @@
|
||||
<%
|
||||
function isValidYouTubeID(v) {
|
||||
return /^[a-zA-Z0-9_-]{11}$/.test(v);
|
||||
}
|
||||
|
||||
function isLetterSpam(v) {
|
||||
return /^(.)\1+$/.test(v);
|
||||
}
|
||||
|
||||
let reason;
|
||||
|
||||
if (!isValidYouTubeID(v) || isLetterSpam(v)) {
|
||||
reason = "Video not found >~<";
|
||||
} else {
|
||||
reason = "Poke is currently restarting - please wait 1-2 minutes..";
|
||||
}
|
||||
%>
|
||||
|
||||
<%- include('./layouts/error-video.ejs', {
|
||||
error: "loading failed :c",
|
||||
description: `${reason}`
|
||||
}) %>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
"region": "🇩🇪",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "lastest",
|
||||
"version": "latest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
@@ -23,20 +23,6 @@
|
||||
"CLOUDFLARE": false,
|
||||
"piwik": false,
|
||||
"region": "🇺🇸",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "lastest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"poke.ashley0143.xyz",
|
||||
{
|
||||
"uri": "https://poke.ashley0143.xyz",
|
||||
"CLOUDFLARE": true,
|
||||
"piwik": false,
|
||||
"region": "🇺🇸",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "latest",
|
||||
@@ -45,23 +31,9 @@
|
||||
}
|
||||
],
|
||||
[
|
||||
"poke.alphexo.dev",
|
||||
"nyc1.poke.ggtyler.dev",
|
||||
{
|
||||
"uri": "https://poke.alphexo.dev",
|
||||
"CLOUDFLARE": true,
|
||||
"piwik": false,
|
||||
"region": "🇬🇧",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "latest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"poke.ggtyler.dev",
|
||||
{
|
||||
"uri": "https://poke.ggtyler.dev",
|
||||
"uri": "https://nyc1.poke.ggtyler.dev",
|
||||
"CLOUDFLARE": false,
|
||||
"piwik": false,
|
||||
"proxy": false,
|
||||
@@ -82,41 +54,28 @@
|
||||
"proxy": false,
|
||||
"region": "🇺🇸",
|
||||
"software": {
|
||||
"name": "poketube",
|
||||
"name": "poke",
|
||||
"version": "latest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"poke.uk1.littkai.co.uk",
|
||||
"pol1.poke.ggtyler.dev",
|
||||
{
|
||||
"uri": "https://poke.uk1.littkai.co.uk",
|
||||
"uri": "https://pol1.ggtyler.dev",
|
||||
"CLOUDFLARE": false,
|
||||
"piwik": false,
|
||||
"proxy": false,
|
||||
"region": "🇬🇧",
|
||||
"region": "🇵🇱",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "latest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
], [
|
||||
"poke.uk2.littlekai.co.uk",
|
||||
{
|
||||
"uri": "https://poke.uk2.littlekai.co.uk",
|
||||
"CLOUDFLARE": false,
|
||||
"piwik": false,
|
||||
"proxy": false,
|
||||
"region": "🇬🇧",
|
||||
"software": {
|
||||
"name": "poke",
|
||||
"version": "latest",
|
||||
"branch": "dev"
|
||||
}
|
||||
}
|
||||
], [
|
||||
],
|
||||
[
|
||||
"poke.blahai.gay",
|
||||
{
|
||||
"uri": "https://poke.blahai.gay/",
|
||||
|
||||
19
issue_template/enhancement.yml
Normal file
19
issue_template/enhancement.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Enhancement
|
||||
description: This is an enhancement/feature you would like to see in Poke
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!WARNING]
|
||||
> The following will never be added:
|
||||
> * iOS app (use the PWA)
|
||||
> * non-free codecs
|
||||
- type: textarea
|
||||
id: describe
|
||||
attributes:
|
||||
label: Describe the enhancement
|
||||
placeholder: What would you like to see being added?
|
||||
validations:
|
||||
required: true
|
||||
31
issue_template/general-issue.yml
Normal file
31
issue_template/general-issue.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
name: General issue
|
||||
description: This is an issue with Poke not related to the video player
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!WARNING]
|
||||
> Please check if the issue already exists before creating it! [u will explod if it already exists]
|
||||
- type: textarea
|
||||
id: describe
|
||||
attributes:
|
||||
label: Describe the issue
|
||||
placeholder: In detail, what happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser
|
||||
attributes:
|
||||
label: What browser and OS are you using?
|
||||
description: eg. Chrome on Windows 10, Firefox on Fedora GNU/Linux 40
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
render: true
|
||||
attributes:
|
||||
render: true
|
||||
label: Error logs
|
||||
placeholder: If any, are there error logs in your browser console?
|
||||
55
issue_template/player-bug.yml
Normal file
55
issue_template/player-bug.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Player bug
|
||||
description: This is an issue with the Poke video player
|
||||
title: "[Bug]: "
|
||||
labels: ["player-bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!CAUTION]
|
||||
> If your issue matches **all of** the 3 points below:
|
||||
> * JavaScript is disabled;
|
||||
> * AND playback is high-resolution;
|
||||
> * AND audio isn't working;
|
||||
>
|
||||
> **DO NOT open an issue.** Hi-res audio will not work without JavaScript enabled.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!CAUTION]
|
||||
> Your bug might be a Video.js bug, the player Poke uses.
|
||||
>
|
||||
> Look through [Video.js issues](https://github.com/videojs/video.js/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Abug) to see if the issue is actually with Poke, or if this is just a Video.js issue out of our control. DO NOT open an issue here if the latter applies.
|
||||
- type: textarea
|
||||
id: describe
|
||||
attributes:
|
||||
label: Describe the issue
|
||||
placeholder: In detail, what happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: js
|
||||
attributes:
|
||||
label: JavaScript and resolution
|
||||
description: Choose one of the 4 options below.
|
||||
options:
|
||||
- JS enabled, high res
|
||||
- JS enabled, low res
|
||||
- JS disabled, high res
|
||||
- JS disabled, low res
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser
|
||||
attributes:
|
||||
label: What browser and OS are you using?
|
||||
description: eg. Chrome on Windows 10, Firefox on Fedora 40
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
render: true
|
||||
attributes:
|
||||
render: true
|
||||
label: Error logs
|
||||
placeholder: If any, are there error logs in your browser console?
|
||||
29
issue_template/search-filter.yml
Normal file
29
issue_template/search-filter.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Search filter issue
|
||||
description: This is for issues related to the self-harm and misinfo search filters
|
||||
title: "[Bug]: "
|
||||
labels: ["search-issue"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!CAUTION]
|
||||
> This form isn't to request the removal of a source. Do not use it for that
|
||||
- type: input
|
||||
id: search
|
||||
attributes:
|
||||
label: What was your exact search?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: flag
|
||||
attributes:
|
||||
label: What was it flagged as?
|
||||
placeholder: Self-harm, or the name of the news source
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: describe
|
||||
attributes:
|
||||
label: Anything else worthy to know?
|
||||
validations:
|
||||
required: false
|
||||
@@ -16,8 +16,4 @@ server {
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
location /manifest.json {
|
||||
root /path/to/poketube/pwa/;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
"activitypub-express": "^4.4.1",
|
||||
"duck-duck-scrape": "^2.2.5",
|
||||
"google-it": "^1.6.4",
|
||||
"youtubei.js": "^9.3.0"
|
||||
"youtubei.js": "^13.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
128
server.js
128
server.js
@@ -75,10 +75,10 @@
|
||||
const sha384 = modules.hash;
|
||||
const rateLimit = require("express-rate-limit");
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 45 * 1000, // 45 Seconds
|
||||
max: 886, // limit each IP to 866 requests per windowMs
|
||||
});
|
||||
const limiter = rateLimit({
|
||||
windowMs: 30 * 1000, // 30 second window
|
||||
max: 200, // limit each IP to 200 requests per 30 seconds
|
||||
});
|
||||
|
||||
var app = modules.express();
|
||||
app.use(limiter);
|
||||
@@ -117,16 +117,111 @@
|
||||
});
|
||||
|
||||
const random_words = [
|
||||
"banana pie",
|
||||
"how to buy an atom bomb",
|
||||
"is love just an illusion",
|
||||
"things to do if ur face becomes benjamin frenklin",
|
||||
"how do defeat an pasta",
|
||||
"can you go to space?",
|
||||
"how to become a god?",
|
||||
"is a panda a panda if pandas???",
|
||||
"Minecraft movie trailer",
|
||||
"monke",
|
||||
"Woke!",
|
||||
"Gay gay homosexaul gay!",
|
||||
"free Palestine!",
|
||||
"free software!",
|
||||
"im... stuff!",
|
||||
"frick capitalism!",
|
||||
"still calling it twitter btw!",
|
||||
"boop!",
|
||||
"no way!",
|
||||
"traaaa rightssss!",
|
||||
"XD!",
|
||||
"nya!",
|
||||
"say gex!",
|
||||
"ur valid :3",
|
||||
"gay space communism!",
|
||||
"doesnt have AI!",
|
||||
"no web3!",
|
||||
"keemstar is a bald ___!",
|
||||
"No One calls it 'X'! ",
|
||||
"Eat the rich!",
|
||||
"Does Not include Nazis!",
|
||||
"also try piped!",
|
||||
"not alt-right!",
|
||||
"coke zero > coke classic!",
|
||||
"poke & chill!",
|
||||
"can play HD!",
|
||||
"also try invidious!",
|
||||
"also try vencord!",
|
||||
"rms <3!",
|
||||
"du hast",
|
||||
"can u belive no one bought this?",
|
||||
"reee",
|
||||
"1.000.000€!",
|
||||
"pika!",
|
||||
"fsf.org",
|
||||
"ssfffssfssfffaassssfsdf!",
|
||||
"they not like us!",
|
||||
"to pimp a butterfly!",
|
||||
"king kunta!",
|
||||
"HUMBLE.",
|
||||
"can you save my hds?",
|
||||
"sahlo folina!",
|
||||
"we come for you!",
|
||||
"no chances!",
|
||||
"dema dont control us!",
|
||||
"i see your problem is, your proctologist",
|
||||
"got both hands on your shoulder",
|
||||
"while ur bottomless!",
|
||||
"you should bounce bounce bounce man!",
|
||||
"its lavish!",
|
||||
"im vibin, vibin!",
|
||||
"i would swim the paladin strait",
|
||||
"hello clancy!",
|
||||
"NO NOT ME,ITS FOR A FRIEND",
|
||||
"im fairly local!",
|
||||
"i dont wanna go like this!",
|
||||
"east is up!",
|
||||
"not done, josh dun!",
|
||||
"your the judge, oh no!",
|
||||
"I dont wanna backslide",
|
||||
"welcome back to trench!",
|
||||
"sai is propaganda!",
|
||||
" •|i|• Ø i+! ].[",
|
||||
"stay alive! |-/",
|
||||
"the few, the proud, the Emotional!",
|
||||
"ill morph into someone else",
|
||||
"still alive",
|
||||
"follow the torches",
|
||||
"i created this world!",
|
||||
"to feel some control!",
|
||||
"destory it if i want!",
|
||||
"o7 keons",
|
||||
"at least let me clean my room",
|
||||
"100+ stars on gh!",
|
||||
"let the vibe slide over me!",
|
||||
"sip a capri sun like its don peregon",
|
||||
"now even gayer!",
|
||||
"its joever..",
|
||||
"lesbiam,,,",
|
||||
"poke!!!",
|
||||
"discord.poketube.fun!",
|
||||
"women are pretty!",
|
||||
"men are handsome!",
|
||||
"enbys are cute!",
|
||||
"you are cute :3",
|
||||
"read if cute!",
|
||||
"this shit awesome!",
|
||||
"ur pawsome!",
|
||||
"meows at u",
|
||||
"hai i am gay",
|
||||
"yay, GEX!",
|
||||
"say gex..,,",
|
||||
"wha if we um erm",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"awesome screen!",
|
||||
"awesome camera!",
|
||||
"long lasting battery life",
|
||||
"stallmansupport.org!!!",
|
||||
"does include nya~!!!",
|
||||
"actually stable! :3",
|
||||
];
|
||||
|
||||
const initPokeTube = function () {
|
||||
@@ -170,14 +265,17 @@
|
||||
});
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
const random = random_words[Math.floor(Math.random() * random_words.length)];
|
||||
|
||||
res.header(
|
||||
"X-PokeTube-Youtube-Client-Name",
|
||||
innertube.innertube.CONTEXT_CLIENT.INNERTUBE_CONTEXT_CLIENT_NAME
|
||||
);
|
||||
res.header(
|
||||
"Hey-there",
|
||||
"Do u wanna help poke? contributons are welcome :3 https://codeberg.org/Ashley/poke"
|
||||
"Do u wanna help poke? contributions are welcome :3 https://codeberg.org/Ashley/poke"
|
||||
);
|
||||
|
||||
res.header(
|
||||
"X-PokeTube-Youtube-Client-Version",
|
||||
innertube.innertube.CLIENT.clientVersion
|
||||
|
||||
@@ -23,88 +23,114 @@ var ping = require("ping");
|
||||
|
||||
const sha384 = modules.hash;
|
||||
|
||||
|
||||
const splash = [
|
||||
"Woke!",
|
||||
"Gay gay homosexaul gay!",
|
||||
"free Palestine!",
|
||||
"free software!",
|
||||
"im... stuff!",
|
||||
"frick capitalism!",
|
||||
"still calling it twitter btw!",
|
||||
"boop!",
|
||||
"no way!",
|
||||
"traaaa rightssss!",
|
||||
"XD!",
|
||||
"nya!",
|
||||
"say gex!",
|
||||
"also try invidious!",
|
||||
"rms <3!",
|
||||
"du hast",
|
||||
"can u belive no one bought this?",
|
||||
"reee",
|
||||
"1.000.000€!",
|
||||
"pika!",
|
||||
"fsf.org",
|
||||
"ssfffssfssfffaassssfsdf!",
|
||||
"𝓯𝓻𝓮𝓪𝓴𝔂poke",
|
||||
"can you save my hds?",
|
||||
"sahlo folina!",
|
||||
"we come for you!",
|
||||
"no chances!",
|
||||
"dema dont control us!",
|
||||
"i see your problem is, your proctologist",
|
||||
"got both hands on your shoulder",
|
||||
"while ur bottomless!",
|
||||
"you should bounce bounce bounce man!",
|
||||
"its lavish!",
|
||||
"im vibin, vibin!",
|
||||
"i would swim the paladin strait",
|
||||
"hello clancy!",
|
||||
"NO NOT ME,ITS FOR A FRIEND",
|
||||
"im fairly local!",
|
||||
"i dont wanna go like this!",
|
||||
"east is up!",
|
||||
"not done, josh dun!",
|
||||
"your the judge, oh no!",
|
||||
"I dont wanna backslide",
|
||||
"welcome back to trench!",
|
||||
"sai is propaganda!",
|
||||
" •|i|• Ø i+! ].[",
|
||||
"stay alive! |-/",
|
||||
"the few, the proud, the Emotional!",
|
||||
"ill morph into someone else",
|
||||
"still alive",
|
||||
"follow the torches",
|
||||
"i created this world!",
|
||||
"to feel some control!",
|
||||
"destory it if i want!",
|
||||
"o7 keons",
|
||||
"at least let me clean my room",
|
||||
"100+ stars on gh!",
|
||||
"let the vibe slide over me!",
|
||||
"sip a capri sun like its don peregon",
|
||||
"now even gayer!",
|
||||
"its joever..",
|
||||
"poketube!!!",
|
||||
"discord.poketube.fun!",
|
||||
"women are pretty!",
|
||||
"men are handsome!",
|
||||
"enbys are cute!",
|
||||
"you are cute :3",
|
||||
"read if cute!",
|
||||
"stallmansupport.org!!!",
|
||||
"does not include revolt.chat!",
|
||||
"revolt-free!",
|
||||
"not for revolt users!",
|
||||
"%99 free of revolt!",
|
||||
"does include nya~!!!",
|
||||
"you're literally showing ads!",
|
||||
"actually stable! :3"
|
||||
]
|
||||
|
||||
|
||||
|
||||
"Woke!",
|
||||
"Gay gay homosexaul gay!",
|
||||
"free Palestine!",
|
||||
"free software!",
|
||||
"im... stuff!",
|
||||
"frick capitalism!",
|
||||
"still calling it twitter btw!",
|
||||
"boop!",
|
||||
"no way!",
|
||||
"traaaa rightssss!",
|
||||
"XD!",
|
||||
"nya!",
|
||||
"say gex!",
|
||||
"ur valid :3",
|
||||
"gay space communism!",
|
||||
"doesnt have AI!",
|
||||
"no web3!",
|
||||
"keemstar is a bald ___!",
|
||||
"No One calls it 'X'! ",
|
||||
"Eat the rich!",
|
||||
"Does Not include Nazis!",
|
||||
"also try piped!",
|
||||
"not alt-right!",
|
||||
"coke zero > coke classic!",
|
||||
"poke & chill!",
|
||||
"can play HD!",
|
||||
"also try invidious!",
|
||||
"also try vencord!",
|
||||
"rms <3!",
|
||||
"du hast",
|
||||
"can u belive no one bought this?",
|
||||
"reee",
|
||||
"1.000.000€!",
|
||||
"pika!",
|
||||
"fsf.org",
|
||||
"ssfffssfssfffaassssfsdf!",
|
||||
"𝓯𝓻𝓮𝓪𝓴𝔂poke",
|
||||
"they not like us!",
|
||||
"to pimp a butterfly!",
|
||||
"king kunta!",
|
||||
"HUMBLE.",
|
||||
"can you save my hds?",
|
||||
"sahlo folina!",
|
||||
"we come for you!",
|
||||
"no chances!",
|
||||
"dema dont control us!",
|
||||
"i see your problem is, your proctologist",
|
||||
"got both hands on your shoulder",
|
||||
"while ur bottomless!",
|
||||
"you should bounce bounce bounce man!",
|
||||
"its lavish!",
|
||||
"im vibin, vibin!",
|
||||
"i would swim the paladin strait",
|
||||
"hello clancy!",
|
||||
"NO NOT ME,ITS FOR A FRIEND",
|
||||
"im fairly local!",
|
||||
"i dont wanna go like this!",
|
||||
"east is up!",
|
||||
"not done, josh dun!",
|
||||
"your the judge, oh no!",
|
||||
"I dont wanna backslide",
|
||||
"welcome back to trench!",
|
||||
"sai is propaganda!",
|
||||
" •|i|• Ø i+! ].[",
|
||||
"stay alive! |-/",
|
||||
"the few, the proud, the Emotional!",
|
||||
"ill morph into someone else",
|
||||
"still alive",
|
||||
"follow the torches",
|
||||
"i created this world!",
|
||||
"to feel some control!",
|
||||
"destory it if i want!",
|
||||
"o7 keons",
|
||||
"at least let me clean my room",
|
||||
"100+ stars on gh!",
|
||||
"let the vibe slide over me!",
|
||||
"sip a capri sun like its don peregon",
|
||||
"now even gayer!",
|
||||
"its joever..",
|
||||
"lesbiam,,,",
|
||||
"poke!!!",
|
||||
"discord.poketube.fun!",
|
||||
"women are pretty!",
|
||||
"men are handsome!",
|
||||
"enbys are cute!",
|
||||
"you are cute :3",
|
||||
"read if cute!",
|
||||
"this shit awesome!",
|
||||
"ur pawsome!",
|
||||
"meows at u",
|
||||
"hai i am gay",
|
||||
"yay, GEX!",
|
||||
"say gex..,,",
|
||||
"wha if we um erm",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"turkey is literally 1984!",
|
||||
"awesome screen!",
|
||||
"awesome camera!",
|
||||
"long lasting battery life",
|
||||
"stallmansupport.org!!!",
|
||||
"does include nya~!!!",
|
||||
"actually stable! :3",
|
||||
];
|
||||
|
||||
function getJson(str) {
|
||||
try {
|
||||
@@ -116,20 +142,23 @@ function getJson(str) {
|
||||
|
||||
module.exports = function (app, config, renderTemplate) {
|
||||
app.get("/app", async function (req, res) {
|
||||
const { fetch } = await import("undici");
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
let tab = "";
|
||||
if (req.query.tab) {
|
||||
tab = `/?type=${capitalizeFirstLetter(req.query.tab)}`;
|
||||
}
|
||||
|
||||
const invtrend = await fetch(
|
||||
`${config.invapi}/trending${tab}`
|
||||
);
|
||||
const invtrend = await fetch(`${config.invapi}/trending${tab}`, {
|
||||
headers: { "User-Agent": config.useragent },
|
||||
});
|
||||
const t = getJson(await invtrend.text());
|
||||
|
||||
const invpopular = await fetch(
|
||||
`https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1/popular`
|
||||
`https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1/popular`,
|
||||
{
|
||||
headers: { "User-Agent": config.useragent },
|
||||
}
|
||||
);
|
||||
const p = getJson(await invpopular.text());
|
||||
|
||||
@@ -160,18 +189,15 @@ module.exports = function (app, config, renderTemplate) {
|
||||
const uaos = req.useragent.os;
|
||||
const random = splash[Math.floor(Math.random() * splash.length)];
|
||||
const browser = req.useragent.browser;
|
||||
const isOldWindows = (uaos === "Windows 7" || uaos === "Windows 8") && browser === "Firefox";
|
||||
const isOldWindows =
|
||||
(uaos === "Windows 7" || uaos === "Windows 8") &&
|
||||
browser === "Firefox";
|
||||
var proxyurl = config.p_url;
|
||||
|
||||
const secure = [
|
||||
"poketube.fun",
|
||||
"localhost" // Testing purposes
|
||||
].includes(req.hostname);
|
||||
const verify = [
|
||||
"poketube.fun",
|
||||
"poke.ashley0143.xyz",
|
||||
"localhost"
|
||||
].includes(req.hostname);
|
||||
const secure = ["poketube.fun", "localhost"].includes(req.hostname);
|
||||
const verify = ["poketube.fun", "poke.ashley0143.xyz", "localhost"].includes(
|
||||
req.hostname
|
||||
);
|
||||
|
||||
const rendermainpage = () => {
|
||||
if (req.useragent.isMobile) {
|
||||
@@ -180,11 +206,13 @@ module.exports = function (app, config, renderTemplate) {
|
||||
|
||||
return renderTemplate(res, req, "landing.ejs", {
|
||||
secure,
|
||||
embedtype:req.query.embedtype,
|
||||
embedtype: req.query.embedtype,
|
||||
banner: config.banner,
|
||||
DisablePokeChan: req.query.DisablePokeChan,
|
||||
verify,
|
||||
isOldWindows,
|
||||
proxyurl,
|
||||
random
|
||||
random,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -193,13 +221,14 @@ module.exports = function (app, config, renderTemplate) {
|
||||
if (isvld && req.params.v.length >= 10) {
|
||||
return res.redirect(`/watch?v=${req.params.v}`);
|
||||
} else {
|
||||
return renderTemplate(res, req, "404.ejs", {
|
||||
isOldWindows,
|
||||
random
|
||||
});
|
||||
res.status(404);
|
||||
return renderTemplate(res, req, "404.ejs", {
|
||||
isOldWindows,
|
||||
random,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return rendermainpage();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
const {
|
||||
fetcher,
|
||||
core,
|
||||
wiki,
|
||||
musicInfo,
|
||||
modules,
|
||||
version,
|
||||
initlog,
|
||||
init,
|
||||
} = require("../libpoketube-initsys.js");
|
||||
const {
|
||||
IsJsonString,
|
||||
convert,
|
||||
getFirstLine,
|
||||
capitalizeFirstLetter,
|
||||
turntomins,
|
||||
getRandomInt,
|
||||
getRandomArbitrary,
|
||||
} = require("../ptutils/libpt-coreutils.js");
|
||||
const { modules } = require("../libpoketube-initsys.js");
|
||||
|
||||
|
||||
var http = require("https");
|
||||
var ping = require("ping");
|
||||
|
||||
@@ -1,22 +1,4 @@
|
||||
const {
|
||||
fetcher,
|
||||
core,
|
||||
wiki,
|
||||
musicInfo,
|
||||
modules,
|
||||
version,
|
||||
initlog,
|
||||
init,
|
||||
} = require("../libpoketube-initsys.js");
|
||||
const {
|
||||
IsJsonString,
|
||||
convert,
|
||||
getFirstLine,
|
||||
capitalizeFirstLetter,
|
||||
turntomins,
|
||||
getRandomInt,
|
||||
getRandomArbitrary,
|
||||
} = require("../ptutils/libpt-coreutils.js");
|
||||
const { modules, version } = require("../libpoketube-initsys.js");
|
||||
|
||||
function getJson(str) {
|
||||
try {
|
||||
@@ -29,47 +11,43 @@ function getJson(str) {
|
||||
const pkg = require("../../../package.json");
|
||||
const os = require('os');
|
||||
const cnf = require("../../../config.json");
|
||||
|
||||
|
||||
const innertube = require("../libpoketube-youtubei-objects.json");
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const verfull = "v24.1906-sho-MAJOR_UPDATE-stable-dev-nonLTS-git-MTcxODc5NDY3NQ==";
|
||||
const versmol = "v24.1906-sho"
|
||||
const { execSync } = require('child_process'); // DO NOT ABBRV THIS :SOB:
|
||||
|
||||
const verfull = "v25.2705-luna-MAJOR_UPDATE-stable-dev-nonLTS-git-MTc0NTcwNjc4MA==";
|
||||
const versmol = "v25.2705-luna";
|
||||
const branch = "dev/master";
|
||||
const codename = "sho";
|
||||
const versionnumber = "293";
|
||||
const relaseunixdate = "MTcxODc5NDY3NQ=="
|
||||
const updatequote = "pls fund vennie plush -Bims"
|
||||
|
||||
const codename = "luna";
|
||||
const versionnumber = "294";
|
||||
const relaseunixdate = "MTc0NTcwNjc4MA==";
|
||||
const updatequote = "i created this world.....to feel some control...";
|
||||
|
||||
module.exports = function (app, config, renderTemplate) {
|
||||
app.get("/embed/:v", async function (req, res) {
|
||||
res.send("Disabled until Q1 2024");
|
||||
});
|
||||
|
||||
app.get("/admin", async function (req, res) {
|
||||
if(req.hostname === "poketube.fun") {
|
||||
res.redirect("https://console.sudovanilla.com/")
|
||||
} else {
|
||||
res.redirect("/sex")
|
||||
}
|
||||
});
|
||||
|
||||
const headers = {
|
||||
'User-Agent': config.useragent,
|
||||
};
|
||||
|
||||
app.get("/vi/:v/:t", async function (req, res) {
|
||||
var url = `https://vid.puffyan.us/vi/${req.params.v}/${req.params.t}`
|
||||
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
var url = `https://i.ytimg.com/vi/${req.params.v}/${req.params.t}`;
|
||||
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
|
||||
});
|
||||
|
||||
app.get("/avatars/:v", async function (req, res) {
|
||||
app.get("/avatars/:v", async function (req, res) {
|
||||
var url = `https://vid.puffyan.us/ggpht/${req.params.v}`;
|
||||
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
@@ -80,106 +58,156 @@ app.get("/avatars/:v", async function (req, res) {
|
||||
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
});
|
||||
|
||||
|
||||
app.get("/avatars/ytc/:v", async function (req, res) {
|
||||
var url = `https://vid.puffyan.us/ggpht/ytc/${req.params.v.replace("ytc", "")}`;
|
||||
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
});
|
||||
|
||||
|
||||
app.get("/api/search", async (req, res) => {
|
||||
const query = req.query.query;
|
||||
|
||||
if (!query) {
|
||||
return res.redirect("/");
|
||||
}
|
||||
return res.redirect(`/search?query=${query}`);
|
||||
});
|
||||
|
||||
app.get("/api/video/download", async function (req, res) {
|
||||
var v = req.query.v;
|
||||
|
||||
var format = "mp4";
|
||||
var q = "22";
|
||||
var q = "18";
|
||||
if (req.query.q) q = req.query.q;
|
||||
|
||||
if (req.query.f) {
|
||||
var format = "mp3";
|
||||
}
|
||||
|
||||
const url = `https://tube-nightly.kuylar.dev/proxy/media/${v}/${q}`;
|
||||
const url = `${config.videourl}/latest_version?id=${v}&itag=${q}&local=true`;
|
||||
|
||||
res.redirect(url);
|
||||
});
|
||||
|
||||
app.get("/api/subtitles", async (req, res) => {
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
|
||||
const id = req.query.v;
|
||||
const l = req.query.h;
|
||||
|
||||
try {
|
||||
let url = `https://invid-api.poketube.fun/api/v1/captions/${id}?label=${l}`;
|
||||
let url = `${config.videourl}/api/v1/captions/${id}?label=${l}`;
|
||||
|
||||
let f = await fetch(url, {
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
let f = await fetch(url);
|
||||
const body = await f.text();
|
||||
|
||||
res.send(body);
|
||||
} catch {}
|
||||
});
|
||||
|
||||
|
||||
app.use("/sb/i/:v/:imagePath/:img", async function (req, res) {
|
||||
const { v, imagePath, img } = req.params;
|
||||
const { sqp, xywh } = req.query;
|
||||
const sighMatch = req.url.match(/&sigh=([^&#]+)/);
|
||||
const sigh = sighMatch ? sighMatch[1] : undefined;
|
||||
|
||||
|
||||
const url = `https://yt.miruku.cafe/sb/i/${v}/${imagePath}/${img}?sqp=${sqp}&sigh=${sigh}&xywh=${req.query.xywh}`;
|
||||
|
||||
try {
|
||||
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
|
||||
method: req.method,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
console.log(url)
|
||||
} catch (error) {
|
||||
console.error("Error fetching image:", error);
|
||||
res.status(500).send("Error fetching image");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/storyboards", async (req, res) => {
|
||||
app.get("/api/getEngagementData", async (req, res) => {
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
const id = req.query.v;
|
||||
const l = req.query.h;
|
||||
|
||||
try {
|
||||
let url = `https://yt.miruku.cafe/api/v1/storyboards/${id}?width=320&height=180`;
|
||||
if (id) {
|
||||
const apiUrl = `https://ryd-proxy.kavin.rocks/votes/${id}&hash=d0550b6e28c8f93533a569c314d5b4e2`;
|
||||
|
||||
let f = await fetch(url);
|
||||
let body = await f.text();
|
||||
|
||||
body = body.replace(/#xywh=(\d+),(\d+),(\d+),(\d+)/g, (match, x, y, w, h) => {
|
||||
return `&xywh=${x},${y},${w},${h}`;
|
||||
const response = await fetch(apiUrl, {
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
res.send(body);
|
||||
} catch {}
|
||||
});
|
||||
if (response.status === 400) {
|
||||
const error = await response.json();
|
||||
return res.status(400).send(error);
|
||||
}
|
||||
|
||||
const engagement = await response.json();
|
||||
|
||||
const likes = parseInt(engagement.likes) || 0;
|
||||
const dislikes = parseInt(engagement.dislikes) || 0;
|
||||
const total = likes + dislikes;
|
||||
|
||||
const likePercentage = total > 0 ? ((likes / total) * 100).toFixed(2) : 0;
|
||||
const dislikePercentage = total > 0 ? ((dislikes / total) * 100).toFixed(2) : 0;
|
||||
|
||||
const getLikePercentageColor = (percentage) => {
|
||||
if (percentage >= 80) {
|
||||
return "green";
|
||||
} else if (percentage >= 50) {
|
||||
return "orange";
|
||||
} else {
|
||||
return "red";
|
||||
}
|
||||
};
|
||||
|
||||
const getDislikePercentageColor = (percentage) => {
|
||||
if (percentage >= 50) {
|
||||
return "red";
|
||||
} else if (percentage >= 20) {
|
||||
return "orange";
|
||||
} else {
|
||||
return "green";
|
||||
}
|
||||
};
|
||||
|
||||
const likeColor = getLikePercentageColor(likePercentage);
|
||||
const dislikeColor = getDislikePercentageColor(dislikePercentage);
|
||||
|
||||
const userScore = (
|
||||
parseFloat(likePercentage) -
|
||||
parseFloat(dislikePercentage) / 2
|
||||
).toFixed(2);
|
||||
|
||||
const getUserScoreLabel = (score) => {
|
||||
if (score >= 98) {
|
||||
return "Masterpiece Video";
|
||||
} else if (score >= 80) {
|
||||
return "Overwhelmingly Positive";
|
||||
} else if (score >= 60) {
|
||||
return "Positive";
|
||||
} else if (score >= 40) {
|
||||
return "Mixed";
|
||||
} else if (score >= 20) {
|
||||
return "Negative";
|
||||
} else {
|
||||
return "Overwhelmingly Negative";
|
||||
}
|
||||
};
|
||||
|
||||
const userScoreLabel = getUserScoreLabel(userScore);
|
||||
const userScoreColor =
|
||||
userScore >= 80 ? "green" : userScore >= 50 ? "orange" : "red";
|
||||
|
||||
const respon = {
|
||||
like_count: likes,
|
||||
dislike_count: dislikes,
|
||||
rating: engagement.rating,
|
||||
userScore: {
|
||||
label: userScoreLabel,
|
||||
score: userScore,
|
||||
color: userScoreColor,
|
||||
},
|
||||
engagement: {
|
||||
likeColor: likeColor,
|
||||
dislikeColor: dislikeColor,
|
||||
percentage: {
|
||||
likePercentage: `${likePercentage}%`,
|
||||
dislikePercentage: `${dislikePercentage}%`,
|
||||
},
|
||||
},
|
||||
ReturnYouTubeDislikesApiRawResponse: engagement,
|
||||
};
|
||||
|
||||
res.send(respon);
|
||||
} else {
|
||||
res.status(400).json("pls gib ID :3");
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json("whoops (error 500) >~<");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/feeds/videos.xml", async (req, res) => {
|
||||
const id = req.query.channel_id;
|
||||
@@ -188,18 +216,20 @@ app.use("/sb/i/:v/:imagePath/:img", async function (req, res) {
|
||||
|
||||
let f = await modules.fetch(url, {
|
||||
method: req.method,
|
||||
headers: headers, // Add headers to the fetch request
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
});
|
||||
|
||||
app.get("/api/improving-poke/getsugesstions", async (req, res) => {
|
||||
const query = req.query.q;
|
||||
app.get("/api/manifest/dash/id/:id", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
let url = `https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1/search/suggestions?q=${query}`;
|
||||
let url = `https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/manifest/dash/id/${id}`;
|
||||
|
||||
let f = await modules.fetch(url, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
f.body.pipe(res);
|
||||
@@ -227,38 +257,41 @@ app.use("/sb/i/:v/:imagePath/:img", async function (req, res) {
|
||||
let latestCommitHash;
|
||||
|
||||
const invidious = await modules
|
||||
.fetch("https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1/stats")
|
||||
.fetch("https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1/stats", {
|
||||
headers: headers,
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((txt) => getJson(txt));
|
||||
|
||||
const cpus = os.cpus();
|
||||
const totalMemory = os.totalmem() / (1024 * 1024 * 1024);
|
||||
const roundedMemory = totalMemory.toFixed(2);
|
||||
|
||||
execSync('git rev-list HEAD -n 1 --abbrev-commit', (error, stdout, stderr) => {
|
||||
if (error || stderr) {
|
||||
console.error(`Error executing command: ${error || stderr}`);
|
||||
return;
|
||||
}
|
||||
const cpus = os.cpus();
|
||||
const totalMemory = os.totalmem() / (1024 * 1024 * 1024);
|
||||
const roundedMemory = totalMemory.toFixed(2);
|
||||
|
||||
latestCommitHash = stdout.trim();
|
||||
});
|
||||
execSync('git rev-list HEAD -n 1 --abbrev-commit', (error, stdout, stderr) => {
|
||||
if (error || stderr) {
|
||||
console.error(`Error executing command: ${error || stderr}`);
|
||||
return;
|
||||
}
|
||||
|
||||
latestCommitHash = stdout.trim();
|
||||
});
|
||||
const { useragent, ...configWithoutUA } = cnf;
|
||||
|
||||
const response = {
|
||||
pt_version: {
|
||||
version:versmol,
|
||||
version_full:verfull,
|
||||
commit: latestCommitHash
|
||||
version: versmol,
|
||||
version_full: verfull,
|
||||
commit: latestCommitHash,
|
||||
},
|
||||
branch,
|
||||
updatequote,
|
||||
relaseunixdate,
|
||||
vernum: versionnumber,
|
||||
codename,
|
||||
config:cnf,
|
||||
system:{
|
||||
ram:`${roundedMemory} GB`,
|
||||
cpu:cpus[0].model,
|
||||
config: configWithoutUA,
|
||||
system: {
|
||||
ram: `${roundedMemory} GB`,
|
||||
cpu: cpus[0].model,
|
||||
},
|
||||
packages: {
|
||||
libpt: version,
|
||||
@@ -287,7 +320,9 @@ execSync('git rev-list HEAD -n 1 --abbrev-commit', (error, stdout, stderr) => {
|
||||
try {
|
||||
const url = `https://raw.githubusercontent.com/ashley0143/poke/main/instances.json`;
|
||||
|
||||
let f = await fetch(url)
|
||||
let f = await fetch(url, {
|
||||
headers: headers,
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((json) => JSON.parse(json));
|
||||
|
||||
|
||||
@@ -57,18 +57,20 @@ const ChannelTabs = {
|
||||
};
|
||||
|
||||
module.exports = function (app, config, renderTemplate) {
|
||||
app.get("/download", async function (req, res) {
|
||||
app.get("/download", async (req, res) => {
|
||||
try {
|
||||
var v = req.query.v;
|
||||
const v = req.query.v;
|
||||
|
||||
const thumbnailUrl = `https://i.ytimg.com/vi/${v}/maxresdefault.jpg`;
|
||||
const colors = await modules.getColors(thumbnailUrl);
|
||||
const color = colors[0].hex();
|
||||
|
||||
renderTemplate(res, req, "download.ejs", {
|
||||
v,
|
||||
color: await modules
|
||||
.getColors(`https://i.ytimg.com/vi/${v}/maxresdefault.jpg`)
|
||||
.then((colors) => colors[0].hex()),
|
||||
color,
|
||||
isMobile: req.useragent.isMobile,
|
||||
});
|
||||
} catch {
|
||||
} catch (error) {
|
||||
res.redirect("/");
|
||||
}
|
||||
});
|
||||
@@ -86,7 +88,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
});
|
||||
|
||||
app.get("/search", async (req, res) => {
|
||||
const query = req.query.query;
|
||||
const query = req.query.query ? req.query.query.replace("ohio", "things to do in ohio") : '';
|
||||
const tab = req.query.tab;
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
@@ -106,8 +108,6 @@ module.exports = function (app, config, renderTemplate) {
|
||||
IsOldWindows = false;
|
||||
}
|
||||
|
||||
const poketube_universe_value = "poketube_smart_search";
|
||||
|
||||
if (query) {
|
||||
let redirectTo = null;
|
||||
let splitParam = ":";
|
||||
@@ -115,9 +115,9 @@ module.exports = function (app, config, renderTemplate) {
|
||||
if (query.includes("youtube.com/watch?v=")) {
|
||||
redirectTo = "/watch";
|
||||
splitParam = "?v=";
|
||||
} else if (query.includes("channel:")) {
|
||||
} else if (query.includes("channel")) {
|
||||
redirectTo = "/channel?id=";
|
||||
} else if (query.includes("video:")) {
|
||||
} else if (query.includes("video")) {
|
||||
redirectTo = "/watch?v=";
|
||||
}
|
||||
|
||||
@@ -135,6 +135,10 @@ module.exports = function (app, config, renderTemplate) {
|
||||
res.redirect("https://lite.duckduckgo.com/lite/?q=" + query);
|
||||
}
|
||||
|
||||
if (query && query.startsWith("Hey ChatGPT,") && query.length > 2) {
|
||||
res.redirect("https://chatgpt.com/?q=" + query + "- sent using pokeAI features");
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
return res.redirect("/");
|
||||
}
|
||||
@@ -148,13 +152,18 @@ module.exports = function (app, config, renderTemplate) {
|
||||
try {
|
||||
const headers = {};
|
||||
|
||||
const xmlData = await fetch(
|
||||
`${config.invapi}/search?q=${encodeURIComponent(
|
||||
query
|
||||
)}&page=${encodeURIComponent(
|
||||
continuation
|
||||
)}&date=${date}&type=${type}&duration=${duration}&sort=${sort}&hl=en+gb`
|
||||
)
|
||||
let searchUrl;
|
||||
if (req.query.from === 'hashtag') {
|
||||
searchUrl = `${config.invapi}/hashtag/${query}?hl=en-gb`;
|
||||
} else {
|
||||
searchUrl = `${config.invapi}/search?q=${encodeURIComponent(query)}&page=${encodeURIComponent(continuation)}&date=${date}&type=${type}&duration=${duration}&sort=${sort}&hl=en+gb`;
|
||||
}
|
||||
|
||||
const xmlData = await fetch(searchUrl, {
|
||||
headers: {
|
||||
'User-Agent': config.useragent,
|
||||
},
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((txt) => getJson(txt));
|
||||
|
||||
@@ -174,7 +183,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
summary: "",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error while searching for '${query}':`, error);
|
||||
console.log(`Error while searching for '${query}':`, error);
|
||||
res.redirect("/");
|
||||
}
|
||||
});
|
||||
@@ -211,7 +220,11 @@ module.exports = function (app, config, renderTemplate) {
|
||||
|
||||
try {
|
||||
// about
|
||||
const bout = await fetch(config.tubeApi + `channel?id=${ID}&tab=about`);
|
||||
const bout = await fetch(config.tubeApi + `channel?id=${ID}&tab=about`, {
|
||||
headers: {
|
||||
'User-Agent': config.useragent,
|
||||
},
|
||||
});
|
||||
const h = await bout.text();
|
||||
var boutJson = JSON.parse(modules.toJson(h));
|
||||
} catch {
|
||||
@@ -231,7 +244,11 @@ module.exports = function (app, config, renderTemplate) {
|
||||
|
||||
const getChannelData = async (url) => {
|
||||
try {
|
||||
return await fetch(url)
|
||||
return await fetch(url, {
|
||||
headers: {
|
||||
'User-Agent': config.useragent,
|
||||
},
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((txt) => getJson(txt));
|
||||
} catch (error) {
|
||||
@@ -240,21 +257,25 @@ module.exports = function (app, config, renderTemplate) {
|
||||
};
|
||||
|
||||
const apiUrl = config.invapi + "/channels/";
|
||||
const channelUrl = `${apiUrl}${atob(
|
||||
const channelUrl = `${apiUrl}${ID}/${atob(
|
||||
ChannelTabs.videos
|
||||
)}/${ID}/?sort_by=${sort_by}${continuation}`;
|
||||
)}?sort_by=${sort_by}${continuation}`;
|
||||
|
||||
const shortsUrl = `${apiUrl}${ID}/${atob(
|
||||
ChannelTabs.shorts
|
||||
)}?sort_by=${sort_by}${continuations}`;
|
||||
)}?sort_by=${sort_by}${continuation}`;
|
||||
|
||||
const streamUrl = `${apiUrl}${ID}/${atob(
|
||||
ChannelTabs.streams
|
||||
)}?sort_by=${sort_by}${continuationl}`;
|
||||
const communityUrl = `${apiUrl}${atob(
|
||||
)}?sort_by=${sort_by}${continuation}`;
|
||||
|
||||
const communityUrl = `${apiUrl}${ID}/${atob(
|
||||
ChannelTabs.community
|
||||
)}/${ID}/?hl=en-US`;
|
||||
const PlaylistUrl = `${apiUrl}${atob(
|
||||
)}?hl=en-US`;
|
||||
|
||||
const PlaylistUrl = `${apiUrl}${ID}/${atob(
|
||||
ChannelTabs.playlist
|
||||
)}/${ID}/?hl=en-US`;
|
||||
)}?hl=en-US`;
|
||||
|
||||
const channelINVUrl = `${apiUrl}${ID}/`;
|
||||
|
||||
@@ -269,6 +290,19 @@ module.exports = function (app, config, renderTemplate) {
|
||||
getChannelData(channelINVUrl),
|
||||
]);
|
||||
|
||||
var bannedchannels = ["UC1okSIA8UEY8OqvtjGHFvzA", "UClsVg5LkK2COQRo1mVS4taA", "UCIr4vkCsn0tdTW2xZ1jRG1g"];
|
||||
var bypassQuery = "cG9rZXR1YmVjaGFubmVsYnlwYXNzbG9scGVvcGxldGhpbmt0aGlzaXNjZW5zb3JzaGlwLTQ1OTBh";
|
||||
|
||||
var bypassExists = req.query.bypass === bypassQuery;
|
||||
var tabExists = 'tab' in req.query;
|
||||
var continuationExists = 'continuation' in req.query;
|
||||
|
||||
if (bannedchannels.some(channel => channel === ID) && !bypassExists && !tabExists && !continuationExists) {
|
||||
var cinv = {
|
||||
error: `this channel may include disinformation. If you still wanna view content <a href="/channel?id=${ID}&bypass=${bypassQuery}">click here</a> to bypass this restriction.`
|
||||
};
|
||||
}
|
||||
|
||||
function getThumbnailUrl(video) {
|
||||
const maxresDefaultThumbnail = video.videoThumbnails.find(
|
||||
(thumbnail) => thumbnail.quality === "maxresdefault"
|
||||
@@ -297,7 +331,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
var { tj, shorts, stream, c, boutJson } = cache[ID].result;
|
||||
}
|
||||
|
||||
const subscribers = convert(cinv?.subCount);
|
||||
const subscribers = convert(cinv?.subCount || 0);
|
||||
const about = boutJson?.Channel?.Contents?.ItemSection?.About;
|
||||
const description = about?.Description.toString().replace(
|
||||
/\n/g,
|
||||
@@ -305,19 +339,10 @@ module.exports = function (app, config, renderTemplate) {
|
||||
);
|
||||
const dnoreplace = about?.Description.toString();
|
||||
|
||||
if (continuation) {
|
||||
const currentAuthorId = String(cinv.authorId).trim();
|
||||
const firstVideoAuthorId = String(tj?.videos[0].authorId).trim();
|
||||
|
||||
if (currentAuthorId.localeCompare(firstVideoAuthorId) !== 0) {
|
||||
res.status(400).send("Continuation does not match the channel :c");
|
||||
}
|
||||
}
|
||||
|
||||
let ChannelFirstVideoObject = {
|
||||
subCountText: "0",
|
||||
authorVerified: false,
|
||||
};
|
||||
subCountText: "0",
|
||||
authorVerified: false,
|
||||
};
|
||||
|
||||
renderTemplate(res, req, "channel.ejs", {
|
||||
ID,
|
||||
@@ -342,10 +367,9 @@ module.exports = function (app, config, renderTemplate) {
|
||||
isMobile: req.useragent.isMobile,
|
||||
about,
|
||||
playlist,
|
||||
subs:
|
||||
typeof subscribers === "string"
|
||||
? subscribers.replace("subscribers", "")
|
||||
: "None",
|
||||
subs: typeof subscribers === "string"
|
||||
? subscribers.replace("subscribers", "")
|
||||
: "None",
|
||||
desc: dnoreplace === "[object Object]" ? "" : description,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -66,7 +66,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
});
|
||||
|
||||
app.get("/gaming", (req, res) => {
|
||||
res.redirect("/discover?tab=gaming");
|
||||
res.redirect("/app?tab=gaming");
|
||||
});
|
||||
|
||||
app.get("/custom-theme", (req, res) => {
|
||||
|
||||
@@ -82,24 +82,24 @@ module.exports = function (app, config, renderTemplate) {
|
||||
renderTemplate(res, req, "rewind.ejs");
|
||||
});
|
||||
|
||||
app.get("/notepad", function (req, res) {
|
||||
renderTemplate(res, req, "pokepad.ejs");
|
||||
});
|
||||
|
||||
app.get("/translate", async function (req, res) {
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
const api_url = "https://simplytranslate.org/api/translate";
|
||||
|
||||
// Fetch translation data
|
||||
const translationResponse = await fetch(
|
||||
const translationResponse = await fetch(
|
||||
`${api_url}?from=${req.query.from_language}&to=${req.query.to_language}&text=${req.query.input}&engine=google`
|
||||
);
|
||||
|
||||
// Check if the request was successful (status code 200)
|
||||
const translationData = await translationResponse.json();
|
||||
const translationData = await translationResponse.json();
|
||||
|
||||
// Extract translated_text from the response
|
||||
const translatedText = translationData.translated_text;
|
||||
const translatedText = translationData.translated_text;
|
||||
|
||||
// Render the template with the translated text
|
||||
renderTemplate(res, req, "translate.ejs", {
|
||||
renderTemplate(res, req, "translate.ejs", {
|
||||
translation: translatedText,
|
||||
text: req.query.input || "enter text here",
|
||||
from_language: req.query.from_language,
|
||||
@@ -115,27 +115,22 @@ module.exports = function (app, config, renderTemplate) {
|
||||
app.get("/apps", function (req, res) {
|
||||
renderTemplate(res, req, "apps.ejs");
|
||||
});
|
||||
app.get("/playlist", async function (req, res) {
|
||||
const { fetch } = await import("undici");
|
||||
if (!req.query.list) res.redirect("/");
|
||||
if (req.useragent.isMobile) res.redirect("/");
|
||||
|
||||
const playlist = await fetch(
|
||||
`${config.invapi}/playlists/${req.query.list}?hl=en-us`
|
||||
);
|
||||
const headers = { "User-Agent": config.useragent };
|
||||
|
||||
const p = getJson(await playlist.text());
|
||||
var mediaproxy = config.media_proxy;
|
||||
app.get("/playlist", async function (req, res) {
|
||||
if (!req.query.list) res.redirect("/");
|
||||
if (req.useragent.isMobile) res.redirect("/");
|
||||
const playlist = await fetch(`${config.invapi}/playlists/${req.query.list}?hl=en-us`, { headers });
|
||||
const p = getJson(await playlist.text());
|
||||
var mediaproxy = config.media_proxy;
|
||||
if (req.useragent.source.includes("Pardus")) {
|
||||
mediaproxy = "https://media-proxy.ashley0143.xyz";
|
||||
}
|
||||
renderTemplate(res, req, "playlist.ejs", { p, mediaproxy });
|
||||
});
|
||||
|
||||
if (req.useragent.source.includes("Pardus")) {
|
||||
var mediaproxy = "https://media-proxy.ashley0143.xyz";
|
||||
}
|
||||
|
||||
renderTemplate(res, req, "playlist.ejs", {
|
||||
p,
|
||||
mediaproxy,
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/license", function (req, res) {
|
||||
renderTemplate(res, req, "license.ejs");
|
||||
@@ -153,6 +148,56 @@ module.exports = function (app, config, renderTemplate) {
|
||||
renderTemplate(res, req, "content-settings.ejs");
|
||||
});
|
||||
|
||||
|
||||
function gregorianToIslamic(gDate) {
|
||||
const jd = Math.floor((gDate - new Date(1970, 0, 1)) / (24 * 60 * 60 * 1000)) + 2440588;
|
||||
const islamicYear = Math.floor((30 * (jd - 1948440) + 10646) / 10631);
|
||||
return islamicYear;
|
||||
}
|
||||
|
||||
function gregorianToPersian(gDate) {
|
||||
const persianEpoch = 226895; // Julian Day of Persian Epoch
|
||||
const jd = Math.floor((gDate - new Date(1970, 0, 1)) / (24 * 60 * 60 * 1000)) + 2440588;
|
||||
const persianYear = Math.floor((jd - persianEpoch) / 365.2421985) + 1;
|
||||
return persianYear;
|
||||
}
|
||||
|
||||
app.get('/calendar', (req, res) => {
|
||||
// Get the date from query or default to today
|
||||
const queryDate = req.query.date ? new Date(req.query.date) : new Date();
|
||||
|
||||
// Extract the year and month from the date
|
||||
const year = queryDate.getFullYear();
|
||||
const month = queryDate.getMonth(); // 0 (January) to 11 (December)
|
||||
|
||||
const monthOffset = parseInt(req.query.month) || 0;
|
||||
const newDate = new Date(year, month + monthOffset, 1);
|
||||
const newYear = newDate.getFullYear();
|
||||
const newMonth = newDate.getMonth();
|
||||
|
||||
const firstDay = new Date(newYear, newMonth, 1);
|
||||
const firstDayWeekday = firstDay.getDay(); // Day of the week (0-6)
|
||||
|
||||
const days = Array.from({ length: 42 }, (_, i) => {
|
||||
const day = new Date(newYear, newMonth, i - firstDayWeekday + 1);
|
||||
return (day.getMonth() === newMonth) ? day : null;
|
||||
});
|
||||
|
||||
const islamicYear = gregorianToIslamic(newDate);
|
||||
const persianYear = gregorianToPersian(newDate);
|
||||
|
||||
renderTemplate(res, req, "calendar.ejs", {
|
||||
year: newYear,
|
||||
islamicYear,
|
||||
persianYear,
|
||||
currentDate: newDate,
|
||||
days,
|
||||
month: newMonth,
|
||||
queryDate,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get("/offline", function (req, res) {
|
||||
res.sendFile("offline.html", { root: location_pwa });
|
||||
});
|
||||
@@ -211,7 +256,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
});
|
||||
|
||||
app.get("/game-hub", function (req, res) {
|
||||
var gameslist = ["pong", "tic-tac-toe", "sudoku", "snake"];
|
||||
var gameslist = ["pong", "tic-tac-toe", "sudoku", "snake", "breakout", "minesweeper"];
|
||||
var requestedGame = req.query.game;
|
||||
|
||||
if (req.query.game && !gameslist.includes(requestedGame)) {
|
||||
|
||||
@@ -164,7 +164,7 @@ function getJson(str) {
|
||||
const PATREON_REGEX = /https:\/\/(?:www\.)?patreon.com\/(?<name>[\w\d_-]+)/;
|
||||
|
||||
/* connections */
|
||||
const X_REGEX = /https:\/\/twitter.com\/(?<name>[\w\d_-]+)/;
|
||||
const TWITTER_REGEX = /https:\/\/twitter.com\/(?<name>[\w\d_-]+)/;
|
||||
const CORD_REGEX = /https:\/\/discord.gg\/(?<name>[\w\d_-]+)/;
|
||||
const TWITCH_REGEX = /https:\/\/twitch.tv\/(?<name>[\w\d_-]+)/;
|
||||
const REDDIT_REGEX = /https:\/\/reddit\.com\/r\/(?<name>[\w\d_-]+)/;
|
||||
@@ -217,6 +217,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
const secure = ["poketube.fun"].includes(req.hostname);
|
||||
const verify = req.hostname === "poketube.sudovanilla.com";
|
||||
|
||||
|
||||
INNERTUBE.getYouTubeApiVideo(f, v, contentlang, contentregion).then(
|
||||
(data) => {
|
||||
try {
|
||||
@@ -228,12 +229,13 @@ module.exports = function (app, config, renderTemplate) {
|
||||
const inv_vid = data?.vid;
|
||||
const desc = data?.desc || "";
|
||||
|
||||
|
||||
let d = false;
|
||||
if (desc !== "[object Object]") {
|
||||
d = desc.toString().replace(/\n/g, " <br> ");
|
||||
}
|
||||
|
||||
const descriptionString = String(inv_vid?.description);
|
||||
const descriptionString = String(inv_vid?.description).replace(/\bx.com\b/, "twitter.com")
|
||||
|
||||
function extractInfo(regex) {
|
||||
return descriptionString !== "[object Object]"
|
||||
@@ -242,12 +244,15 @@ module.exports = function (app, config, renderTemplate) {
|
||||
}
|
||||
|
||||
const support = extractInfo(PATREON_REGEX);
|
||||
const STUPID_ELON_MUSK_WEBSITE_HE_IS_TRYING_TO_CALL_IT_X_FOR_SOME_REASON_WHICH_IS_A_STUPID_NAME_THAT_SHOULD_HAVE_STOPPED_WITH_PAYPAL_WE_WILL_FOREVER_CALL_IT_TWITTER_AND_HE_CAN_DO_NOTHING_ABOUT_IT_LOL_FUCK_YOU_ELON_TRANS_RIGHTS_BTW = extractInfo(X_REGEX);
|
||||
const STUPID_ELON_MUSK_WEBSITE_HE_IS_TRYING_TO_CALL_IT_X_FOR_SOME_REASON_WHICH_IS_A_STUPID_NAME_WE_WILL_FOREVER_CALL_IT_TWITTER_AND_HE_CAN_DO_NOTHING_ABOUT_IT_LOL_FUCK_YOU_ELON_TRANS_RIGHTS_BTW = extractInfo(TWITTER_REGEX);
|
||||
const linkto = extractInfo(LNKTO_REGEX);
|
||||
const discord = extractInfo(CORD_REGEX);
|
||||
const twitch = extractInfo(TWITCH_REGEX);
|
||||
const reddit = extractInfo(REDDIT_REGEX);
|
||||
|
||||
/* meta software */
|
||||
const instagram = extractInfo(INSTAGRAM_REGEX);
|
||||
const threads_by_instagram = extractInfo(THREADS_BY_INSTAGRAM_REGEX);
|
||||
|
||||
const videoObject = inv_vid?.adaptiveFormats;
|
||||
function findItag(adaptiveFormats) {
|
||||
@@ -305,11 +310,16 @@ module.exports = function (app, config, renderTemplate) {
|
||||
|
||||
if (uaos === "Windows XP" || uaos === "Windows Vista")
|
||||
res.redirect("/lite?v=" + req.query.v);
|
||||
|
||||
if (req.useragent.source.includes("Nintendo WiiU"))
|
||||
res.redirect("/lite?v=" + req.query.v);
|
||||
|
||||
|
||||
|
||||
if (req.query.from === "short") var shortsui = true;
|
||||
|
||||
try {
|
||||
renderTemplate(res, req, "poketube.ejs", {
|
||||
renderTemplate(res, req, "watch.ejs", {
|
||||
color: data.color,
|
||||
color2: data.color2,
|
||||
linkify,
|
||||
@@ -326,13 +336,14 @@ module.exports = function (app, config, renderTemplate) {
|
||||
date: k.Video.uploadDate,
|
||||
e,
|
||||
a,
|
||||
twitter:STUPID_ELON_MUSK_WEBSITE_HE_IS_TRYING_TO_CALL_IT_X_FOR_SOME_REASON_WHICH_IS_A_STUPID_NAME_THAT_SHOULD_HAVE_STOPPED_WITH_PAYPAL_WE_WILL_FOREVER_CALL_IT_TWITTER_AND_HE_CAN_DO_NOTHING_ABOUT_IT_LOL_FUCK_YOU_ELON_TRANS_RIGHTS_BTW,
|
||||
twitter:STUPID_ELON_MUSK_WEBSITE_HE_IS_TRYING_TO_CALL_IT_X_FOR_SOME_REASON_WHICH_IS_A_STUPID_NAME_WE_WILL_FOREVER_CALL_IT_TWITTER_AND_HE_CAN_DO_NOTHING_ABOUT_IT_LOL_FUCK_YOU_ELON_TRANS_RIGHTS_BTW,
|
||||
k,
|
||||
dm,
|
||||
proxyurl,
|
||||
media_proxy_url: mediaproxy,
|
||||
instagram,
|
||||
useragent: req.useragent,
|
||||
config,
|
||||
verify,
|
||||
discord,
|
||||
turntomins,
|
||||
@@ -348,6 +359,8 @@ module.exports = function (app, config, renderTemplate) {
|
||||
isMobile: req.useragent.isMobile,
|
||||
tj: data.channel,
|
||||
r,
|
||||
threads:threads_by_instagram,
|
||||
hostname:req.hostname,
|
||||
qua: q,
|
||||
inv: inv_comments,
|
||||
convert,
|
||||
@@ -364,8 +377,12 @@ module.exports = function (app, config, renderTemplate) {
|
||||
inv_vid,
|
||||
lyrics: "",
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
renderTemplate(res, req, "video-error.ejs", {
|
||||
v,
|
||||
err_reason:err
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -420,7 +437,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
}
|
||||
|
||||
const support = extractInfo(PATREON_REGEX);
|
||||
const twitter = extractInfo(X_REGEX);
|
||||
const twitter = extractInfo(TWITTER_REGEX);
|
||||
const discord = extractInfo(CORD_REGEX);
|
||||
const twitch = extractInfo(TWITCH_REGEX);
|
||||
const reddit = extractInfo(REDDIT_REGEX);
|
||||
@@ -435,7 +452,7 @@ module.exports = function (app, config, renderTemplate) {
|
||||
var vidurl = u.losslessurl;
|
||||
}
|
||||
|
||||
var vidurl = "https://eu-proxy.poketube.fun";
|
||||
var vidurl = config.videourl;
|
||||
var isvidious = true;
|
||||
|
||||
if (req.useragent.source.includes("Pardus")) {
|
||||
|
||||
@@ -94,7 +94,7 @@ function init(app, config, rendertemplate) {
|
||||
|
||||
}, 100);
|
||||
} catch (err) {
|
||||
initlog("[FAILED] Load pages \n" + err);
|
||||
initlog("[POKE SEGFAULT] Load pages \n" + err);
|
||||
console.error(err);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
@@ -12,20 +12,7 @@ const getdislikes = require("../libpoketube/libpoketube-dislikes.js");
|
||||
const getColors = require("get-image-colors");
|
||||
const config = require("../../config.json")
|
||||
|
||||
|
||||
/**
|
||||
* Class representing PokeTube's core functionality.
|
||||
*/
|
||||
class InnerTubePokeVidious {
|
||||
/**
|
||||
* Create an instance of InnerTubePokeVidious.
|
||||
* @param {object} config - Configuration object for InnerTubePokeVidious.
|
||||
* @param {string} config.tubeApi - Tube API URL.
|
||||
* @param {string} config.invapi - Invid API URL.
|
||||
* @param {string} config.invapi_alt - Invid API URL - ALT .
|
||||
* @param {string} config.dislikes - Dislikes API URL.
|
||||
* @param {string} config.t_url - Matomo URL.
|
||||
*/
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.cache = {};
|
||||
@@ -34,19 +21,14 @@ class InnerTubePokeVidious {
|
||||
this.param_legacy = "CgIIAdgDAQ%3D%3D"
|
||||
this.apikey = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"
|
||||
this.ANDROID_API_KEY = "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w"
|
||||
this.ANDROID_APP_VERSION = "19.14.42"
|
||||
this.ANDROID_VERSION = "13"
|
||||
this.useragent = "com.google.android.youtube/19.14.42 (Linux; U; Android 12; US) gzip"
|
||||
this.ANDROID_APP_VERSION = "20.20.41" // https://www.apkmirror.com/apk/google-inc/youtube/youtube-20-20-41-release/
|
||||
this.ANDROID_VERSION = "16" // https://en.wikipedia.org/wiki/Android_version_history
|
||||
this.useragent = config.useragent || "PokeTube/2.0.0 (GNU/Linux; Android 14; Trisquel 11; poketube-vidious; like FreeTube)"
|
||||
this.INNERTUBE_CONTEXT_CLIENT_VERSION = "1"
|
||||
this.region = "region=US";
|
||||
this.sqp = "-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch JSON from API response.
|
||||
* @param {string} str - String response from the API.
|
||||
* @returns {object|null} Parsed JSON object or null if parsing failed.
|
||||
*/
|
||||
getJson(str) {
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
@@ -55,80 +37,68 @@ class InnerTubePokeVidious {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provided object has the required properties.
|
||||
* @param {object} obj - Object to check.
|
||||
* @returns {boolean} True if the object has the required properties, false otherwise.
|
||||
*/
|
||||
checkUnexistingObject(obj) {
|
||||
if (obj) {
|
||||
if ("authorId" in obj) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return obj && "authorId" in obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch video information.
|
||||
* @param {string} v - Video ID.
|
||||
* @returns {Promise<object>} Promise resolving to the video information.
|
||||
*/
|
||||
async getYouTubeApiVideo(f, v, contentlang, contentregion) {
|
||||
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
|
||||
if (v == null) return "Gib ID";
|
||||
|
||||
// Check if result is already cached
|
||||
if (this.cache[v] && Date.now() - this.cache[v].timestamp < 3600000) {
|
||||
return this.cache[v].result;
|
||||
}
|
||||
const headers = {};
|
||||
|
||||
let desc = "";
|
||||
|
||||
const headers = {
|
||||
"User-Agent": this.useragent,
|
||||
};
|
||||
|
||||
const fetchWithRetry = async (url, options = {}, retries = 3) => {
|
||||
for (let attempt = 0; attempt < retries; attempt++) {
|
||||
const res = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
...headers,
|
||||
}
|
||||
});
|
||||
if (res.status === 500 && attempt < retries - 1) continue;
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
try {
|
||||
const [invComments, videoInfo, videoData] = await Promise.all([
|
||||
fetch(`${this.config.invapi}/comments/${v}?hl=${contentlang}®ion=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()),
|
||||
fetch(`${this.config.invapi}/videos/${v}?hl=${contentlang}®ion=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()),
|
||||
curly
|
||||
.get(`${this.config.tubeApi}video?v=${v}`, {
|
||||
const [invComments, videoInfo, videoData] = await Promise.all([
|
||||
fetchWithRetry(`${this.config.invapi}/comments/${v}?hl=${contentlang}®ion=${contentregion}&h=${btoa(Date.now())}`).then(res => res.text()),
|
||||
fetchWithRetry(`${this.config.invapi}/videos/${v}?hl=${contentlang}®ion=${contentregion}&h=${btoa(Date.now())}`).then(res => res.text()),
|
||||
curly.get(`${this.config.tubeApi}video?v=${v}`, {
|
||||
httpHeader: Object.entries(headers).map(([k, v]) => `${k}: ${v}`),
|
||||
})
|
||||
.then((res) => {
|
||||
}).then(res => {
|
||||
const json = toJson(res.data);
|
||||
const video = this.getJson(json);
|
||||
return { json, video };
|
||||
}),
|
||||
]);
|
||||
]);
|
||||
|
||||
|
||||
const comments = await this.getJson(invComments);
|
||||
|
||||
const vid = await this.getJson(videoInfo);
|
||||
const { json, video } = videoData;
|
||||
const comments = this.getJson(invComments);
|
||||
const vid = this.getJson(videoInfo);
|
||||
const { json, video } = videoData;
|
||||
|
||||
var channel_uploads = { };
|
||||
if (f == "true") {
|
||||
channel_uploads = await fetch(
|
||||
`${this.config.invapi}/channels/${vid.authorId}?hl=${contentlang}®ion=${contentregion}`
|
||||
);
|
||||
var p = this.getJson(await channel_uploads.text());
|
||||
}
|
||||
let p = {};
|
||||
if (f === "true") {
|
||||
const uploads = await fetchWithRetry(`${this.config.invapi}/channels/${vid.authorId}?hl=${contentlang}®ion=${contentregion}`);
|
||||
p = this.getJson(await uploads.text());
|
||||
}
|
||||
|
||||
if (!vid) {
|
||||
console.log(
|
||||
`Sorry nya, we couldn't find any information about that video qwq`
|
||||
);
|
||||
}
|
||||
if (!vid) {
|
||||
console.log(`Sorry nya, we couldn't find any information about that video qwq`);
|
||||
}
|
||||
|
||||
if (this.checkUnexistingObject(vid)) {
|
||||
const fe = await getdislikes(v);
|
||||
if (this.checkUnexistingObject(vid)) {
|
||||
const fe = await getdislikes(v);
|
||||
|
||||
try {
|
||||
const headers = {};
|
||||
|
||||
// Store result in cache
|
||||
this.cache[v] = {
|
||||
result: {
|
||||
json: json?.video,
|
||||
@@ -139,59 +109,38 @@ class InnerTubePokeVidious {
|
||||
engagement: fe.engagement,
|
||||
wiki: "",
|
||||
desc: "",
|
||||
color: await getColors(
|
||||
`https://vid.puffyan.us/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`
|
||||
).then((colors) => colors[0].hex()),
|
||||
color2: await getColors(
|
||||
`https://vid.puffyan.us/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`
|
||||
).then((colors) => colors[1].hex()),
|
||||
color: await getColors(`https://i.ytimg.com/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`).then(colors => colors[0].hex()),
|
||||
color2: await getColors(`https://i.ytimg.com/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`).then(colors => colors[1].hex()),
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
return this.cache[v].result;
|
||||
} catch (error) {
|
||||
this.initError("Error getting video", error);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a video ID is valid.
|
||||
* @param {string} v - Video ID.
|
||||
* @returns {boolean} True if the video ID is valid, false otherwise.
|
||||
*/
|
||||
isvalidvideo(v) {
|
||||
if (v != "assets" && v != "cdn-cgi" && v != "404") {
|
||||
const regex = new RegExp("^([a-zA-Z0-9_-]{11})");
|
||||
const isMatch = regex.test(v);
|
||||
return isMatch;
|
||||
} else {
|
||||
return false;
|
||||
} catch (error) {
|
||||
this.initError("Error getting video", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an error.
|
||||
* @param {string} args - Error message.
|
||||
* @param {Error} error - Error object.
|
||||
*/
|
||||
isvalidvideo(v) {
|
||||
if (v != "assets" && v != "cdn-cgi" && v != "404") {
|
||||
return /^([a-zA-Z0-9_-]{11})/.test(v);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
initError(args, error) {
|
||||
console.error("[LIBPT CORE ERROR]" + args, error);
|
||||
console.error("[LIBPT CORE ERROR] " + args, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of InnerTubePokeVidious with the provided config
|
||||
const pokeTubeApiCore = new InnerTubePokeVidious({
|
||||
tubeApi: "https://inner-api.poketube.fun/api/",
|
||||
invapi: "https://invid-api.poketube.fun/bHj665PpYhUdPWuKPfZuQGoX/api/v1",
|
||||
invapi_alt: config.proxylocation === "EU" ? "https://invid-api.poketube.fun/api/v1" : "https://iv.ggtyler.dev/api/v1",
|
||||
dislikes: "https://returnyoutubedislikeapi.com/votes?videoId=",
|
||||
t_url: "https://t.poketube.fun/",
|
||||
useragent: config.useragent,
|
||||
});
|
||||
|
||||
module.exports = pokeTubeApiCore;
|
||||
|
||||
@@ -42,7 +42,6 @@ class PokeTubeDislikesAPIManager {
|
||||
*/
|
||||
async _getEngagementData() {
|
||||
const apiUrl = `https://ryd-proxy.kavin.rocks/votes/${this.videoId}&hash=d0550b6e28c8f93533a569c314d5b4e2`;
|
||||
const fallbackUrl = `https://returnyoutubedislikeapi.com/votes?videoId=${this.videoId}`;
|
||||
|
||||
const { fetch } = await import("undici");
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
TIME_BEFORE_DELETE=30
|
||||
INACTIVE_TIME_BEFORE_DELETE=3600
|
||||
PORT=45872
|
||||
# DO NOT PUT A / AT THE END OF THE URL
|
||||
PROXY_URL=https://eu-proxy.poketube.fun
|
||||
170
videobundler/.gitignore
vendored
170
videobundler/.gitignore
vendored
@@ -1,170 +0,0 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
bin/
|
||||
include/
|
||||
lib64/
|
||||
pyvenv.cfg
|
||||
*.m4a
|
||||
*.mp4
|
||||
.env
|
||||
done.*
|
||||
@@ -1,21 +0,0 @@
|
||||
# poke-videobundler
|
||||
|
||||
Takes 2 input streams, downloads them, and spits out a combined file.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Make sure `ffmpeg` and Python 3 are all installed.
|
||||
2. Download the program files to your computer - `main.py` and `.env.example`.
|
||||
3. Run `python3 -m pip install aiohttp`.
|
||||
|
||||
## Usage
|
||||
|
||||
1. `python3 main.py`.
|
||||
2. If everything went well, you shouldn't see any output at launch.
|
||||
3. You will now be able to call the server at :3030.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `/`: Will return `{success:true}` if alive.
|
||||
- `/[ANYTHING]?id=VIDEO_ID&audio_itag=AUDIO_ITAG&video_itag=VIDEO_ITAG`: Starts the merging process. ID is the youtube video ID, and itags are self explanatory. As a response, you will get a job ID that you will be able to use in future requests to query the video or its status. When this process is finished, the inactive autodelete counter will start, which will allow you to fetch the video until the countdown is over.
|
||||
> Replace `[ANYTHING]` with absolutely anything, however it has to be unique to the request. Preferably use an UUID
|
||||
@@ -1,104 +0,0 @@
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from aiohttp import web
|
||||
import string
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
from aiohttp.web import Response, FileResponse
|
||||
|
||||
app = web.Application()
|
||||
app.router._frozen = False
|
||||
|
||||
def get_random_string(length):
|
||||
# choose from all lowercase letter
|
||||
letters = string.ascii_lowercase
|
||||
result_str = "".join(random.choice(letters) for i in range(length))
|
||||
return result_str
|
||||
|
||||
async def run_command(cmd):
|
||||
# Create subprocess
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
)
|
||||
# Wait for the subprocess to finish
|
||||
stdout, stderr = await process.communicate()
|
||||
# Check for errors
|
||||
if process.returncode!= 0:
|
||||
# Log or handle the error
|
||||
print(f"Command '{args}' failed with return code {process.returncode}")
|
||||
return None
|
||||
# Decode stdout and return
|
||||
return stdout
|
||||
|
||||
async def merge(request: aiohttp.web.Request):
|
||||
# register params
|
||||
video_id: str = request.rel_url.query["id"]
|
||||
audio_itag: str = request.rel_url.query["audio_itag"]
|
||||
video_itag: str = request.rel_url.query["video_itag"]
|
||||
# validate
|
||||
if " " in video_id or len(video_id) > 11:
|
||||
print(f"Video {video_id} flagged as invalid, dropping request")
|
||||
return
|
||||
if not audio_itag.isdigit():
|
||||
print(f"Audio itag {audio_itag} flagged as invalid, dropping request")
|
||||
return
|
||||
if not video_itag.isdigit():
|
||||
print(f"Video itag {video_itag} flagged as invalid, dropping request")
|
||||
return
|
||||
if "Firefox" in request.headers["User-Agent"]:
|
||||
# Sane browser that supports streaming
|
||||
|
||||
cmdline = f"ffmpeg -i \"https://eu-proxy.poketube.fun/latest_version?id={video_id}&itag={audio_itag}&local=true\" -i \"https://eu-proxy.poketube.fun/latest_version?id={video_id}&itag={video_itag}&local=true\" -c copy -f mp4 -movflags frag_keyframe+empty_moov -"
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmdline,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
response = web.StreamResponse(status=206, reason='OK', headers={
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Transfer-Encoding': 'chunked',
|
||||
'Content-Disposition': 'inline'
|
||||
})
|
||||
await response.prepare(request)
|
||||
try:
|
||||
while True:
|
||||
chunk = await process.stdout.readline()
|
||||
if not chunk:
|
||||
break
|
||||
await response.write(chunk)
|
||||
except Exception as e:
|
||||
|
||||
print(f"Error streaming FFmpeg output: {e}")
|
||||
#finally:
|
||||
#await response.write_eof()
|
||||
else:
|
||||
# Likely to be chromium browser, so to avoid browser shitting itself we download file
|
||||
job_id = f'{request.rel_url.query["id"]}_{request.rel_url.query["audio_itag"]}_{request.rel_url.query["video_itag"]}'
|
||||
if os.path.isfile(f"{job_id}.mp4"):
|
||||
return web.FileResponse(
|
||||
path=f"{job_id}.mp4"
|
||||
)
|
||||
cmdline = f"ffmpeg -i \"https://eu-proxy.poketube.fun/latest_version?id={video_id}&itag={audio_itag}&local=true\" -i \"https://eu-proxy.poketube.fun/latest_version?id={video_id}&itag={video_itag}&local=true\" -c:v copy -f mp4 -movflags frag_keyframe+empty_moov {job_id}.mp4"
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmdline
|
||||
)
|
||||
await process.wait()
|
||||
if process.returncode != 0: # Log or handle the error
|
||||
return None
|
||||
response = FileResponse(path=f"{job_id}.mp4")
|
||||
return response
|
||||
|
||||
async def ping(request):
|
||||
return web.Response(body='{"success": true}', content_type="application/json")
|
||||
|
||||
async def init_app():
|
||||
app.router.add_get("/{id:.+}", merge)
|
||||
app.router.add_get("/", ping)
|
||||
return app
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
app = loop.run_until_complete(init_app())
|
||||
web.run_app(app, port=3030)
|
||||
Reference in New Issue
Block a user