mirror of
https://github.com/PreMiD/PreMiD.git
synced 2026-04-06 04:41:58 +02:00
Compare commits
1496 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c461c83ff | ||
|
|
dc8dacd5f5 | ||
|
|
db47ae9043 | ||
|
|
a0f4e141b5 | ||
|
|
5bc4c0e5c0 | ||
|
|
c4765b8eeb | ||
|
|
ebc953b549 | ||
|
|
b407003450 | ||
|
|
3857702040 | ||
|
|
e997e946a0 | ||
|
|
2bcb87ec9c | ||
|
|
1d5af93390 | ||
|
|
8a0e10eab0 | ||
|
|
13e413329c | ||
|
|
d0fac8c495 | ||
|
|
74525de586 | ||
|
|
115923b935 | ||
|
|
b7af6bb80e | ||
|
|
b053e6395c | ||
|
|
698ea34848 | ||
|
|
d689324e62 | ||
|
|
fee8133965 | ||
|
|
b93db93236 | ||
|
|
1efa4b9bb0 | ||
|
|
4cf00f5ca9 | ||
|
|
94303a9bca | ||
|
|
fa611923b4 | ||
|
|
c57d4db5a3 | ||
|
|
5d7760caa3 | ||
|
|
0d5fe8e5ab | ||
|
|
1ac552f3cd | ||
|
|
4cf4e01286 | ||
|
|
e96e2c0cb6 | ||
|
|
8fb93a09f9 | ||
|
|
4d20ba95b1 | ||
|
|
85f428a295 | ||
|
|
a3018f528d | ||
|
|
3d8431c4d5 | ||
|
|
074d55b6d4 | ||
|
|
d22d88b3a6 | ||
|
|
f55a83977b | ||
|
|
4ccc02f16c | ||
|
|
759b2abef9 | ||
|
|
f7f04213de | ||
|
|
76cb9026f9 | ||
|
|
501b632828 | ||
|
|
39840a34a3 | ||
|
|
de336dda25 | ||
|
|
21cb3041b5 | ||
|
|
b340cd9b5d | ||
|
|
37d0516679 | ||
|
|
46618ca6eb | ||
|
|
a7b4fb1615 | ||
|
|
731024523b | ||
|
|
545d9315d2 | ||
|
|
8e7ae26081 | ||
|
|
cd45426657 | ||
|
|
fe26213210 | ||
|
|
e4e10669ed | ||
|
|
d35f4bced6 | ||
|
|
048cb8a828 | ||
|
|
9f1d9892c8 | ||
|
|
7ce6d935dd | ||
|
|
0f3af215e2 | ||
|
|
16bd93f566 | ||
|
|
9fb52a94b9 | ||
|
|
58a597526a | ||
|
|
fd42f07989 | ||
|
|
6ba5219225 | ||
|
|
16718e8bd4 | ||
|
|
46b13cba7c | ||
|
|
f010eba40e | ||
|
|
fea6d8bbc9 | ||
|
|
66f9abcbcd | ||
|
|
4610218121 | ||
|
|
12321cb0d6 | ||
|
|
feb74e0fb9 | ||
|
|
b10eb97a57 | ||
|
|
bc049b9b15 | ||
|
|
36019acbd3 | ||
|
|
c0d19f02e7 | ||
|
|
18f7e95665 | ||
|
|
06b5255ccc | ||
|
|
0ab4821c74 | ||
|
|
3e09ddf024 | ||
|
|
e142f7750d | ||
|
|
ddec0a7957 | ||
|
|
dfc90c143f | ||
|
|
1f887642bf | ||
|
|
1a28153fcf | ||
|
|
349b9cf837 | ||
|
|
32f53c98e0 | ||
|
|
80ef747e84 | ||
|
|
cc425b5a42 | ||
|
|
5db7f80e28 | ||
|
|
b27b04b3ea | ||
|
|
713fa607e0 | ||
|
|
8b492bc468 | ||
|
|
c20c7d4c0e | ||
|
|
5aab0ba965 | ||
|
|
aa1ba3c6a3 | ||
|
|
6d54b6e518 | ||
|
|
fee54828fc | ||
|
|
cbe200cdf3 | ||
|
|
09d296a875 | ||
|
|
53e1a92138 | ||
|
|
65509641b0 | ||
|
|
badd6dc45d | ||
|
|
dd40dbe43b | ||
|
|
07926d5a80 | ||
|
|
f155b90672 | ||
|
|
3f820c6527 | ||
|
|
8e41051dd7 | ||
|
|
17bda0dc95 | ||
|
|
cf281971cf | ||
|
|
5f1d6e6da3 | ||
|
|
6e7cb8e3c2 | ||
|
|
69c3fc5f67 | ||
|
|
d5e3ba8709 | ||
|
|
531c6405f3 | ||
|
|
b94f7b41ac | ||
|
|
a3a23065c7 | ||
|
|
07acf57e0d | ||
|
|
16010543f5 | ||
|
|
b0ec1f63de | ||
|
|
386a47390e | ||
|
|
9b662a4333 | ||
|
|
e119b4bf0e | ||
|
|
7e58c83777 | ||
|
|
896c7d426f | ||
|
|
e3b179fa9d | ||
|
|
f5efb5664d | ||
|
|
2ae23cac2c | ||
|
|
94b24fb079 | ||
|
|
c50e3e922a | ||
|
|
9adc3a72e7 | ||
|
|
70255ef9a8 | ||
|
|
2af5cffeef | ||
|
|
0dca8768b8 | ||
|
|
5942c4ac08 | ||
|
|
3c34c6571c | ||
|
|
f5d23cc7c1 | ||
|
|
9081b440d5 | ||
|
|
db7a638729 | ||
|
|
eff85171e6 | ||
|
|
508137efde | ||
|
|
991c651082 | ||
|
|
8d91013608 | ||
|
|
9ab00bace5 | ||
|
|
88e50540e8 | ||
|
|
015a788901 | ||
|
|
c3bb662e7b | ||
|
|
638d0eec34 | ||
|
|
c5274421a5 | ||
|
|
fdafdf5dce | ||
|
|
743cf8b2af | ||
|
|
b23bd7ceb3 | ||
|
|
570c6ef730 | ||
|
|
bb7d7edad5 | ||
|
|
9a14d38e63 | ||
|
|
f65bce0de5 | ||
|
|
8005b9b86b | ||
|
|
1b4ed842a6 | ||
|
|
7cd372cc59 | ||
|
|
3b2b35d760 | ||
|
|
464e39f0d1 | ||
|
|
f66a883272 | ||
|
|
7e2172533c | ||
|
|
4237c89589 | ||
|
|
45542ce1a8 | ||
|
|
5cb286efa9 | ||
|
|
ab8284163c | ||
|
|
1ce819d324 | ||
|
|
e557afb7b2 | ||
|
|
7eb28067d8 | ||
|
|
e08a1072a7 | ||
|
|
1031206441 | ||
|
|
5a6c14b9b9 | ||
|
|
2487053341 | ||
|
|
e53ed627d6 | ||
|
|
4e66c1795c | ||
|
|
b668366d8a | ||
|
|
6caa35889a | ||
|
|
6c0f66cdd5 | ||
|
|
6d74ae4723 | ||
|
|
606e62ec3f | ||
|
|
a916982107 | ||
|
|
9f43b3d9e1 | ||
|
|
313b228e14 | ||
|
|
a180d7f7ee | ||
|
|
7bf2c309d0 | ||
|
|
bfc1fb6db5 | ||
|
|
5373630030 | ||
|
|
f82e6fc29b | ||
|
|
3390c7ad30 | ||
|
|
40401ed8ac | ||
|
|
9cdc0d343a | ||
|
|
11bb4de08b | ||
|
|
37fdfebc74 | ||
|
|
29aaed7f4b | ||
|
|
3c4e08f6a4 | ||
|
|
6718c49854 | ||
|
|
c1c34b7be3 | ||
|
|
256a9baa57 | ||
|
|
e28e8571ef | ||
|
|
ba79a7e1ff | ||
|
|
44a7cc9b2f | ||
|
|
8fb4ef7ce0 | ||
|
|
86a1d05a35 | ||
|
|
d7a50e9984 | ||
|
|
1a27221563 | ||
|
|
780d61d84a | ||
|
|
f73e5f4c51 | ||
|
|
388b1a6196 | ||
|
|
327e347ebe | ||
|
|
0e9790a721 | ||
|
|
ea81cd0e16 | ||
|
|
6b2d494f6e | ||
|
|
3866716326 | ||
|
|
8de108548c | ||
|
|
3595dae0f1 | ||
|
|
2d266508f8 | ||
|
|
833bacc65d | ||
|
|
283434aa09 | ||
|
|
87b44a6f40 | ||
|
|
f5817f4c2f | ||
|
|
8a43fd7894 | ||
|
|
a2f57095d7 | ||
|
|
1827e0da5b | ||
|
|
6993cd792d | ||
|
|
4c9454cae8 | ||
|
|
1e86025c3d | ||
|
|
af44b45ea6 | ||
|
|
4ac7384b2f | ||
|
|
83146c71c0 | ||
|
|
993d747a64 | ||
|
|
6ecc95bbb0 | ||
|
|
177e4d081f | ||
|
|
b3ed1566ca | ||
|
|
0333dcdd20 | ||
|
|
f52cd72f02 | ||
|
|
b96e383e6a | ||
|
|
feca8660f3 | ||
|
|
3c561c642a | ||
|
|
068f41f8e5 | ||
|
|
e534e65d2d | ||
|
|
9c95de2f14 | ||
|
|
0e380b72b4 | ||
|
|
4b3489f8d9 | ||
|
|
3f4bb10562 | ||
|
|
f3524bf564 | ||
|
|
8805e62f22 | ||
|
|
e825207787 | ||
|
|
e1a6dfee5d | ||
|
|
e05c7e7dd1 | ||
|
|
4743bd13e8 | ||
|
|
d8ba83e7f1 | ||
|
|
98f1cc4277 | ||
|
|
6d30154fce | ||
|
|
fc0dac1981 | ||
|
|
1cac465520 | ||
|
|
d01504cc73 | ||
|
|
6b27336c5c | ||
|
|
cc2f9342b3 | ||
|
|
198defa47b | ||
|
|
b6e4127183 | ||
|
|
abe67a2694 | ||
|
|
5a08428129 | ||
|
|
809c9e31f9 | ||
|
|
07d3e2753a | ||
|
|
f82005c573 | ||
|
|
9a2a6afadb | ||
|
|
bad410fd78 | ||
|
|
bb67f12a89 | ||
|
|
977d1da3b0 | ||
|
|
3e80669e23 | ||
|
|
a1f23efb42 | ||
|
|
4f065454c0 | ||
|
|
c31128939e | ||
|
|
a27c8dc44b | ||
|
|
3d67b0e07d | ||
|
|
d01de7337f | ||
|
|
6e1ca7987e | ||
|
|
717cf66b90 | ||
|
|
c84cc4655f | ||
|
|
28ff813c49 | ||
|
|
7cef8a3854 | ||
|
|
73a18ed957 | ||
|
|
69a7938955 | ||
|
|
808aaea818 | ||
|
|
57d8928966 | ||
|
|
e46655654b | ||
|
|
0c1508f761 | ||
|
|
a2ee25b559 | ||
|
|
a3968bf9f7 | ||
|
|
799a626005 | ||
|
|
154d398724 | ||
|
|
a7005b91da | ||
|
|
6b508d472f | ||
|
|
9266b43c7a | ||
|
|
5721a7eecb | ||
|
|
7f2f09137c | ||
|
|
3b80fe6cdf | ||
|
|
b995c8c564 | ||
|
|
f13517d75b | ||
|
|
06945b7a3c | ||
|
|
8beaf37f5a | ||
|
|
8cb9f006de | ||
|
|
0f2c818cb2 | ||
|
|
fcfedce514 | ||
|
|
c2541eb2ac | ||
|
|
8262784152 | ||
|
|
13bfba8f22 | ||
|
|
e708a779c7 | ||
|
|
f93cca8043 | ||
|
|
e3133749f7 | ||
|
|
0e870dbb1b | ||
|
|
8911232658 | ||
|
|
b8abcd1848 | ||
|
|
ea69588488 | ||
|
|
f32243a402 | ||
|
|
80a5d4eea5 | ||
|
|
40a663ad21 | ||
|
|
6aa42d4ee0 | ||
|
|
f57289cc46 | ||
|
|
f84e74f230 | ||
|
|
55ed97bfeb | ||
|
|
e4320d9a16 | ||
|
|
01e43a9223 | ||
|
|
f204afdc6d | ||
|
|
5b9ffe320a | ||
|
|
e291c110fe | ||
|
|
3b83839f6c | ||
|
|
26f6e97ad8 | ||
|
|
5381789bfd | ||
|
|
50da27c698 | ||
|
|
7d7a6f83a1 | ||
|
|
1bd865dd56 | ||
|
|
ffe43387e3 | ||
|
|
2359da9ed5 | ||
|
|
41cd3c0add | ||
|
|
494c0625a7 | ||
|
|
0a2e215846 | ||
|
|
a94ff3e660 | ||
|
|
c98bf61041 | ||
|
|
e639a988fd | ||
|
|
f86c283f9e | ||
|
|
23f7074913 | ||
|
|
19913013b8 | ||
|
|
e50ef300e4 | ||
|
|
4298eb0ee2 | ||
|
|
b2e0012d6e | ||
|
|
c4700a6f77 | ||
|
|
6755117bff | ||
|
|
9d8c5dadfc | ||
|
|
ee914305f7 | ||
|
|
883b85db9f | ||
|
|
977f0bf8e0 | ||
|
|
c4c05ffc0b | ||
|
|
07ef916183 | ||
|
|
7e240dee16 | ||
|
|
45e454c467 | ||
|
|
42f0d942d0 | ||
|
|
d525e3c87c | ||
|
|
74b0f5cf2b | ||
|
|
ac49523bfa | ||
|
|
eb702a29b5 | ||
|
|
59f739a60f | ||
|
|
7c8cc19c0e | ||
|
|
c0bf16f5f8 | ||
|
|
f9038a69ed | ||
|
|
dceaeadcdd | ||
|
|
23e71ddcb3 | ||
|
|
080417fc64 | ||
|
|
24d61c6dd0 | ||
|
|
dfceb60f4d | ||
|
|
369e810c48 | ||
|
|
cf56d681b3 | ||
|
|
7d9604a067 | ||
|
|
00725b6f9c | ||
|
|
ff3341c506 | ||
|
|
2a5dfcc617 | ||
|
|
a57e9245a7 | ||
|
|
48cfd57476 | ||
|
|
16afffad83 | ||
|
|
914aaa0bce | ||
|
|
237f751824 | ||
|
|
d76a58732e | ||
|
|
c58188f52c | ||
|
|
f61120230c | ||
|
|
1c7bd5fcb6 | ||
|
|
d39d6051aa | ||
|
|
16d1d0af57 | ||
|
|
64871f5e72 | ||
|
|
27aeadb946 | ||
|
|
21b5b58951 | ||
|
|
cf13342286 | ||
|
|
42565a61bb | ||
|
|
0c8b337acf | ||
|
|
baa9efb97e | ||
|
|
f8df39a62a | ||
|
|
e4258e1a2a | ||
|
|
8e77d5cdcc | ||
|
|
3aff551a85 | ||
|
|
2eaac6ce00 | ||
|
|
8d3c5b2925 | ||
|
|
d6e80c2689 | ||
|
|
c7210b89cb | ||
|
|
adf34a4c9a | ||
|
|
41223b02e3 | ||
|
|
7e3e6f82f4 | ||
|
|
f29728d915 | ||
|
|
96f2cf8468 | ||
|
|
87dfc30866 | ||
|
|
a4508e0d88 | ||
|
|
398dedb2ae | ||
|
|
b9db443fe4 | ||
|
|
4da95c4c15 | ||
|
|
75844f00b4 | ||
|
|
e5ab4cf3b9 | ||
|
|
484f49346c | ||
|
|
7cd02f2c9a | ||
|
|
b965eb49c9 | ||
|
|
b21281678b | ||
|
|
d5046d9173 | ||
|
|
315bd4ea96 | ||
|
|
9708d78ff2 | ||
|
|
6dd8749cc4 | ||
|
|
bb222f4858 | ||
|
|
5bfb150b89 | ||
|
|
86846c6631 | ||
|
|
5c0fd897ac | ||
|
|
61ebf7207e | ||
|
|
57fcfc7377 | ||
|
|
6e35f9393d | ||
|
|
57e83f8d46 | ||
|
|
7e20dba636 | ||
|
|
ce4c61671e | ||
|
|
06a330d25e | ||
|
|
eb2a614094 | ||
|
|
a27cf3a765 | ||
|
|
26a77642a1 | ||
|
|
3191c9176e | ||
|
|
8866af8c30 | ||
|
|
a06c120b28 | ||
|
|
d5361c085d | ||
|
|
7d7573f58f | ||
|
|
55bb2f377e | ||
|
|
738a25fcc4 | ||
|
|
538888df75 | ||
|
|
a2c767181d | ||
|
|
63f954d8e7 | ||
|
|
fea67deaa3 | ||
|
|
e969d5471a | ||
|
|
b8987b279e | ||
|
|
890e0c5361 | ||
|
|
8fb9364c3a | ||
|
|
c6019e181f | ||
|
|
58a00d7eb6 | ||
|
|
c05cb798f2 | ||
|
|
fed5e453a8 | ||
|
|
68a3478301 | ||
|
|
e3704dd8e7 | ||
|
|
fb76dd7b6a | ||
|
|
9fddc7ff34 | ||
|
|
605b496a01 | ||
|
|
d1c6b957f7 | ||
|
|
5953a74e8d | ||
|
|
85b16aa6b9 | ||
|
|
e1253cadae | ||
|
|
acef51a73e | ||
|
|
82649cbfc2 | ||
|
|
c7a2c5347e | ||
|
|
eca57f9276 | ||
|
|
1ca392fb40 | ||
|
|
0cfec775b1 | ||
|
|
0d3d9472c7 | ||
|
|
9360f17c23 | ||
|
|
c0369461fc | ||
|
|
3bef8009de | ||
|
|
7c0d78ee3b | ||
|
|
9439055ebb | ||
|
|
74e3896af8 | ||
|
|
688cc2924c | ||
|
|
7049d8f0b9 | ||
|
|
ff47789095 | ||
|
|
f58079b62b | ||
|
|
08efebc87c | ||
|
|
d953ab89f4 | ||
|
|
d171c61bcc | ||
|
|
cce63f02b5 | ||
|
|
655c7babdb | ||
|
|
22703796d6 | ||
|
|
f8aeb97d6c | ||
|
|
8739d2ae42 | ||
|
|
f27fe16d8f | ||
|
|
8453db60fb | ||
|
|
18267465df | ||
|
|
a8fa434e23 | ||
|
|
11f4c3defb | ||
|
|
9e7db72b70 | ||
|
|
0c856bad5a | ||
|
|
e24b8963e6 | ||
|
|
c4ea44d025 | ||
|
|
c80b248ad4 | ||
|
|
c44f8a7ab1 | ||
|
|
02f0458e09 | ||
|
|
ff7fd622a8 | ||
|
|
1d066654b8 | ||
|
|
0b9167413e | ||
|
|
0f02624dbc | ||
|
|
f9728f1847 | ||
|
|
63488d82eb | ||
|
|
819d2e6ea0 | ||
|
|
2878b9df60 | ||
|
|
f08c695702 | ||
|
|
5d8d5b0c27 | ||
|
|
686a95514e | ||
|
|
a3eea0d968 | ||
|
|
4be969d189 | ||
|
|
5123dc58c8 | ||
|
|
12f2ea38b0 | ||
|
|
baf4a663df | ||
|
|
8fce5cd523 | ||
|
|
7eb97c0103 | ||
|
|
33e83626bf | ||
|
|
38d6b07318 | ||
|
|
e5b95f10a2 | ||
|
|
44ce8e8d09 | ||
|
|
008422434b | ||
|
|
3e3dfd5636 | ||
|
|
5838a98d2f | ||
|
|
f6be32d492 | ||
|
|
8c7fee9f6d | ||
|
|
e9a245b144 | ||
|
|
2eb2514e74 | ||
|
|
7e69bf8ac3 | ||
|
|
53dbe582ac | ||
|
|
30e94a8964 | ||
|
|
c89bfcd4f9 | ||
|
|
ed3bbc5032 | ||
|
|
855618d926 | ||
|
|
26fcdc4b6e | ||
|
|
7e9b5582c0 | ||
|
|
a850aba829 | ||
|
|
e672acac43 | ||
|
|
0e39061f1b | ||
|
|
c0ed81298d | ||
|
|
dffed32515 | ||
|
|
a679908c6b | ||
|
|
63fac649d9 | ||
|
|
78a78340a0 | ||
|
|
1f1f3efb43 | ||
|
|
4bc8ea068e | ||
|
|
a85b73b935 | ||
|
|
e4b01de2b5 | ||
|
|
b21be7d2c9 | ||
|
|
d410c4699b | ||
|
|
e7b259f764 | ||
|
|
0405098dcc | ||
|
|
6aa7b2afe7 | ||
|
|
255edefa03 | ||
|
|
32e8538e88 | ||
|
|
5bb697bb85 | ||
|
|
46d8e1ecf5 | ||
|
|
53e4615a7a | ||
|
|
7b9eae280d | ||
|
|
e5934f9e85 | ||
|
|
26fae1dd06 | ||
|
|
c0aaba73de | ||
|
|
e849d230e7 | ||
|
|
2c23822ace | ||
|
|
342e44a20f | ||
|
|
121891f825 | ||
|
|
1da64adca1 | ||
|
|
96c8fecb9d | ||
|
|
178858d67c | ||
|
|
18b2899b25 | ||
|
|
8f6c324267 | ||
|
|
9fad4423f4 | ||
|
|
f82f03a06f | ||
|
|
73b82b12a7 | ||
|
|
f49c9ea60d | ||
|
|
f0c53c54b7 | ||
|
|
ab2547cfa0 | ||
|
|
496512f84c | ||
|
|
b954427f1a | ||
|
|
aaebe5e65f | ||
|
|
7fc6cefba1 | ||
|
|
a30ed090c4 | ||
|
|
d60d22d0ec | ||
|
|
890cf02a5e | ||
|
|
af68656310 | ||
|
|
21203685ba | ||
|
|
9caca125d0 | ||
|
|
cb182ac1b2 | ||
|
|
58bebf1b33 | ||
|
|
0ee1d67122 | ||
|
|
e44da5e088 | ||
|
|
18e2801bf8 | ||
|
|
c0dc7fec56 | ||
|
|
2ceba8a949 | ||
|
|
a4b9a1f415 | ||
|
|
3bb7e12a1c | ||
|
|
1704c31a85 | ||
|
|
65cf34b3e0 | ||
|
|
c17e9238a7 | ||
|
|
e56d281631 | ||
|
|
0361dbe153 | ||
|
|
25f0ed2b29 | ||
|
|
ddae09fe85 | ||
|
|
ac9b1369da | ||
|
|
7422c27c7d | ||
|
|
ccc133046c | ||
|
|
bf0e4abece | ||
|
|
e7abd2bebe | ||
|
|
a18ec8a20f | ||
|
|
4b58daac14 | ||
|
|
e76c1ecaf1 | ||
|
|
df4a4b1dce | ||
|
|
028f155268 | ||
|
|
0ccf272a1a | ||
|
|
b2442b3702 | ||
|
|
47d50d0500 | ||
|
|
7c69006e60 | ||
|
|
38bd00212e | ||
|
|
f3a1c5f569 | ||
|
|
6961907eec | ||
|
|
5b66cdd8d4 | ||
|
|
06d5a08dd0 | ||
|
|
6cf14a099b | ||
|
|
93cee61bfb | ||
|
|
82c8262a2c | ||
|
|
b455c5aa2c | ||
|
|
df352a53bc | ||
|
|
2546d38fce | ||
|
|
68984fa817 | ||
|
|
804ff2d2c7 | ||
|
|
da5db665eb | ||
|
|
a457a69c21 | ||
|
|
0d4fb97292 | ||
|
|
b39fa254b9 | ||
|
|
8e7996424c | ||
|
|
2b10984921 | ||
|
|
ba6cb75501 | ||
|
|
d792a73dcf | ||
|
|
dc3477b4cd | ||
|
|
45655ed3cc | ||
|
|
e0a0048d39 | ||
|
|
39c842c026 | ||
|
|
d4e3b4d40f | ||
|
|
7aac2586ef | ||
|
|
fe56d63258 | ||
|
|
79d05914bd | ||
|
|
cb336f47fe | ||
|
|
c95d54c2e9 | ||
|
|
0b47b11601 | ||
|
|
eed3895f1b | ||
|
|
fd6c4d0bf6 | ||
|
|
e50d5a4a88 | ||
|
|
7770fb81d1 | ||
|
|
bd4fcaf2f9 | ||
|
|
297e174a84 | ||
|
|
71c704588d | ||
|
|
26b2ac1830 | ||
|
|
266988327f | ||
|
|
58b55bf2ba | ||
|
|
df0f9e01bc | ||
|
|
d6cc83a8ff | ||
|
|
44f23d804d | ||
|
|
cc06e52488 | ||
|
|
a6fe077d88 | ||
|
|
df39ea19e7 | ||
|
|
2f455d03f5 | ||
|
|
98c1de387d | ||
|
|
53d185bf9c | ||
|
|
0ef3987956 | ||
|
|
a7ce1d1468 | ||
|
|
9cc2942a49 | ||
|
|
4ffe34c538 | ||
|
|
22b12073f9 | ||
|
|
83489fff84 | ||
|
|
2511dd5b4e | ||
|
|
767bd08f0c | ||
|
|
d2ba2ff544 | ||
|
|
f6ad1226f5 | ||
|
|
785b0d7395 | ||
|
|
ea91a55bd6 | ||
|
|
c73f13baa8 | ||
|
|
a44c68ef46 | ||
|
|
218627e62d | ||
|
|
5d6e775b02 | ||
|
|
2b86cff3f3 | ||
|
|
0bc70dbc9e | ||
|
|
d1f7baad2b | ||
|
|
6d57bc2c5a | ||
|
|
8e3cf93011 | ||
|
|
51f6d17fbb | ||
|
|
b2e5229c78 | ||
|
|
87ec87e09d | ||
|
|
25bf7ac915 | ||
|
|
bcadad5564 | ||
|
|
bacda4624f | ||
|
|
9c5434e32c | ||
|
|
058421b9aa | ||
|
|
f947f50213 | ||
|
|
54688e6a71 | ||
|
|
d789bd7710 | ||
|
|
b3fa69f7c7 | ||
|
|
3ec47b2310 | ||
|
|
9bc37d8ecd | ||
|
|
dd21c46331 | ||
|
|
3c69cf7d5a | ||
|
|
97f8a0faf6 | ||
|
|
ce8b6dc995 | ||
|
|
d78eebdb0e | ||
|
|
01f78e0595 | ||
|
|
fc8e710a07 | ||
|
|
929131f5e6 | ||
|
|
4fbb0e454e | ||
|
|
3832a8811d | ||
|
|
8f6461e80f | ||
|
|
e21e12820b | ||
|
|
1edff1048f | ||
|
|
8e71e6b0ea | ||
|
|
1efd3dbea9 | ||
|
|
d02ad5a5b6 | ||
|
|
8758047ff0 | ||
|
|
c17df3c2b6 | ||
|
|
fe54d6537f | ||
|
|
dcc3516587 | ||
|
|
7039296c64 | ||
|
|
cdba36628f | ||
|
|
b2465f9bdc | ||
|
|
4b4a398c99 | ||
|
|
d292bd033f | ||
|
|
faa5683b26 | ||
|
|
ce0ae1bf04 | ||
|
|
b8f49b9312 | ||
|
|
7b9bcdfd10 | ||
|
|
72d65a18ef | ||
|
|
160a008a0b | ||
|
|
030f5ed315 | ||
|
|
018051572c | ||
|
|
fc2d1ffaca | ||
|
|
418eb81e1c | ||
|
|
5c2c576cb4 | ||
|
|
9cf11bf2d9 | ||
|
|
a050d220d0 | ||
|
|
5e64d55ac0 | ||
|
|
d32a657d62 | ||
|
|
78e5e298b3 | ||
|
|
44ad0f3a51 | ||
|
|
e783cc64f5 | ||
|
|
c1f10a1c46 | ||
|
|
a0231ed598 | ||
|
|
900ba2f3a2 | ||
|
|
519e224914 | ||
|
|
2e067f98ff | ||
|
|
63ca296037 | ||
|
|
2221053c66 | ||
|
|
5ce6354bd3 | ||
|
|
44ae334492 | ||
|
|
4020de976f | ||
|
|
9929a5f578 | ||
|
|
3dd1e916c1 | ||
|
|
5b810e8cf5 | ||
|
|
a9356fbd4a | ||
|
|
6aad9e6130 | ||
|
|
258b4113db | ||
|
|
66d5b7a6f4 | ||
|
|
4c4df5011e | ||
|
|
2b99b888bc | ||
|
|
618e1f64df | ||
|
|
fca00e7084 | ||
|
|
7d92df6626 | ||
|
|
f00b552305 | ||
|
|
d7b5f5f25b | ||
|
|
a4c3088e02 | ||
|
|
0e44df8cfa | ||
|
|
eddc2214ed | ||
|
|
db3f87d219 | ||
|
|
d962124da2 | ||
|
|
e1c8afd5ba | ||
|
|
01614667a4 | ||
|
|
43bd4ff468 | ||
|
|
d5bdb78de6 | ||
|
|
acd5e547fd | ||
|
|
201eb38a35 | ||
|
|
9b13c2554a | ||
|
|
21918cadd9 | ||
|
|
55191a6e9b | ||
|
|
e93a30d2e1 | ||
|
|
443fce9fd1 | ||
|
|
02f4a730c7 | ||
|
|
f02626a118 | ||
|
|
7632219a83 | ||
|
|
4e563b3c05 | ||
|
|
92a60bd03e | ||
|
|
1238ef62fa | ||
|
|
0a101e3b69 | ||
|
|
ecd2f0e0f2 | ||
|
|
9be3cc1f13 | ||
|
|
c64c503ba9 | ||
|
|
d704b2ecac | ||
|
|
66539aa422 | ||
|
|
f8c20e55a4 | ||
|
|
3b5448a334 | ||
|
|
4b1614e247 | ||
|
|
bb4f843a33 | ||
|
|
9008039a2a | ||
|
|
fb1ee381fe | ||
|
|
8ad0ce8911 | ||
|
|
59c4b6db86 | ||
|
|
f4e15215ec | ||
|
|
b57cc559d9 | ||
|
|
4a1d6047ac | ||
|
|
3d411826f2 | ||
|
|
898c0a2f7c | ||
|
|
2a9bc2278d | ||
|
|
989209758a | ||
|
|
1d4386e9e9 | ||
|
|
d8e45814bb | ||
|
|
ce01f8bce0 | ||
|
|
76384e38c8 | ||
|
|
ac64512ff7 | ||
|
|
7d9261dace | ||
|
|
6187b849a7 | ||
|
|
724d0b2567 | ||
|
|
2d24e2e0eb | ||
|
|
d4d65044b3 | ||
|
|
717f1e7eec | ||
|
|
30b58d213e | ||
|
|
0404b5f1e4 | ||
|
|
f2a946790d | ||
|
|
37ea2260b8 | ||
|
|
938ad5f8b9 | ||
|
|
4e881415d2 | ||
|
|
96af470708 | ||
|
|
3275abafb0 | ||
|
|
2538a41fcb | ||
|
|
d93b76a664 | ||
|
|
ea5703d99d | ||
|
|
22c3c797ff | ||
|
|
0cc0ec6c55 | ||
|
|
d1e5fdc1e0 | ||
|
|
ed9c5a0dc7 | ||
|
|
e1fae2e219 | ||
|
|
e5cf24e1cc | ||
|
|
3d1f970262 | ||
|
|
a002655b3d | ||
|
|
9fe2384b47 | ||
|
|
a6781414a5 | ||
|
|
cbdbac4e01 | ||
|
|
c867e62502 | ||
|
|
341fe5d46e | ||
|
|
e70eefdbbf | ||
|
|
67c2daf4ef | ||
|
|
178cfd4a6b | ||
|
|
6d868e21da | ||
|
|
691a6d0956 | ||
|
|
6eda60d314 | ||
|
|
41b0896c2e | ||
|
|
e8bdeed556 | ||
|
|
c29dd321e3 | ||
|
|
6062534d91 | ||
|
|
d6bb61c428 | ||
|
|
65a56684ec | ||
|
|
e1dfe30661 | ||
|
|
87cb1cc4f7 | ||
|
|
0be75ca38e | ||
|
|
54b2561da1 | ||
|
|
fbba7a30d0 | ||
|
|
42d361b7ad | ||
|
|
6d41e6b962 | ||
|
|
ed32aad64a | ||
|
|
e4896fe935 | ||
|
|
2f81690783 | ||
|
|
dc3ec24cd7 | ||
|
|
797171fc3a | ||
|
|
16e12a8a7b | ||
|
|
0e07124cbc | ||
|
|
5db66e3fcb | ||
|
|
01770d25f8 | ||
|
|
e4366c8dfa | ||
|
|
f19446eda9 | ||
|
|
0d7f1657de | ||
|
|
e905f01305 | ||
|
|
e5e7fa3f8a | ||
|
|
69575622a2 | ||
|
|
ee229e9ed8 | ||
|
|
21a9481459 | ||
|
|
f28732904d | ||
|
|
06828e2cfd | ||
|
|
e89c58e2e9 | ||
|
|
cb1203a2b8 | ||
|
|
be64aba265 | ||
|
|
316281d42b | ||
|
|
91520753b0 | ||
|
|
94bb94e8f4 | ||
|
|
fcf020658a | ||
|
|
4d8cd8a926 | ||
|
|
ec871c1a41 | ||
|
|
acf7fe3117 | ||
|
|
55f99980cc | ||
|
|
7079cc0d20 | ||
|
|
dfb68c512b | ||
|
|
85ff73c055 | ||
|
|
96bb31de5f | ||
|
|
b06f13e32a | ||
|
|
512282c446 | ||
|
|
0986583365 | ||
|
|
7e5c07c6e5 | ||
|
|
99d14139d2 | ||
|
|
b5419ee747 | ||
|
|
22e9f3308b | ||
|
|
d1161207da | ||
|
|
f8aad34bb1 | ||
|
|
8b9686edba | ||
|
|
e0e8a66d48 | ||
|
|
6d029505ed | ||
|
|
e5c7c4b02f | ||
|
|
a17bbc4d15 | ||
|
|
bc5c9df40d | ||
|
|
59c5dfe703 | ||
|
|
953e2e6a73 | ||
|
|
f16b3cb2dd | ||
|
|
b8d5978668 | ||
|
|
6dc2d5092d | ||
|
|
45227c6628 | ||
|
|
76f88ffd66 | ||
|
|
5a6864f26e | ||
|
|
8eca4c8054 | ||
|
|
1a12ce8992 | ||
|
|
db8d6ce4ab | ||
|
|
9874c5f417 | ||
|
|
9f2e4277bb | ||
|
|
2d933416f8 | ||
|
|
6886f3081f | ||
|
|
3edbbda7fa | ||
|
|
7e7160e0bf | ||
|
|
a9f92dc9f3 | ||
|
|
57d6da52bb | ||
|
|
72dc85d90d | ||
|
|
ff5ff54e88 | ||
|
|
6ab0fa3fbd | ||
|
|
4d4c02ebb3 | ||
|
|
a34542171c | ||
|
|
fce6138031 | ||
|
|
38c8197989 | ||
|
|
b7f4578c34 | ||
|
|
e5ea72e334 | ||
|
|
73a0f610c3 | ||
|
|
ee2d884434 | ||
|
|
a9ac568dc0 | ||
|
|
365dac5c2b | ||
|
|
511069b641 | ||
|
|
342d3bbd49 | ||
|
|
28da67c0c5 | ||
|
|
47c9de2e22 | ||
|
|
9e6f8c16a5 | ||
|
|
1f8d3aa7f4 | ||
|
|
209c16eff8 | ||
|
|
49b0135798 | ||
|
|
1d55dad4b1 | ||
|
|
f4e9434294 | ||
|
|
97cf766466 | ||
|
|
debcd3dc0d | ||
|
|
495d7ba9aa | ||
|
|
aac405a99d | ||
|
|
22fed1dfba | ||
|
|
ce79a638f5 | ||
|
|
ae420910e9 | ||
|
|
8068d4e591 | ||
|
|
cd3a7375e6 | ||
|
|
be9f60abf3 | ||
|
|
6431afec31 | ||
|
|
43c744df59 | ||
|
|
116d7b4365 | ||
|
|
a4fd51445d | ||
|
|
66393224ba | ||
|
|
8faceb1376 | ||
|
|
3ce0379c99 | ||
|
|
24a8dfdfdb | ||
|
|
3952044a82 | ||
|
|
a11128ae9f | ||
|
|
ca802a7254 | ||
|
|
ae7d4770fa | ||
|
|
430c00c689 | ||
|
|
302f7018d4 | ||
|
|
804445637d | ||
|
|
e9d1718793 | ||
|
|
d9953ff0ac | ||
|
|
5433bf013d | ||
|
|
244cbb39ca | ||
|
|
a0eac8e0dc | ||
|
|
f8c473d039 | ||
|
|
fc259f5c20 | ||
|
|
a3a6ec4838 | ||
|
|
c32a8c1614 | ||
|
|
50f4e623f7 | ||
|
|
b7de63c5f5 | ||
|
|
4ed41cf06a | ||
|
|
d613ceb3eb | ||
|
|
91ee57997b | ||
|
|
50a7a94dac | ||
|
|
61c5d7e637 | ||
|
|
816ba32bb0 | ||
|
|
3c6937ea94 | ||
|
|
bf897eb02a | ||
|
|
254bb9e0a9 | ||
|
|
2cdd9a7fc0 | ||
|
|
774abcc612 | ||
|
|
bbea1fba25 | ||
|
|
1c68b02343 | ||
|
|
1f2054beb7 | ||
|
|
2385942f7c | ||
|
|
36bef805ea | ||
|
|
ed1f88db9d | ||
|
|
c4390a0bb0 | ||
|
|
301e761897 | ||
|
|
72173298e3 | ||
|
|
c4265edaf4 | ||
|
|
93f9bde307 | ||
|
|
0bf8f978f9 | ||
|
|
a6522bbcf2 | ||
|
|
44aeddcdbd | ||
|
|
c6a630875f | ||
|
|
b18fa8fa76 | ||
|
|
689808a9db | ||
|
|
8bb45d6f43 | ||
|
|
cd1ff1699b | ||
|
|
4ca13ca884 | ||
|
|
b05138630a | ||
|
|
9394ac47f4 | ||
|
|
c43716b0d9 | ||
|
|
e8b54ddc1f | ||
|
|
37d756b1b0 | ||
|
|
e3140aaab8 | ||
|
|
238aa2e509 | ||
|
|
54c6d4fb96 | ||
|
|
969b921fae | ||
|
|
40add5ca44 | ||
|
|
06db4610ca | ||
|
|
e4334f256a | ||
|
|
4286baec0f | ||
|
|
cd1df94235 | ||
|
|
649b73f0e1 | ||
|
|
e967d4b344 | ||
|
|
e5a71b8044 | ||
|
|
9886957034 | ||
|
|
3e7d155002 | ||
|
|
b96add529c | ||
|
|
e91db6aeec | ||
|
|
1b25283284 | ||
|
|
2bc61baac3 | ||
|
|
f862f1a2c4 | ||
|
|
3074169446 | ||
|
|
afee575f10 | ||
|
|
94a53a278c | ||
|
|
4a3ac7176f | ||
|
|
305c5bad68 | ||
|
|
4570da2e26 | ||
|
|
7ec0487e37 | ||
|
|
3aa3dccda7 | ||
|
|
21efa7f639 | ||
|
|
614fd1a85d | ||
|
|
05bd6a5d48 | ||
|
|
208fa85e8f | ||
|
|
2b703cac3a | ||
|
|
ce59872a66 | ||
|
|
c402c416e5 | ||
|
|
466b4314c5 | ||
|
|
40f8aac655 | ||
|
|
2afd1ab938 | ||
|
|
722fcaa2e1 | ||
|
|
5d3830d3de | ||
|
|
da244fe836 | ||
|
|
c00ff163f1 | ||
|
|
6bb18f18fd | ||
|
|
b8ea8f8a97 | ||
|
|
f9c01d03d3 | ||
|
|
f05dcf99ff | ||
|
|
a8edec2184 | ||
|
|
de3db81222 | ||
|
|
e0a49bc71e | ||
|
|
031e65efa7 | ||
|
|
3a1978e2fe | ||
|
|
edffa28d89 | ||
|
|
2c60fadb99 | ||
|
|
adfd9f63fa | ||
|
|
22f3f245b5 | ||
|
|
53e832fbf0 | ||
|
|
884c180e43 | ||
|
|
a1d5c0252d | ||
|
|
94a40f4b79 | ||
|
|
523467f47e | ||
|
|
eea48c257f | ||
|
|
d9e6718217 | ||
|
|
84afda69c0 | ||
|
|
820a1c1604 | ||
|
|
941d79410b | ||
|
|
78d2b8a68b | ||
|
|
0d147c7585 | ||
|
|
2f23d6a67d | ||
|
|
df6c5fee9c | ||
|
|
41f6bc588c | ||
|
|
660eedb917 | ||
|
|
37cc205dfa | ||
|
|
78beaf0b45 | ||
|
|
8c5cb09e70 | ||
|
|
3163e53502 | ||
|
|
1a58cdb08f | ||
|
|
183ebfad96 | ||
|
|
49e7e14422 | ||
|
|
6576099748 | ||
|
|
5d6bc5bc55 | ||
|
|
edf0c62ce3 | ||
|
|
0a9f3efecc | ||
|
|
a7bc98d1e3 | ||
|
|
ac03bc50b7 | ||
|
|
61a8d07912 | ||
|
|
2d744f1080 | ||
|
|
2582fe20db | ||
|
|
6a322ef9d5 | ||
|
|
bbcbd55e5a | ||
|
|
99f6e1b346 | ||
|
|
a7c564ee20 | ||
|
|
bf9b6f634c | ||
|
|
1698c9311b | ||
|
|
6b7c2c6f01 | ||
|
|
54e52758e5 | ||
|
|
4f9a85b3a7 | ||
|
|
7fe63a2cfe | ||
|
|
a7c418ac79 | ||
|
|
3a51ce523c | ||
|
|
ff0f8bd86e | ||
|
|
12d4b1448d | ||
|
|
3e552f19b5 | ||
|
|
13e2ce88d5 | ||
|
|
276814f2d8 | ||
|
|
4263c59117 | ||
|
|
786e1441d7 | ||
|
|
4100673705 | ||
|
|
759d4475fc | ||
|
|
2e986a4bdd | ||
|
|
005afae9aa | ||
|
|
739cdea934 | ||
|
|
894e774523 | ||
|
|
397a3f5d80 | ||
|
|
c30c6a4cfb | ||
|
|
01134c94b8 | ||
|
|
0cd963a9cf | ||
|
|
1db452829d | ||
|
|
9e7d0d7429 | ||
|
|
de891f783f | ||
|
|
dd4922ea77 | ||
|
|
e0ac7022fc | ||
|
|
0809c80965 | ||
|
|
1e5e8ef5fc | ||
|
|
bc44ee346a | ||
|
|
2ed9a55c9b | ||
|
|
88196afc14 | ||
|
|
60605eb347 | ||
|
|
d361621dc3 | ||
|
|
1e99b7ffa2 | ||
|
|
143e02d16c | ||
|
|
92cb18389e | ||
|
|
ce4383505c | ||
|
|
6e0ab7f24d | ||
|
|
64461d649b | ||
|
|
19acd42336 | ||
|
|
5d050348ea | ||
|
|
6d34e8e9bb | ||
|
|
5a80658ec5 | ||
|
|
be3ba2219a | ||
|
|
00026ee452 | ||
|
|
706e381f02 | ||
|
|
d06655316b | ||
|
|
17308694d8 | ||
|
|
ef7dfa7709 | ||
|
|
871ece2514 | ||
|
|
106ea10d93 | ||
|
|
5398b98195 | ||
|
|
4caef40690 | ||
|
|
a7ab6f11fb | ||
|
|
41b1d68de6 | ||
|
|
c322c8818b | ||
|
|
6a2de3a9a7 | ||
|
|
f40c52d317 | ||
|
|
c2774fc562 | ||
|
|
bad7fd1234 | ||
|
|
3bc2227f70 | ||
|
|
8c2c25ad52 | ||
|
|
0dc4511f0f | ||
|
|
3cdf057be5 | ||
|
|
727e11996c | ||
|
|
7259154e0c | ||
|
|
8e5212b37a | ||
|
|
b09b4929ea | ||
|
|
5629bb63c0 | ||
|
|
2c100466c4 | ||
|
|
a963567d95 | ||
|
|
6f9b1586bf | ||
|
|
3076ed37dd | ||
|
|
38fe1c60f6 | ||
|
|
fc551a7acd | ||
|
|
1e974807e5 | ||
|
|
0dc258ebde | ||
|
|
b26e92ecb7 | ||
|
|
6488a62c45 | ||
|
|
cf3825be83 | ||
|
|
a4bb14d1a0 | ||
|
|
a5e431e31c | ||
|
|
4005f2c6de | ||
|
|
b0b9be552b | ||
|
|
acacdc870a | ||
|
|
66a89572ca | ||
|
|
a12c817821 | ||
|
|
d19912ff5b | ||
|
|
111ede487d | ||
|
|
6f7ca3cb5e | ||
|
|
e6b63095ad | ||
|
|
a40e8e3c57 | ||
|
|
0d129be808 | ||
|
|
d70ab1beab | ||
|
|
5666cbd9c0 | ||
|
|
b0d4eb7c3f | ||
|
|
ed5773883b | ||
|
|
fc668cb395 | ||
|
|
d7746c48f2 | ||
|
|
b5cb340a43 | ||
|
|
44089e28f7 | ||
|
|
1c6fb7f1ac | ||
|
|
4198b31cd7 | ||
|
|
638364d3d2 | ||
|
|
e7195e40b0 | ||
|
|
1e084234fd | ||
|
|
833813098d | ||
|
|
df0e27e56a | ||
|
|
5b8fbf8ea6 | ||
|
|
3286d38fc8 | ||
|
|
8838185cbf | ||
|
|
0be5bfd050 | ||
|
|
154b265e1f | ||
|
|
7a9e965cc2 | ||
|
|
e05a770f51 | ||
|
|
fd5e5ff7d4 | ||
|
|
53e73cc42a | ||
|
|
c419eef405 | ||
|
|
0c24ac38be | ||
|
|
67535d8691 | ||
|
|
1205d5d113 | ||
|
|
7be63d8e99 | ||
|
|
695cf18449 | ||
|
|
a117d42f67 | ||
|
|
a98dd3382c | ||
|
|
34e0755b78 | ||
|
|
975f8d81ce | ||
|
|
769b6465d1 | ||
|
|
12d3a781ff | ||
|
|
0bab251f3d | ||
|
|
8eb0d43287 | ||
|
|
44e69429ad | ||
|
|
d787de821a | ||
|
|
7c616718fd | ||
|
|
2edfab25d7 | ||
|
|
584fc6088e | ||
|
|
99254b3315 | ||
|
|
e5f3d86b9f | ||
|
|
0322aeada2 | ||
|
|
6d9ca8a820 | ||
|
|
c57f647ddd | ||
|
|
1f5669532f | ||
|
|
055d6a6790 | ||
|
|
9eb03f2c16 | ||
|
|
f6569f5091 | ||
|
|
cb0a632944 | ||
|
|
576c3cc7d2 | ||
|
|
8490e93b99 | ||
|
|
31d318b7d2 | ||
|
|
cd50872acb | ||
|
|
ff7ff8ee58 | ||
|
|
14d29dc167 | ||
|
|
99aa8579cd | ||
|
|
5df7be8fef | ||
|
|
4fbd56cf08 | ||
|
|
01f9ccdb4d | ||
|
|
c6749105e0 | ||
|
|
7a582a6576 | ||
|
|
dfab4af72c | ||
|
|
a200246843 | ||
|
|
89a6c467d4 | ||
|
|
a4bc108cdc | ||
|
|
d0f5b803fa | ||
|
|
10f1acc0ac | ||
|
|
10da4bc4e6 | ||
|
|
cb6d5865a4 | ||
|
|
fe19187dad | ||
|
|
024e9b247e | ||
|
|
d058940728 | ||
|
|
5ca16e8f1c | ||
|
|
d52ddfed36 | ||
|
|
b1ba8e4d6e | ||
|
|
2e9c5d3bac | ||
|
|
ac79d8482f | ||
|
|
597594b5bc | ||
|
|
7736259f70 | ||
|
|
f3558f4aa7 | ||
|
|
e6fa3dd823 | ||
|
|
c4b1502385 | ||
|
|
ccb75f4166 | ||
|
|
bc3932d008 | ||
|
|
9e29398b11 | ||
|
|
a0646862fc | ||
|
|
2085b0340f | ||
|
|
1550997d55 | ||
|
|
18f6c24809 | ||
|
|
7c387140d4 | ||
|
|
91765df50d | ||
|
|
ac4cd8f5c6 | ||
|
|
27ef9efb38 | ||
|
|
874dd9ff62 | ||
|
|
a3cce7edcd | ||
|
|
1ac98acefd | ||
|
|
9370a9331b | ||
|
|
cfe2522699 | ||
|
|
ee9bd8dd1a | ||
|
|
532ac4f8a7 | ||
|
|
1859fce603 | ||
|
|
452c1da6b8 | ||
|
|
75569abbe2 | ||
|
|
0ce772a6d6 | ||
|
|
92c28368f6 | ||
|
|
216228d9e4 | ||
|
|
c13145971a | ||
|
|
c78e90588a | ||
|
|
8e3b7b0418 | ||
|
|
f3eac47dc7 | ||
|
|
bf7304df3b | ||
|
|
c5ddf1a1c7 | ||
|
|
de951e3ea0 | ||
|
|
44b7e34d7f | ||
|
|
e025496bbc | ||
|
|
db747ea246 | ||
|
|
84c1110f18 | ||
|
|
73253cda17 | ||
|
|
34f6ac59f6 | ||
|
|
da814b0a5a | ||
|
|
99421e7617 | ||
|
|
c30eb02d91 | ||
|
|
59185b6112 | ||
|
|
79c070ec0d | ||
|
|
ce4b1c8014 | ||
|
|
3d3d44440a | ||
|
|
a91f7126e1 | ||
|
|
d9358b6134 | ||
|
|
fd4cdcaba6 | ||
|
|
13fd612b50 | ||
|
|
905872232d | ||
|
|
409fa79f59 | ||
|
|
82304058b2 | ||
|
|
ec457a0e44 | ||
|
|
f176938831 | ||
|
|
33c5a40e9f | ||
|
|
57874362c3 | ||
|
|
c1581f3845 | ||
|
|
72d510a6c1 | ||
|
|
723233be84 | ||
|
|
ad9a0b2f50 | ||
|
|
b881ea1e03 | ||
|
|
e2fb3802d7 | ||
|
|
eb7674cf04 | ||
|
|
3ee169ba14 | ||
|
|
4a903053de | ||
|
|
961ad02e8a | ||
|
|
472a063979 | ||
|
|
4fd61c97df | ||
|
|
5b81c31ba2 | ||
|
|
ef9507a282 | ||
|
|
4efe137e82 | ||
|
|
cea5a282c2 | ||
|
|
2ad69bf1e9 | ||
|
|
397cfaf23a | ||
|
|
8223c073ef | ||
|
|
b564b5d0f6 | ||
|
|
c744f1ff93 | ||
|
|
39656bec03 | ||
|
|
5b1f7db2c5 | ||
|
|
f914026c14 | ||
|
|
156150bd36 | ||
|
|
99b7e83324 | ||
|
|
4222d98e49 | ||
|
|
cd00881afb | ||
|
|
d1cf1e4243 | ||
|
|
cf5227a81f | ||
|
|
2110fbee8d | ||
|
|
680116082b | ||
|
|
94092eb6af | ||
|
|
1286fb165c | ||
|
|
36a22bd5eb | ||
|
|
15fa4b80b2 | ||
|
|
2c5281a41c | ||
|
|
2e433d73db | ||
|
|
1f915c6c5e | ||
|
|
bec69b3abf | ||
|
|
530e82cc6d | ||
|
|
29765230b1 | ||
|
|
29a556c7d1 | ||
|
|
7f3d4ba8aa | ||
|
|
9c714c9c4e | ||
|
|
91ef9955b4 | ||
|
|
97f0f3784d | ||
|
|
42d903ac16 | ||
|
|
b9111348d3 | ||
|
|
69b7fa79de | ||
|
|
4c67233e83 | ||
|
|
c9d14992b9 | ||
|
|
f06411e018 | ||
|
|
73b9c419ff | ||
|
|
e763d98134 | ||
|
|
0533256b87 | ||
|
|
4d0c874a95 | ||
|
|
ae1608e0bb | ||
|
|
88ae7c8994 | ||
|
|
d697ce8c81 | ||
|
|
a684cf1294 | ||
|
|
b9d4028074 | ||
|
|
916cb7e76f | ||
|
|
6dd61af5dd | ||
|
|
5bee7c0791 | ||
|
|
a4a731f04a | ||
|
|
b15d3c5375 | ||
|
|
99ba73f194 | ||
|
|
1456c15f91 | ||
|
|
8a26580453 | ||
|
|
af393ad0ca | ||
|
|
8a46f3a7d2 | ||
|
|
0aff71496c | ||
|
|
f3a5642bcd | ||
|
|
b97e6e0870 | ||
|
|
3e704c3d22 | ||
|
|
ae02fa80ce | ||
|
|
ec5c1038b3 | ||
|
|
e5e6295a83 | ||
|
|
19560fe60f | ||
|
|
1f1b469a4b | ||
|
|
eb095a7497 | ||
|
|
6415b095ab | ||
|
|
d93c959da8 | ||
|
|
09893e1409 | ||
|
|
93ec6384d0 | ||
|
|
a221bf33b2 | ||
|
|
c7ec3d2197 | ||
|
|
18d04099fd | ||
|
|
fec52ee19f | ||
|
|
d87a22da23 | ||
|
|
4bda273947 | ||
|
|
7c8a2e32b4 | ||
|
|
45e8d0c2b6 | ||
|
|
3fe0b93567 | ||
|
|
06147dd5cc | ||
|
|
44dae3832e | ||
|
|
4f06d804e9 | ||
|
|
fb87b201da | ||
|
|
a58090b334 | ||
|
|
0050bfe36f | ||
|
|
d40233cb1e | ||
|
|
3125f8e7d1 | ||
|
|
0646fb77f2 | ||
|
|
c5a3a17a94 | ||
|
|
e4b1880025 | ||
|
|
c0b74c1605 | ||
|
|
96aed66af2 | ||
|
|
65e23a90a1 | ||
|
|
f3405b5724 | ||
|
|
5bef1e5663 | ||
|
|
5120cee05a | ||
|
|
895e593d02 | ||
|
|
32d8070119 | ||
|
|
46273ecbe5 | ||
|
|
4268099a06 | ||
|
|
921db2f6cb | ||
|
|
f640c45464 | ||
|
|
d4dc7a32a4 | ||
|
|
33e5171742 | ||
|
|
ebb4857782 | ||
|
|
c47d253967 | ||
|
|
c58e7d2ccf | ||
|
|
f3cad86f6f | ||
|
|
a23655de42 | ||
|
|
b7b899ea40 | ||
|
|
89029b1bc3 | ||
|
|
9b4212e00c | ||
|
|
6b158e1e23 | ||
|
|
6cae350ef2 |
1
.devcontainer/Dockerfile
Normal file
1
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1 @@
|
||||
FROM mcr.microsoft.com/devcontainers/base:bullseye
|
||||
24
.devcontainer/devcontainer.json
Normal file
24
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,24 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
|
||||
{
|
||||
"name": "PreMiD",
|
||||
"dockerComposeFile": ["docker-compose.yml"],
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "lts",
|
||||
"nvmVersion": "latest"
|
||||
},
|
||||
"ghcr.io/joshuanianji/devcontainer-features/mount-pnpm-store:1": {},
|
||||
"ghcr.io/dhoeric/features/act:1": {}
|
||||
},
|
||||
"overrideFeatureInstallOrder": ["ghcr.io/devcontainers/features/node:1", "ghcr.io/joshuanianji/devcontainer-features/mount-pnpm-store:1"],
|
||||
"postCreateCommand": "pnpm i --frozen-lockfile",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["Gruntfuggly.todo-tree", "YoavBls.pretty-ts-errors", "EditorConfig.EditorConfig", "DeepScan.vscode-deepscan", "esbenp.prettier-vscode"]
|
||||
}
|
||||
},
|
||||
"shutdownAction": "stopCompose"
|
||||
}
|
||||
32
.devcontainer/docker-compose.yml
Normal file
32
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
# Update this to the name of the service you want to work with in your docker-compose.yml file
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
# Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
|
||||
# folder. Note that the path of the Dockerfile and context is relative to the *primary*
|
||||
# docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
|
||||
# array). The sample below assumes your primary file is in the root of your project.
|
||||
#
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: .devcontainer/Dockerfile
|
||||
|
||||
volumes:
|
||||
# Update this to wherever you want VS Code to mount the folder of your project
|
||||
- ..:/workspaces:cached
|
||||
|
||||
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
|
||||
# cap_add:
|
||||
# - SYS_PTRACE
|
||||
# security_opt:
|
||||
# - seccomp:unconfined
|
||||
|
||||
# Overrides default command so things don't shut down after the process ends.
|
||||
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.vscode
|
||||
.DS_Store
|
||||
.Trashes
|
||||
.nuxt
|
||||
dist
|
||||
node_modules
|
||||
.env
|
||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
dist
|
||||
node_modules
|
||||
coverage
|
||||
12
.eslintrc.cjs
Normal file
12
.eslintrc.cjs
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
extends: ["@recodive/eslint-config"],
|
||||
overrides: [
|
||||
{
|
||||
extends: ["plugin:@typescript-eslint/disable-type-checked"],
|
||||
files: ["./**/*.{cjs,js,jsx}"],
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
};
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
github: Timeraa
|
||||
patreon: Timeraa
|
||||
ko_fi: Timeraa
|
||||
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,17 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for YT Presence
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
36
.github/actions/build-and-push-docker/action.yaml
vendored
Normal file
36
.github/actions/build-and-push-docker/action.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: "Build and Push Docker Image"
|
||||
description: "Builds a Docker image and pushes it to GitHub Container Registry"
|
||||
inputs:
|
||||
app:
|
||||
description: "Name of the app"
|
||||
required: true
|
||||
token:
|
||||
description: "GitHub token"
|
||||
required: true
|
||||
outputs:
|
||||
version:
|
||||
description: "Version of the app"
|
||||
value: ${{ steps.get_version.outputs.version }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get package.json version
|
||||
id: get_version
|
||||
run: echo ::set-output name=version::$(node -p "require('./apps/${{ inputs.app }}/package.json').version")
|
||||
shell: bash
|
||||
|
||||
- name: Convert repository owner to lowercase
|
||||
id: repo
|
||||
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
||||
shell: bash
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
uses: premid/premid/.github/actions/build-docker@monorepo
|
||||
with:
|
||||
dockerfile: ./apps/${{ inputs.app }}/Dockerfile
|
||||
push: true
|
||||
token: ${{ inputs.token }}
|
||||
tags: ghcr.io/${{ steps.repo.outputs.lowercase }}/${{ inputs.app }}:${{ steps.get_version.outputs.version }},ghcr.io/${{ steps.repo.outputs.lowercase }}/${{ inputs.app }}:latest
|
||||
45
.github/actions/build-docker/action.yaml
vendored
Normal file
45
.github/actions/build-docker/action.yaml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: "Build Docker Image"
|
||||
description: "Builds a Docker image using Docker Buildx"
|
||||
inputs:
|
||||
dockerfile:
|
||||
description: "Path to the Dockerfile"
|
||||
required: true
|
||||
tags:
|
||||
description: "Comma-separated list of tags for the Docker image"
|
||||
required: true
|
||||
push:
|
||||
description: "Whether to push the Docker image to the registry"
|
||||
required: false
|
||||
default: "false"
|
||||
token:
|
||||
description: "GitHub Token"
|
||||
required: true
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ inputs.token }}
|
||||
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ inputs.push }}
|
||||
tags: ${{ inputs.tags }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
5
.github/renovate.json
vendored
Normal file
5
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>Recodive/Recodive:renovate-config"],
|
||||
"automerge": false
|
||||
}
|
||||
17
.github/workflows/cd-pd.yaml
vendored
Normal file
17
.github/workflows/cd-pd.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Build pd
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "pd-v*"
|
||||
permissions:
|
||||
packages: write
|
||||
jobs:
|
||||
build:
|
||||
name: Build pd
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build and Push Docker Image
|
||||
uses: premid/premid/.github/actions/build-and-push-docker@monorepo
|
||||
with:
|
||||
app: pd
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
17
.github/workflows/cd-schemas.yaml
vendored
Normal file
17
.github/workflows/cd-schemas.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Build schema-server
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "schema-server-v*"
|
||||
permissions:
|
||||
packages: write
|
||||
jobs:
|
||||
build:
|
||||
name: Build schema-server
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build and Push Docker Image
|
||||
uses: premid/premid/.github/actions/build-and-push-docker@monorepo
|
||||
with:
|
||||
app: schema-server
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
38
.github/workflows/ci.yaml
vendored
Normal file
38
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Build, Lint and Test
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: Build, Lint and Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: package.json
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
build-docker:
|
||||
name: Build Docker Images
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
project: [pd, schema-server]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Build Docker Image
|
||||
uses: premid/premid/.github/actions/build-docker@monorepo
|
||||
with:
|
||||
dockerfile: ./apps/pd/Dockerfile
|
||||
tags: ${{ matrix.project }}
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,4 +1,23 @@
|
||||
node_modules
|
||||
out
|
||||
dist
|
||||
tmp
|
||||
|
||||
.vscode
|
||||
dist
|
||||
.env
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
.pnp.*
|
||||
|
||||
src/package-lock.json
|
||||
src/package.json
|
||||
src/update.ini
|
||||
|
||||
*.exe
|
||||
*.app
|
||||
*.xml.backup
|
||||
*.js
|
||||
|
||||
coverage
|
||||
*.tsbuildinfo
|
||||
2
.husky/commit-msg
Normal file
2
.husky/commit-msg
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pnpm exec commitlint --edit $1
|
||||
2
.husky/post-checkout
Normal file
2
.husky/post-checkout
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
if ! diff --quiet HEAD@{1} HEAD -- package.json; then pnpm i --frozen-lockfile; fi
|
||||
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pnpm run lint
|
||||
2
.husky/pre-push
Normal file
2
.husky/pre-push
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pnpm run test
|
||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
*.js
|
||||
*.cjs
|
||||
*.ts
|
||||
*.vue
|
||||
pnpm-lock.yaml
|
||||
cache
|
||||
1
.prettierrc.cjs
Normal file
1
.prettierrc.cjs
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@recodive/configs").default.prettier;
|
||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
* @Timeraa
|
||||
39
CODE_OF_CONDUCT.md
Normal file
39
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@premid.app or by contacting a staff member on our [Discord server](https://discord.premid.app). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
@@ -1,20 +0,0 @@
|
||||
//* Used to send response to app
|
||||
function sendAlive() {
|
||||
$.ajax({
|
||||
async: true,
|
||||
crossDomain: true,
|
||||
url: "http://localhost:3000/",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
processData: false,
|
||||
data: JSON.stringify({
|
||||
connected: true,
|
||||
service: "keepAlive"
|
||||
}),
|
||||
error: function(jqXHR, textStatus, errorThrown) {}
|
||||
})
|
||||
}
|
||||
|
||||
setInterval(sendAlive, 1*5000)
|
||||
@@ -1,56 +0,0 @@
|
||||
//* Start interval
|
||||
window.onload = function() {
|
||||
setInterval(updateData, 1000)
|
||||
}
|
||||
|
||||
//* Create needed variables
|
||||
let urlForVideo,
|
||||
songTime,
|
||||
calcDifference = []
|
||||
|
||||
function updateData() {
|
||||
if (document.location.href.includes("youtube"))
|
||||
urlForVideo = document.location.href
|
||||
if ($(".ytp-time-current").html() != " ") {
|
||||
const data = {
|
||||
connected: true,
|
||||
service: "yt",
|
||||
url: urlForVideo,
|
||||
currentSongTitle: $(".style-scope.ytd-video-primary-info-renderer")
|
||||
.children()
|
||||
.eq(2)
|
||||
.contents()
|
||||
.first()
|
||||
.html(),
|
||||
currentSongAuthor: $(
|
||||
"#upload-info .style-scope .ytd-video-owner-renderer"
|
||||
)
|
||||
.contents()
|
||||
.first()
|
||||
.html(),
|
||||
currentSongStartTime: getSeconds($(".ytp-time-current").html()),
|
||||
currentSongEndTime: getSeconds($(".ytp-time-duration").html()),
|
||||
currentSongCover: "NAN"
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
async: true,
|
||||
crossDomain: true,
|
||||
url: "http://localhost:3000/",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
processData: false,
|
||||
data: JSON.stringify(data),
|
||||
error: function(jqXHR, textStatus, errorThrown) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getSeconds(string) {
|
||||
const a = string.split(":")
|
||||
|
||||
const seconds = +a[0] * 60 + +a[1]
|
||||
return seconds
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
//* Start interval
|
||||
$(document).ready(() => {
|
||||
setInterval(updateData, 1000)
|
||||
})
|
||||
|
||||
//* Create needed variables
|
||||
let songTime,
|
||||
splitTime,
|
||||
songStartTime,
|
||||
songEndTime,
|
||||
songTitle,
|
||||
songCover,
|
||||
songAuthors = []
|
||||
|
||||
function updateData() {
|
||||
//* Clear old authors
|
||||
songAuthors = []
|
||||
|
||||
if($(".time-info.style-scope.ytmusic-player-bar").html() != "" &&
|
||||
$(".time-info.style-scope.ytmusic-player-bar").html() != " ") {
|
||||
//* Get song Time String (2:10 / 3:21)
|
||||
songTime = $(".time-info.style-scope.ytmusic-player-bar").html()
|
||||
//* Split to array ["2:10", "3:21"]
|
||||
splitTime = songTime.split(" / ", 2)
|
||||
//* Convert to seconds
|
||||
songStartTime = getSeconds(splitTime[0])
|
||||
songEndTime = getSeconds(splitTime[1])
|
||||
//* Get Song title
|
||||
songTitle = $(".title.style-scope.ytmusic-player-bar").html()
|
||||
//* Get Song cover/thumbnail
|
||||
songCover = $(".image.style-scope.ytmusic-player-bar").attr("src")
|
||||
|
||||
//* Get all authors
|
||||
$(".byline.ytmusic-player-bar").contents().each(function(index, item) {
|
||||
if(item.classList != undefined) {
|
||||
if(item.classList.contains("yt-simple-endpoint") == true) {
|
||||
songAuthors.push(item.innerHTML)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//* If no authors found in previous method use this
|
||||
if(songAuthors.length == 0 || songAuthors.length == 1) {
|
||||
//* Clear old list
|
||||
songAuthors = []
|
||||
songAuthors.push($(".byline.ytmusic-player-bar").contents().first().text())
|
||||
}
|
||||
|
||||
var startTime = Date.now();
|
||||
var endTime =
|
||||
Math.floor(startTime / 1000) -
|
||||
songStartTime +
|
||||
songEndTime;
|
||||
|
||||
const data = {
|
||||
connected: true,
|
||||
service: "ytm",
|
||||
currentSongTitle: songTitle,
|
||||
currentSongAuthors: songAuthors,
|
||||
currentSongStartTimeSeconds: songStartTime,
|
||||
currentSongStartTime: startTime,
|
||||
currentSongEndTime: endTime,
|
||||
currentSongCover: songCover
|
||||
}
|
||||
|
||||
//* Send data to app/server
|
||||
$.ajax({
|
||||
async: true,
|
||||
crossDomain: true,
|
||||
url: "http://localhost:3000/",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
processData: false,
|
||||
data: JSON.stringify(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//* Used to extract seconds from Syntax
|
||||
//* 1:39 => 99
|
||||
function getSeconds(string) {
|
||||
const s = string.split(":")
|
||||
|
||||
const seconds = +s[0] * 60 + +s[1]
|
||||
return seconds
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
2
Extension/jquery-3.3.1.min.js
vendored
2
Extension/jquery-3.3.1.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "YT Presence",
|
||||
"version": "0.3",
|
||||
"description": "Discord Rich Presence for YouTube and YouTube Music.",
|
||||
"manifest_version": 3,
|
||||
"icons": {
|
||||
"2024": "images/icon.png"
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["jquery-3.3.1.min.js", "background.js"],
|
||||
"persistent": true
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"2024": "images/icon.png"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": ["jquery-3.3.1.min.js", "handleYTM.js"],
|
||||
"matches": ["https://music.youtube.com/*"]
|
||||
},
|
||||
{
|
||||
"js": ["jquery-3.3.1.min.js", "handleYT.js"],
|
||||
"matches": ["https://www.youtube.com/*"]
|
||||
}
|
||||
],
|
||||
"permissions": ["http://localhost/*", "tabs"]
|
||||
}
|
||||
386
LICENSE
386
LICENSE
@@ -1,21 +1,373 @@
|
||||
MIT License
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
Copyright (c) 2018 Florian Metz
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
108
README.md
108
README.md
@@ -1,100 +1,40 @@
|
||||
## 
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/PreMiD/PreMiD)
|
||||
|
||||
# <img src="icon.png" width="20" draggable="false"><b> </b>YouTube Presence · Watcha watching there? 👀
|
||||
# PreMiD Monorepo
|
||||
|
||||
_Show your current YouTube video/song in your Discord profile_
|
||||
This is the monorepo for the PreMiD project. PreMiD is a simple, configurable utility that allows you to show what you're watching/listening to on your Discord profile.
|
||||
|
||||
## Table of contents
|
||||
## Table of Contents
|
||||
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Installing the Extension](#installing-the-extension)
|
||||
- [Installing the Application](#installing-the-application)
|
||||
- Mac OS
|
||||
- Windows
|
||||
- [FAQ](#faq-·-frequently-asked-questions)
|
||||
- Why can't i install it on the Chrome Web Store?
|
||||
- Is this project up to date?
|
||||
- Will this project stay free?
|
||||
- [Packages](#packages)
|
||||
- [License](#license)
|
||||
|
||||
## Installing the Extension
|
||||
## Packages
|
||||
|
||||
<details>
|
||||
<summary><b><u>Installing the Chrome extension</u></b> (Click to expand)</summary>
|
||||
<h1>Chrome extension installation</h1>
|
||||
<ol>
|
||||
<li>Download the latest version of the <a href="https://github.com/Timeraa/YT-Presence/releases/latest">extension</a>
|
||||
</li>
|
||||
<li>Extract the downloaded <b>.zip</b> file</li>
|
||||
<li>Open Chrome</li>
|
||||
<li>Go to <a href="chrome://extensions/">chrome://extensions/</a></li>
|
||||
<li>Turn on <b>Developer mode</b><br>
|
||||
<img src="developerMode.png">
|
||||
</li>
|
||||
<li>Click "<b>Load unpacked extension</b>"<br>
|
||||
<img src="loadUnpackedExtension.png">
|
||||
</li>
|
||||
<li>Load the extracted <b>Extension</b> folder</li>
|
||||
<li>Install the <a href="#installing-the-application">application</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
<details>
|
||||
<summary><b><u>Installing the Opera extension</u></b> (Click to expand)</summary>
|
||||
<h1>Opera extension installation</h1>
|
||||
<ol>
|
||||
<li>Download the latest version of the <a href="https://github.com/Timeraa/YT-Presence/releases/latest">extension</a>
|
||||
</li>
|
||||
<li>Extract the downloaded <b>.zip</b> file</li>
|
||||
<li>Open Opera</li>
|
||||
<li>Go to <a href="chrome://extensions/">chrome://extensions/</a></li>
|
||||
<li>Drag and drop the extension's folder on the page<br>
|
||||
<li>Load the extracted <b>Extension</b> folder</li>
|
||||
<li>Install the <a href="#installing-the-application">application</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
This monorepo is split into multiple packages / projects. Here's a list of them:
|
||||
|
||||
## Installing the Application
|
||||
- [apps/docs](apps/docs) - The official documentation for PreMiD.
|
||||
- [apps/pd](apps/pd/README.md) - A simple url shortener service to shorten urls longer than 256 characters.
|
||||
- [apps/schema-server](apps/schema-server) - Simple Schema server for the Presence manifest.
|
||||
|
||||
<details>
|
||||
<summary><b><u>Mac OS</u></b> (Click to expand)</summary>
|
||||
<h1>Installation on Mac OS</h1>
|
||||
<ol>
|
||||
<li>Download the latest version of the <a href="https://github.com/Timeraa/YT-Presence/releases/latest">application</a>
|
||||
</li>
|
||||
<li>Extract the downloaded <b>.zip</b> file</li>
|
||||
<li>Open the extracted folder and run the <b>.dmg</b> file</li>
|
||||
<li>Drag <b>YT Presence</b> Into your <b>Applications</b> Folder</li>
|
||||
<li>Open your Launchpad</li>
|
||||
<li>Open <b>YT Presence</b></li>
|
||||
<li>Enjoy!</li>
|
||||
</ol>
|
||||
</details>
|
||||
## Development
|
||||
|
||||
<details>
|
||||
<summary><b><u>Windows</u></b> (Click to expand)</summary>
|
||||
<h1>Installation on Windows</h1>
|
||||
<ol>
|
||||
<li>Extract the downloaded <b>.zip</b> file</li>
|
||||
<li>Open extracted folder and run <b>YT Presence.exe</b></li>
|
||||
</ol>
|
||||
</details>
|
||||
<br>
|
||||
### Release
|
||||
|
||||
**Note: The windows version does not have an installer at the moment. I am working hard on packaging the application into an installer. Please be patient. Thanks**
|
||||
To release a new version of a package, run the following command:
|
||||
|
||||
## FAQ · Frequently Asked Questions
|
||||
```bash
|
||||
pnpm bumpp -y -t <app>-v
|
||||
```
|
||||
|
||||
> ## Why can't i install the extension on the **Chrome Web Store**?<br>
|
||||
Replace `<app>` with the name of the package you want to release. For example, to release a new version of the `schema-server` package, you would run:
|
||||
|
||||
- Because i need to activate my google account as a Developer account but that requires **5$** and i am not paying it until i receive donations for this Project. <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZU8Q766ACS2WS&lc=US">Donate here</a>
|
||||
```bash
|
||||
pnpm bumpp -y -t schema-server-v
|
||||
```
|
||||
|
||||
> ## Is this project still up to date?<br>
|
||||
This will use bumpp to bump the version of the package in the `package.json` file, create a tag for the new version, and push the changes to the remote repository.
|
||||
|
||||
- Yes! I use and work on this program and extension almost everyday!<br>There are currently no plans on discontinuing this Project.
|
||||
## License
|
||||
|
||||
> ## Will this project stay free?<br>
|
||||
|
||||
- Of course! Its open source! But i would highly appreciate a little bit of Money to activate the Google Developer Account so that i can put the Chrome extension on the Chrome Web store. And it would help me to get the money needed for future plans like a **Driving license** 😎<br><br><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZU8Q766ACS2WS&lc=US">Donate here</a>
|
||||
|
||||
---
|
||||
|
||||
### YT Presence version 0.3 · By Timeraa
|
||||
This project is licensed under the [MPL-2.0 License](LICENSE).
|
||||
|
||||
BIN
appIcon.icns
BIN
appIcon.icns
Binary file not shown.
BIN
appIcon.ico
BIN
appIcon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
1
apps/docs/.vitepress/.gitignore
vendored
Normal file
1
apps/docs/.vitepress/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cache
|
||||
129
apps/docs/.vitepress/config.mts
Normal file
129
apps/docs/.vitepress/config.mts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "Documentation",
|
||||
description: "Official Documentation",
|
||||
locales: {
|
||||
root: {
|
||||
label: "English",
|
||||
lang: "en-US",
|
||||
},
|
||||
de: {
|
||||
label: "Deutsch",
|
||||
lang: "de-DE",
|
||||
},
|
||||
},
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{
|
||||
text: "Presence Development",
|
||||
link: "/presence-development/",
|
||||
},
|
||||
{
|
||||
text: "Reference",
|
||||
link: "/reference/presence",
|
||||
},
|
||||
],
|
||||
sidebar: {
|
||||
"/default": {
|
||||
base: "/",
|
||||
items: [
|
||||
{
|
||||
text: "Getting Started",
|
||||
link: "/",
|
||||
items: [
|
||||
{
|
||||
text: "Introduction",
|
||||
link: "/introduction/",
|
||||
},
|
||||
{
|
||||
text: "Installation",
|
||||
link: "/installation/",
|
||||
},
|
||||
{
|
||||
text: "Setup",
|
||||
link: "/setup/",
|
||||
},
|
||||
{
|
||||
text: "FAQ",
|
||||
link: "/faq/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Development",
|
||||
link: "/development/",
|
||||
items: [
|
||||
{
|
||||
text: "Presence Development",
|
||||
link: "/presence-development/",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Getting Started",
|
||||
link: "/presence-development/getting-started/",
|
||||
},
|
||||
{
|
||||
text: "Creating a Presence",
|
||||
link: "/presence-development/creating-a-presence/",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Contribute",
|
||||
link: "/contribute/",
|
||||
items: [
|
||||
{
|
||||
text: "Report a Bug",
|
||||
link: "https://github.com/PreMiD",
|
||||
},
|
||||
{
|
||||
text: "Submit a Feature",
|
||||
link: "https://discord.premid.app",
|
||||
},
|
||||
{
|
||||
text: "Donate",
|
||||
link: "https://patreon.com/Timeraa",
|
||||
},
|
||||
{
|
||||
text: "Translate",
|
||||
link: "https://crowdin.com/project/premid",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"/reference/": {
|
||||
base: "/reference",
|
||||
items: [
|
||||
{
|
||||
text: "Reference",
|
||||
link: "/presence",
|
||||
items: [
|
||||
{
|
||||
text: "Presence",
|
||||
link: "/presence",
|
||||
},
|
||||
{
|
||||
text: "Iframe",
|
||||
link: "/iframe",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/PreMiD" },
|
||||
{ icon: "discord", link: "https://discord.premid.app" },
|
||||
{ icon: "x", link: "https://x.com/PreMiDapp" },
|
||||
],
|
||||
i18nRouting: true,
|
||||
logo: "/logo.svg",
|
||||
search: { provider: "local" },
|
||||
},
|
||||
lastUpdated: true,
|
||||
});
|
||||
5
apps/docs/getting-started.md
Normal file
5
apps/docs/getting-started.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Getting Started
|
||||
|
||||
Welcome to the official documentation for PreMiD! This guide will help you get started with PreMiD.
|
||||
|
||||
## Installation
|
||||
51
apps/docs/index.md
Normal file
51
apps/docs/index.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "PreMiD"
|
||||
text: "Documentation"
|
||||
tagline: "The official documentation for PreMiD."
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: PreMiD Logo
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
link: /getting-started
|
||||
- theme: alt
|
||||
text: Presence Development
|
||||
link: /presence-development
|
||||
features:
|
||||
- icon: 🛠️
|
||||
title: Extensible
|
||||
details: Add Presences for your favorite websites and services. Or create your own!
|
||||
- icon: 🌐
|
||||
title: Cross-Platform
|
||||
details: PreMiD is available for all major browsers and platforms.
|
||||
- icon: 🚀
|
||||
title: Lightweight
|
||||
details: PreMiD is designed to be as lightweight as possible, so it won't slow down your system.
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, rgb(209, 122, 254) 30%, rgb(89, 195, 246));
|
||||
|
||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, rgb(209, 122, 254) 50%, rgb(89, 195, 246) 50%);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
14
apps/docs/package.json
Normal file
14
apps/docs/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@premid/docs",
|
||||
"version": "0.0.0",
|
||||
"description": "Documentation for Premid",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev",
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitepress": "1.0.0-rc.42"
|
||||
}
|
||||
}
|
||||
1
apps/docs/public/logo.svg
Normal file
1
apps/docs/public/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 288 KiB |
68
apps/docs/reference/presence.md
Normal file
68
apps/docs/reference/presence.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Presence Class
|
||||
|
||||
The `Presence` class is the main class used to create a Presence.
|
||||
|
||||
## Overview
|
||||
|
||||
The `Presence` class is the main class used to create a Presence. It is used to interact with the PreMiD Extension.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
const presence = new Presence({
|
||||
clientId: "<Your Client ID>",
|
||||
});
|
||||
|
||||
presence.on("UpdateData", () => {
|
||||
// Logic to update the presence data
|
||||
|
||||
presence.setActivity({
|
||||
details: "Example Presence",
|
||||
state: "Example State",
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Constructor
|
||||
|
||||
### `new Presence(options: PresenceOptions)`
|
||||
|
||||
Creates a new `Presence` instance.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `options` (`PresenceOptions`): The options for the Presence.
|
||||
|
||||
#### Returns
|
||||
|
||||
- `Presence`: The new `Presence` instance.
|
||||
|
||||
## Properties
|
||||
|
||||
### `clientId: string`
|
||||
|
||||
The Client ID of the Presence.
|
||||
|
||||
## Methods
|
||||
|
||||
### `setActivity(activity: PresenceActivity)`
|
||||
|
||||
Sets the activity of the Presence.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `activity` (`PresenceActivity`): The activity to set.
|
||||
|
||||
### `clearActivity()`
|
||||
|
||||
Clears the activity of the Presence.
|
||||
|
||||
### `on(event: string, listener: Function)`
|
||||
|
||||
Adds a listener to an event.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `event` (`string`): The event to listen to.
|
||||
|
||||
- `listener` (`Function`): The listener to add.
|
||||
26
apps/pd/Dockerfile
Normal file
26
apps/pd/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM node:20-alpine as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
COPY package.json pnpm-lock.yaml /app/
|
||||
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm fetch --frozen-lockfile
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN pnpm install --frozen-lockfile --offline
|
||||
|
||||
RUN pnpm --filter @premid/pd build && \
|
||||
pnpm deploy --filter @premid/pd deploy
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/deploy /app
|
||||
|
||||
USER node
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
3
apps/pd/README.md
Normal file
3
apps/pd/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @premid/pd
|
||||
|
||||
A simple url shortener service to shorten urls longer than 256 characters.
|
||||
10
apps/pd/environment.d.ts
vendored
Normal file
10
apps/pd/environment.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NODE_ENV?: "development" | "production" | "test";
|
||||
REDIS_URL?: string;
|
||||
MAX_FILE_SIZE?: number;
|
||||
PORT?: string;
|
||||
HOST?: string;
|
||||
}
|
||||
}
|
||||
BIN
apps/pd/fixtures/1x1.png
Normal file
BIN
apps/pd/fixtures/1x1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 B |
BIN
apps/pd/fixtures/test.mp4
Normal file
BIN
apps/pd/fixtures/test.mp4
Normal file
Binary file not shown.
35
apps/pd/package.json
Normal file
35
apps/pd/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@premid/pd",
|
||||
"version": "1.0.0",
|
||||
"description": "A small service to shorten image urls to get around Discord's 256 character limit",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps .",
|
||||
"dev": "node --watch --enable-source-maps .",
|
||||
"build": "tsc -p tsconfig.app.json"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@fastify/multipart": "^8.1.0",
|
||||
"@fastify/rate-limit": "^9.1.0",
|
||||
"@keyv/redis": "^2.8.4",
|
||||
"fastify": "^4.26.0",
|
||||
"file-type": "^19.0.0",
|
||||
"got": "^14.2.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"ipaddr.js": "^2.1.0",
|
||||
"keyv": "^4.5.4",
|
||||
"mime-types": "^2.1.35",
|
||||
"nanoid": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
}
|
||||
5
apps/pd/src/constants.ts
Normal file
5
apps/pd/src/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import createKeyv from "./functions/createKeyv.js";
|
||||
import getGoogleAddresses from "./functions/getGoogleAddresses.js";
|
||||
|
||||
export const keyv = createKeyv(),
|
||||
googleCIDRs = await getGoogleAddresses();
|
||||
9
apps/pd/src/functions/createKeyv.test.ts
Normal file
9
apps/pd/src/functions/createKeyv.test.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
test("should return keyv instance", async () => {
|
||||
const { default: createKeyv } = await import("../functions/createKeyv.js"),
|
||||
|
||||
keyv = createKeyv();
|
||||
|
||||
expect(keyv).toStrictEqual(expect.any(Object));
|
||||
});
|
||||
12
apps/pd/src/functions/createKeyv.ts
Normal file
12
apps/pd/src/functions/createKeyv.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import Keyv from "keyv";
|
||||
|
||||
export default function createKeyv() {
|
||||
const keyv = new Keyv();
|
||||
|
||||
/* c8 ignore next 3 */
|
||||
keyv.on("error", (error) => {
|
||||
console.error("Keyv connection error:", error);
|
||||
});
|
||||
|
||||
return keyv;
|
||||
}
|
||||
10
apps/pd/src/functions/createServer.test.ts
Normal file
10
apps/pd/src/functions/createServer.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe("createServer", () => {
|
||||
it("should return a fastify instance", async () => {
|
||||
const server = await createServer();
|
||||
expect(server).toBeDefined();
|
||||
});
|
||||
});
|
||||
61
apps/pd/src/functions/createServer.ts
Normal file
61
apps/pd/src/functions/createServer.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import cors from "@fastify/cors";
|
||||
import fastifyMultipart from "@fastify/multipart";
|
||||
import ratelimit from "@fastify/rate-limit";
|
||||
import fastify from "fastify";
|
||||
|
||||
import createFromBase64 from "../routes/createFromBase64.js";
|
||||
import createFromImage from "../routes/createFromImage.js";
|
||||
import createShortenedLink from "../routes/createShortenedLink.js";
|
||||
import getFullLink from "../routes/getFullLink.js";
|
||||
|
||||
export async function createServer() {
|
||||
const server = fastify({
|
||||
trustProxy: true,
|
||||
});
|
||||
|
||||
await server.register(cors, {
|
||||
methods: ["GET"],
|
||||
origin: "*",
|
||||
});
|
||||
|
||||
await server.register(ratelimit, {
|
||||
max: 25,
|
||||
nameSpace: "pd",
|
||||
// TODO
|
||||
/* redis, */
|
||||
timeWindow: "1 minute",
|
||||
});
|
||||
|
||||
await server.register(fastifyMultipart, {
|
||||
limits: {
|
||||
fileSize: process.env.MAX_FILE_SIZE ?? 5 * 1024 * 1024,
|
||||
files: 1,
|
||||
},
|
||||
});
|
||||
|
||||
server.post("/create/image", createFromImage);
|
||||
server.post("/create/base64", createFromBase64);
|
||||
server.get("/create/*", createShortenedLink);
|
||||
|
||||
server.get(
|
||||
"/*",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
getFullLink,
|
||||
);
|
||||
|
||||
server.get(
|
||||
"/health",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
(_, reply) => reply.status(204).send(),
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
30
apps/pd/src/functions/getGoogleAddresses.test.ts
Normal file
30
apps/pd/src/functions/getGoogleAddresses.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import got from "got";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe.concurrent("getGoogleAddresses", async () => {
|
||||
it("should return an array of CIDR objects", async () => {
|
||||
const { default: getGoogleAddresses } = await import("./getGoogleAddresses.js");
|
||||
|
||||
vi.spyOn(got, "get").mockResolvedValue({
|
||||
body: JSON.stringify({
|
||||
prefixes: [
|
||||
{ ipv4Prefix: "0.0.0.0" },
|
||||
{ ipv6Prefix: "0000:0000:0000::/16" },
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await getGoogleAddresses();
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"ipv4Prefix": "0.0.0.0",
|
||||
},
|
||||
{
|
||||
"ipv6Prefix": "0000:0000:0000::/16",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
24
apps/pd/src/functions/getGoogleAddresses.ts
Normal file
24
apps/pd/src/functions/getGoogleAddresses.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import got from "got";
|
||||
|
||||
import { CIDR } from "./isInCidRange.js";
|
||||
|
||||
export default async function getGoogleAddresses(): Promise<CIDR> {
|
||||
const { body } = await got.get("https://www.gstatic.com/ipranges/cloud.json"),
|
||||
result: GoogleResult = JSON.parse(body);
|
||||
return result.prefixes.map(({ ipv4Prefix, ipv6Prefix }) => {
|
||||
return ipv6Prefix ? { ipv6Prefix } : { ipv4Prefix };
|
||||
});
|
||||
}
|
||||
|
||||
interface GoogleResult {
|
||||
syncToken: string;
|
||||
creationTime: string;
|
||||
prefixes: GoogleIP[];
|
||||
}
|
||||
|
||||
interface GoogleIP {
|
||||
ipv6Prefix: string;
|
||||
ipv4Prefix: string;
|
||||
service: string;
|
||||
scope: string;
|
||||
}
|
||||
31
apps/pd/src/functions/isInCidRange.test.ts
Normal file
31
apps/pd/src/functions/isInCidRange.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import isInCIDRRange from "./isInCidRange.js";
|
||||
|
||||
test("isInCIDRRange - IPv4 - in range", async () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }],
|
||||
ip = "192.0.2.123",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv4 - not in range", async () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }],
|
||||
ip = "192.0.3.123",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv6 - in range", async () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }],
|
||||
ip = "2001:db8::1234",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv6 - not in range", async () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }],
|
||||
ip = "2001:db9::1234",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
26
apps/pd/src/functions/isInCidRange.ts
Normal file
26
apps/pd/src/functions/isInCidRange.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import ipaddr from "ipaddr.js";
|
||||
|
||||
export default function isInCIDRRange(CIDRs: CIDR, ip: string) {
|
||||
const parsed = ipaddr.parse(ip);
|
||||
|
||||
for (const CIDR of CIDRs.filter((c) => {
|
||||
if (parsed.kind() === "ipv4" && "ipv4Prefix" in c) return true;
|
||||
else if (parsed.kind() === "ipv6" && "ipv6Prefix" in c) return true;
|
||||
else return false;
|
||||
})) {
|
||||
const check = parsed.match(ipaddr.parseCIDR("ipv4Prefix" in CIDR ? CIDR.ipv4Prefix : CIDR.ipv6Prefix));
|
||||
|
||||
if (check) return check;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export type CIDR = (
|
||||
| {
|
||||
ipv4Prefix: string;
|
||||
}
|
||||
| {
|
||||
ipv6Prefix: string;
|
||||
}
|
||||
)[];
|
||||
14
apps/pd/src/index.test.ts
Normal file
14
apps/pd/src/index.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
|
||||
test("/health", async () => {
|
||||
const server = await createServer(),
|
||||
result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/health",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(204);
|
||||
expect(result.body).toBe("");
|
||||
});
|
||||
14
apps/pd/src/index.ts
Normal file
14
apps/pd/src/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/* c8 ignore start */
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
|
||||
if (!process.env.REDIS_URL) console.log("WARNING: No REDIS_URL environment variable set");
|
||||
|
||||
export const server = await createServer();
|
||||
|
||||
const url = await server.listen({
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
port: Number.parseInt(process.env.PORT ?? "80"),
|
||||
});
|
||||
|
||||
console.log(`Server listening at ${url}`);
|
||||
// TODO Make proper error codes & json responses
|
||||
98
apps/pd/src/routes/createFromBase64.test.ts
Normal file
98
apps/pd/src/routes/createFromBase64.test.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("createFromBase64", async () => {
|
||||
const server = await createServer();
|
||||
|
||||
it("should return a 400 when the body is not present", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
method: "POST",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid body\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the body is not a string", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
method: "POST",
|
||||
payload: new Blob([]),
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid body\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the body is not a valid base64 string", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64t",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid base64 string\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the base64 string is not a valid image", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/sv;base64,a",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid base64 string\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the base64 string is not a valid image", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/svg+xml;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Supported types: png, jpeg, jpg, gif, webp\"");
|
||||
});
|
||||
|
||||
it("should return a 200 when the base64 string is valid", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 when the base64 string is valid", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
});
|
||||
47
apps/pd/src/routes/createFromBase64.ts
Normal file
47
apps/pd/src/routes/createFromBase64.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
import mime from "mime-types";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { keyv } from "../constants.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
const { body } = request;
|
||||
|
||||
if (!body) return reply.status(400).send("Invalid body");
|
||||
|
||||
if (typeof body !== "string") return reply.status(400).send("Invalid body");
|
||||
|
||||
const matches = body.match(/^data:(.+);base64,(.+)/);
|
||||
|
||||
if (!matches || matches.length === 0) return reply.status(400).send("Invalid base64 string");
|
||||
|
||||
const type = mime.extension(matches.at(1) as string);
|
||||
|
||||
if (!type) return reply.status(400).send("Invalid base64 string");
|
||||
|
||||
if (!["png", "jpeg", "jpg", "gif", "webp"].includes(type)) return reply.status(400).send("Supported types: png, jpeg, jpg, gif, webp");
|
||||
|
||||
const url = await keyv.get(body);
|
||||
|
||||
if (url) {
|
||||
await Promise.all([
|
||||
keyv.set(url, body, 30 * 60 * 1000),
|
||||
keyv.set(body, url, 30 * 60 * 1000),
|
||||
]);
|
||||
|
||||
reply.header("Cache-control", `public, max-age=${30 * 60}`);
|
||||
return reply.send(process.env.HOST + url);
|
||||
}
|
||||
|
||||
const uniqueId = `${nanoid(10)}.${type}`;
|
||||
|
||||
await Promise.all([
|
||||
keyv.set(body, uniqueId, 30 * 60 * 1000),
|
||||
keyv.set(uniqueId, body, 30 * 60 * 1000),
|
||||
]);
|
||||
|
||||
reply.header("Cache-control", `public, max-age=${30 * 60}`);
|
||||
return reply.send(process.env.BASE_URL + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
107
apps/pd/src/routes/createFromImage.test.ts
Normal file
107
apps/pd/src/routes/createFromImage.test.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { RequestOptions } from "node:http";
|
||||
import { AddressInfo } from "node:net";
|
||||
|
||||
import { afterAll, beforeAll, describe, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("createFromImage", async () => {
|
||||
const server = await createServer(),
|
||||
form = new FormData(),
|
||||
defaultRequestOptions: RequestOptions = {
|
||||
hostname: "localhost",
|
||||
method: "POST",
|
||||
path: "/create/image",
|
||||
protocol: "http:",
|
||||
};
|
||||
|
||||
let url: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
url = await server.listen();
|
||||
defaultRequestOptions.port = (server.server.address() as AddressInfo).port;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("should return a 400 when request is not multipart", async ({ expect }) => {
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Request is not multipart\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when no file is provided", async ({ expect }) => {
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
});
|
||||
it("should return a 400 status code when the file is invalid", async ({ expect }) => {
|
||||
form.set("file", Buffer.alloc(1024 * 1024 * 2));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the file is invalid", async ({ expect }) => {
|
||||
form.set("file", new Blob([new Uint8Array(1024 * 1024 * 2)]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the file is not an image", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/test.mp4", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Only png, jpeg, jpg, gif and webp are supported\"");
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the file is valid", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/1x1.png", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(await result.text()).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the file is valid and the same file is uploaded again", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/1x1.png", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(await result.text()).toStrictEqual(expect.any(String));
|
||||
});
|
||||
});
|
||||
52
apps/pd/src/routes/createFromImage.ts
Normal file
52
apps/pd/src/routes/createFromImage.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
import { fileTypeFromBuffer } from "file-type";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { keyv } from "../constants.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
if (!request.isMultipart()) return reply.status(400).send("Request is not multipart");
|
||||
|
||||
const file = await request.file();
|
||||
|
||||
if (!file) return reply.status(400).send("Invalid file");
|
||||
|
||||
const type = await fileTypeFromBuffer(await file.toBuffer());
|
||||
|
||||
if (!type) return reply.status(400).send("Invalid file");
|
||||
|
||||
if (![
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
].includes(type.mime))
|
||||
return reply.status(400).send("Only png, jpeg, jpg, gif and webp are supported");
|
||||
|
||||
const buffer = await file.toBuffer(),
|
||||
body = `data:${type.mime};base64,${buffer.toString("base64")}`,
|
||||
url = await keyv.get(body);
|
||||
|
||||
if (url) {
|
||||
await Promise.all([
|
||||
keyv.set(url, body, 30 * 60 * 1000),
|
||||
keyv.set(body, url, 30 * 60 * 1000),
|
||||
]);
|
||||
|
||||
reply.header("Cache-control", `public, max-age=${30 * 60}`);
|
||||
return reply.send(process.env.HOST + url);
|
||||
}
|
||||
|
||||
const uniqueId = `${nanoid(10)}.${type.ext}`;
|
||||
|
||||
await Promise.all([
|
||||
keyv.set(body, uniqueId, 30 * 60 * 1000),
|
||||
keyv.set(uniqueId, body, 30 * 60 * 1000),
|
||||
]);
|
||||
|
||||
reply.header("Cache-control", `public, max-age=${30 * 60}`);
|
||||
return reply.send(process.env.BASE_URL + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
64
apps/pd/src/routes/createShortenedLink.test.ts
Normal file
64
apps/pd/src/routes/createShortenedLink.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("/create", async () => {
|
||||
const server = await createServer();
|
||||
|
||||
it("should return a 400 status code when no URL is provided", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"Invalid URL"');
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is too short", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/https://www.google.com",
|
||||
});
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"URL is too short"');
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is invalid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/file://www.googl${"e".repeat(256)}`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"Invalid URL"');
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"e".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid and already exists", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
const { body } = result,
|
||||
result2 = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result2.statusCode).toBe(200);
|
||||
expect(result2.body).toStrictEqual(body);
|
||||
});
|
||||
});
|
||||
33
apps/pd/src/routes/createShortenedLink.ts
Normal file
33
apps/pd/src/routes/createShortenedLink.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { keyv } from "../constants.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
const url = request.url.replace("/create/", "").trim();
|
||||
|
||||
if (url.length === 0) return reply.status(400).send("Invalid URL");
|
||||
|
||||
if (url.length < 256) return reply.status(400).send("URL is too short");
|
||||
|
||||
const urlObject = new URL(url);
|
||||
if (!["http:", "https:"].includes(urlObject.protocol)) return reply.status(400).send("Invalid URL");
|
||||
|
||||
const keyvUrl = (await keyv.get(url)) as string | undefined;
|
||||
|
||||
void reply.header("Cache-control", "public, max-age=1800");
|
||||
|
||||
if (keyvUrl) {
|
||||
await Promise.all([keyv.set(url, keyvUrl, 1800), keyv.set(keyvUrl, url, 1800)]);
|
||||
|
||||
return reply.send(process.env.BASE_URL + keyvUrl);
|
||||
}
|
||||
|
||||
const uniqueId = nanoid(10);
|
||||
|
||||
await Promise.all([keyv.set(url, uniqueId, 1800), keyv.set(uniqueId, url, 1800)]);
|
||||
|
||||
return reply.send(process.env.BASE_URL + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
100
apps/pd/src/routes/getFullLink.test.ts
Normal file
100
apps/pd/src/routes/getFullLink.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
import * as isInCIDRRange from "../functions/isInCidRange.js";
|
||||
|
||||
describe("getFullLink", async () => {
|
||||
const server = await createServer();
|
||||
let url: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
url = await server.listen();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it("should fail if not a Google Cloud IP", async () => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/1234567890",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(401);
|
||||
});
|
||||
|
||||
it("should fail if not a valid ID", async () => {
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/123",
|
||||
});
|
||||
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
const result2 = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/1234567890.",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(404);
|
||||
expect(result2.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("should redirect to the correct URL", async () => {
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const { body } = await server.inject({
|
||||
url: `/create/https://${"a".repeat(256)}`,
|
||||
});
|
||||
|
||||
expect(body).toStrictEqual(expect.any(String));
|
||||
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: body,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(302);
|
||||
expect(result.headers.location).toBe(`https://${"a".repeat(256)}`);
|
||||
});
|
||||
|
||||
it("should return the correct image", async () => {
|
||||
const imageBuffer = await readFile(new URL("../../fixtures/test.mp4", import.meta.url)),
|
||||
imageBase64 = `data:image/png;base64,${imageBuffer.toString("base64")}`,
|
||||
|
||||
{ body } = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: imageBase64,
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(body).toStrictEqual(expect.any(String));
|
||||
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const result = await fetch(`${url}${body}`, {
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.headers.get("content-type")).toBe("image/png");
|
||||
expect(Buffer.from(await result.arrayBuffer())).toStrictEqual(imageBuffer);
|
||||
});
|
||||
});
|
||||
40
apps/pd/src/routes/getFullLink.ts
Normal file
40
apps/pd/src/routes/getFullLink.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
|
||||
import { googleCIDRs, keyv } from "../constants.js";
|
||||
import isInCIDRRange from "../functions/isInCidRange.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
/* c8 ignore next 1 */
|
||||
const ip = request.headers["cf-connecting-ip"]?.toString() || request.socket.remoteAddress || request.ip;
|
||||
|
||||
if (
|
||||
!isInCIDRRange(
|
||||
googleCIDRs,
|
||||
ip,
|
||||
)
|
||||
)
|
||||
return reply.status(401).send("Not a Google Cloud IP");
|
||||
|
||||
const id = (request.params as { "*": string })["*"].trim();
|
||||
|
||||
if (id.split(".")[0]?.length !== 10) return reply.code(404).send("Invalid ID");
|
||||
|
||||
const url = await keyv.get(id);
|
||||
if (!url) return reply.code(404).send("Unknown ID");
|
||||
|
||||
await keyv.set(url, id, 1800);
|
||||
reply.header("Cache-control", "public, max-age=1800");
|
||||
|
||||
//* If it is not a base64 string, redirect to it
|
||||
if (!url.startsWith("data:image")) return reply.redirect(url);
|
||||
|
||||
const image = Buffer.from(
|
||||
url.replace(/^data:image\/\w+;base64,/, ""),
|
||||
"base64",
|
||||
),
|
||||
mime = url.split(";")[0].split(":")[1];
|
||||
|
||||
return reply.type(mime).send(image);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
8
apps/pd/tsconfig.app.json
Normal file
8
apps/pd/tsconfig.app.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"composite": true
|
||||
}
|
||||
}
|
||||
8
apps/pd/tsconfig.json
Normal file
8
apps/pd/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"include": ["environment.d.ts", "src"],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["@types/node"]
|
||||
}
|
||||
}
|
||||
26
apps/schema-server/Dockerfile
Normal file
26
apps/schema-server/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM node:20-alpine as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
COPY package.json pnpm-lock.yaml /app/
|
||||
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm fetch --frozen-lockfile
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN pnpm install --frozen-lockfile --offline
|
||||
|
||||
RUN pnpm --filter @premid/schema-server build && \
|
||||
pnpm deploy --filter @premid/schema-server deploy
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/deploy /app
|
||||
|
||||
USER node
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
8
apps/schema-server/environment.d.ts
vendored
Normal file
8
apps/schema-server/environment.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NODE_ENV?: "development" | "production" | "test";
|
||||
PORT?: string;
|
||||
HOST?: string;
|
||||
}
|
||||
}
|
||||
23
apps/schema-server/package.json
Normal file
23
apps/schema-server/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@premid/schema-server",
|
||||
"version": "1.0.0",
|
||||
"description": "A small service to serve the JSON schemas for PreMiD",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist",
|
||||
"schemas"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps .",
|
||||
"dev": "node --watch --enable-source-maps .",
|
||||
"build": "tsc -p tsconfig.app.json"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"fastify": "^4.26.0",
|
||||
"globby": "^14.0.1"
|
||||
}
|
||||
}
|
||||
241
apps/schema-server/schemas/metadata/1.0.json
Normal file
241
apps/schema-server/schemas/metadata/1.0.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
252
apps/schema-server/schemas/metadata/1.1.json
Normal file
252
apps/schema-server/schemas/metadata/1.1.json
Normal file
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
277
apps/schema-server/schemas/metadata/1.10.json
Normal file
277
apps/schema-server/schemas/metadata/1.10.json
Normal file
@@ -0,0 +1,277 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.10",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"matches": {
|
||||
"type": "array",
|
||||
"description": "A glob pattern required to match Google's match pattern (https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns). This is required for Presences.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A glob pattern.",
|
||||
"pattern": "^(https|http|[*])://.*/.*"
|
||||
}
|
||||
},
|
||||
|
||||
"iFrameMatches": {
|
||||
"type": "array",
|
||||
"description": "A glob pattern required to match Google's match pattern (https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns). This is required for Presences.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A glob pattern.",
|
||||
"pattern": "^(https|http|[*])://.*/.*"
|
||||
}
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.2.json
Normal file
257
apps/schema-server/schemas/metadata/1.2.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.3.json
Normal file
257
apps/schema-server/schemas/metadata/1.3.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.3",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.4.json
Normal file
257
apps/schema-server/schemas/metadata/1.4.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.4",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpg)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpg)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.5.json
Normal file
257
apps/schema-server/schemas/metadata/1.5.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.5",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.6.json
Normal file
257
apps/schema-server/schemas/metadata/1.6.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.6",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.7.json
Normal file
257
apps/schema-server/schemas/metadata/1.7.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.7",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.8.json
Normal file
257
apps/schema-server/schemas/metadata/1.8.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.8",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(cdn[.]rcd[.]gg|i[.]imgur[.]com)/(.*?)[.](png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(cdn[.]rcd[.]gg|i[.]imgur[.]com)/(.*?)[.](png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.9.json
Normal file
257
apps/schema-server/schemas/metadata/1.9.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.9",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
35
apps/schema-server/schemas/metadata/README.md
Normal file
35
apps/schema-server/schemas/metadata/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Metadata Schema
|
||||
|
||||
This schema is used to validate the `metadata.json` files in [PreMiD/Presences](https://github.com/PreMiD/Presences).
|
||||
|
||||
## 1.7
|
||||
|
||||
- Updates `icon` regex for compability with FontAwesome v6 icon labelling
|
||||
|
||||
## 1.6
|
||||
|
||||
- Adds support for language locales that end in numbers (e.g. es_419)
|
||||
|
||||
## 1.5
|
||||
|
||||
- Allows `.gif` extensions for service logos and `.jpeg` extensions for service logos and thumbnails.
|
||||
|
||||
## 1.4
|
||||
|
||||
- Adds regex for only allowing imgur links for `logo` & `thumbnail`.
|
||||
|
||||
## 1.3
|
||||
|
||||
- Fixes validation for icons.
|
||||
|
||||
## 1.2
|
||||
|
||||
- Adds "readLogs".
|
||||
|
||||
## 1.1
|
||||
|
||||
- Adds "altnames" and "multiLanguage".
|
||||
|
||||
## 1.0
|
||||
|
||||
- First iteration of the metadata file format.
|
||||
37
apps/schema-server/src/index.test.ts
Normal file
37
apps/schema-server/src/index.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { app } from "./index.js";
|
||||
|
||||
describe("schemas", () => {
|
||||
test("/ should return date", async () => {
|
||||
const result = await app.inject({ url: "/" });
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual({ date: expect.any(String) });
|
||||
});
|
||||
|
||||
test("/metadata/1.0 should return metadata schema", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1.0" }),
|
||||
schema = await import(resolve(import.meta.dirname, "../schemas/metadata/1.0.json"));
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual(schema.default);
|
||||
});
|
||||
|
||||
test("/metadata/1.0 should return cached metadata schema", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1.0" }),
|
||||
schema = await import(resolve(import.meta.dirname, "../schemas/metadata/1.0.json"));
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual(schema.default);
|
||||
});
|
||||
|
||||
test("/metadata/1 should return 404", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1" });
|
||||
|
||||
expect(result.statusCode).toBe(404);
|
||||
expect(result.json()).toEqual({ error: "Schema not found." });
|
||||
});
|
||||
});
|
||||
61
apps/schema-server/src/index.ts
Normal file
61
apps/schema-server/src/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { extname, resolve } from "node:path";
|
||||
|
||||
import helmet from "@fastify/helmet";
|
||||
import fastify, { RequestGenericInterface } from "fastify";
|
||||
import { globby } from "globby";
|
||||
|
||||
export const app = fastify();
|
||||
|
||||
app.register(helmet);
|
||||
|
||||
app.get("/", (_, reply) => reply.send({ date: new Date() }));
|
||||
|
||||
interface versionRequest extends RequestGenericInterface {
|
||||
Params: {
|
||||
schemaName: string;
|
||||
version: string;
|
||||
};
|
||||
}
|
||||
|
||||
const availableSchemas: Record<string, {
|
||||
version: string;
|
||||
content: Record<string, unknown>;
|
||||
}[]> = {};
|
||||
|
||||
for (const schemaPath of await globby(resolve(import.meta.dirname, "../schemas/*/*.json"), { onlyFiles: true })) {
|
||||
const [schemaName, version] = schemaPath.split("/").slice(-2) as [string, string];
|
||||
|
||||
let schemaVersions = availableSchemas[schemaName];
|
||||
|
||||
if (!schemaVersions)
|
||||
schemaVersions = [];
|
||||
|
||||
const { default: content } = await import(schemaPath, { with: { type: "json" } });
|
||||
schemaVersions.push({
|
||||
content,
|
||||
version: version.replace(extname(version), ""),
|
||||
});
|
||||
|
||||
availableSchemas[schemaName] = schemaVersions;
|
||||
}
|
||||
|
||||
app.get<versionRequest>("/:schemaName/:version", async (request, reply) => {
|
||||
const { schemaName, version } = request.params;
|
||||
|
||||
if (!availableSchemas[schemaName]?.some(schema => schema.version === version))
|
||||
return reply.status(404).send({ error: "Schema not found." });
|
||||
|
||||
return reply.send(availableSchemas[schemaName]?.find(schema => schema.version === version)?.content);
|
||||
});
|
||||
|
||||
/* c8 ignore start */
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
const url = await app.listen({
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
port: Number.parseInt(process.env.PORT || "80"),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Listening on ${url}`);
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
8
apps/schema-server/tsconfig.app.json
Normal file
8
apps/schema-server/tsconfig.app.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"composite": true
|
||||
}
|
||||
}
|
||||
8
apps/schema-server/tsconfig.json
Normal file
8
apps/schema-server/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"include": ["environment.d.ts", "src"],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["@types/node"]
|
||||
}
|
||||
}
|
||||
2
commitlint.config.cjs
Normal file
2
commitlint.config.cjs
Normal file
@@ -0,0 +1,2 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
module.exports = require("@recodive/configs").default.commitlint;
|
||||
@@ -1,9 +0,0 @@
|
||||
var electronInstaller = require('electron-winstaller');
|
||||
|
||||
resultPromise = electronInstaller.createWindowsInstaller({
|
||||
appDirectory: '/out/YT Presence-win32-64x',
|
||||
outputDirectory: '/dist/installerWindows',
|
||||
authors: 'Timeraa',
|
||||
});
|
||||
|
||||
resultPromise.then(() => console.log("It worked!"), (e) => console.log(`No dice: ${e.message}`));
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.8 KiB |
@@ -1,15 +0,0 @@
|
||||
// config.forge.js
|
||||
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
packagerConfig: {
|
||||
appBundleId: "eu.Timeraa.YTPresence",
|
||||
icon: path.resolve(__dirname, 'appIcon.icns')
|
||||
},
|
||||
make_targets: {
|
||||
win32: ["squirrel"], // An array of win32 make targets
|
||||
darwin: ["dmg"], // An array of darwin make targets
|
||||
linux: ["deb", "rpm", "flatpak", "snap"] // An array of linux make targets
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
4837
package-lock.json
generated
4837
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
82
package.json
82
package.json
@@ -1,45 +1,41 @@
|
||||
{
|
||||
"name": "yt-presence",
|
||||
"productName": "YT Presence",
|
||||
"version": "0.3.0",
|
||||
"description": "Show your current YouTube Video / Song in your Discord profile.",
|
||||
"main": "src/index.js",
|
||||
"repository": "Timeraa/YT-Presence",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "echo \"No linting configured\"",
|
||||
"create-installer-mac": "electron-installer-dmg ./out/YT\\ Presence-darwin-x64/YT\\ Presence.app YT\\ Presence\\ Installer --overwrite --icon=./appIcon.icns --out=./dist",
|
||||
"create-installer-windows": "electron-installer-windows --src ./out/YT\\ Presence-win32-x64/ --dest ./dist"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Timeraa",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"forge": "./forge.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"auto-launch": "^5.0.5",
|
||||
"chalk": "^2.4.1",
|
||||
"discord-rpc": "^3.0.0",
|
||||
"electron-config": "^1.0.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"express": "^4.16.3",
|
||||
"fs": "0.0.1-security",
|
||||
"html-entities": "^1.2.1",
|
||||
"menubar": "^5.2.3",
|
||||
"request": "^2.87.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.22",
|
||||
"electron": "2.0.4",
|
||||
"electron-installer-dmg": "^1.0.0",
|
||||
"electron-installer-windows": "^1.1.0"
|
||||
}
|
||||
"name": "premid",
|
||||
"version": "1.0.0",
|
||||
"description": "Monorepo containing most of PreMiD's codebase",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"prepare": "husky",
|
||||
"lint": "eslint . --ext .ts,.js && prettier --check .",
|
||||
"lint:fix": "eslint . --ext .ts,.js --fix && prettier --write .",
|
||||
"build": "pnpm clean && tsc -b tsconfig.app.json",
|
||||
"build:watch": "pnpm clean && tsc -b tsconfig.app.json -w",
|
||||
"clean": "tsc -b tsconfig.app.json --clean",
|
||||
"test": "vitest --run",
|
||||
"test:ui": "vitest --ui"
|
||||
},
|
||||
"author": {
|
||||
"name": "Recodive oHG",
|
||||
"email": "contact@recodive.com",
|
||||
"url": "https://recodive.com"
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18.6.0",
|
||||
"@recodive/configs": "^1.7.5",
|
||||
"@recodive/eslint-config": "^1.7.5",
|
||||
"@rushstack/eslint-patch": "^1.7.2",
|
||||
"@types/node": "^20.11.17",
|
||||
"@vitest/coverage-v8": "^1.2.2",
|
||||
"@vitest/ui": "^1.2.2",
|
||||
"bumpp": "^9.3.0",
|
||||
"eslint": "^8.56.0",
|
||||
"husky": "^9.0.10",
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.2.2"
|
||||
},
|
||||
"packageManager": "pnpm@8.14.3",
|
||||
"engines": {
|
||||
"node": "^20.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
5044
pnpm-lock.yaml
generated
Normal file
5044
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- apps/*
|
||||
@@ -1,80 +0,0 @@
|
||||
/* The switch - the box around the slider */
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 35px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
|
||||
.switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: 0.25s;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
left: -2.5px;
|
||||
top: -2.5px;
|
||||
background-color: white;
|
||||
-webkit-transition: 0.25s;
|
||||
transition: 0.25s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196f3;
|
||||
}
|
||||
|
||||
input:disabled + .slider {
|
||||
background-color: rgb(160, 160, 160);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input:disabled + .slider:before {
|
||||
background-color: lightgray;
|
||||
border: 1px solid rgb(160, 160, 160);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196f3;
|
||||
}
|
||||
|
||||
input:hover + .slider {
|
||||
transform: scale(1.25);
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(20px);
|
||||
-ms-transform: translateX(20px);
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
#header {
|
||||
height: 50px;
|
||||
width: 100vw;
|
||||
background-color: #ce1d2a;
|
||||
box-shadow: 0px 0px 8px 2px #cccccc;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#title {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#options {
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
}
|
||||
|
||||
#options th:nth-child(1) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#options th:nth-child(2) {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
#options tbody tr {
|
||||
transition: 1s ease-out;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
#options tbody tr:not(:last-child) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#close-btn {
|
||||
transition: 0.1s all ease-out;
|
||||
margin-top: 10px;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
background-color: #ce1d2a;
|
||||
color: white;
|
||||
width: 75px;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 20px;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
box-shadow: 0px 0px 8px 2px #cccccc;
|
||||
}
|
||||
|
||||
#close-btn:hover {
|
||||
transform: translateX(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
#close-btn:active {
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
src: url("../fonts/Roboto/Roboto-Thin.ttf") format("truetype");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
src: url("../fonts/Roboto/Roboto-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
src: url("../fonts/Roboto/Roboto-Black.ttf") format("truetype");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user