Compare commits
1382 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96e0c31bf9 | ||
|
|
b2f51c4682 | ||
|
|
902cc9df11 | ||
|
|
72ba01d34b | ||
|
|
6b69001b03 | ||
|
|
3e06af930f | ||
|
|
a80ab6721a | ||
|
|
ffc324fab9 | ||
|
|
87bfb443fe | ||
|
|
3f4662d90e | ||
|
|
1c4834211d | ||
|
|
1e53f371f6 | ||
|
|
9b3f2d00c5 | ||
|
|
e8208c32ea | ||
|
|
ceaaf540a1 | ||
|
|
fd83f09b3c | ||
|
|
69d118d511 | ||
|
|
3cdb6c756d | ||
|
|
4e093c3377 | ||
|
|
fc182882a0 | ||
|
|
64da529ec2 | ||
|
|
6b926ca92e | ||
|
|
be6f91f17e | ||
|
|
abee56d2ce | ||
|
|
a95828843b | ||
|
|
5478b6a6e2 | ||
|
|
58ccdada3b | ||
|
|
61647e96c7 | ||
|
|
6777ace942 | ||
|
|
a157daf7ec | ||
|
|
e4bafbc0e5 | ||
|
|
4d25db31df | ||
|
|
78c2f57679 | ||
|
|
ae827d295d | ||
|
|
15eeb3cc60 | ||
|
|
8cb8c46f2f | ||
|
|
e01bef2ace | ||
|
|
f540f10cca | ||
|
|
93cffa9ded | ||
|
|
f3be9dbb91 | ||
|
|
5e563110e3 | ||
|
|
000239514b | ||
|
|
5cc4cfaf55 | ||
|
|
c29f4fc390 | ||
|
|
6a3851a765 | ||
|
|
a5134928b5 | ||
|
|
30885e332b | ||
|
|
529e8f2f38 | ||
|
|
3662e90e28 | ||
|
|
a5231b42b5 | ||
|
|
af384de79e | ||
|
|
eb45eb48fc | ||
|
|
a6e91ce590 | ||
|
|
6dabe2963e | ||
|
|
089471cd14 | ||
|
|
88f18240df | ||
|
|
daa5f16945 | ||
|
|
5adf770af0 | ||
|
|
9a00c426c6 | ||
|
|
dd8aac577a | ||
|
|
fc0f03e171 | ||
|
|
1535ac7251 | ||
|
|
d395648f2d | ||
|
|
0cfb1b6389 | ||
|
|
9273576fe6 | ||
|
|
58362f14bb | ||
|
|
6bac97c15f | ||
|
|
a44856eb57 | ||
|
|
5a2cb39454 | ||
|
|
88b1426bb1 | ||
|
|
3fbca854d7 | ||
|
|
2a8ee0673d | ||
|
|
c8bfe33253 | ||
|
|
6be0fceb78 | ||
|
|
435fb23fd3 | ||
|
|
1d9b3a42b1 | ||
|
|
bd9b42fd9e | ||
|
|
9164541750 | ||
|
|
1394d6420d | ||
|
|
c245ec80ff | ||
|
|
d8ecd34960 | ||
|
|
37ffa194e2 | ||
|
|
7fde03e88e | ||
|
|
72f204825e | ||
|
|
02667f4348 | ||
|
|
355926ddff | ||
|
|
20922fb494 | ||
|
|
c710fb56e2 | ||
|
|
d15c5c9e89 | ||
|
|
e5460bf605 | ||
|
|
bb59378a2d | ||
|
|
c03d85b83f | ||
|
|
a02cdbcf88 | ||
|
|
3048527635 | ||
|
|
264948a88a | ||
|
|
9ccdcf7aec | ||
|
|
f518a06ed2 | ||
|
|
4e87a6309f | ||
|
|
25f515ffa7 | ||
|
|
2794ff7234 | ||
|
|
859062204c | ||
|
|
8d14b14c65 | ||
|
|
1da48a7f1a | ||
|
|
910ad7ed76 | ||
|
|
a9d82566ef | ||
|
|
4d3d61b887 | ||
|
|
b6b7118fd8 | ||
|
|
701ff55588 | ||
|
|
2a9f5dc434 | ||
|
|
95d518d42f | ||
|
|
f70cf00fd1 | ||
|
|
2e29f8e20f | ||
|
|
5786e84974 | ||
|
|
83683b497f | ||
|
|
a26bf00634 | ||
|
|
a81e21fd99 | ||
|
|
b0dfa2e5b7 | ||
|
|
e9c7f63d5d | ||
|
|
7412837739 | ||
|
|
8d07cfa772 | ||
|
|
053ca27abe | ||
|
|
c4d20da02e | ||
|
|
088ba1babd | ||
|
|
372eb8439a | ||
|
|
90292762c9 | ||
|
|
dbba54c7ad | ||
|
|
6ea96c769a | ||
|
|
b4dc90c3ea | ||
|
|
e6d9143fa8 | ||
|
|
fba021aeff | ||
|
|
a4ebe56254 | ||
|
|
6d97345719 | ||
|
|
572df5a8e5 | ||
|
|
27233c2272 | ||
|
|
c1a83648d3 | ||
|
|
1abced50ca | ||
|
|
a5248f0f39 | ||
|
|
15532d8f9a | ||
|
|
0fdd491bfd | ||
|
|
4fc0151e9f | ||
|
|
8974e8cde0 | ||
|
|
e73ce3a702 | ||
|
|
7755060c6a | ||
|
|
714b885dc5 | ||
|
|
f566a19857 | ||
|
|
d5376799cf | ||
|
|
be94303365 | ||
|
|
c3e982ee8a | ||
|
|
ed38c213ac | ||
|
|
eb69e1e67b | ||
|
|
c0400d7999 | ||
|
|
6de7391be3 | ||
|
|
82140ace20 | ||
|
|
8c1c0011a2 | ||
|
|
dba60cd866 | ||
|
|
f87527c7c0 | ||
|
|
63f6c438ee | ||
|
|
897553f127 | ||
|
|
f337ad3197 | ||
|
|
0df26727da | ||
|
|
8180b284af | ||
|
|
b6439a679b | ||
|
|
3f0f8eb5d9 | ||
|
|
1d7331cb86 | ||
|
|
631c13bb2d | ||
|
|
c21eee838a | ||
|
|
79fa8805b2 | ||
|
|
9b7be4224a | ||
|
|
82e643140e | ||
|
|
8a3b2113f8 | ||
|
|
cddce88207 | ||
|
|
913382c7f2 | ||
|
|
68fb72aa9f | ||
|
|
ee085bcf07 | ||
|
|
1774efd26c | ||
|
|
2fef90d375 | ||
|
|
0dcdd617c3 | ||
|
|
01480b74de | ||
|
|
b68fc778f1 | ||
|
|
259ef0fe7e | ||
|
|
64e4a84012 | ||
|
|
57abb0b28f | ||
|
|
d757b95b44 | ||
|
|
43af39c03a | ||
|
|
19a5dc9c27 | ||
|
|
fd8df5041a | ||
|
|
421482ec3a | ||
|
|
25004f1a7a | ||
|
|
2a0c2b276a | ||
|
|
5e7b74ef46 | ||
|
|
88f7813a3f | ||
|
|
924cc44a20 | ||
|
|
ad8bd1bff4 | ||
|
|
bdf014b4ff | ||
|
|
57c64d8e29 | ||
|
|
a34f7570cc | ||
|
|
e3469875c2 | ||
|
|
ed8fd2bc89 | ||
|
|
3bbc627473 | ||
|
|
91be64626f | ||
|
|
552cd5eb9e | ||
|
|
946bbb1c65 | ||
|
|
2192b340c9 | ||
|
|
b2801794ea | ||
|
|
b863afaa1a | ||
|
|
c80e049844 | ||
|
|
e2fe15f09f | ||
|
|
991d8a6e6f | ||
|
|
786a6e0b5a | ||
|
|
99f737d0b3 | ||
|
|
0d333ac83a | ||
|
|
76e7001e51 | ||
|
|
785813b842 | ||
|
|
ceebba6253 | ||
|
|
e6eb066911 | ||
|
|
7d10360c81 | ||
|
|
dea93456b0 | ||
|
|
a6b63be03b | ||
|
|
78ffcec66d | ||
|
|
731f26e3d4 | ||
|
|
5c1e91467f | ||
|
|
76776cdcd4 | ||
|
|
740cf021f8 | ||
|
|
fa7071ecb6 | ||
|
|
b9bc22d1d2 | ||
|
|
5e9e682932 | ||
|
|
1e00cbb9e7 | ||
|
|
c9353e0236 | ||
|
|
89d87a143d | ||
|
|
e0ee2142b5 | ||
|
|
a7948d97c6 | ||
|
|
ec9b9676fb | ||
|
|
7aad759c26 | ||
|
|
c70b6a0ea9 | ||
|
|
7fda70965f | ||
|
|
e720776365 | ||
|
|
af947d3fd2 | ||
|
|
151cbcd49f | ||
|
|
92a7c88e42 | ||
|
|
557bc521a5 | ||
|
|
c6ec847306 | ||
|
|
ec128823d9 | ||
|
|
e97d4c5f68 | ||
|
|
0401d824eb | ||
|
|
76d8973509 | ||
|
|
f678576a1a | ||
|
|
7aa4b2d22a | ||
|
|
adc1b2bf31 | ||
|
|
9839c0bdda | ||
|
|
724fca9fec | ||
|
|
a63b2a41e0 | ||
|
|
a6083d3402 | ||
|
|
97e06e3726 | ||
|
|
2e8ef6739c | ||
|
|
7b66575ea8 | ||
|
|
9268db4bcc | ||
|
|
ded7fa9a14 | ||
|
|
0383c035f4 | ||
|
|
8614ba0731 | ||
|
|
dad95b3fd2 | ||
|
|
9f94a828d1 | ||
|
|
31cadc18a6 | ||
|
|
eb78e1e769 | ||
|
|
0c2298524c | ||
|
|
d2b3759913 | ||
|
|
370e9ae72b | ||
|
|
3efefb51d9 | ||
|
|
8326c1b5f9 | ||
|
|
9516460d02 | ||
|
|
836b80ea67 | ||
|
|
d970522b31 | ||
|
|
cde92696ca | ||
|
|
16edaab2c6 | ||
|
|
ce656b97bc | ||
|
|
78fad1bbe0 | ||
|
|
f84b1d55d3 | ||
|
|
45fe704285 | ||
|
|
c341edc33a | ||
|
|
e623e067ac | ||
|
|
181fd93e69 | ||
|
|
86f8a936fc | ||
|
|
21692a9c71 | ||
|
|
973a4f6fa9 | ||
|
|
78adcf1caf | ||
|
|
8bd4effe9b | ||
|
|
c71234d71e | ||
|
|
bc13e83f90 | ||
|
|
9848da5608 | ||
|
|
371414d1e0 | ||
|
|
8f696b574f | ||
|
|
c6b5bfb7f0 | ||
|
|
5aadb5ab4f | ||
|
|
48cd95a11e | ||
|
|
9a877fa18e | ||
|
|
df1dc29735 | ||
|
|
b407da74ff | ||
|
|
afb6ab1c09 | ||
|
|
29a07ca099 | ||
|
|
7f2557ba7b | ||
|
|
90735948bb | ||
|
|
69abf6961a | ||
|
|
52143238f0 | ||
|
|
c7e43762b4 | ||
|
|
5011a3cf10 | ||
|
|
6bf2e3a5eb | ||
|
|
1da45805f6 | ||
|
|
3c8f43a575 | ||
|
|
0c2ec57ed0 | ||
|
|
6a51292016 | ||
|
|
1faf89e474 | ||
|
|
95ee5f7a35 | ||
|
|
1c222b3812 | ||
|
|
a369fed608 | ||
|
|
5c1d0b4096 | ||
|
|
a45b82f9b9 | ||
|
|
e713d80487 | ||
|
|
1b52a06029 | ||
|
|
cd90affedf | ||
|
|
a4fd6f380e | ||
|
|
ef21083c33 | ||
|
|
c3b8b53c6c | ||
|
|
958710440c | ||
|
|
df681c295e | ||
|
|
062e3630a3 | ||
|
|
fcecdade1a | ||
|
|
7adc036dcd | ||
|
|
bceda13245 | ||
|
|
306bfaf0eb | ||
|
|
9f5159afb7 | ||
|
|
0449442783 | ||
|
|
d04f12fb7b | ||
|
|
77d7fb9eb5 | ||
|
|
b047ccd9ef | ||
|
|
69b4aa4b33 | ||
|
|
c1d56ebb55 | ||
|
|
fe722acacc | ||
|
|
47d5889c33 | ||
|
|
8eb9e99924 | ||
|
|
c5f86108c7 | ||
|
|
c9d1b5312f | ||
|
|
626a6acb5f | ||
|
|
0b2819649c | ||
|
|
aa724d3af9 | ||
|
|
47b7b27376 | ||
|
|
62944eb976 | ||
|
|
4bca663bb3 | ||
|
|
40d3fd3a21 | ||
|
|
fca61a82eb | ||
|
|
fedaedca8c | ||
|
|
2645a9b9dc | ||
|
|
bcbfeed151 | ||
|
|
00e9083d09 | ||
|
|
4fb88537cb | ||
|
|
3d2bd86284 | ||
|
|
367f63e94e | ||
|
|
c0a2e3331f | ||
|
|
db7f993c18 | ||
|
|
01a42c7d68 | ||
|
|
38bceb1a53 | ||
|
|
8169e2dd07 | ||
|
|
2cec9f92ab | ||
|
|
587eddfff5 | ||
|
|
9c965c83b1 | ||
|
|
0a9b439368 | ||
|
|
1ef809d062 | ||
|
|
132944e176 | ||
|
|
93005c895b | ||
|
|
0c0e455336 | ||
|
|
cd491441f0 | ||
|
|
c9446e151c | ||
|
|
c5ef3404b7 | ||
|
|
c39f71f64c | ||
|
|
72ad1a546b | ||
|
|
ae68d41a90 | ||
|
|
99fbd85c95 | ||
|
|
2f6f12e3e3 | ||
|
|
38c280dc05 | ||
|
|
a2a78c1a87 | ||
|
|
41ee83d6ea | ||
|
|
ea0a18e5dd | ||
|
|
6d53455d41 | ||
|
|
664b6c473f | ||
|
|
e36fa619ae | ||
|
|
0f68967044 | ||
|
|
c9a2b9367c | ||
|
|
dcb01e5034 | ||
|
|
8cef8df219 | ||
|
|
fd6db734f4 | ||
|
|
379f0ecb31 | ||
|
|
fa5bdac1be | ||
|
|
8d57fd4774 | ||
|
|
05980fe33c | ||
|
|
a6e87696b8 | ||
|
|
26fcc9f6cb | ||
|
|
6c437ba258 | ||
|
|
d5a2a75ddc | ||
|
|
aaab4e2bbb | ||
|
|
c3f7ed891f | ||
|
|
d45829ed61 | ||
|
|
f0bf000f4a | ||
|
|
2da1002d38 | ||
|
|
2403646488 | ||
|
|
99af0e8339 | ||
|
|
81b935cadf | ||
|
|
bf395b061f | ||
|
|
79eb814677 | ||
|
|
cbd8872569 | ||
|
|
cc96eb150c | ||
|
|
8c4f23dc56 | ||
|
|
6fb415bc32 | ||
|
|
96e29f6856 | ||
|
|
ec515baca2 | ||
|
|
7eaec006fa | ||
|
|
6968e0cdff | ||
|
|
24ba522cfb | ||
|
|
5777966d82 | ||
|
|
ab820b963a | ||
|
|
30d2b8f394 | ||
|
|
34512e34dd | ||
|
|
08195afdcb | ||
|
|
12c70f7afd | ||
|
|
7135c594e3 | ||
|
|
0606b22c39 | ||
|
|
aa9f57cab1 | ||
|
|
a7bb74f8af | ||
|
|
817ec47a20 | ||
|
|
68c8827630 | ||
|
|
dbbd518978 | ||
|
|
f6e94162cb | ||
|
|
8e146e52f6 | ||
|
|
0cb3de6fae | ||
|
|
65f0fcd4f9 | ||
|
|
4afcbda9db | ||
|
|
6df3af6978 | ||
|
|
a0ab993ffb | ||
|
|
505e2ef47b | ||
|
|
956dfaef6b | ||
|
|
2c442b4e60 | ||
|
|
19717de985 | ||
|
|
b6e0d28017 | ||
|
|
7f89cf80a7 | ||
|
|
e6b8064d23 | ||
|
|
6319b06608 | ||
|
|
e62fbe7d21 | ||
|
|
8d7252c0f9 | ||
|
|
e344b9615a | ||
|
|
f319238567 | ||
|
|
8f49e4c1ee | ||
|
|
fe728ad3c2 | ||
|
|
03db8aa60a | ||
|
|
2aaa461e30 | ||
|
|
e7cddf00f9 | ||
|
|
8d7d3ebabc | ||
|
|
41f71a2f95 | ||
|
|
bab88edd10 | ||
|
|
77023e65e2 | ||
|
|
65d1b9cd61 | ||
|
|
b94fa58db6 | ||
|
|
3897923840 | ||
|
|
8cc5963ce0 | ||
|
|
fda32fe306 | ||
|
|
047dd256f0 | ||
|
|
4e6d8cc5c4 | ||
|
|
f0d44b76f8 | ||
|
|
9b1d1429a3 | ||
|
|
181d9741e2 | ||
|
|
48f85e9b69 | ||
|
|
d4a2798dc8 | ||
|
|
e719dc632b | ||
|
|
9652abf10b | ||
|
|
572c63f011 | ||
|
|
0578863317 | ||
|
|
bdf531f80e | ||
|
|
76aa1d1417 | ||
|
|
2fee4b359b | ||
|
|
789a3cc1e7 | ||
|
|
b92ff8146a | ||
|
|
3afb93a21a | ||
|
|
31b47cac1d | ||
|
|
748f329f38 | ||
|
|
324b0438f4 | ||
|
|
4505e2df1e | ||
|
|
8b1737df91 | ||
|
|
fdfe832ef9 | ||
|
|
035f47d6c1 | ||
|
|
8219c25e42 | ||
|
|
0ebd18de16 | ||
|
|
1adff62a45 | ||
|
|
7b0d3c3232 | ||
|
|
8cc9a66948 | ||
|
|
a43d9a327f | ||
|
|
b2f38b21e1 | ||
|
|
4064e60976 | ||
|
|
23ad15e662 | ||
|
|
527904d49b | ||
|
|
82047a5e2d | ||
|
|
a8190faebc | ||
|
|
15b6c2788f | ||
|
|
68a72befcd | ||
|
|
dc7a347802 | ||
|
|
96c4cb443a | ||
|
|
adf72fa296 | ||
|
|
c4168b6c07 | ||
|
|
4e58c0a77f | ||
|
|
3462879cf9 | ||
|
|
59e7edb5e3 | ||
|
|
9c4342c57e | ||
|
|
12c8ea2299 | ||
|
|
814e1eaac9 | ||
|
|
f047ae1e1f | ||
|
|
521dfe2f2c | ||
|
|
2d4939f14c | ||
|
|
4b70d862de | ||
|
|
b66888ada0 | ||
|
|
721b1d1a25 | ||
|
|
28e822fbb1 | ||
|
|
e8a2a34e21 | ||
|
|
47e4cecdef | ||
|
|
88585b793e | ||
|
|
1debe715d4 | ||
|
|
fd585bcfbf | ||
|
|
dc8290ff28 | ||
|
|
9bcf20269a | ||
|
|
845d0a423e | ||
|
|
b9811a5391 | ||
|
|
6745c50d6b | ||
|
|
ddfb94be01 | ||
|
|
375046d270 | ||
|
|
1b9b21bacd | ||
|
|
7a62057a30 | ||
|
|
5a08358f62 | ||
|
|
b03213d61a | ||
|
|
3050d56bf8 | ||
|
|
c73f70d99c | ||
|
|
f86a5382f0 | ||
|
|
e5013599c8 | ||
|
|
dd357ce7e3 | ||
|
|
120006c866 | ||
|
|
62934c10c0 | ||
|
|
f6fc3b4c81 | ||
|
|
92438a301e | ||
|
|
f90af8d2e1 | ||
|
|
2e58559407 | ||
|
|
016f95052a | ||
|
|
79c3844d6e | ||
|
|
6f28e10041 | ||
|
|
736ae6abea | ||
|
|
47a1668cd5 | ||
|
|
605468e891 | ||
|
|
9ede1f8d2b | ||
|
|
1e7dcd7188 | ||
|
|
847c42220e | ||
|
|
7606f91480 | ||
|
|
c86372b0fc | ||
|
|
4175eaa271 | ||
|
|
d02de27ff5 | ||
|
|
66a4ba19f7 | ||
|
|
7bf551dfce | ||
|
|
532bc851c0 | ||
|
|
124ab116e7 | ||
|
|
b00fe9a33e | ||
|
|
98149e1fe5 | ||
|
|
01c95f90f5 | ||
|
|
c00e92c6f4 | ||
|
|
2d0d8d5a50 | ||
|
|
1bf34d0bdb | ||
|
|
1b51bf9389 | ||
|
|
a81bd8600a | ||
|
|
09316e6363 | ||
|
|
3fef6379dc | ||
|
|
8765f72641 | ||
|
|
c3183e746c | ||
|
|
56d7db4257 | ||
|
|
084550d84c | ||
|
|
95f6ae4b57 | ||
|
|
795686df05 | ||
|
|
b776e03da8 | ||
|
|
d7677a5f7a | ||
|
|
5145b8d03c | ||
|
|
48971cb7d3 | ||
|
|
787ea6f1e4 | ||
|
|
fab74f236a | ||
|
|
35e2460773 | ||
|
|
57faabd8dd | ||
|
|
4850d3558c | ||
|
|
135b78df1f | ||
|
|
a64d2d8bc8 | ||
|
|
5304feaebb | ||
|
|
de007073bc | ||
|
|
8de842f154 | ||
|
|
c501b162e8 | ||
|
|
a31cc6c759 | ||
|
|
bfcfb6d140 | ||
|
|
e08c72f98c | ||
|
|
bbce393a5f | ||
|
|
3b8e4ce952 | ||
|
|
1dbf9931ea | ||
|
|
96e46fdbbb | ||
|
|
d62bd31aa9 | ||
|
|
2e685ef9dd | ||
|
|
074ae2bdaf | ||
|
|
ceb5ed309e | ||
|
|
a588bffb17 | ||
|
|
84c5981e60 | ||
|
|
0a17e736e8 | ||
|
|
68a18ef869 | ||
|
|
f351b6bb99 | ||
|
|
a5fccb3a5e | ||
|
|
68b6317f1a | ||
|
|
1504496219 | ||
|
|
4eb4fe995e | ||
|
|
0be7813428 | ||
|
|
9a4df38122 | ||
|
|
a7f147eb1f | ||
|
|
90ee089b37 | ||
|
|
f53a6ecedd | ||
|
|
831f163ea9 | ||
|
|
6a3f945ce5 | ||
|
|
c90253993b | ||
|
|
aa0d35c854 | ||
|
|
5830c4c06b | ||
|
|
f5facd0b70 | ||
|
|
acadfb5b19 | ||
|
|
3b4cfe2af7 | ||
|
|
1fa7a7e439 | ||
|
|
fb35851cc5 | ||
|
|
6e3ca04fe8 | ||
|
|
2a1bcc51ba | ||
|
|
70360f3760 | ||
|
|
c7816f5d15 | ||
|
|
73c73fbcff | ||
|
|
a9b3adb368 | ||
|
|
3fbfb6c12f | ||
|
|
0f6e9f8161 | ||
|
|
f8bbf7a278 | ||
|
|
4933b6f8b9 | ||
|
|
89a5c3b485 | ||
|
|
f2e7c67457 | ||
|
|
3d0ed3aeb5 | ||
|
|
296327ba7d | ||
|
|
b25ca4eadd | ||
|
|
e0c17676cd | ||
|
|
b1b3ed4f28 | ||
|
|
e3ce00ca18 | ||
|
|
a65ae66e03 | ||
|
|
5b0b779530 | ||
|
|
47974a4711 | ||
|
|
6daf87ceaf | ||
|
|
d789118aa7 | ||
|
|
d8c1abfba9 | ||
|
|
3b50d84af8 | ||
|
|
520558166b | ||
|
|
5d51159832 | ||
|
|
2daa9607d2 | ||
|
|
94d7d50789 | ||
|
|
f38b5ad3e9 | ||
|
|
80c37d9cba | ||
|
|
ec2ea167b3 | ||
|
|
c04614e95b | ||
|
|
f13ba939a4 | ||
|
|
1a84051252 | ||
|
|
c873e85a1c | ||
|
|
fd47f9ca8b | ||
|
|
1a52a9f769 | ||
|
|
f88d53b132 | ||
|
|
2339327a6e | ||
|
|
9fd9cd301b | ||
|
|
b55598b5d9 | ||
|
|
a810e3365e | ||
|
|
ac6a07b9b2 | ||
|
|
3aadcbd9e8 | ||
|
|
17c65267d5 | ||
|
|
31e2681e2f | ||
|
|
811d8f0737 | ||
|
|
4a3dbd3aef | ||
|
|
f71d641f8f | ||
|
|
94004af00c | ||
|
|
2535da4e40 | ||
|
|
0570bfd45a | ||
|
|
ef329b9b71 | ||
|
|
c14c9717b7 | ||
|
|
e0ca8eab68 | ||
|
|
0ee6a9fde7 | ||
|
|
182ebeca50 | ||
|
|
a41a1c1728 | ||
|
|
c2c0cfcc58 | ||
|
|
41488aecf4 | ||
|
|
70f9eda634 | ||
|
|
b246c2029f | ||
|
|
733ced2697 | ||
|
|
55e50edca7 | ||
|
|
bb40c21f97 | ||
|
|
bc397c1ef1 | ||
|
|
5c78119a3a | ||
|
|
9372bf3bf0 | ||
|
|
68d243c93a | ||
|
|
758f7cebc4 | ||
|
|
34633c6e33 | ||
|
|
36d56828f0 | ||
|
|
7a89defb3b | ||
|
|
c4967ae120 | ||
|
|
5f1c6549c9 | ||
|
|
77c374f9b7 | ||
|
|
52c7552b67 | ||
|
|
130538f462 | ||
|
|
440d5dc677 | ||
|
|
71f5be88df | ||
|
|
c3935c993b | ||
|
|
78bf0f6750 | ||
|
|
4db99a148e | ||
|
|
9663898e9b | ||
|
|
76ece66b66 | ||
|
|
de92bae7f5 | ||
|
|
1f342c4090 | ||
|
|
0be7816edc | ||
|
|
7d2cecd6be | ||
|
|
733d839b85 | ||
|
|
850be93973 | ||
|
|
5bbde84a7c | ||
|
|
d0e53ec1f5 | ||
|
|
f241243d28 | ||
|
|
a6d238e4df | ||
|
|
c8e8e841e6 | ||
|
|
bc71e67ca4 | ||
|
|
22b62e36cd | ||
|
|
2931dfc99b | ||
|
|
165ca4ad32 | ||
|
|
a42b746abf | ||
|
|
a8fa6a2901 | ||
|
|
5be5934ea2 | ||
|
|
e629f70906 | ||
|
|
26e4214a50 | ||
|
|
be5056f712 | ||
|
|
62250f890a | ||
|
|
a2e7bb7ffb | ||
|
|
a6a7c86975 | ||
|
|
4d9db5cba8 | ||
|
|
253b5d75d9 | ||
|
|
e8ed8fa815 | ||
|
|
e606361371 | ||
|
|
7c4e56b00c | ||
|
|
02081d9bb5 | ||
|
|
977d3846f4 | ||
|
|
657ace250a | ||
|
|
9efe6ab34e | ||
|
|
ce4d49cc8f | ||
|
|
43af2219d1 | ||
|
|
bf8563bd74 | ||
|
|
8f1a8f60e3 | ||
|
|
cbdacf299b | ||
|
|
a7d213630c | ||
|
|
0fecac6cca | ||
|
|
fd71c4406e | ||
|
|
546baa09cc | ||
|
|
05ce8923e6 | ||
|
|
7e96a1e584 | ||
|
|
2b07dccc39 | ||
|
|
d99ef644ad | ||
|
|
e27e547f8d | ||
|
|
b79e38af34 | ||
|
|
f1f374763c | ||
|
|
052cdc229a | ||
|
|
2673892974 | ||
|
|
d55680425b | ||
|
|
29c7af98f2 | ||
|
|
2afacf6408 | ||
|
|
7ccb9298c0 | ||
|
|
7f5d52ca30 | ||
|
|
4c7b8e17e1 | ||
|
|
0483250196 | ||
|
|
94e041e002 | ||
|
|
5702b4cf8a | ||
|
|
93424424eb | ||
|
|
b429f646f3 | ||
|
|
95a15f4b6e | ||
|
|
84cbdfaba0 | ||
|
|
47e018bb99 | ||
|
|
8041de8e56 | ||
|
|
c010bcc4df | ||
|
|
b92949fd18 | ||
|
|
26cd2feb1b | ||
|
|
f05f061d89 | ||
|
|
09d858486d | ||
|
|
a5bb390479 | ||
|
|
7298df62a9 | ||
|
|
8427c1bd70 | ||
|
|
3830279f69 | ||
|
|
f204f5d341 | ||
|
|
f618fabfda | ||
|
|
2bae0af081 | ||
|
|
e6c7682380 | ||
|
|
bd950fc1e7 | ||
|
|
9a721102c0 | ||
|
|
11d473373d | ||
|
|
51dcb53aa5 | ||
|
|
49dab75c35 | ||
|
|
24432e0d0d | ||
|
|
4595a70239 | ||
|
|
67ab79aa68 | ||
|
|
6ac548ee37 | ||
|
|
2f60f92dbd | ||
|
|
7e8136752b | ||
|
|
083cc921a1 | ||
|
|
e320dca191 | ||
|
|
a025953f3f | ||
|
|
e76f9f59b5 | ||
|
|
2a87d4f10e | ||
|
|
7e0b1d9034 | ||
|
|
a0c1fa293a | ||
|
|
df99dd2b92 | ||
|
|
5bb185e181 | ||
|
|
e106d3defd | ||
|
|
0458d93d96 | ||
|
|
cb16d2186e | ||
|
|
b992baf136 | ||
|
|
9a57ad1b59 | ||
|
|
991e093b8d | ||
|
|
b11b0faf0e | ||
|
|
26d558c725 | ||
|
|
9df7c1802a | ||
|
|
03e608b1b1 | ||
|
|
946b646822 | ||
|
|
3b0d7df4df | ||
|
|
b79bea899c | ||
|
|
b6bcad56e1 | ||
|
|
d2ce406088 | ||
|
|
d8bae1142b | ||
|
|
947cf6df0d | ||
|
|
10934c53e0 | ||
|
|
7383c8731d | ||
|
|
4757bf263c | ||
|
|
b6f7a834d8 | ||
|
|
a20f454642 | ||
|
|
bbf3f884be | ||
|
|
b17615bb11 | ||
|
|
5500e89579 | ||
|
|
57b1b68fb3 | ||
|
|
aa3679737b | ||
|
|
568a141b21 | ||
|
|
7f13a71771 | ||
|
|
a85623dbbf | ||
|
|
558deadd45 | ||
|
|
eb3085a23c | ||
|
|
c93957bb18 | ||
|
|
9379297be1 | ||
|
|
4dbfbe262e | ||
|
|
6cf5577907 | ||
|
|
19c0c45433 | ||
|
|
dfc8519e28 | ||
|
|
2de4f4f8b6 | ||
|
|
7a3c2b7c86 | ||
|
|
daabc07268 | ||
|
|
1257075c03 | ||
|
|
52218cafaf | ||
|
|
7678ce5dbe | ||
|
|
c10477f4e0 | ||
|
|
e8adcafecc | ||
|
|
44fc43d5c6 | ||
|
|
5c49e8d7bc | ||
|
|
4901c46457 | ||
|
|
03433f0329 | ||
|
|
d7eecc5de8 | ||
|
|
fb9572c6cf | ||
|
|
f17ac8728b | ||
|
|
94c6ac091f | ||
|
|
cfecbbf3e2 | ||
|
|
39797f82ab | ||
|
|
9273e61cb4 | ||
|
|
2cbf055fe2 | ||
|
|
696afe722f | ||
|
|
14d84f821f | ||
|
|
f8a3c5ede9 | ||
|
|
2aa9af8778 | ||
|
|
f43b3b50e5 | ||
|
|
f9359636a3 | ||
|
|
a9edf426e9 | ||
|
|
2caa7b1487 | ||
|
|
d86cb764ab | ||
|
|
0ffe14cbdf | ||
|
|
7e83116a61 | ||
|
|
0db90fe416 | ||
|
|
b6b19cc8b7 | ||
|
|
b9349d53a0 | ||
|
|
b6c6600f1f | ||
|
|
e72f9a635c | ||
|
|
c8cc177bbe | ||
|
|
54d33022b3 | ||
|
|
8605ac49cd | ||
|
|
1ded3c391c | ||
|
|
6dead53184 | ||
|
|
a16ec66017 | ||
|
|
4102c85f5f | ||
|
|
3f0ff5d40f | ||
|
|
e84c8695ab | ||
|
|
adc5f9438a | ||
|
|
c426bd1da3 | ||
|
|
d40fed6213 | ||
|
|
c581c11d04 | ||
|
|
f240877928 | ||
|
|
a0d245b818 | ||
|
|
2d00c1df13 | ||
|
|
4851a8a4ed | ||
|
|
a84f338534 | ||
|
|
627719a967 | ||
|
|
f1d4b80636 | ||
|
|
60a7610a28 | ||
|
|
417c928b77 | ||
|
|
92ba3f1a82 | ||
|
|
1a2513920b | ||
|
|
a680b3ce0a | ||
|
|
22c8743aa8 | ||
|
|
267ac9a86e | ||
|
|
f71dcb51e9 | ||
|
|
b9aa04f360 | ||
|
|
611bee8a99 | ||
|
|
7f09c1a999 | ||
|
|
aa1ca995f0 | ||
|
|
7714729093 | ||
|
|
9d06c2bda7 | ||
|
|
f8ec4cd888 | ||
|
|
eee681a5a5 | ||
|
|
c980ca3ae4 | ||
|
|
554912a18f | ||
|
|
c3ca04a747 | ||
|
|
5186bbd4f1 | ||
|
|
adbd1bb88d | ||
|
|
fa38f69f75 | ||
|
|
01bb2c02e6 | ||
|
|
49734b935d | ||
|
|
e57dc9867d | ||
|
|
da2711e307 | ||
|
|
730cf0d724 | ||
|
|
c9af861073 | ||
|
|
5f8984d2d1 | ||
|
|
ef6d3af3a9 | ||
|
|
84dd2d7b9f | ||
|
|
a5257c62f6 | ||
|
|
de6a3cc2ca | ||
|
|
743870d2fb | ||
|
|
9bc885ceab | ||
|
|
a53b484fad | ||
|
|
ffe5a70f01 | ||
|
|
b6e578015c | ||
|
|
0685003d9c | ||
|
|
566367d8ba | ||
|
|
df5565caae | ||
|
|
c05a49028c | ||
|
|
dd322529c4 | ||
|
|
8c17f7c711 | ||
|
|
a3e3a6a950 | ||
|
|
9461488dec | ||
|
|
5300959499 | ||
|
|
228b8b1928 | ||
|
|
380a6cd60e | ||
|
|
0a16370ba7 | ||
|
|
f366b9a0dc | ||
|
|
f8e5d63fba | ||
|
|
1d97e9b37e | ||
|
|
e3fba808b1 | ||
|
|
c523f44cd9 | ||
|
|
cef9af4d2d | ||
|
|
d40e0d0080 | ||
|
|
9f3e2fddb9 | ||
|
|
824e061996 | ||
|
|
53cf85046b | ||
|
|
3c2603b33f | ||
|
|
c97fc41399 | ||
|
|
7942c8b121 | ||
|
|
2a9eeda325 | ||
|
|
36592d55f9 | ||
|
|
ee846b7dff | ||
|
|
4055b52ba0 | ||
|
|
407e3a67d0 | ||
|
|
8bb17343bf | ||
|
|
66987b9339 | ||
|
|
fa7a8a0e0f | ||
|
|
4c9a17208c | ||
|
|
cc61a0cb89 | ||
|
|
f2a98d6bae | ||
|
|
0b082b8484 | ||
|
|
aff12c0f7e | ||
|
|
7998ffb906 | ||
|
|
b738589414 | ||
|
|
151dbd5b47 | ||
|
|
0236493e53 | ||
|
|
d503d3a41b | ||
|
|
2b531039c0 | ||
|
|
ebd0fe64af | ||
|
|
49031905c8 | ||
|
|
ab863addd0 | ||
|
|
1a028a1fbf | ||
|
|
195176e04a | ||
|
|
ca524be461 | ||
|
|
f1f0659899 | ||
|
|
7e2d16ed20 | ||
|
|
8068b2eb68 | ||
|
|
f39426686e | ||
|
|
750623ea55 | ||
|
|
61b2e42472 | ||
|
|
fdb9cd7b43 | ||
|
|
d8d14dd77c | ||
|
|
82723e101f | ||
|
|
bd298544bd | ||
|
|
17b3450deb | ||
|
|
9ae2634ec8 | ||
|
|
18e6c82e28 | ||
|
|
a1f42d8646 | ||
|
|
f00071ce3c | ||
|
|
4afc7dd0bd | ||
|
|
d1f0dd3323 | ||
|
|
3b27913da3 | ||
|
|
6372b946bd | ||
|
|
5694a8d484 | ||
|
|
a57826201a | ||
|
|
f6211944be | ||
|
|
d2820f942e | ||
|
|
cbe270594d | ||
|
|
df44f83341 | ||
|
|
60b2016fdf | ||
|
|
1fbbc31ff0 | ||
|
|
95c7ca62ff | ||
|
|
77cae92007 | ||
|
|
835ffb7f39 | ||
|
|
c8901a7c1b | ||
|
|
82d734f96e | ||
|
|
744ca2c219 | ||
|
|
d6d31ffb14 | ||
|
|
8754511c64 | ||
|
|
48bd42a795 | ||
|
|
c26f1d67e4 | ||
|
|
11459c45c2 | ||
|
|
492236376c | ||
|
|
f46790b7f0 | ||
|
|
55b13366b5 | ||
|
|
67ae9159bc | ||
|
|
9a4004a166 | ||
|
|
0cab9db135 | ||
|
|
5baf8772cc | ||
|
|
d67d85781b | ||
|
|
0756da0293 | ||
|
|
815c757367 | ||
|
|
157cb80c93 | ||
|
|
7053132cfc | ||
|
|
9d6022a0b2 | ||
|
|
6e71c57079 | ||
|
|
e13aee068e | ||
|
|
cbc2a26eac | ||
|
|
7ea5f6e81c | ||
|
|
4f1372c38c | ||
|
|
6348f0bec8 | ||
|
|
afbea29b36 | ||
|
|
7f23474b6d | ||
|
|
4efa995599 | ||
|
|
7774626ec3 | ||
|
|
14939ae97b | ||
|
|
30757fa0ec | ||
|
|
c6118d50ff | ||
|
|
f14bfefdf4 | ||
|
|
a746cbc889 | ||
|
|
87d74c0100 | ||
|
|
3006237805 | ||
|
|
4d6dffc35d | ||
|
|
96070e2591 | ||
|
|
efcd71cb02 | ||
|
|
5dfa27518e | ||
|
|
770753e1f8 | ||
|
|
aa92447e4e | ||
|
|
c76e765904 | ||
|
|
ab7c0ecfe8 | ||
|
|
131139f6af | ||
|
|
b1d1b0764d | ||
|
|
41b14437cf | ||
|
|
76166ee719 | ||
|
|
6b94159cce | ||
|
|
db1c25c2f3 | ||
|
|
9d4bb0b68b | ||
|
|
35a428b801 | ||
|
|
5ff69f557b | ||
|
|
480b3b040d | ||
|
|
86d977bb74 | ||
|
|
ca659e18e5 | ||
|
|
1db3928c2a | ||
|
|
70a301ecf5 | ||
|
|
4047cce1cd | ||
|
|
4a3cc246dc | ||
|
|
0ccdda4815 | ||
|
|
4eec51e271 | ||
|
|
88d0eb2ef6 | ||
|
|
e264c18b7c | ||
|
|
d0b6553251 | ||
|
|
9b7a8a00e3 | ||
|
|
c4afbfb2ed | ||
|
|
5549434132 | ||
|
|
53b4bb81cb | ||
|
|
f2ca6da200 | ||
|
|
350ab85143 | ||
|
|
82a3a42c07 | ||
|
|
26be5df1e9 | ||
|
|
cc61d70218 | ||
|
|
4eb3ac6f10 | ||
|
|
132aeb3e5c | ||
|
|
e460ed34c2 | ||
|
|
40cb7d5597 | ||
|
|
970709d2ef | ||
|
|
0fa7ec8cea | ||
|
|
1649756f29 | ||
|
|
9a52e5f7dd | ||
|
|
3b3a984689 | ||
|
|
ab2a1b0942 | ||
|
|
01e1cefc70 | ||
|
|
3dc7a89718 | ||
|
|
cf3289048a | ||
|
|
857c39e872 | ||
|
|
0ca90f223e | ||
|
|
d6e90896ec | ||
|
|
470064500c | ||
|
|
f7f2d3252a | ||
|
|
dd7ac82527 | ||
|
|
d09a05fc5f | ||
|
|
581b1eea4d | ||
|
|
1bcf63f84e | ||
|
|
def2846e97 | ||
|
|
6563f0c09f | ||
|
|
ce50a53d33 | ||
|
|
3d4ed9b66c | ||
|
|
da45f27cd1 | ||
|
|
75a713dfbb | ||
|
|
2f8e2f285b | ||
|
|
303849ea43 | ||
|
|
2fbdf66783 | ||
|
|
ee34c88276 | ||
|
|
a4a06838c2 | ||
|
|
156d3e8c50 | ||
|
|
84aa951149 | ||
|
|
7641ba0fa8 | ||
|
|
5953b4a538 | ||
|
|
948b4a6692 | ||
|
|
d1cb1bfe98 | ||
|
|
a06ecd9d17 | ||
|
|
0f86a3197e | ||
|
|
cff94a3a60 | ||
|
|
09c75ed8b6 | ||
|
|
c72c2c9a3a | ||
|
|
d665b59c7a | ||
|
|
0790002b6a | ||
|
|
f54119b8da | ||
|
|
4508099fb5 | ||
|
|
31d661acd1 | ||
|
|
eeb99f2c7b | ||
|
|
3f8c8380e4 | ||
|
|
885e9e15ed | ||
|
|
68a70a5687 | ||
|
|
6459c3543c | ||
|
|
ee9d42da37 | ||
|
|
01d4a38353 | ||
|
|
02b946448f | ||
|
|
2697315ed9 | ||
|
|
c35ef1bc96 | ||
|
|
3633df97cd | ||
|
|
ee9f8831c3 | ||
|
|
49bcec5fb7 | ||
|
|
a40921668c | ||
|
|
d23ee61082 | ||
|
|
2b3bf29ee6 | ||
|
|
fa9abe7b63 | ||
|
|
9b2772ccb9 | ||
|
|
1305dd3072 | ||
|
|
275d1a95b2 | ||
|
|
870e805743 | ||
|
|
9d21e059f7 | ||
|
|
e515915ec8 | ||
|
|
8561b715e4 | ||
|
|
f8e8d0c99b | ||
|
|
7e650d73f8 | ||
|
|
06223bcef1 | ||
|
|
91f15abc11 | ||
|
|
211e29bcc4 | ||
|
|
aa7ed10522 | ||
|
|
0c1353b660 | ||
|
|
5ecb4f36bc | ||
|
|
73759e1200 | ||
|
|
a5703d33d3 | ||
|
|
b920b6d100 | ||
|
|
9bba760d9b | ||
|
|
02938c7b01 | ||
|
|
c4b016366b | ||
|
|
8bb9f88037 | ||
|
|
107092277e | ||
|
|
6274ba6d5b | ||
|
|
b4c2d75f00 | ||
|
|
1c65b6d035 | ||
|
|
70ff818b43 | ||
|
|
d66c07bd46 | ||
|
|
7f1d3dd51c | ||
|
|
cbf73d3d7c | ||
|
|
1b802a1d87 | ||
|
|
a63f74d9ac | ||
|
|
62390e168c | ||
|
|
bb8dbafa9c | ||
|
|
4a4184ae3a | ||
|
|
0f9c1f8d17 | ||
|
|
06b96f827e | ||
|
|
a7e16c7680 | ||
|
|
171c01f1f3 | ||
|
|
f9012e75e8 | ||
|
|
813401f481 | ||
|
|
7a5b273cb3 | ||
|
|
1306b9eb0e | ||
|
|
58d0cbc7fa | ||
|
|
198578a5c9 | ||
|
|
8e11bc30a2 | ||
|
|
66570a6813 | ||
|
|
ccf6f7cc8a | ||
|
|
e0993e5ec9 | ||
|
|
26f88721c3 | ||
|
|
4331082903 | ||
|
|
1b9f80072c | ||
|
|
6e9e8eb544 | ||
|
|
982f81bdb2 | ||
|
|
74761dcc07 | ||
|
|
c5d9b2f0d1 | ||
|
|
d35a6599c2 | ||
|
|
b3908781c3 | ||
|
|
62a7a91f9b | ||
|
|
50ce96f4f2 | ||
|
|
9fd0d5e786 | ||
|
|
ee06ff94b7 | ||
|
|
4c3d8246ef | ||
|
|
a372494f55 | ||
|
|
dd6fdc493c | ||
|
|
236e813e89 | ||
|
|
5fc125b117 | ||
|
|
3e68196cf1 | ||
|
|
3cd0018a2d | ||
|
|
91af9ad701 | ||
|
|
ce35ec42e3 | ||
|
|
1d1d68fb6f | ||
|
|
a87bbe778f | ||
|
|
b442645dd1 | ||
|
|
5c577272f2 | ||
|
|
28306908c2 | ||
|
|
fb89ac2fe8 | ||
|
|
57c8f72e65 | ||
|
|
ce5f9bc086 | ||
|
|
ccfea3fa5d | ||
|
|
05b28db083 | ||
|
|
f866aa8a48 | ||
|
|
104aa85b65 | ||
|
|
4592f4d7f9 | ||
|
|
db2887c610 | ||
|
|
66dde0c2e0 | ||
|
|
35bc7f5177 | ||
|
|
ec6f4ae436 | ||
|
|
da9565bf63 | ||
|
|
1a968715d9 | ||
|
|
4682d7dca6 | ||
|
|
fb07076d25 | ||
|
|
65375765bc | ||
|
|
bbb0d6b7a6 | ||
|
|
2e34170a82 | ||
|
|
dd7e9ecd7a | ||
|
|
2377031c91 | ||
|
|
7192e34bbe | ||
|
|
9d3cc12724 | ||
|
|
8ac79898b8 | ||
|
|
b1a41ca74a | ||
|
|
8bb0f3f886 | ||
|
|
bfbba70685 | ||
|
|
5536fbda96 | ||
|
|
3efc7109f5 | ||
|
|
fe1fe6f4a8 | ||
|
|
6d52617fab | ||
|
|
fbb03c0e65 | ||
|
|
3c9b29c29e | ||
|
|
7e950ea734 | ||
|
|
d52c169873 | ||
|
|
42ce8a4067 | ||
|
|
64fc093bbc | ||
|
|
be007299d5 | ||
|
|
c4bd1f31f7 | ||
|
|
c09930e079 | ||
|
|
1220947601 | ||
|
|
8e738a4500 | ||
|
|
fc27388534 | ||
|
|
3e5eed4af5 | ||
|
|
e0a1045252 | ||
|
|
ffc977157c | ||
|
|
0ccf943934 | ||
|
|
4eaedfe621 | ||
|
|
4c90e62569 | ||
|
|
4a53bc4a85 | ||
|
|
450ad44550 | ||
|
|
73bf5feec8 | ||
|
|
48f14c644e | ||
|
|
05a24e63ba | ||
|
|
88dafeb561 | ||
|
|
b648316b51 | ||
|
|
9aebd4445c | ||
|
|
ced8db7de1 | ||
|
|
692b0361c6 | ||
|
|
2080de37fe | ||
|
|
97ad0e3d2b | ||
|
|
4261eee360 | ||
|
|
47d68814dc | ||
|
|
066e6dac81 | ||
|
|
7606fa5810 | ||
|
|
0d3ac5c6d6 | ||
|
|
f88142abc1 | ||
|
|
673f9a7a0e | ||
|
|
37ee42fab5 | ||
|
|
071744440d | ||
|
|
8854f1d687 | ||
|
|
5206144d06 | ||
|
|
f4c3a4a321 | ||
|
|
e74e188bd9 | ||
|
|
40b552dc49 | ||
|
|
a4a41f1e59 | ||
|
|
e6d7404660 | ||
|
|
fece31e31a | ||
|
|
c12db4c3bf | ||
|
|
342268c399 | ||
|
|
6a5350e648 | ||
|
|
9d9f7f3d4d | ||
|
|
8d7f987ccb | ||
|
|
4cac15cf97 | ||
|
|
6e7c4caa62 | ||
|
|
b43bf1664d | ||
|
|
524b8ada86 | ||
|
|
d5c9008d3e | ||
|
|
6ceb241170 | ||
|
|
8037fdd233 | ||
|
|
2c4ca881be | ||
|
|
3629d82499 | ||
|
|
d3bd4c5db9 | ||
|
|
b44ef845db | ||
|
|
8c218d1086 | ||
|
|
90f8a8f1a6 | ||
|
|
31d6da3c85 | ||
|
|
0a94c3079f | ||
|
|
36933f1bab | ||
|
|
2355bc10bc | ||
|
|
eebd1113c3 | ||
|
|
814252375a | ||
|
|
38817162b3 | ||
|
|
9a291c065c | ||
|
|
1f25aaedfc | ||
|
|
c6d6270c4a | ||
|
|
d5acdb2161 | ||
|
|
f3778ae260 | ||
|
|
5bfdd25962 | ||
|
|
bded7f726e | ||
|
|
5469c00cfa | ||
|
|
e78937ae35 | ||
|
|
f67340dc84 | ||
|
|
c9dc08fe52 | ||
|
|
d6e9278a30 | ||
|
|
8b162b3069 | ||
|
|
febebb2b0e | ||
|
|
7c8be00c9a | ||
|
|
1f5e071767 | ||
|
|
93ed57e720 | ||
|
|
a0fd178eca | ||
|
|
13bb52cded | ||
|
|
5956090c9f | ||
|
|
892f00e2f4 | ||
|
|
f6b373cb79 | ||
|
|
172ab7edc8 | ||
|
|
01989bc39a | ||
|
|
63f6e19fc0 | ||
|
|
70576ce0c9 | ||
|
|
85081497aa | ||
|
|
3f152acc57 | ||
|
|
436ccf88d5 | ||
|
|
b96e566d61 | ||
|
|
3a36fafdc6 | ||
|
|
cdca1f136e | ||
|
|
4b11cb18cf | ||
|
|
0600486bd8 | ||
|
|
c3189d6e30 | ||
|
|
970395a4d7 | ||
|
|
a9a02b56ba | ||
|
|
4359144925 |
17
.gitignore
vendored
@@ -1,3 +1,18 @@
|
||||
static/images/thumbnails/
|
||||
server/public/assets/images/thumbnails/
|
||||
server/config/mailconfig.js
|
||||
server/config/api_key.js
|
||||
server/config/mongo_config.js
|
||||
server/config/cert_config.js
|
||||
server/config/recaptcha.js
|
||||
server/config/analytics.js
|
||||
server/public/assets/dist/callback.min.js
|
||||
server/public/assets/dist/token.min.js
|
||||
server/public/assets/dist/embed.min.js
|
||||
server/public/assets/dist/main.min.js
|
||||
server/public/assets/dist/remote.min.js
|
||||
*/node_modules
|
||||
node_modules/
|
||||
scripts/
|
||||
.DS_Store
|
||||
npm-debug.log
|
||||
server/npm-debug.log
|
||||
|
||||
43
.htaccess
@@ -1,43 +0,0 @@
|
||||
Options +FollowSymLinks
|
||||
RewriteEngine on
|
||||
RewriteBase /
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^(.*)$ https://%1/$1 [L]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^(.*zoff.no.*)$
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [L]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^remote.zoff.no
|
||||
RewriteCond %{REQUEST_URI} !/static
|
||||
RewriteRule ^(.*)$ php/controller.php [L,NC,QSA]
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^bot.zoff.no
|
||||
RewriteCond %{REQUEST_URI} !/static
|
||||
RewriteRule ^(.*)$ php/bot.php [L,NC,QSA]
|
||||
|
||||
#RewriteCond %{HTTP_HOST} ^(remote\.)?zoff\.no
|
||||
#RewriteCond %{REQUEST_URI} !remote/
|
||||
#RewriteRule ^(.*)$ remote/$1 [L]
|
||||
|
||||
#RewriteCond %{HTTP_HOST} ^(www\.)?etys\.no
|
||||
#RewriteCond %{REQUEST_URI} !etys/
|
||||
#RewriteRule ^(.*)$ etys/$1 [L]
|
||||
|
||||
#Comment out the two folling lines when running server locally to fix issues with localhost
|
||||
#RewriteCond %{HTTPS} !=on
|
||||
#RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
|
||||
|
||||
|
||||
#RewriteRule (?i)^remote/(.*) php/controller.php?id=$1 [L]
|
||||
#RewriteRule (?i)^remote php/controller.php [L]
|
||||
|
||||
RewriteCond %{REQUEST_URI} !(/$|\.)
|
||||
RewriteRule (.*) %{REQUEST_URI}/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule /(.*)$ /$1 [L]
|
||||
|
||||
Options -Indexes
|
||||
113
README.md
@@ -1,17 +1,48 @@
|
||||
Zöff
|
||||
Zoff
|
||||
====
|
||||
|
||||
##### The server-side is required for this website, and it's found at: <a href="https://github.com/zoff-music/zoff-server">here</a>
|
||||
Zoff (pronounced __søff__) is a shared (free) YouTube based radio service, built upon the YouTube API, with integrated casting with Chromecast.
|
||||
|
||||
##### Config
|
||||
## Install
|
||||
|
||||
To get the website to run as intended, please enabled mod_rewrite for apache2, and add ```AllowOverride All``` to the directory containing the webserver in Apache2 (it's usually /etc/apache2/sites-enabled/000-default.conf)
|
||||
Prerequisites:
|
||||
|
||||
```
|
||||
MongoDB : https://www.mongodb.org/
|
||||
NodeJS : https://nodejs.org/en/
|
||||
npm : https://www.npmjs.com/
|
||||
```
|
||||
|
||||
Clone this repository into a folder, and navigate to it. Use ```$ npm install``` in the project folder.
|
||||
|
||||
For the server to run, you have to have the files
|
||||
|
||||
```
|
||||
api_key.js
|
||||
mongo_config.js
|
||||
```
|
||||
|
||||
in ```/server/config```. There are ```*.example.js``` files for all the ones mentioned above. If you're going to deploy the server with a certificate, you also need to create the ```cert_config.js``` in ```/server/config/```. If you want the mailing to work, take a look at ```mailconfig.example.js``` and ```recaptcha.example.js```. You'll need ```mailconfig.js``` and ```recaptcha.js``` for this to work.
|
||||
|
||||
If you want to use Google Analytics, have a look at ```analytics.example.js``` in ```server/config/```.
|
||||
|
||||
If you have run the server before the table-structures where added, please run ```node server/apps/rewrite.js```. This will fix any crashes that occurs because of faulty document-collectionnames due to moving channel-settings to a separate collection.
|
||||
|
||||
Run
|
||||
```
|
||||
db.chat_logs.createIndex({ "createdAt": 1 }, { expireAfterSeconds: X });
|
||||
db.timeout_api.createIndex({ "createdAt": 1 }, { expireAfterSeconds: Y });
|
||||
db.api_links.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 86400 });
|
||||
```
|
||||
in mongo to have chat_logs and api be deleted after X and Y seconds.
|
||||
|
||||
Use ```$ npm start``` to start the server. (Alternative you can use the ```pm2.json``` in the project-root, if you prefer pm2 for running the apps.)
|
||||
|
||||
More info in <a href="https://github.com/zoff-music/zoff/blob/master/server/README.md">server/ README</a>
|
||||
|
||||
### About
|
||||
|
||||
Zöff is a shared (free) YouTube based radio service, built upon the YouTube API.
|
||||
|
||||
Zöff is mainly a webbased service. The website uses <a href="https://nodejs.org/">NodeJS</a> with <a href="http://socket.io/">Socket.IO</a>, <a href="https://www.mongodb.org/">MongoDB</a> and PHP on the backend, with JavaScript, jQuery and <a href="http://materializecss.com/">Materialize</a> on the frontend.
|
||||
Zoff is mainly a webbased service. The website uses <a href="https://nodejs.org/">NodeJS</a> with <a href="http://socket.io/">Socket.IO</a>, <a href="https://www.mongodb.org/">MongoDB</a> and express on the backend, with JavaScript and <a href="http://materializecss.com/">Materialize</a> on the frontend.
|
||||
|
||||
The team consists of Kasper Rynning-Tønnesen and Nicolas Almagro Tonne, and the project has been worked on since late 2014.
|
||||
|
||||
@@ -19,66 +50,32 @@ The team consists of Kasper Rynning-Tønnesen and Nicolas Almagro Tonne, and the
|
||||
|
||||
The team can be reached on <a href="mailto:contact@zoff.no?Subject=Contact%20Zoff">contact@zoff.no</a>
|
||||
|
||||
###Screenshots of desktop version:
|
||||
### Screenshots of desktop version:
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
### Screenshots of the mobile version:
|
||||
|
||||
  
|
||||
|
||||
### Android exclusive screens:
|
||||
|
||||
<div style="text-align:center;">
|
||||
<img src="http://i.imgur.com/2LMOnUe.png" alt="android1" height="600px">
|
||||
<img src="http://i.imgur.com/mIOrtng.png" alt="android2" height="400px">
|
||||
<img src="http://i.imgur.com/aWlEmIx.png" alt="frontpage" height="600px">
|
||||
<br>
|
||||
<img src="https://puu.sh/xCI6X/1aead5e1b6.png" alt="channel" height="600px">
|
||||
|
||||
<img src="https://puu.sh/yg5y5/2e0f202d6d.png" alt="channel search" height="600px">
|
||||
</div>
|
||||
|
||||
### Events
|
||||
|
||||
Emitted events between the server and client
|
||||
```
|
||||
socket.emit("end", VIDEO_ID); Tells the server the song is clientside
|
||||
socket.emit("pos"); Asks server where in the song it should be
|
||||
socket.emit('list', CHANNEL_NAME); Tells the server the client wants the list
|
||||
socket.emit("add", [VIDEO_ID, VIDEO_TITLE, sha256(PASSWORD), VIDEO_DURATION]); Sends info about a song the client wants to add
|
||||
socket.emit("change_channel"); Tells the server to disconnect the user from the current channel, is used for remote controlling on the host side
|
||||
socket.emit("all,chat", TEXT); Sends chat text to all chat
|
||||
socket.emit("chat", TEXT); Sends chat text to channelchat
|
||||
socket.emit('vote', [CHANNEL_NAME, VIDEO_ID, VOTE_TYPE, PASSWORD]); Sends info about song the user wants to vote on. If VOTE_TYPE is del, its deleting the song, if its pos, its just voting
|
||||
socket.emit('skip', [CHANNEL_NAME, PASSWORD]); Sends skip message to server
|
||||
socket.emit("password", [PASSWORD, CHANNEL_NAME]); Sends password for instant log in to server
|
||||
socket.emit('frontpage_lists'); Tells the server the client wants frontpage lists
|
||||
socket.emit("id", [CHANNEL_ID, "play", "mock"]); Sends message to the host channel for play
|
||||
socket.emit("id", [CHANNEL_ID, "pause", "mock"]); Sends message to the host channel for pause
|
||||
socket.emit("id", [CHANNEL_ID, "skip", "mock"]); Sends message to the host channel for skip
|
||||
socket.emit("id", [CHANNEL_ID, "volume", VALUE]); Sends message to the host channel to change volume
|
||||
socket.emit("id", [CHANNEL_ID, "channel", NEW_CHANNEL_NAME]); Sends message to the host channel to change channel
|
||||
|
||||
socket.on("toast", STRING) Recieves a string from server for what type of toast to be triggered
|
||||
socket.on("pw", STRING) Recieves the password for the channel if the user sent the right in the first place
|
||||
socket.on("conf", [ARRAY]) Recieves configuration array from server
|
||||
socket.on("chat.all", [CLIENT_NAME, STRING, CLIENT_CHANNEL_NAME]) Recieves chat message from allchat
|
||||
socket.on("chat", [CLIENT_NAME, STRING]) Recieves chat message from channelchat
|
||||
socket.on("id", STRING) Recieves the ID of the current client, used for remote listening
|
||||
socket.on(id, [ARRAY]) Recieves the messages sent on CHANNEL_ID above
|
||||
socket.on("channel", [TYPE, [TYPE_SPECIFIC_VALUE]]) Recieves updates from channel. [0] is one of the following: list, added, deleted, vote, song_change
|
||||
socket.on("get_list") Recieves message from the server that its ready to send the playlist and info
|
||||
socket.on('playlists', [ARRAY]) Recieves the playlists for the frontpage
|
||||
socket.on("np", [NOW_PLAYING, CONFIGURATION, SERVER_TIME]) Recieves array of now playing song. Is triggered on song-change
|
||||
socket.on("viewers", VALUE) Recieves number of viewers on the current channel
|
||||
```
|
||||
|
||||
### Legal
|
||||
|
||||
Copyright © 2016
|
||||
Nicolas Almagro Tonne and Kasper Rynning-Tønnesen
|
||||
|
||||
Creative Commons License
|
||||
Zöff is licensed under a
|
||||
<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/no/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Norway License.</a>.
|
||||
Do not redistribute without permission from the developers.
|
||||
Zoff is licensed under a
|
||||
<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/no/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Norway License.</a>.
|
||||
Do not redistribute without permission from the developers.
|
||||
|
||||
Copyright © 2018
|
||||
Kasper Rynning-Tønnesen and Nicolas Almagro Tonne
|
||||
|
||||
22
embed.html
@@ -1,22 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="author" content="Nicolas 'Nixo' Almagro Tonne & Kasper 'KasperRT' Rynning-Tønnesen"/>
|
||||
<meta name="description" content="The Shared (free) YouTube radio. Being built around the YouTube search and video API it enables the creation of collaborative and shared live playlists, with billions of videos and songs to choose from, all for free and without registration. Enjoy!"/>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
function receiveMessage(event)
|
||||
{
|
||||
if(event.data == "lower") {
|
||||
window.setVolume(10);
|
||||
}else if(event.data == "reset") {
|
||||
window.setVolume(100);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage, false);
|
||||
</script>
|
||||
<div id="song-title"></div><div id="container" style="display:inline-flex;"><div id="player-container"><div id="player"></div><div id="controls" class="noselect"><div id="zoffbutton" title="Visit the channel!"></div><div id="playpause"><i id="play" class="mdi-av-play-arrow hide"></i><i id="pause" class="mdi-av-pause"></i></div><div id="duration">00:00 / 00:00</div><div id="volume-button"><i id="v-mute" class="mdi-av-volume-off"></i><i id="v-low" class="mdi-av-volume-mute"></i><i id="v-medium" class="mdi-av-volume-down"></i><i id="v-full" class="mdi-av-volume-up"></i></div><div id="volume"></div><div id="viewers"></div><div id="bar"></div></div></div><div id="playlist"><div id="wrapper"><div id="preloader" class="progress channel_preloader"><div class="indeterminate"></div></div><div id="list-song-html"><div id="list-song" class="card left-align list-song"><span class="clickable vote-container" title="Vote!"><a class="clickable center-align votebg"><div class="lazy card-image cardbg list-image" style=""></div></a><span class="card-content"><span class="flow-text truncate list-title"></span><span class="vote-span"><span class="list-votes"></span><span class="highlighted vote-text"> votes</span></span></span></span></div></div></div></div></div><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script><script type="text/javascript" src="//cdn.socket.io/socket.io-1.3.5.js"></script><script src="static/dist/embed.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
50
gulpfile.js
@@ -4,53 +4,69 @@ var gulp = require('gulp'),
|
||||
concat = require('gulp-concat');
|
||||
|
||||
gulp.task('js', function () {
|
||||
gulp.src(['static/js/*.js', '!static/js/embed*', '!static/js/remotecontroller.js'])
|
||||
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/*.js', '!server/public/assets/js/embed*', '!server/public/assets/js/token*', '!server/public/assets/js/remotecontroller.js', '!server/public/assets/js/callback.js'])
|
||||
.pipe(uglify({
|
||||
mangle: true,
|
||||
compress: true,
|
||||
enclose: true
|
||||
}))
|
||||
.pipe(concat('main.min.js'))
|
||||
.pipe(gulp.dest('static/dist'));
|
||||
.pipe(gulp.dest('server/public/assets/dist'));
|
||||
});
|
||||
|
||||
gulp.task('embed', function () {
|
||||
gulp.src(['static/js/player.js', 'static/js/helpers.js', 'static/js/playercontrols.js', 'static/js/list.js', 'static/js/embed.js', '!static/js/nochan*', '!static/js/remotecontroller.js'])
|
||||
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/player.js', 'server/public/assets/js/helpers.js', 'server/public/assets/js/playercontrols.js', 'server/public/assets/js/list.js', 'server/public/assets/js/embed.js', '!server/public/assets/js/frontpage*', '!server/public/assets/js/remotecontroller.js', 'server/public/assets/js/hostcontroller.js'])
|
||||
.pipe(uglify({
|
||||
mangle: true,
|
||||
compress: true,
|
||||
enclose: true
|
||||
}))
|
||||
.pipe(concat('embed.min.js'))
|
||||
.pipe(gulp.dest('static/dist'));
|
||||
.pipe(gulp.dest('server/public/assets/dist'));
|
||||
});
|
||||
|
||||
/*
|
||||
gulp.task('nochan', function () {
|
||||
gulp.src(['static/js/nochan.js', 'static/js/helpers.js'])
|
||||
gulp.task('token', function() {
|
||||
gulp.src(['server/public/assets/js/token*', 'server/public/assets/js/helpers.js'])
|
||||
.pipe(uglify({
|
||||
mangle: true,
|
||||
compress: true,
|
||||
enclose: true
|
||||
}))
|
||||
.pipe(concat('frontpage.min.js'))
|
||||
.pipe(gulp.dest('static/dist'));
|
||||
});*/
|
||||
.pipe(concat('token.min.js'))
|
||||
.pipe(gulp.dest('server/public/assets/dist'));
|
||||
})
|
||||
|
||||
gulp.task('callback', function () {
|
||||
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/callback.js'])
|
||||
.pipe(uglify({
|
||||
mangle: true,
|
||||
compress: true,
|
||||
enclose: true
|
||||
}))
|
||||
.pipe(concat('callback.min.js'))
|
||||
.pipe(gulp.dest('server/public/assets/dist'));
|
||||
});
|
||||
|
||||
gulp.task('build', function() {
|
||||
gulp.run(['js', 'embed', 'remotecontroller', 'callback', 'token']);
|
||||
})
|
||||
|
||||
gulp.task('remotecontroller', function () {
|
||||
gulp.src(['static/js/remotecontroller.js'])
|
||||
gulp.src(['server/VERSION.js', 'server/config/api_key.js', 'server/public/assets/js/remotecontroller.js', 'server/public/assets/js/helpers.js'])
|
||||
.pipe(uglify({
|
||||
mangle: true,
|
||||
compress: true,
|
||||
enclose: true
|
||||
}))
|
||||
.pipe(concat('remote.min.js'))
|
||||
.pipe(gulp.dest('static/dist'));
|
||||
.pipe(gulp.dest('server/public/assets/dist'));
|
||||
});
|
||||
|
||||
gulp.task('default', function(){
|
||||
gulp.watch('static/js/*.js', ['js']);
|
||||
gulp.watch('static/js/*.js', ['embed']);
|
||||
//gulp.watch('static/js/*.js', ['nochan']);
|
||||
gulp.watch('static/js/remotecontroller.js', ['remotecontroller']);
|
||||
});
|
||||
gulp.watch(['server/VERSION.js', 'server/public/assets/js/*.js'], ['js']);
|
||||
gulp.watch(['server/public/assets/js/token*.js', 'server/public/assets/js/helpers.js'], ['token']);
|
||||
gulp.watch(['server/VERSION.js', 'server/public/assets/js/*.js'], ['embed']);
|
||||
gulp.watch(['server/VERSION.js', 'server/public/assets/js/callback.js', 'server/public/assets/js/helpers.js'], ['callback']);
|
||||
//gulp.watch('server/public/assets/js/*.js', ['nochan']);
|
||||
gulp.watch(['server/VERSION.js', 'server/public/assets/js/remotecontroller.js'], ['remotecontroller']);
|
||||
});
|
||||
|
||||
278
index.php
@@ -1,278 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<?php
|
||||
$guid=substr(base64_encode(crc32($_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_ACCEPT_LANGUAGE'])), 0, 8);
|
||||
if(isset($_GET['chan'])) {header('Location: '.$_GET['chan']); exit;}
|
||||
$list = explode("/", htmlspecialchars(strtolower($_SERVER["REQUEST_URI"])));
|
||||
if($list[1]==""||!isset($list[1])||count($list)<=1){$list="";include('php/nochan.php');die();}
|
||||
else $list=preg_replace("/[^A-Za-z0-9 ]/", '', $list[1]);
|
||||
?>
|
||||
<html lang="en">
|
||||
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
|
||||
<?php include("php/header.php"); ?>
|
||||
<script type="text/javascript" src="/static/dist/main.min.js"></script>
|
||||
</head>
|
||||
<body id="channelpage" class="noselect cursor-default">
|
||||
<header>
|
||||
<div class="navbar-fixed">
|
||||
<nav id="nav">
|
||||
<div class="nav-wrapper">
|
||||
<a href="/" class="brand-logo brand-logo-navigate hide-on-med-and-down noselect">
|
||||
<img id="zicon" src="static/images/squareicon_small.png" alt="zöff" title="Zöff" />
|
||||
</a>
|
||||
<div class="brand-logo truncate zbrand">
|
||||
<a href="/" class="hide-on-large-only brand-logo-navigate">Zöff</a>
|
||||
<span class="hide-on-large-only">/</span>
|
||||
<span id="chan" class="chan clickable" title="Show big URL"><?php echo(ucfirst($list));?></span>
|
||||
</div>
|
||||
|
||||
<ul class="title-container">
|
||||
<li class="song-title cursor-pointer truncate" id="song-title">
|
||||
Loading...
|
||||
</li>
|
||||
<li class="search-container hide" id="search-wrapper">
|
||||
<input id="search" class="search_input" type="text" title="Search for songs..." placeholder="Find song on YouTube..." onsubmit="null;" autocomplete="off" />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="right control-list noselect">
|
||||
<li id="search_loader" class="valign-wrapper hide">
|
||||
<div class="valign">
|
||||
<div class="preloader-wrapper small active">
|
||||
<div class="spinner-layer spinner-blue">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-red">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-yellow">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-green">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-btn" href="#find" id="search-btn">
|
||||
<i class="mdi-action-search"></i>
|
||||
<span class="hover-text">Find</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-btn" href="#skip" id="skip">
|
||||
<i class="mdi-av-skip-next"></i>
|
||||
<span class="hover-text">Skip</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-btn hide-on-small-only" href="#stir" id="shuffle">
|
||||
<i class="mdi-av-shuffle"></i>
|
||||
<span class="hover-text">Stir</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-btn" href="#settings" data-activates="settings-bar" id="settings">
|
||||
<i class="mdi-action-settings"></i>
|
||||
<span class="hover-text">Conf</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="side-nav" id="settings-bar">
|
||||
<?php include("php/panel.php");?>
|
||||
</ul>
|
||||
<div id="results" class="search_results hide">
|
||||
<div id="temp-results-container">
|
||||
<div id="temp-results" class="result-object">
|
||||
<div id="result" class="result">
|
||||
<img class="thumb" src="/static/images/loading.png" alt="Thumb"/>
|
||||
|
||||
<div class="search-title truncate"></div>
|
||||
<span class="result_info"></span>
|
||||
|
||||
<div class="waves-effect waves-orange btn-flat" id="add-many" title="Add several videos">
|
||||
<i class="mdi-av-playlist-add"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="empty-results-container">
|
||||
<div id='empty-results' class='valign-wrapper'>
|
||||
<span class='valign'>No results found..</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="help" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>So you need help?</h4>
|
||||
<p>When listening on a channel, there are some different buttons you can click.</p>
|
||||
<p>If you click the cogwheel, you'll open the settings panel. Here you can change channel settings, decide if you want the computer you're on can be remote-controlled, and import playlists from YouTube.</p>
|
||||
<p>The search-icon, opens up a search inputfield. If you start typing here, the site will automagically search for your input!</p>
|
||||
<p>If you click the button next to the search icon, you'll skip on a song. The one next to that one, is shuffleling of the list. Next one there again is to open the chat.</p>
|
||||
<p>Clicking a song in the playlist, gives it a vote. If you're logged in, you'll have a delete button at your disposal.</p>
|
||||
<p>Also, whenever you're logged in, you'll have two tabs in the top of the playlist thats called "Playlist" and "Suggested". The playlist obviously shows the playlist. But the suggested tab, shows 5 songs that YouTube recommends based on the current song. There might also be user recommended songs. To add any of these, just click them as you'd click a song to vote.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="embed" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Embed code</h4>
|
||||
<p>Copy the code in the textarea, and paste on your website.</p>
|
||||
<p>
|
||||
<input type="checkbox" id="autoplay" checked="checked" />
|
||||
<label for="autoplay">Autoplay</label>
|
||||
</p>
|
||||
<textarea id="embed-area"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div id="channel-load" class="progress">
|
||||
<div class="indeterminate" id="channel-load-move"></div>
|
||||
</div>
|
||||
<main class="container center-align main">
|
||||
<div id="main-row" class="row">
|
||||
<div id="video-container" class="col s12 m9 video-container no-opacity click-through">
|
||||
<!--
|
||||
width: calc(100% - 261px);
|
||||
display: inline;
|
||||
-->
|
||||
<div id="player" class="ytplayer"></div>
|
||||
<div id="main_components">
|
||||
<div id="player_overlay" class="hide valign-wrapper">
|
||||
<div id="player_overlay_text" class="valign center-align">
|
||||
Waiting for Video
|
||||
</div>
|
||||
</div>
|
||||
<div id="controls" class="noselect">
|
||||
<div id="playpause">
|
||||
<i id="play" class="mdi-av-play-arrow hide"></i>
|
||||
<i id="pause" class="mdi-av-pause"></i>
|
||||
</div>
|
||||
<div id="duration"></div>
|
||||
<div id="fullscreen">
|
||||
<i class="mdi-navigation-fullscreen"></i>
|
||||
</div>
|
||||
<div id="volume-button">
|
||||
<i id="v-mute" class="mdi-av-volume-off"></i>
|
||||
<i id="v-low" class="mdi-av-volume-mute"></i>
|
||||
<i id="v-medium" class="mdi-av-volume-down"></i>
|
||||
<i id="v-full" class="mdi-av-volume-up"></i>
|
||||
</div>
|
||||
<div id="volume"></div>
|
||||
<div id="viewers"></div>
|
||||
<div id="bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="playlist" class="col s12 m3">
|
||||
<div id="top-button" title="Scroll to the top" class="rounded-bottom hide top-button-with-tabs hide-on-small-only">Top</div>
|
||||
<div id="bottom-button" title="Scroll to the bottom" class="rounded-top hide hide-on-small-only">Bottom</div>
|
||||
<ul class="tabs playlist-tabs" style="width:96%">
|
||||
<li class="tab col s3"><a class="playlist-tab-links playlist-link active" href="#wrapper">Playlist</a></li>
|
||||
<li class="tab col s3"><a class="playlist-tab-links chat-link" href="#chat">Chat</a></li>
|
||||
</ul>
|
||||
<ul class="tabs playlist-tabs-loggedIn hide" style="width: 96%;">
|
||||
<li class="tab col s3"><a class="playlist-tab-links playlist-link active" href="#wrapper">Playlist</a></li>
|
||||
<li class="tab col s3"><a class="playlist-tab-links suggested-link" href="#suggestions">Suggested</a></li>
|
||||
<li class="tab col s3"><a class="playlist-tab-links chat-link" href="#chat">Chat</a></li>
|
||||
</ul>
|
||||
<div id="wrapper" class="tabs_height">
|
||||
<div id="list-song-html">
|
||||
<div id="list-song" class="card left-align list-song">
|
||||
<div class="clickable vote-container" title="Vote!">
|
||||
<a class="clickable center-align votebg">
|
||||
<span class="lazy card-image cardbg list-image" style="background-image:url('/static/images/loading.png');"></span>
|
||||
</a>
|
||||
<span class="card-content">
|
||||
<span class="flow-text truncate list-title"></span>
|
||||
<span class="vote-span">
|
||||
<span class="list-votes"></span>
|
||||
<span class="highlighted vote-text"> votes</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-action center-align list-remove hide">
|
||||
<a title="Remove song" id="del" class="waves-effect btn-flat clickable">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="suggestions" class="tabs_height" style="display:none;">
|
||||
<p class="suggest-title-info">YouTube Suggests:</p>
|
||||
<div class="suggest_bar" id="suggest-song-html">
|
||||
</div>
|
||||
<p class="suggest-title-info" id="user_suggests">Users Suggests:</p>
|
||||
<div class="suggest_bar" id="user-suggest-html">
|
||||
</div>
|
||||
</div>
|
||||
<div id="chatPlaylist" class="tabs_height" style="display:none;">
|
||||
<ul class="" id="chat-bar">
|
||||
<li id="chat-log">
|
||||
<ul class="inherit-height">
|
||||
<li class="active inherit-height">
|
||||
<!--<ul id="chat inherit-height">-->
|
||||
<div class="row inherit-height">
|
||||
<div class="col s12">
|
||||
<ul class="tabs chatTabs">
|
||||
<li class="tab col s3 chat-tab-li"><a class="active chat-tab truncate" href="#channelchat"><?php echo $list; ?></a></li>
|
||||
<li class="tab col s3 chat-tab-li"><a class="chat-tab" href="#all_chat">All</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="channelchat" class="col s12 inherit-height"><ul id="chatchannel" class="inherit-height"></ul></div>
|
||||
<div id="all_chat" class="col s12 inherit-height"><ul id="chatall" class="inherit-height"></ul></div>
|
||||
</div>
|
||||
<!--</ul>-->
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="chat-input">
|
||||
<form action="#" id="chatForm">
|
||||
<input id="text-chat-input" name="input" type="text" autocomplete="off" placeholder="Chat" maxlength="150" />
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="playbar">
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include("php/footer.php"); ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"short_name": "Zöff",
|
||||
"name": "Zöff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "static/images/144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait"
|
||||
}
|
||||
4936
package-lock.json
generated
Normal file
61
package.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "zoff",
|
||||
"version": "2.0.2",
|
||||
"description": "Zoff, the shared YouTube based radio services",
|
||||
"main": "server/app.js",
|
||||
"scripts": {
|
||||
"start": "gulp build && node server/app.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zoff-music/zoff.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Kasper Rynning Tønnesen",
|
||||
"email": "kasper@kasperrt.no"
|
||||
},
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/zoff-music/zoff/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp-util": "~3.0.6",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-concat": "~2.6.0",
|
||||
"gulp-uglifyjs": "~0.6.2"
|
||||
},
|
||||
"homepage": "https://github.com/zoff-music/zoff#readme",
|
||||
"dependencies": {
|
||||
"bad-words": "^1.6.1",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"body-parser": "^1.17.1",
|
||||
"color-thief-jimp": "^2.0.2",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"cors": "^2.8.4",
|
||||
"express": "^4.16.3",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"express-recaptcha": "^3.0.1",
|
||||
"express-session": "^1.15.6",
|
||||
"express-sessions": "^1.0.6",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-uglifyjs": "^0.6.2",
|
||||
"gulp-util": "^3.0.8",
|
||||
"helmet": "^3.12.0",
|
||||
"jimp": "^0.2.28",
|
||||
"mongodb": "^2.2.35",
|
||||
"mongojs": "^2.5.0",
|
||||
"mongoose": "^5.0.16",
|
||||
"mpromise": "^0.5.5",
|
||||
"nodemailer": "^4.6.4",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"redis": "^2.8.0",
|
||||
"request": "^2.85.0",
|
||||
"socket.io": "^2.1.0",
|
||||
"socket.io-redis": "^5.2.0",
|
||||
"sticky-session": "^1.1.2",
|
||||
"uniqid": "^4.1.1"
|
||||
}
|
||||
}
|
||||
93
php/bot.php
@@ -1,93 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
|
||||
<?php include("header.php"); ?>
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/jemjlblambcgjmmhheaklfnphncdmfmb" />
|
||||
</head>
|
||||
<body class="noselect cursor-default">
|
||||
<header>
|
||||
<nav id="fp-nav">
|
||||
<div class="nav-wrapper">
|
||||
<a href="//zoff.no" class="brand-logo hide-on-small-only">
|
||||
<img id="zicon" src="/static/images/squareicon_small.png" alt="zöff" title="Zöff" />
|
||||
</a>
|
||||
<a href="//zoff.no" class="brand-logo hide-on-med-and-up">Zöff</a>
|
||||
<ul id="nav-mobile" class="right hide-on-med-and-down">
|
||||
<li><a class="modal-trigger waves-effect waves-red" title="Need help with the site?" onclick="$('#help').openModal()">Help</a></li>
|
||||
<li><a class="waves-effect green" title="Remote control a Zöff player" href="https://remote.zoff.no">Remote</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-orange" onclick="$('#about').openModal()">About</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-yellow" onclick="$('#legal').openModal()">Legal</a></li>
|
||||
<li><a class="waves-effect waves-purple" href="https://github.com/zoff-music/">GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="legal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Legal</h4>
|
||||
<p>Copyright © 2015 <br>Nicolas Almagro Tonne and Kasper Rynning-Tønnesen
|
||||
<br><br>
|
||||
Creative Commons License<br>
|
||||
Zöff is licensed under a <br><a href="http://creativecommons.org/licenses/by-nc-nd/3.0/no/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Norway License.</a>
|
||||
<br>
|
||||
Do not redistribute without permission from the developers.
|
||||
<br>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="about" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>About</h4>
|
||||
<p>Zöff is a shared (free) YouTube based radio service, built upon the YouTube API. <br><br>
|
||||
Zöff is mainly a webbased service. The website uses <a href="https://nodejs.org/">NodeJS</a> with <a href="http://socket.io/">Socket.IO</a>, <a href="https://www.mongodb.org/">MongoDB</a> and PHP on the backend, with JavaScript, jQuery and <a href="http://materializecss.com/">Materialize</a> on the frontend. More about the project itself can be found on <a href="https://github.com/zoff-music/Zoff">GitHub</a><br><br>
|
||||
The team consists of Kasper Rynning-Tønnesen and Nicolas Almagro Tonne, and the project has been worked on since late 2014.<br><br>
|
||||
The team can be reached on <a href="mailto:contact@zoff.no?Subject=Contact%20Zoff">contact@zoff.no</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="help" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>So you need help?</h4>
|
||||
<p>To remote-controll a computer, just type in the ID for that computer. (This can be found in the settings panel on the computer you want to remote controll. There is also a QR code for you to scan.</p>
|
||||
<p>When you've entered the ID for the computer you want to controll, you'll be able to change the volume, have the controlled computer vote for skipping, pause the video or play the video.</p>
|
||||
<p>The inputfield you used to enter the ID (if you entered it), has now changed some. If you type in something here now, the controlled computer will change channel!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="center-align container remote-container">
|
||||
<div class="section">
|
||||
<h3 id="remote-text">Twitch Bot</h3>
|
||||
<div class="row">
|
||||
<div class="col s6">
|
||||
<p>
|
||||
Commands
|
||||
</p>
|
||||
<p>
|
||||
join
|
||||
</p>
|
||||
<p>
|
||||
leave
|
||||
</p>
|
||||
<p>
|
||||
np
|
||||
</p>
|
||||
</div>
|
||||
<div class="col s6">Sign up</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include("footer.php"); ?>
|
||||
|
||||
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
|
||||
<!--<script type="text/javascript" src="/static/dist/remote.min.js"></script>-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,117 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
|
||||
<?php include("header.php"); ?>
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/jemjlblambcgjmmhheaklfnphncdmfmb" />
|
||||
</head>
|
||||
<body class="noselect cursor-default">
|
||||
<header>
|
||||
<nav id="fp-nav">
|
||||
<div class="nav-wrapper">
|
||||
<a href="//zoff.no" class="brand-logo hide-on-small-only">
|
||||
<img id="zicon" src="/static/images/squareicon_small.png" alt="zöff" title="Zöff" />
|
||||
</a>
|
||||
<a href="//zoff.no" class="brand-logo hide-on-med-and-up">Zöff</a>
|
||||
<ul id="nav-mobile" class="right hide-on-med-and-down">
|
||||
<li><a class="modal-trigger waves-effect waves-red" title="Need help with the site?" onclick="$('#help').openModal()">Help</a></li>
|
||||
<li><a class="waves-effect green" title="Remote control a Zöff player" href="https://remote.zoff.no">Remote</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-orange" onclick="$('#about').openModal()">About</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-yellow" onclick="$('#legal').openModal()">Legal</a></li>
|
||||
<li><a class="waves-effect waves-purple" href="https://github.com/zoff-music/">GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="legal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Legal</h4>
|
||||
<p>Copyright © 2015 <br>Nicolas Almagro Tonne and Kasper Rynning-Tønnesen
|
||||
<br><br>
|
||||
Creative Commons License<br>
|
||||
Zöff is licensed under a <br><a href="http://creativecommons.org/licenses/by-nc-nd/3.0/no/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Norway License.</a>
|
||||
<br>
|
||||
Do not redistribute without permission from the developers.
|
||||
<br>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="about" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>About</h4>
|
||||
<p>Zöff is a shared (free) YouTube based radio service, built upon the YouTube API. <br><br>
|
||||
Zöff is mainly a web-based service. The website uses <a href="https://nodejs.org/">NodeJS</a> with <a href="http://socket.io/">Socket.IO</a>, <a href="https://www.mongodb.org/">MongoDB</a> and PHP on the backend, with JavaScript, jQuery and <a href="http://materializecss.com/">Materialize</a> on the frontend. More about the project itself can be found on <a href="https://github.com/zoff-music/Zoff">GitHub</a><br><br>
|
||||
The team consists of Kasper Rynning-Tønnesen and Nicolas Almagro Tonne, and the project has been worked on since late 2014.<br><br>
|
||||
The team can be reached on <a href="mailto:contact@zoff.no?Subject=Contact%20Zoff">contact@zoff.no</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="help" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>So you need help?</h4>
|
||||
<p>To remote-control a computer, just type in the ID for that computer. (This can be found in the settings panel on the computer you want to remote control. There is also a QR code for you to scan.</p>
|
||||
<p>When you've entered the ID for the computer you want to control, you'll be able to change the volume, have the controled computer vote for skipping, pause the video or play the video.</p>
|
||||
<p>The input field you used to enter the ID (if you entered it), has now changed some. If you type in something here now, the controled computer will change channel!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="center-align container remote-container">
|
||||
<div class="section">
|
||||
<h3 id="remote-text">Remote Controller</h3>
|
||||
</div>
|
||||
<div class="section">
|
||||
<form action="#" class="row" id="remoteform">
|
||||
<div class="input-field col s12">
|
||||
<input
|
||||
class="input-field"
|
||||
type="text"
|
||||
id="search"
|
||||
name="chan"
|
||||
title="Type channel name here to create or listen to a channel. Only alphanumerical chars. [a-zA-Z0-9]+"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
maxlength="10"
|
||||
data-length="10"
|
||||
/>
|
||||
<label for="search" id="forsearch">Type ID of host to be controlled</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="rc" id="remote-controls">
|
||||
<a id="playbutton" class="remote-button chan-link waves-effect btn green">
|
||||
<i id="remote_play" class="mdi-av-play-arrow"></i>
|
||||
</a>
|
||||
<a id="pausebutton" class="remote-button chan-link waves-effect btn gray">
|
||||
<i id="remote_pause" class="mdi-av-pause"></i>
|
||||
</a>
|
||||
<a id="skipbutton" class="remote-button chan-link waves-effect btn blue">
|
||||
<i id="remote_skip" class="mdi-av-skip-next"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<i class="mdi-av-volume-up slider-vol rc"></i>
|
||||
<div class="rc" id="volume-control" title="Volume"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="section about-remote">
|
||||
<b>Here you can control another Zöff player from any device.</b>
|
||||
<br>
|
||||
To find the ID of your player, click the Conf <i class="mdi-action-settings"></i> icon on the top right of the player page, then "Remote Control".
|
||||
<br>You can either scan the QR code or type the ID manually.
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include("footer.php"); ?>
|
||||
|
||||
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/remote.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
138
php/footer.php
@@ -1,138 +0,0 @@
|
||||
<div id="contact" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Want to contact us?</h4>
|
||||
<div id="contact-container">
|
||||
<form id="contact-form" method="post" onsubmit="return false;">
|
||||
<div class="input-field">
|
||||
<input id="contact-form-from" name="from" type="email" autocomplete="off" class="validate" />
|
||||
<label for="contact-form-from" class="noselect">Email</label>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<input id="contact-form-message" name="message" type="text" autocomplete="off">
|
||||
<label for="contact-form-message" class="noselect">Message</label>
|
||||
</div>
|
||||
<button class="contact-button-submit" action="submit" id="submit-contact-form">Send</button>
|
||||
<div class="valign hide" id="send-loader">
|
||||
<div class="preloader-wrapper small active">
|
||||
<div class="spinner-layer spinner-blue">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-red">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-yellow">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spinner-layer spinner-green">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="page-footer cursor-default">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col l6 s12">
|
||||
<h5 class="white-text">Zöff</h5>
|
||||
<p class="grey-text text-lighten-4">The shared YouTube radio</p>
|
||||
<p class="grey-text text-lighten-4">
|
||||
Being built around the YouTube search and video API
|
||||
it enables the creation of collaborative and shared live playlists,
|
||||
with billions of videos and songs to choose from, all for free and without registration.
|
||||
<br />
|
||||
Enjoy!
|
||||
</p>
|
||||
<ul id="footer-buttons">
|
||||
<li>
|
||||
<a class="modal-trigger waves-effect cyan darken-2 btn help-button-footer" title="Need help with the site?" onclick="$('#help').openModal()">
|
||||
<i class="material-icons left footer-button-icon">info_outline</i>HELP
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="modal-trigger waves-effect blue-grey darken-2 btn help-button-footer hide-on-small-only" id="embed-button" title="Want to embed this channel?" onclick="$('#embed').openModal()">
|
||||
<i class="material-icons left footer-button-icon">code</i>EMBED
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="modal-trigger waves-effect red darken-2 btn help-button-footer" id="contact-button" title="Contact us" onclick="$('#contact').openModal()">
|
||||
<i class="mdi-communication-email left footer-button-icon"></i>CONTACT
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p id="latest-commit" class="grey-text text-lighten-4 truncate"></p>
|
||||
</div>
|
||||
<div class="col l4 offset-l2 s12 valign-wrapper">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/zoff-music/">
|
||||
<img title="Contribute on GitHub" src="/static/images/GitHub_Logo.png" alt="GitHub" />
|
||||
</a>
|
||||
<p>
|
||||
<a id="facebook-code-link" class="waves-effect waves-light btn light-blue share shareface" href="https://www.facebook.com/sharer/sharer.php?u=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>" target="popup" onclick="window.open('https://www.facebook.com/sharer/sharer.php?u=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>','Share Playlist','width=600,height=300')">
|
||||
<img class="left" src="/static/images/facebook.png" alt="Share on Facebook" />Share on Facebook
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a id="twitter-code-link" class="waves-effect waves-light btn light-blue share" href="http://twitter.com/intent/tweet?url=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>&text=Check%20out%20this%20playlist%20<?php echo ucfirst($list); ?>%20on%20Zöff!&via=zoffmusic" target="popup" onclick="window.open('http://twitter.com/intent/tweet?url=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>&text=Check%20out%20this%20playlist%20<?php echo ucfirst($list); ?>%20on%20Zöff!&via=zoffmusic','Share Playlist','width=600,height=300')">
|
||||
<img class="left" src="/static/images/twitter.png" alt="Share on Twitter" />Share on Twitter
|
||||
</a>
|
||||
</p>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank" id="donate_form">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="JEXDYP59N5VWE">
|
||||
<a title="Like what we made? Help us by donating (a) beer!" class="waves-effect waves-light btn orange light-blue share" onclick="document.getElementById('donate_form').submit();">
|
||||
<i class="mdi-action-payment left footer-button-icon"></i>Donate
|
||||
</a>
|
||||
</form>
|
||||
<p class="hide-on-small-only">
|
||||
<a id="qr-code-link" target="_blank" href="//chart.googleapis.com/chart?chs=500x500&cht=qr&chl=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>&choe=UTF-8&chld=L%7C1" >
|
||||
<img id="qr-code-image-link" class="card rounded" src="//chart.googleapis.com/chart?chs=150x150&cht=qr&chl=http://<?php echo $_SERVER['HTTP_HOST'].'/'.$list; ?>&choe=UTF-8&chld=L%7C1" alt="QRCode for link" title="QR code for this page, for easy sharing!" />
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-copyright">
|
||||
<div class="container">
|
||||
© <?php echo date("Y"); ?>
|
||||
<a href="http://nixo.no">Nixo</a> &
|
||||
<a href="http://kasperrt.no">KasperRT</a>
|
||||
All Rights Reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
| Zöff |
|
||||
| Project is on github: https://github.com/zoff-music/Zoff |
|
||||
| Made by: Nicolas Almagro Tonne and Kasper Rynning-Tønnesen |
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<title>Zöff</title>
|
||||
<meta name="author" content="Nicolas 'Nixo' Almagro Tonne & Kasper 'KasperRT' Rynning-Tønnesen"/>
|
||||
<meta name="description" content="The Shared (free) YouTube radio. Being built around the YouTube search and video API it enables the creation of collaborative and shared live playlists, with billions of videos and songs to choose from, all for free and without registration. Enjoy!"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#2D2D2D" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta property="og:image" content="/static/images/favicon.png" />
|
||||
<meta property="og:title" content="Zöff"/>
|
||||
<meta property="og:description" content="The Shared (free) YouTube radio. Being built around the YouTube search and video API it enables the creation of collaborative and shared live playlists, with billions of videos and songs to choose from, all for free and without registration. Enjoy!"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.min.css">
|
||||
<link tyle="text/css" rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/materialize.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css" title="Default" />
|
||||
<link rel="icon" id="favicon" type="image/png" href="/static/images/favicon.png"/>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '***REMOVED***', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/static/dist/lib/jquery-2.1.3.min.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/jquery-ui-1.10.3.min.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/materialize.min.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/socket.io-1.4.5.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/jquery.lazyload.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/color-thief.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/sha256.js"></script>
|
||||
<script type="text/javascript" src="/static/dist/lib/aes.js"></script>
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
$url = file_get_contents("https://img.youtube.com/vi/".$_POST['id']."/mqdefault.jpg");
|
||||
|
||||
$image = new Imagick();
|
||||
$image->readImageBlob($url);
|
||||
|
||||
|
||||
$image->blurImage(30,50);
|
||||
|
||||
$output = $image->getimageblob();
|
||||
|
||||
$image->setImageFormat("jpeg");
|
||||
|
||||
file_put_contents ("../static/images/thumbnails/".$_POST['id'].".jpg", $image);
|
||||
|
||||
echo base64_encode($output);
|
||||
?>
|
||||
22
php/mail.php
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
if(isset($_POST['from']) && isset($_POST['message'])){
|
||||
|
||||
$from = htmlspecialchars($_POST['from']);
|
||||
$message = htmlspecialchars($_POST['message']);
|
||||
$headers = "From: " . $from . "\r\n";
|
||||
|
||||
if(filter_var($from, FILTER_VALIDATE_EMAIL) && $message != ""){
|
||||
$result = mail("contact@zoff.no", "Contact from form", $message, $headers);
|
||||
|
||||
if($result == FALSE){
|
||||
echo "failure";
|
||||
}else{
|
||||
echo "success";
|
||||
}
|
||||
}else{
|
||||
echo "failure";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
177
php/nochan.php
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
if(isset($_GET['chan'])){
|
||||
$chan = htmlspecialchars($_GET['chan']);
|
||||
header('Location: '.$chan);
|
||||
}
|
||||
|
||||
?>
|
||||
<html lang="en">
|
||||
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
|
||||
<?php include("header.php"); ?>
|
||||
<script type="text/javascript" src="/static/dist/main.min.js"></script>
|
||||
</head>
|
||||
<body class="noselect cursor-default">
|
||||
<header>
|
||||
<nav id="fp-nav">
|
||||
<div class="nav-wrapper">
|
||||
<a href="#" class="brand-logo hide-on-small-only noselect">
|
||||
<img id="zicon" src="static/images/squareicon_small.png" alt="zöff" title="Zöff" />
|
||||
</a>
|
||||
<span id="frontpage-viewer-counter" class="hide-on-small-only noselect" title="Divided among all channels. Hidden or not"></span>
|
||||
<a href="//zoff.no" class="brand-logo brand-mobile hide-on-med-and-up">Zöff</a>
|
||||
<ul id="nav-mobile" class="right hide-on-med-and-down">
|
||||
<li><a class="modal-trigger waves-effect waves-red" title="Need help with the site?" onclick="$('#help').openModal()">Help</a></li>
|
||||
<li><a class="waves-effect green" title="Remote control a Zöff player" href="https://remote.zoff.no">Remote</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-orange" onclick="$('#about').openModal()">About</a></li>
|
||||
<li><a class="modal-trigger waves-effect waves-yellow" onclick="$('#legal').openModal()">Legal</a></li>
|
||||
<li><a class="waves-effect waves-purple" href="https://github.com/zoff-music/">GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="legal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Legal</h4>
|
||||
<p>Copyright © 2015 <br>Nicolas Almagro Tonne and Kasper Rynning-Tønnesen
|
||||
<br><br>
|
||||
Creative Commons License<br>
|
||||
Zöff is licensed under a <br><a href="http://creativecommons.org/licenses/by-nc-nd/3.0/no/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Norway License.</a>
|
||||
<br>
|
||||
Do not redistribute without permission from the developers.
|
||||
<br>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="about" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>About</h4>
|
||||
<p>Zöff is a shared (free) YouTube based radio service, built upon the YouTube API. <br><br>
|
||||
Zöff is mainly a web-based service. The website uses <a href="https://nodejs.org/">NodeJS</a> with <a href="http://socket.io/">Socket.IO</a>, <a href="https://www.mongodb.org/">MongoDB</a> and PHP on the backend, with JavaScript, jQuery and <a href="http://materializecss.com/">Materialize</a> on the frontend. More about the project itself can be found on <a href="https://github.com/zoff-music/Zoff">GitHub</a><br><br>
|
||||
The team consists of Kasper Rynning-Tønnesen and Nicolas Almagro Tonne, and the project has been worked on since late 2014.<br><br>
|
||||
The team can be reached on <a href="mailto:contact@zoff.no?Subject=Contact%20Zoff">contact@zoff.no</a><br><br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="donation" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Thanks!</h4>
|
||||
<p>Thanks for your donation, we love you <3
|
||||
<br><br>
|
||||
We will use the money for something awesome, just you wait and see!
|
||||
<br><br>
|
||||
We might also add your name somewhere in the code as a sign of gratitude, see if you can find it! (Might take a day or two for us to see the donation and implement it..)
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="modal-action modal-close waves-effect waves-green btn-flat">I'm awesome! (Close)</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="help" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>So you need help?</h4>
|
||||
<p>At the center of the site, you'll see a input field. This is meant to navigate to new or existing channels. If you input something here that already doesn't exist, a new channel will be create at the blink of an eye! Remember to put a password on the list you've created, so no one else takes it from you! (It's on a first come, first serve basis). When you're ready to proceed, just click the listen button!</p>
|
||||
<p>Underneath the input fields, there are several tiles. These are channels that already exists, and they can be clicked! To enter one of these channels and listen to it's content, it is just to click the tile.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class=" modal-action modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="section mega">
|
||||
<div id="mega-background"></div>
|
||||
<h5>Create a radio channel, collaborate and listen</h5>
|
||||
<form class="channel-finder" onsubmit="return false;">
|
||||
<p class="prething">zoff.no/</p>
|
||||
<input
|
||||
class="input-field room-namer"
|
||||
type="text"
|
||||
id="searchFrontpage"
|
||||
name="chan"
|
||||
placeholder="chill"
|
||||
title="Type channel name here to create or listen to a channel. Only alphanumerical chars. [a-zA-Z0-9]+"
|
||||
autocomplete="off"
|
||||
autofocus=""
|
||||
list="searches"
|
||||
required
|
||||
pattern="[a-zA-Z0-9]+"
|
||||
spellcheck="false"
|
||||
maxlength="18"
|
||||
/>
|
||||
<datalist id="searches"></datalist>
|
||||
<button class="listen-button" action="submit">Listen</button>
|
||||
</form>
|
||||
<div class="pitch outline">
|
||||
<div>Live & democratic playlists with YouTube Music</div>
|
||||
<div>Play everywhere — No login required</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section mobile-search">
|
||||
<form class="row" id="base" method="get" onsubmit="return false;">
|
||||
<div class="input-field col s12">
|
||||
<input
|
||||
class="input-field"
|
||||
type="text"
|
||||
id="search-mobile"
|
||||
name="chan"
|
||||
title="Type channel name here to create or listen to a channel. Only alphanumerical chars. [a-zA-Z0-9]+"
|
||||
autocomplete="off"
|
||||
list="searches"
|
||||
required pattern="[a-zA-Z0-9]+"
|
||||
spellcheck="false"
|
||||
maxlength="18"
|
||||
data-length="18"
|
||||
/>
|
||||
<label for="search-mobile" class="noselect">Find or create radio channel</label>
|
||||
<datalist id="searches">
|
||||
</datalist>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="channel-load" class="progress">
|
||||
<div class="indeterminate" id="channel-load-move"></div>
|
||||
</div>
|
||||
<main class="center-align container">
|
||||
<div id="main_section_frontpage" class="section">
|
||||
<div id="preloader" class="progress">
|
||||
<div class="indeterminate"></div>
|
||||
</div>
|
||||
<div id="channel-list-container">
|
||||
<ul class="row" id="channels">
|
||||
<li id="chan-card" class="col s12 m4 l3">
|
||||
<div class="card">
|
||||
<a class="chan-link">
|
||||
<div class="chan-bg card-image cardbg"></div>
|
||||
<div class="card-content">
|
||||
<i class="mdi-action-star-rate pin"></i>
|
||||
<p class="left-align">
|
||||
<span class="chan-name flow-text truncate"></span>
|
||||
<br>
|
||||
<span class="highlighted">Viewers: </span>
|
||||
<span class="chan-views"></span>
|
||||
<br>
|
||||
<span class="highlighted">Songs: </span>
|
||||
<span class="chan-songs"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-action noselect">
|
||||
<a class="chan-link waves-effect waves-orange btn-flat">Listen</a>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include("php/footer.php"); ?>
|
||||
</body>
|
||||
</html>
|
||||
204
php/panel.php
@@ -1,204 +0,0 @@
|
||||
<li class="no-padding">
|
||||
<ul class="collapsible collapsible-accordion">
|
||||
<li>
|
||||
<a class="col s9 collapsible-header bold waves-effect admin-settings">
|
||||
Channel Settings
|
||||
<i class="mdi-image-tune"></i>
|
||||
<div class="nav-btn close-settings clickable" title="Close" id="closeSettings">
|
||||
<i class="mdi-navigation-close auto-margin"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="collapsible-body">
|
||||
<form action="#" id="adminForm" onsubmit="return false;">
|
||||
<ul>
|
||||
<li class="white-bg">
|
||||
<div class="input-field field-settings">
|
||||
<i id="admin-lock" class="mdi-action-lock" title="Click to log out"></i>
|
||||
<input placeholder="Enter channel password" id="password" type="password" class="validate" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<span class="switch-text">
|
||||
Add songs
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Anyone
|
||||
<input name="addsongs" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Admin
|
||||
</label></div></li>
|
||||
|
||||
<li>
|
||||
<span class="switch-text">
|
||||
Vote
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Anyone
|
||||
<input name="vote" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Admin
|
||||
</label></div></li>
|
||||
|
||||
<li><span class="switch-text">
|
||||
Shuffle
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Anyone
|
||||
<input name="shuffle" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Admin
|
||||
</label></div></li>
|
||||
|
||||
<li><span class="switch-text">
|
||||
Skip
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Anyone
|
||||
<input name="skip" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Admin
|
||||
</label></div></li>
|
||||
|
||||
<li><span class="switch-text">
|
||||
Song length
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Any
|
||||
<input name="longsongs" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Short
|
||||
</label></div></li>
|
||||
|
||||
<li><span class="switch-text">
|
||||
Type
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Any
|
||||
<input name="allvideos" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Song
|
||||
</label></div></li>
|
||||
|
||||
|
||||
<li><span class="switch-text">
|
||||
Frontpage
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Hide
|
||||
<input name="frontpage" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Display
|
||||
</label></div></li>
|
||||
|
||||
<li><span class="switch-text">
|
||||
After play
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Keep
|
||||
<input name="removeplay" type="checkbox" class="conf" /><span class="lever"></span>
|
||||
Remove
|
||||
</label></div></li>
|
||||
|
||||
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="no-padding remote-panel">
|
||||
<ul class="collapsible collapsible-accordion">
|
||||
<li>
|
||||
<a class="collapsible-header bold waves-effect">Remote Control
|
||||
<i class="material-icons">settings_remote</i>
|
||||
</a>
|
||||
<div class="collapsible-body">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="switch-text">
|
||||
Enable Remote
|
||||
</span>
|
||||
<div class="switch"><label>
|
||||
Disabled
|
||||
<input name="remote_switch" type="checkbox" class="remote_switch_class" checked /><span class="lever"></span>
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
<a id="code-link" target="_blank">
|
||||
<img id="code-qr" alt="QR code for control" title="Link to control this Zöff player" src="https://chart.googleapis.com/chart?chs=221x221&cht=qr&choe=UTF-8&chld=L%7C1&chl=http://zoff.no" />
|
||||
<h4 id="code-text">ABBADUR</h4>
|
||||
</a>
|
||||
<a>
|
||||
You can control this Zöff instance from another device by going to <b>https://remote.zoff.no/</b>
|
||||
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="no-padding remote-panel">
|
||||
<ul class="collapsible collapsible-accordion">
|
||||
<li>
|
||||
<a class="collapsible-header bold waves-effect import-a">Import Playlist
|
||||
<i class="mdi-communication-import-export"></i>
|
||||
</a>
|
||||
<div class="collapsible-body">
|
||||
<ul>
|
||||
<li class="white-bg">
|
||||
<div class="input-field field-settings">
|
||||
<form action="#" id="listImport" onsubmit="return false;">
|
||||
<i class="mdi-av-playlist-add import-icon"></i>
|
||||
<input title="Input YouTube-playlist id here!" placeholder="Enter YouTube-list ID" id="import" type="text" class="validate" autocomplete="off" />
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="no-padding show-only-mobile">
|
||||
<ul class="collapsible collapsible-accordion">
|
||||
<li>
|
||||
<a class="collapsible-header bold waves-effect import-a">Remote Controller
|
||||
<i class="material-icons">settings_remote</i>
|
||||
</a>
|
||||
<div class="collapsible-body">
|
||||
<ul id="remote-mobile-container">
|
||||
<li class="white-bg">
|
||||
<p id="remote_header">Control another client</p>
|
||||
<form action="#" class="row" id="remoteform">
|
||||
<div class="input-field col s12">
|
||||
<input
|
||||
class="input-field"
|
||||
type="text"
|
||||
id="remote_channel"
|
||||
name="chan"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
maxlength="10"
|
||||
data-length="10"
|
||||
placeholder="ID to remotecontroll"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<button id="playbutton_remote" class="remote-button waves-effect btn green" disabled>
|
||||
<i id="remote_play" class="mdi-av-play-arrow"></i>
|
||||
</button>
|
||||
<button id="pausebutton_remote" class="remote-button waves-effect btn gray" disabled>
|
||||
<i id="remote_pause" class="mdi-av-pause"></i></button>
|
||||
<button id="skipbutton_remote" class="remote-button waves-effect btn blue" disabled>
|
||||
<i id="remote_skip" class="mdi-av-skip-next"></i>
|
||||
</button>
|
||||
<i class="mdi-av-volume-up slider-vol"></i>
|
||||
<div class="" id="volume-control-remote" title="Volume"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--
|
||||
<li class="no-padding">
|
||||
<h5 id="desc-title">List description</h5>
|
||||
<span id="description"></span>
|
||||
</li>
|
||||
-->
|
||||
@@ -1,37 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>promp</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$files = scandir('../oldFiles/');
|
||||
foreach($files as $list) {
|
||||
if($list != "." && $list != "..")
|
||||
{
|
||||
$file = file_get_contents("../oldFiles/".$list);
|
||||
$data = json_decode($file);
|
||||
if(count($data) > 0)
|
||||
{
|
||||
$array = array("nowPlaying" => array(), "songs" => array(), "conf" => array("startTime" => time(), "views" => 0, "skips" => array()));
|
||||
for($i = 0; $i < count($data[0]); $i++)
|
||||
{
|
||||
if($i > 0)
|
||||
{
|
||||
|
||||
$arr = "songs";
|
||||
}
|
||||
else{
|
||||
$arr = "nowPlaying";
|
||||
}
|
||||
$array[$arr][$data[0][0]] = array("id" => $data[0][0], "title" => str_replace("\"", "\'", $data[3][0]), "votes" => 0, "added" => time(), "guids" => array());
|
||||
array_shift($data[0]);
|
||||
array_shift($data[3]);
|
||||
}
|
||||
file_put_contents("../oldFiles/".$list, json_encode($array));
|
||||
echo $list."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
16
pm2.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"apps" : [
|
||||
{
|
||||
"name" : "zoff",
|
||||
"script" : "./server/app.js",
|
||||
"watch" : true,
|
||||
"ignore_watch": ["./node_modules", "./server/public/assets/images/thumbnails"],
|
||||
},
|
||||
{
|
||||
"name" : "gulp",
|
||||
"script" : "./gulpfile.js",
|
||||
"watch" : true,
|
||||
"ignore_watch": ["./node_modules", "./server/"],
|
||||
}
|
||||
]
|
||||
}
|
||||
224
server/EVENTS.md
Normal file
@@ -0,0 +1,224 @@
|
||||
## Events
|
||||
|
||||
### To server
|
||||
```
|
||||
// Tells the server the song is over
|
||||
'end', {
|
||||
id: video_id,
|
||||
channel: channel_name,
|
||||
pass: Base64(channel_pass)
|
||||
}
|
||||
|
||||
// Asks server where in the song it should be
|
||||
'pos', {
|
||||
channel: channel_name,
|
||||
pass: Base64(hannel_pass)
|
||||
}
|
||||
|
||||
// Tells the server the client wants the list
|
||||
'list', {
|
||||
channel: channel_name,
|
||||
pass: Base64(channel_pass),
|
||||
version: system_version (can be checked in VERSION.js)
|
||||
}
|
||||
|
||||
// Sends info about a song the client wants to add
|
||||
'add', {
|
||||
id: VIDEO_ID,
|
||||
title: VIDEO_TITLE,
|
||||
adminpass: Base64(PASSWORD),
|
||||
duration: VIDEO_DURATION,
|
||||
list: channel_name,
|
||||
pass: Base64(channel_pass)
|
||||
}
|
||||
|
||||
'addPlaylist', {
|
||||
channel: CHANNEL_NAME,
|
||||
userpass: Base64(CHANNEL_PASSWORD),
|
||||
adminpass: Base64(PASSWORD),
|
||||
songs: [
|
||||
{
|
||||
id: song_id,
|
||||
title: song_title,
|
||||
duration: song_duration
|
||||
}, ... { ... }
|
||||
]
|
||||
}
|
||||
|
||||
// Imports songs from another zoff-channel
|
||||
'import_zoff', {
|
||||
channel: CHANNELNAME,
|
||||
new_channel: CHANNELNAME-TO-IMPORT-FROM,
|
||||
adminpass: Base64(PASSWORD),
|
||||
userpass: Bse64(CHANNEL_PASSWORD)
|
||||
}
|
||||
|
||||
// Tells the server to disconnect the user from the current channel, is used for remote controlling on the host side
|
||||
'change_channel', {
|
||||
channel: channel_name
|
||||
}
|
||||
|
||||
// Sends chat text to all chat
|
||||
'all,chat', {
|
||||
channel: channel_name,
|
||||
data: input
|
||||
}
|
||||
|
||||
// Sends chat text to channelchat
|
||||
'chat',{
|
||||
channel: channel_name,
|
||||
data: input,
|
||||
pass: Base64(channel_pass)
|
||||
}
|
||||
|
||||
// Sends info about song the user wants to vote on. If VOTE_TYPE is del, its deleting the song, if its pos, its just voting
|
||||
'vote', {
|
||||
channel: CHANNEL_NAME,
|
||||
id: VIDEO_ID,
|
||||
type: VOTE_TYPE,
|
||||
adminpass: Base64(PASSWORD)
|
||||
}
|
||||
|
||||
// Sends shuffle to the server (Only works every 5 seconds per list)
|
||||
'shuffle', {
|
||||
adminpass: Base64(PASSWORD),
|
||||
channel: CHANNELNAME,
|
||||
pass: Base64(USER_PASSWORD)
|
||||
}
|
||||
|
||||
// Sends skip message to server
|
||||
'skip', {
|
||||
pass: Base64(PASSWORD),
|
||||
id:video_id,
|
||||
channel: chan,
|
||||
userpass: Base64(channel_pass)
|
||||
}
|
||||
|
||||
// Sends password for instant log in to server
|
||||
'password', {
|
||||
password: Base64(PASSWORD),
|
||||
channel: CHANNEL_NAME,
|
||||
oldpass: Base64(old_pass_if_changing_password)
|
||||
}
|
||||
|
||||
// Sends message to the host channel for play
|
||||
'id', {
|
||||
id: CHANNEL_ID,
|
||||
type: "play",
|
||||
value: "mock"
|
||||
}
|
||||
|
||||
// Sends message to the host channel for pause
|
||||
'id', {
|
||||
id: CHANNEL_ID,
|
||||
type: "pause",
|
||||
value: "mock"
|
||||
}
|
||||
|
||||
// Sends message to the host channel for skip
|
||||
'id', {
|
||||
id: CHANNEL_ID,
|
||||
type: "skip",
|
||||
value: "mock"
|
||||
}
|
||||
|
||||
// Sends message to the host channel to change volume
|
||||
'id', {
|
||||
id: CHANNEL_ID,
|
||||
type: "volume",
|
||||
value: VALUE
|
||||
}
|
||||
|
||||
// Sends message to the host channel to change channel
|
||||
'id', {
|
||||
id: CHANNEL_ID,
|
||||
type: "channel",
|
||||
value: NEW_CHANNEL_NAME
|
||||
}
|
||||
|
||||
// Sends a video that triggered an error
|
||||
'error_video', {
|
||||
channel: CHANNEL_NAME,
|
||||
id: VIDEO_ID,
|
||||
title: VIDEO_TITLE
|
||||
}
|
||||
|
||||
// Requests chat-history from the last 10 minutes
|
||||
'get_history', {
|
||||
channel: CHANNEL_NAME,
|
||||
all: BOOLEAN (if true, it requests for all-chat),
|
||||
pass: Base64(USERPASS)
|
||||
}
|
||||
```
|
||||
|
||||
### From server
|
||||
```
|
||||
// Receives a string from server for what type of toast to be triggered
|
||||
'toast', STRING
|
||||
|
||||
// Receives a boolean if the password was correct
|
||||
'pw', BOOLEAN
|
||||
|
||||
// Receives configuration array from server
|
||||
'conf', [ARRAY]
|
||||
|
||||
// Receives chat message from allchat
|
||||
'chat.all', {
|
||||
from: name,
|
||||
msg: message,
|
||||
channel: channel,
|
||||
icon: icon_src
|
||||
}
|
||||
|
||||
// Receives chat-history for all and for current channel
|
||||
'chat_history', {
|
||||
all: BOOLEAN (if true, it is for all-chat),
|
||||
data: CHAT_HISTORY
|
||||
}
|
||||
|
||||
// Receives chat message from channelchat
|
||||
'chat', {
|
||||
from: name,
|
||||
msg: message,
|
||||
icon: icon_src
|
||||
}
|
||||
|
||||
// Receives the ID of the current client, used for remote listening
|
||||
'id', STRING
|
||||
|
||||
// Receives the messages sent on CHANNEL_ID above
|
||||
id, {
|
||||
type: STRING,
|
||||
value: VALUE
|
||||
}
|
||||
|
||||
// Receives updates from channel. type is one of the following: list, added, deleted, vote, song_change, changed_values (see further down for better explanation here)
|
||||
'channel', {
|
||||
type: TYPE,
|
||||
value: value,
|
||||
time: time_of_occurence
|
||||
}
|
||||
|
||||
// Receives message from the server that its ready to send the playlist and info
|
||||
'get_list'
|
||||
|
||||
// Receives array of now playing song. Is triggered on song-change
|
||||
'np', {
|
||||
np: NOW_PLAYING,
|
||||
conf: CONFIGURATION,
|
||||
time: SERVER_TIME
|
||||
}
|
||||
|
||||
// Receives number of viewers on the current channel
|
||||
'viewers', VALUE
|
||||
|
||||
// Receives a newly updated video, that was checked for errors (song_generated contains .id which is the current id of the video, and a .new_id for the new video to change the video to)
|
||||
'channel', {
|
||||
type: "changed_values",
|
||||
value: song_generated
|
||||
}
|
||||
|
||||
'update_required', {
|
||||
description of what is wrong as an object
|
||||
}
|
||||
```
|
||||
12
server/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## Apps
|
||||
|
||||
Under ``` /server/apps/ ```, there are two files, ``` admin.js ``` and ``` client.js ```.``` admin.js ``` are for the adminpanel, and ``` client.js ``` are for zoff itself.
|
||||
|
||||
|
||||
## REST
|
||||
|
||||
[Rest API info](REST.md)
|
||||
|
||||
## Events
|
||||
|
||||
[Events sent form the server/to the server info](EVENTS.md)
|
||||
190
server/REST.md
Normal file
@@ -0,0 +1,190 @@
|
||||
## REST
|
||||
|
||||
All PUT, DELETE and POST endpoints have a 1-second waitlimit for each command per client. You'll get a response with Retry-After header for how long you have to wait. Shuffling in a player has a 5-second waitlimit, but per channel instead of per client.
|
||||
|
||||
If you want to skip the wait-times, create a token at <a href="https://zoff.me/api/apply">https://zoff.me/api/apply</a>. Tokens are added to all the POST, PUT, DELETE, requests as ``` token: TOKEN ```.
|
||||
|
||||
All requests return things on this form (results field is added if successful.)
|
||||
|
||||
```
|
||||
{
|
||||
status: STATUSCODE,
|
||||
error: MESSAGE,
|
||||
success: IF_SUCCESSFULL,
|
||||
results: [RESULTS] (if something went wrong, there is one element in this array. This tells you what is wrong with the request, and what was expected)
|
||||
}
|
||||
```
|
||||
|
||||
Add song
|
||||
|
||||
```
|
||||
POST /api/list/:channel_name/:video_id
|
||||
{
|
||||
"title": TITLE,
|
||||
"duration": END_TIME - START_TIME,
|
||||
"end_time": END_TIME,
|
||||
"start_time": START_TIME,
|
||||
"adminpass": PASSWORD, (leave this blank if there is no password/you don't know the password)
|
||||
"userpass": USER_PASSWORD
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication (but will return a song object, with the type == "suggested", and the song will show up in the suggested tab for channel-admins)
|
||||
Returns 409 if the song exists
|
||||
Returns 429 if you're doing too much of this request, with a Retry-After int value in the header.
|
||||
Returns 200 and the added song object if successful
|
||||
```
|
||||
|
||||
Delete song
|
||||
```
|
||||
DELETE /api/list/:channel_name/:video_id
|
||||
{
|
||||
"adminpass": PASSWORD,
|
||||
"userpass": USER_PASSWORD
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the song doesnt exist or is the currently playing song
|
||||
Returns 429 if you're doing too much of this request, with a Retry-After int value in the header.
|
||||
Returns 200 if successful
|
||||
```
|
||||
|
||||
Vote on song
|
||||
```
|
||||
PUT /api/list/:channel_name/:video_id
|
||||
{
|
||||
"adminpass": PASSWORD,
|
||||
"userpass": USER_PASSWORD
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the song doesnt exist
|
||||
Returns 409 if you've already voted on that song
|
||||
Returns 429 if you're doing too much of this request, with a Retry-After int value in the header.
|
||||
Returns 200 and the added song object if successful
|
||||
```
|
||||
|
||||
Change channel configurations
|
||||
```
|
||||
PUT /api/conf/:channel_name
|
||||
{
|
||||
"userpass": USER_PASSWORD,
|
||||
"adminpass": PASSWORD,
|
||||
"vote": BOOLEAN,
|
||||
"addsongs": BOOLEAN,
|
||||
"longsongs": BOOLEAN,
|
||||
"frontpage": BOOLEAN (if you want to set userpassword, this MUST be false for it to work),
|
||||
"allvideos": BOOLEAN,
|
||||
"removeplay": BOOLEAN,
|
||||
"skip": BOOLEAN,
|
||||
"shuffle": BOOLEAN,
|
||||
"userpass_changed": BOOLEAN (this must be true if you want to keep the userpassword you're sending)
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the list doesn't exist
|
||||
Returns 429 if you're doing too much of this request, with a Retry-After int value in the header.
|
||||
Returns 200 and the newly added configuration if successful
|
||||
```
|
||||
|
||||
Get song in channel
|
||||
```
|
||||
GET /api/list/:channel_name/:video_id
|
||||
|
||||
Returns 403 for bad authentication (if you get this, the channel is protected, try getting the full channel with POST, and search through the object)
|
||||
Returns 404 if the song doesn't exist
|
||||
Returns 200 and the song
|
||||
```
|
||||
|
||||
Get song in channel (protected)
|
||||
```
|
||||
// Important fetch_song is present, or else the request will try to add a song to the channel
|
||||
POST /api/list/:channel_name/:video_id
|
||||
{
|
||||
"fetch_song": ANYTHING_HERE,
|
||||
"userpass": USERPASS
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the song doesn't exist
|
||||
Returns 200 and the song
|
||||
```
|
||||
|
||||
Get list
|
||||
```
|
||||
GET /api/list/:channel_name/
|
||||
|
||||
Returns 403 for bad authentication (if you get this, the channel is protected, try getting the full channel with POST, and search through the object)
|
||||
Returns 404 if the song doesn't exist
|
||||
Returns 200 and the song
|
||||
```
|
||||
|
||||
Get list (protected)
|
||||
```
|
||||
// Important fetch_song is present, or else the request will try to add a song to the channel
|
||||
POST /api/list/:channel_name/
|
||||
{
|
||||
"userpass": USERPASS
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the list doesn't exist
|
||||
Returns 200 and the song
|
||||
```
|
||||
|
||||
Get channelsettings
|
||||
```
|
||||
GET /api/conf/:channel_name/
|
||||
|
||||
Returns 403 for bad authentication (if you get this, try POST with userpassword attached)
|
||||
Returns 404 if the channel doesn't exist
|
||||
Returns 200 and the objects in the channel
|
||||
```
|
||||
|
||||
Get channelsettings (protected)
|
||||
```
|
||||
POST /api/conf/:channel_name/
|
||||
{
|
||||
"userpass": USERPASS
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication
|
||||
Returns 404 if the channel doesn't exist
|
||||
Returns 200 and the objects in the channel
|
||||
```
|
||||
|
||||
Get now playing song
|
||||
```
|
||||
GET /api/list/:channel_name/__np__
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication (if you get this, try POST with userpassword attached)
|
||||
Returns 404 if the channel doesn't exist
|
||||
Returns 200 and the now playing object
|
||||
```
|
||||
|
||||
Get now playing song (protected)
|
||||
```
|
||||
POST /api/list/:channel_name/__np__
|
||||
{
|
||||
"userpass": USERPASS
|
||||
}
|
||||
|
||||
Returns 400 for bad request
|
||||
Returns 403 for bad authentication (if you get this, try POST with userpassword attached)
|
||||
Returns 404 if the channel doesn't exist
|
||||
Returns 200 and the now playing object
|
||||
```
|
||||
|
||||
Get all lists
|
||||
```
|
||||
GET /api/frontpages
|
||||
|
||||
Returns 200 and the frontpage-lists
|
||||
```
|
||||
5
server/VERSION.js
Normal file
@@ -0,0 +1,5 @@
|
||||
VERSION = 5;
|
||||
|
||||
try {
|
||||
module.exports = VERSION;
|
||||
} catch(e) {}
|
||||
130
server/app.js
Normal file
@@ -0,0 +1,130 @@
|
||||
var cluster = require('cluster'),
|
||||
net = require('net'),
|
||||
path = require('path'),
|
||||
//publicPath = path.join(__dirname, 'public'),
|
||||
http = require('http'),
|
||||
port = 8080,
|
||||
num_processes = require('os').cpus().length;
|
||||
|
||||
publicPath = path.join(__dirname, 'public');
|
||||
pathThumbnails = __dirname;
|
||||
|
||||
|
||||
try {
|
||||
var redis = require("redis");
|
||||
var client = redis.createClient({host: "localhost", port: 6379});
|
||||
client.on("error", function (err) {
|
||||
console.log("Couldn't connect to redis-server, assuming non-clustered run");
|
||||
num_processes = 1;
|
||||
startClustered(false);
|
||||
client.quit();
|
||||
});
|
||||
client.on("connect", function() {
|
||||
startClustered(true);
|
||||
client.quit();
|
||||
});
|
||||
} catch(e) {
|
||||
console.log("Couldn't connect to redis-server, assuming non-clustered run");
|
||||
num_processes = 1;
|
||||
startClustered(false);
|
||||
}
|
||||
|
||||
function startClustered(redis_enabled) {
|
||||
//Found https://stackoverflow.com/questions/40885592/use-node-js-cluster-with-socket-io-chat-application
|
||||
if (cluster.isMaster) {
|
||||
var workers = [];
|
||||
var spawn = function(i) {
|
||||
workers[i] = cluster.fork();
|
||||
workers[i].on('exit', function(code, signal) {
|
||||
console.log('respawning worker', i);
|
||||
spawn(i);
|
||||
});
|
||||
};
|
||||
|
||||
for (var i = 0; i < num_processes; i++) {
|
||||
spawn(i);
|
||||
}
|
||||
|
||||
var worker_index = function(ip, len) {
|
||||
var s = '';
|
||||
for (var i = 0, _len = ip.length; i < _len; i++) {
|
||||
if (!isNaN(ip[i])) {
|
||||
s += ip[i];
|
||||
}
|
||||
}
|
||||
return Number(s) % len;
|
||||
};
|
||||
|
||||
var server = net.createServer({ pauseOnConnect: true }, function(connection, a) {
|
||||
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
|
||||
worker.send('sticky-session:connection', connection);
|
||||
}).listen(port);
|
||||
} else {
|
||||
startSingle(true, redis_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
function startSingle(clustered, redis_enabled) {
|
||||
var server;
|
||||
var client = require('./apps/client.js');
|
||||
try {
|
||||
var cert_config = require(path.join(path.join(__dirname, 'config'), 'cert_config.js'));
|
||||
var fs = require('fs');
|
||||
var privateKey = fs.readFileSync(cert_config.privateKey).toString();
|
||||
var certificate = fs.readFileSync(cert_config.certificate).toString();
|
||||
var ca = fs.readFileSync(cert_config.ca).toString();
|
||||
var credentials = {
|
||||
key: privateKey,
|
||||
cert: certificate,
|
||||
ca: ca
|
||||
};
|
||||
var https = require('https');
|
||||
server = https.Server(credentials, routingFunction);
|
||||
} catch(err){
|
||||
console.log("Starting without https (probably on localhost)");
|
||||
server = http.createServer(routingFunction);
|
||||
}
|
||||
|
||||
if(clustered) {
|
||||
server.listen(onListen);
|
||||
} else {
|
||||
server.listen(port, onListen);
|
||||
}
|
||||
|
||||
var socketIO = client.socketIO;
|
||||
|
||||
if(redis_enabled) {
|
||||
var redis = require('socket.io-redis');
|
||||
try {
|
||||
socketIO.adapter(redis({ host: 'localhost', port: 6379 }));
|
||||
} catch(e) {
|
||||
console.log("No redis-server to connect to..");
|
||||
}
|
||||
}
|
||||
socketIO.listen(server);
|
||||
|
||||
process.on('message', function(message, connection) {
|
||||
if (message !== 'sticky-session:connection') {
|
||||
return;
|
||||
}
|
||||
server.emit('connection', connection);
|
||||
connection.resume();
|
||||
});
|
||||
}
|
||||
|
||||
function onListen() {
|
||||
console.log("Started with pid [" + process.pid + "]");
|
||||
}
|
||||
|
||||
function routingFunction(req, res, next) {
|
||||
var client = require('./apps/client.js');
|
||||
var admin = require('./apps/admin.js');
|
||||
var url = req.headers['x-forwarded-host'] ? req.headers['x-forwarded-host'] : req.headers.host.split(":")[0];
|
||||
var subdomain = req.headers['x-forwarded-host'] ? req.headers['x-forwarded-host'].split(".") : req.headers.host.split(":")[0].split(".");
|
||||
|
||||
if(subdomain.length > 1 && subdomain[0] == "admin") {
|
||||
admin(req, res, next);
|
||||
} else {
|
||||
client(req, res, next);
|
||||
}
|
||||
}
|
||||
45
server/apps/addtype.js
Normal file
@@ -0,0 +1,45 @@
|
||||
path = require('path'),
|
||||
pathThumbnails = __dirname;
|
||||
db = require(pathThumbnails + '/../handlers/db.js');
|
||||
var usual = [];
|
||||
var settings = [];
|
||||
|
||||
db.getCollectionNames(function(err, docs) {
|
||||
for(var i = 0; i < docs.length; i++) {
|
||||
//console.log(docs[i] == "");
|
||||
if(docs[i].indexOf("_settings") == -1 && docs[i].substring(0,1) != "." && docs[i].substring(docs[i].length - 1, docs[i].length) != ".") {
|
||||
t(docs[i]);
|
||||
}
|
||||
}
|
||||
/*for(var i = 0; i < docs.length; i++) {
|
||||
if(docs[i].indexOf("_settings") > -1) {
|
||||
settings.push(docs[0]);
|
||||
} else {
|
||||
usual.push(docs[0]);
|
||||
}
|
||||
//addType(docs[i]);
|
||||
}
|
||||
for(var i = 0; i < usual.length; i++) {
|
||||
if(settings.indexOf(usual + "_settings") < 0) {
|
||||
console.log(usual);
|
||||
}
|
||||
}*/
|
||||
})
|
||||
|
||||
function t(docs) {
|
||||
db.collection(docs).find({id: {$exists: true}}, function(e, _docs) {
|
||||
if(_docs.length > 0) {
|
||||
db.collection(docs).createIndex({id: 1}, {unique: true}, function(e,d){
|
||||
console.log(docs);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addType(name) {
|
||||
if(name.indexOf("_settings") > -1) {
|
||||
db.collection(name).update({views: {$exists: true}}, {$set: { id: "config" }}, {multi: true}, function(err, doc) {
|
||||
console.log(name);
|
||||
});
|
||||
}
|
||||
}
|
||||
219
server/apps/admin.js
Normal file
@@ -0,0 +1,219 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
const path = require('path');
|
||||
const publicPath = path.join(__dirname + "", '../public');
|
||||
var exphbs = require('express-handlebars');
|
||||
var hbs = exphbs.create({
|
||||
defaultLayout: publicPath + '/layouts/admin/main',
|
||||
layoutsDir: publicPath + '/layouts',
|
||||
partialsDir: publicPath + '/partials'
|
||||
});
|
||||
|
||||
var passport = require('passport');
|
||||
var mpromise = require('mpromise');
|
||||
var LocalStrategy = require('passport-local').Strategy;
|
||||
var mongoose = require('mongoose');
|
||||
var mongo_db_cred = require(pathThumbnails + '/config/mongo_config.js');
|
||||
var mongojs = require('mongojs');
|
||||
var db = mongojs(mongo_db_cred.config);
|
||||
var token_db = mongojs("tokens");
|
||||
var bodyParser = require('body-parser');
|
||||
var session = require('express-session');
|
||||
var api = require(pathThumbnails + '/routing/admin/api.js');
|
||||
|
||||
var User = require(pathThumbnails + '/models/user.js');
|
||||
var url = 'mongodb://' + mongo_db_cred.host + '/' + mongo_db_cred.users;
|
||||
mongoose.connect(url);
|
||||
|
||||
|
||||
app.engine('handlebars', hbs.engine);
|
||||
app.set('view engine', 'handlebars');
|
||||
app.enable('view cache');
|
||||
app.set('views', publicPath);
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
app.use(session({
|
||||
secret: mongo_db_cred.secret,
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
store: new (require('express-sessions'))({
|
||||
storage: 'mongodb',
|
||||
instance: mongoose,
|
||||
host: mongo_db_cred.host,
|
||||
port: 27017,
|
||||
collection: 'sessions',
|
||||
expire: mongo_db_cred.expire
|
||||
})
|
||||
})); // session secret
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session()); // persistent login sessions
|
||||
|
||||
//app.use('/assets', express.static(publicPath + '/assets'));
|
||||
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user.id);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// used to deserialize the user
|
||||
passport.deserializeUser(function(id, done) {
|
||||
User.findById(id, function(err, user) {
|
||||
done(err, user);
|
||||
});
|
||||
});
|
||||
|
||||
passport.use('local-signup', new LocalStrategy({
|
||||
// by default, local strategy uses username and password, we will override with username
|
||||
usernameField : 'username',
|
||||
passwordField : 'password',
|
||||
passReqToCallback : true // allows us to pass back the entire request to the callback
|
||||
},
|
||||
function(req, username, password, done) {
|
||||
// asynchronous
|
||||
// User.findOne wont fire unless data is sent back
|
||||
process.nextTick(function() {
|
||||
|
||||
// find a user whose username is the same as the forms username
|
||||
// we are checking to see if the user trying to login already exists
|
||||
var token = req.body.token;
|
||||
token_db.collection("tokens").find({token: token}, function(err, docs){
|
||||
if(docs.length == 1){
|
||||
token_db.collection("tokens").remove({token: token}, function(err, docs){
|
||||
User.findOne({ 'username' : username }, function(err, user) {
|
||||
// if there are any errors, return the error
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
// check to see if theres already a user with that username
|
||||
if (user) {
|
||||
return done(null, false);
|
||||
} else {
|
||||
|
||||
// if there is no user with that username
|
||||
// create the user
|
||||
var newUser = new User();
|
||||
|
||||
// set the user's local credentials
|
||||
newUser.username = username;
|
||||
newUser.password = newUser.generateHash(password);
|
||||
|
||||
// save the user
|
||||
newUser.save(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
return done(null, newUser);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return done(null, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
passport.use('local-login', new LocalStrategy({
|
||||
// by default, local strategy uses username and password, we will override with email
|
||||
usernameField : 'username',
|
||||
passwordField : 'password',
|
||||
passReqToCallback : true // allows us to pass back the entire request to the callback
|
||||
}, function(req, username, password, done) { // callback with email and password from our form
|
||||
|
||||
// find a user whose email is the same as the forms email
|
||||
// we are checking to see if the user trying to login already exists
|
||||
User.findOne({ 'username' : username }, function(err, user) {
|
||||
// if there are any errors, return the error before anything else
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
// if no user is found, return the message
|
||||
if (!user)
|
||||
return done(null, false); // req.flash is the way to set flashdata using connect-flash
|
||||
|
||||
// if the user is found but the password is wrong
|
||||
if (!user.validPassword(password))
|
||||
return done(null, false); // create the loginMessage and save it to session as flashdata
|
||||
|
||||
// all is well, return successful user
|
||||
|
||||
return done(null, user);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
app.post('/signup', passport.authenticate('local-signup', {
|
||||
successRedirect : '/', // redirect to the secure profile section
|
||||
failureRedirect : '/signup', // redirect back to the signup page if there is an error
|
||||
failureFlash : true // allow flash messages
|
||||
}));
|
||||
|
||||
app.post('/login', passport.authenticate('local-login', {
|
||||
successRedirect : '/', // redirect to the secure profile section
|
||||
failureRedirect : '/login', // redirect back to the signup page if there is an error
|
||||
failureFlash : true // allow flash messages
|
||||
}));
|
||||
|
||||
app.use('/login', isLoggedInTryingToLogIn, function(req, res) {
|
||||
var data = {
|
||||
where_get: "not_authenticated"
|
||||
};
|
||||
|
||||
res.render('layouts/admin/not_authenticated', data);
|
||||
});
|
||||
|
||||
app.use('/signup', isLoggedInTryingToLogIn, function(req, res) {
|
||||
var data = {
|
||||
where_get: "not_authenticated"
|
||||
};
|
||||
|
||||
res.render('layouts/admin/not_authenticated', data);
|
||||
});
|
||||
|
||||
app.use('/', api);
|
||||
|
||||
app.use('/logout', function(req, res) {
|
||||
req.logout();
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.use('/assets/admin/authenticated', function(req, res, next) {
|
||||
if(!req.isAuthenticated()) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
return next();
|
||||
});
|
||||
|
||||
app.use('/assets', express.static(publicPath + '/assets'));
|
||||
|
||||
app.use('/', isLoggedIn, function(req, res) {
|
||||
var data = {
|
||||
where_get: "authenticated",
|
||||
year: new Date().getYear()+1900,
|
||||
};
|
||||
|
||||
res.render('layouts/admin/authenticated', data);
|
||||
});
|
||||
|
||||
function isLoggedInTryingToLogIn(req, res, next){
|
||||
if(!req.isAuthenticated()){
|
||||
return next();
|
||||
}
|
||||
res.redirect("/");
|
||||
}
|
||||
|
||||
function isLoggedIn(req, res, next) {
|
||||
if (req.isAuthenticated())
|
||||
return next();
|
||||
res.redirect('/login');
|
||||
}
|
||||
|
||||
//app.listen(default_port);
|
||||
|
||||
module.exports = app;
|
||||
135
server/apps/client.js
Executable file
@@ -0,0 +1,135 @@
|
||||
VERSION = require(pathThumbnails + '/VERSION.js');
|
||||
var secure = false;
|
||||
var path = require('path');
|
||||
try {
|
||||
var cert_config = require(path.join(path.join(__dirname, '../config/'), 'cert_config.js'));
|
||||
var fs = require('fs');
|
||||
var privateKey = fs.readFileSync(cert_config.privateKey).toString();
|
||||
var certificate = fs.readFileSync(cert_config.certificate).toString();
|
||||
var ca = fs.readFileSync(cert_config.ca).toString();
|
||||
var credentials = {
|
||||
key: privateKey,
|
||||
cert: certificate,
|
||||
ca: ca
|
||||
};
|
||||
secure = true;
|
||||
} catch(err){}
|
||||
|
||||
var add = "";
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var exphbs = require('express-handlebars');
|
||||
var cors = require('cors');
|
||||
|
||||
var hbs = exphbs.create({
|
||||
defaultLayout: publicPath + '/layouts/client/main',
|
||||
layoutsDir: publicPath + '/layouts/client',
|
||||
partialsDir: publicPath + '/partials'
|
||||
});
|
||||
uniqid = require('uniqid');
|
||||
|
||||
|
||||
app.engine('handlebars', hbs.engine);
|
||||
app.set('view engine', 'handlebars');
|
||||
app.enable('view cache');
|
||||
app.set('views', publicPath);
|
||||
|
||||
var bodyParser = require('body-parser');
|
||||
var cookieParser = require("cookie-parser");
|
||||
var helmet = require('helmet')
|
||||
app.use(helmet({
|
||||
frameguard: false
|
||||
}));
|
||||
app.use( bodyParser.json() ); // to support JSON-encoded bodies
|
||||
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
|
||||
extended: true
|
||||
}));
|
||||
app.use(cookieParser());
|
||||
|
||||
/* Starting DB and socketio */
|
||||
io = require('socket.io')({
|
||||
pingTimeout: 25000,
|
||||
//path: '/zoff',
|
||||
//"origins": ("https://zoff.me:443*,https://zoff.me:8080*,zoff.me:8080*,https://remote.zoff.me:443*,https://remote.zoff.me:8080*,https://fb.zoff.me:443*,https://fb.zoff.me:8080*,https://admin.zoff.me:443*,https://admin.zoff.me:8080*, http://localhost:8080*")});
|
||||
});
|
||||
db = require(pathThumbnails + '/handlers/db.js');
|
||||
var socketIO = require(pathThumbnails +'/handlers/io.js');
|
||||
socketIO();
|
||||
|
||||
app.socketIO = io;
|
||||
|
||||
request = require('request');
|
||||
|
||||
/* Globally needed "libraries" and files */
|
||||
Functions = require(pathThumbnails + '/handlers/functions.js');
|
||||
ListChange = require(pathThumbnails + '/handlers/list_change.js');
|
||||
Chat = require(pathThumbnails + '/handlers/chat.js');
|
||||
List = require(pathThumbnails + '/handlers/list.js');
|
||||
Suggestions = require(pathThumbnails + '/handlers/suggestions.js');
|
||||
ListSettings = require(pathThumbnails + '/handlers/list_settings.js');
|
||||
Frontpage = require(pathThumbnails + '/handlers/frontpage.js');
|
||||
Notifications = require(pathThumbnails + '/handlers/notifications.js');
|
||||
Search = require(pathThumbnails + '/handlers/search.js');
|
||||
crypto = require('crypto');
|
||||
emojiStrip = Functions.removeEmojis;
|
||||
Filter = require('bad-words');
|
||||
filter = new Filter({ placeHolder: 'x'});
|
||||
|
||||
var router = require(pathThumbnails + '/routing/client/router.js');
|
||||
var api = require(pathThumbnails + '/routing/client/api.js');
|
||||
var ico_router = require(pathThumbnails + '/routing/client/icons_routing.js');
|
||||
|
||||
app.get('/robots.txt', function (req, res) {
|
||||
res.type('text/plain');
|
||||
res.send("User-agent: *\nAllow: /$\nDisallow: /");
|
||||
});
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var cookie = req.cookies._uI;
|
||||
if (cookie === undefined) {
|
||||
var user_name = Functions.hash_pass(Functions.rndName(uniqid.time(), 15));
|
||||
res.cookie('_uI', user_name, {
|
||||
maxAge: 365 * 10000 * 3600000,
|
||||
httpOnly: true,
|
||||
secure: secure,
|
||||
sameSite: true,
|
||||
});
|
||||
} else {
|
||||
res.cookie('_uI', cookie, {
|
||||
maxAge: 365 * 10000 * 3600000,
|
||||
httpOnly: true,
|
||||
secure: secure,
|
||||
sameSite: true,
|
||||
});
|
||||
}
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/service-worker.js', function(req, res) {
|
||||
res.sendFile(publicPath + '/service-worker.js');
|
||||
});
|
||||
|
||||
app.use('/', ico_router);
|
||||
app.use('/', api);
|
||||
app.use('/', cors(), router);
|
||||
|
||||
app.use('/assets/js', function(req, res, next) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
});
|
||||
|
||||
app.use('/assets/admin', function(req, res, next) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
});
|
||||
|
||||
app.use('/assets', express.static(publicPath + '/assets'));
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.status(404);
|
||||
res.redirect("/404");
|
||||
})
|
||||
|
||||
module.exports = app;
|
||||
23
server/apps/rewrite.js
Normal file
@@ -0,0 +1,23 @@
|
||||
path = require('path'),
|
||||
pathThumbnails = __dirname;
|
||||
db = require(pathThumbnails + '/../handlers/db.js');
|
||||
|
||||
db.getCollectionNames(function(err, docs) {
|
||||
for(var i = 0; i < docs.length; i++) {
|
||||
makeNewAndDelete(docs[i]);
|
||||
}
|
||||
})
|
||||
|
||||
function makeNewAndDelete(name) {
|
||||
db.collection(name).find({views: {$exists: true}}, function(err, doc) {
|
||||
if(doc.length == 0) {
|
||||
} else if(doc.length == 1) {
|
||||
db.collection(name + "_settings").insert(doc[0], function(err, result){
|
||||
console.log("Result insert", result);
|
||||
db.collection(name).remove({views: {$exists: true}}, function(err, result_del) {
|
||||
console.log("Result delete", result_del);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
3
server/config/analytics.example.js
Normal file
@@ -0,0 +1,3 @@
|
||||
var analytics = "xxxx";
|
||||
|
||||
module.exports = analytics;
|
||||
5
server/config/api_key.example.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var api_key = "xxxx";
|
||||
|
||||
try {
|
||||
module.exports = api_key;
|
||||
} catch(e) {}
|
||||
7
server/config/cert_config.example.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var cert = {
|
||||
privateKey: 'XX',
|
||||
certificate: 'XX',
|
||||
ca: 'XX'
|
||||
}
|
||||
|
||||
module.exports = cert;
|
||||
24
server/config/mailconfig.example.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
*
|
||||
* Have a look at nodemailer's config on how to set this up https://nodemailer.com/about/
|
||||
*
|
||||
*/
|
||||
|
||||
var mail_config = {
|
||||
port: 587,
|
||||
host: 'smtp.example.com',
|
||||
auth: {
|
||||
user: 'ex@amp.le',
|
||||
pass: 'example'
|
||||
},
|
||||
secure: true,
|
||||
authMethod: 'PLAIN',
|
||||
tls: {
|
||||
ciphers:'SSLv3'
|
||||
},
|
||||
from: 'no-reply@zoff.me',
|
||||
to: 'contact@zoff.me'
|
||||
notify_mail: 'notify@mail.example',
|
||||
};
|
||||
|
||||
module.exports = mail_config;
|
||||
10
server/config/mongo_config.example.js
Normal file
@@ -0,0 +1,10 @@
|
||||
var mongo_config = {
|
||||
config: 'mydb',
|
||||
secret: 'secret',
|
||||
users: 'users',
|
||||
host: 'localhost',
|
||||
port: '27017',
|
||||
expire: 86400,
|
||||
};
|
||||
|
||||
module.exports = mongo_config;
|
||||
6
server/config/recaptcha.example.js
Normal file
@@ -0,0 +1,6 @@
|
||||
var recaptcha = {
|
||||
site: "xxxx",
|
||||
key: "xxxxx",
|
||||
}
|
||||
|
||||
module.exports = recaptcha;
|
||||
296
server/handlers/chat.js
Normal file
@@ -0,0 +1,296 @@
|
||||
function get_history(channel, all, socket) {
|
||||
var query = {};
|
||||
if(all) {
|
||||
query = {
|
||||
all: true,
|
||||
};
|
||||
} else {
|
||||
query = {
|
||||
all: false,
|
||||
channel: channel,
|
||||
};
|
||||
}
|
||||
channel = channel.replace(/ /g,'');
|
||||
var pass = "";
|
||||
if(!query.all) {
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), channel, function(userpass) {
|
||||
if(userpass != "" || pass == undefined) {
|
||||
pass = userpass;
|
||||
}
|
||||
db.collection(channel + "_settings").find({id: "config"}, function(err, conf) {
|
||||
if(conf.length > 0) {
|
||||
if(conf[0].userpass == "" || conf[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(pass)).digest('base64')) {
|
||||
getAndSendLogs(channel, all, socket, pass, query);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
getAndSendLogs(channel, all, socket, pass, query);
|
||||
}
|
||||
}
|
||||
|
||||
function getAndSendLogs(channel, all, socket, pass, query) {
|
||||
channel = channel.replace(/ /g,'');
|
||||
db.collection("chat_logs").find(query, {
|
||||
from: 1,
|
||||
createdAt: 1,
|
||||
all: 1,
|
||||
channel: 1,
|
||||
msg: 1,
|
||||
icon: 1,
|
||||
_id: 0
|
||||
}).sort({createdAt: 1}).limit(20, function(err, docs) {
|
||||
socket.emit("chat_history", {all: all, data: docs});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function chat(msg, guid, offline, socket) {
|
||||
if(typeof(msg) !== 'object' || !msg.hasOwnProperty('data') ||
|
||||
!msg.hasOwnProperty('channel') || typeof(msg.data) != "string" || typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
data: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("data") ? typeof(msg.data) : undefined,
|
||||
},
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
var coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass) {
|
||||
if(userpass != "" || msg.pass == undefined) {
|
||||
msg.pass = userpass;
|
||||
}
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64")))) {
|
||||
var data = msg.data;
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
if(data !== "" && data !== undefined && data !== null &&
|
||||
data.length < 151 && data.replace(/\s/g, '').length){
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
db.collection("registered_users").find({"_id": docs[0].name}, function(err, n) {
|
||||
var icon = false;
|
||||
if(n.length > 0 && n[0].icon) {
|
||||
icon = n[0].icon;
|
||||
}
|
||||
db.collection("chat_logs").insert({ "createdAt": new Date(), all: false, channel: coll, from: docs[0].name, msg: ": " + data, icon: icon });
|
||||
io.to(coll).emit('chat', {from: docs[0].name, msg: ": " + data, icon: icon});
|
||||
});
|
||||
} else if(docs.length == 0){
|
||||
get_name(guid, {announce: false, channel: coll, message: data, all: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
socket.emit('auth_required');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function all_chat(msg, guid, offline, socket) {
|
||||
if(typeof(msg) !== 'object' || !msg.hasOwnProperty("channel") ||
|
||||
!msg.hasOwnProperty("data") || typeof(msg.data) != "string" ||
|
||||
typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
data: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("data") ? typeof(msg.data) : undefined,
|
||||
},
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
var coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
var data = msg.data;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
if(data !== "" && data !== undefined && data !== null &&
|
||||
data.length < 151 && data.replace(/\s/g, '').length){
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
db.collection("registered_users").find({"_id": docs[0].name}, function(err, n) {
|
||||
var icon = false;
|
||||
if(n.length > 0 && n[0].icon) {
|
||||
icon = n[0].icon;
|
||||
}
|
||||
db.collection("chat_logs").insert({ "createdAt": new Date(), all: true, channel: coll, from: docs[0].name, msg: ": " + data, icon: icon }, function(err, docs) {});
|
||||
io.sockets.emit('chat.all', {from: docs[0].name, msg: ": " + data, channel: coll, icon: icon});
|
||||
});
|
||||
} else if(docs.length == 0) {
|
||||
get_name(guid, {announce: false, channel: coll, message: data, all: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function namechange(data, guid, socket, tried) {
|
||||
/*if(!data.hasOwnProperty("channel") ||
|
||||
typeof(data.channel) != "string") return;*/
|
||||
var pw = "";
|
||||
var new_password;
|
||||
var first = false;
|
||||
Functions.getSessionChatPass(Functions.getSession(socket), function(name, pass) {
|
||||
if(data.hasOwnProperty("first") && data.first) {
|
||||
pw = pass;
|
||||
name = name;
|
||||
data.name = name;
|
||||
data.password = pass;
|
||||
new_password = false;
|
||||
if(name == "" || pass == "") {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
var name = data.name;
|
||||
if(data.hasOwnProperty("first")) {
|
||||
first = data.first;
|
||||
}
|
||||
if(data.hasOwnProperty("password")) {
|
||||
pw = data.password;
|
||||
new_password = false;
|
||||
} else if(data.hasOwnProperty("new_password") && data.hasOwnProperty("old_password")) {
|
||||
pw = data.old_password;
|
||||
new_password = Functions.decrypt_string(data.new_password);
|
||||
}
|
||||
}
|
||||
if(name == "") {
|
||||
return;
|
||||
}
|
||||
var password = Functions.decrypt_string(pw);
|
||||
db.collection("registered_users").find({"_id": name.toLowerCase()}, function(err, docs) {
|
||||
var accepted_password = false;
|
||||
var icon = false;
|
||||
if(docs.length == 0) {
|
||||
if(new_password) {
|
||||
return;
|
||||
}
|
||||
accepted_password = true;
|
||||
Functions.setSessionChatPass(Functions.getSession(socket), name.toLowerCase(), data.password, function() {
|
||||
db.collection("registered_users").update({"_id": name.toLowerCase()}, {$set: {password: Functions.hash_pass(password)}}, {upsert: true}, function() {});
|
||||
});
|
||||
} else if(docs[0].password == Functions.hash_pass(password)) {
|
||||
if(docs[0].icon) {
|
||||
icon = docs[0].icon;
|
||||
}
|
||||
accepted_password = true;
|
||||
if(new_password) {
|
||||
Functions.setSessionChatPass(Functions.getSession(socket), name.toLowerCase(), data.new_password, function() {
|
||||
db.collection("registered_users").update({"_id": name.toLowerCase(), password: Functions.hash_pass(password)}, {$set: {password: Functions.hash_pass(new_password)}}, function() {});
|
||||
});
|
||||
} else {
|
||||
Functions.setSessionChatPass(Functions.getSession(socket), name.toLowerCase(), data.password, function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
if(accepted_password) {
|
||||
db.collection("user_names").find({"guid": guid}, function(err, names) {
|
||||
if(names.length > 0) {
|
||||
var old_name = names[0].name;
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$pull: {names: old_name}}, function() {});
|
||||
db.collection("user_names").update({"guid": guid}, {$set: {name: name, icon: icon}}, function(err, docs) {
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$addToSet: {names: name}}, function(err, docs) {
|
||||
//socket.emit('name', {type: "name", accepted: true});
|
||||
if(old_name != name && !first) {
|
||||
if(data.hasOwnProperty("channel") && typeof(data.channel) == "string") {
|
||||
io.to(data.channel.replace(/ /g,'')).emit('chat', {from: old_name, msg: " changed name to " + name});
|
||||
io.sockets.emit('chat.all', {from: old_name , msg: " changed name to " + name, channel: data.channel});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if(tried < 3 || tried == undefined) {
|
||||
if(tried == undefined) {
|
||||
tried = 1;
|
||||
}
|
||||
namechange(data, guid, socket, tried + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Functions.removeSessionChatPass(Functions.getSession(socket), function() {
|
||||
socket.emit('name', {type: "name", accepted: false});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removename(guid, coll, socket) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
var old_name = docs[0].name;
|
||||
Functions.removeSessionChatPass(Functions.getSession(socket), function() {
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$pull: {names: old_name}}, function(err, updated) {
|
||||
db.collection("user_names").remove({"guid": guid}, function(err, removed) {
|
||||
get_name(guid, {announce: true, old_name: old_name, channel: coll});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generate_name(guid, announce_payload, second) {
|
||||
var tmp_name = Functions.rndName(second ? second : guid, 8);
|
||||
db.collection("registered_users").find({"_id": tmp_name}, function(err, docs) {
|
||||
if(docs.length == 0) {
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$addToSet: {names: tmp_name}}, {upsert: true}, function(err, updated) {
|
||||
if(updated.nModified == 1 || (updated.hasOwnProperty("upserted") && n == 1)) {
|
||||
db.collection("user_names").update({"guid": guid}, {$set: {name: tmp_name, icon: false}}, {upsert: true}, function(err, update){
|
||||
name = tmp_name;
|
||||
if(announce_payload.announce) {
|
||||
io.to(announce_payload.channel).emit('chat', {from: announce_payload.old_name, msg: " changed name to " + name});
|
||||
io.sockets.emit('chat.all', {from: announce_payload.old_name , msg: " changed name to " + name, channel: announce_payload.channel});
|
||||
} else if(announce_payload.message && !announce_payload.all) {
|
||||
io.to(announce_payload.channel).emit('chat', {from: name, msg: ": " + announce_payload.message});
|
||||
} else if(announce_payload.message && announce_payload.all) {
|
||||
io.sockets.emit('chat.all', {from: name, msg: ": " + announce_payload.message, channel: announce_payload.channel});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Chat.generate_name(guid, announce_payload, tmp_name);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Chat.generate_name(guid, announce_payload, tmp_name);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function get_name(guid, announce_payload, first) {
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 0) {
|
||||
Chat.generate_name(guid, announce_payload);
|
||||
} else {
|
||||
name = docs[0].name;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.get_history = get_history;
|
||||
module.exports.chat = chat;
|
||||
module.exports.all_chat = all_chat;
|
||||
module.exports.namechange = namechange;
|
||||
module.exports.removename = removename;
|
||||
module.exports.generate_name = generate_name;
|
||||
module.exports.get_name = get_name;
|
||||
39
server/handlers/db.js
Normal file
@@ -0,0 +1,39 @@
|
||||
var path = require('path');
|
||||
try {
|
||||
var mongo_config = require(path.join(path.join(__dirname, '../config/'), 'mongo_config.js'));
|
||||
} catch(e) {
|
||||
console.log("Error - missing file");
|
||||
console.log("Seems you forgot to create the file mongo_config.js in /server/config/. Have a look at mongo_config.example.js.");
|
||||
process.exit();
|
||||
}
|
||||
var mongojs = require('mongojs');
|
||||
var db = mongojs('mongodb://' + mongo_config.host + '/' + mongo_config.config);
|
||||
var connected_db = mongojs('mongodb://' + mongo_config.host + '/user_credentials');
|
||||
var ObjectId = mongojs.ObjectId;
|
||||
|
||||
db.collection("chat_logs").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 600 }, function(){});
|
||||
db.collection("timeout_api").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 5 }, function(){});
|
||||
db.collection("api_links").createIndex({ "createdAt": 1 }, { expireAfterSeconds: 86400 }, function(){});
|
||||
db.on('connected', function(err) {
|
||||
console.log("connected");
|
||||
});
|
||||
|
||||
db.on('error',function(err) {
|
||||
console.log("\n" + new Date().toString() + "\n Database error: ", err);
|
||||
});
|
||||
|
||||
|
||||
db.on('error',function(err) {
|
||||
console.log("\n" + new Date().toString() + "\n Database error: ", err);
|
||||
});
|
||||
|
||||
/* Resetting usernames, and connected users */
|
||||
db.collection("unique_ids").update({"_id": "unique_ids"}, {$set: {unique_ids: []}}, {multi: true, upsert: true}, function(err, docs){});
|
||||
db.collection("user_names").remove({"guid": {$exists: true}}, {multi: true, upsert: true}, function(err, docs){});
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$set: {names: []}}, {multi: true, upsert: true}, function(err, docs){});
|
||||
db.collection("connected_users").update({users: {$exists: true}}, {$set: {users: []}}, {multi: true, upsert: true}, function(err, docs){});
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$set: {total_users: []}}, {multi: true, upsert: true}, function(err, docs) {});
|
||||
db.collection("frontpage_lists").update({viewers: {$ne: 0}}, {$set: {"viewers": 0}}, {multi: true, upsert: true}, function(err, docs) {});
|
||||
|
||||
|
||||
module.exports = db;
|
||||
32
server/handlers/frontpage.js
Normal file
@@ -0,0 +1,32 @@
|
||||
function frontpage_lists(msg, socket) {
|
||||
if(msg == undefined || !msg.hasOwnProperty('version') || msg.version != VERSION || msg.version == undefined) {
|
||||
var result = {
|
||||
version: {
|
||||
expected: VERSION,
|
||||
got: msg.hasOwnProperty("version") ? msg.version : undefined,
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
|
||||
db.collection("frontpage_lists").find({frontpage:true}, function(err, docs){
|
||||
db.collection("connected_users").find({"_id": "total_users"}, function(err, tot){
|
||||
socket.compress(true).emit("playlists", {channels: docs, viewers: tot[0].total_users.length});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function update_frontpage(coll, id, title, callback) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection("frontpage_lists").update({_id: coll}, {$set: {
|
||||
id: id,
|
||||
title: title,
|
||||
accessed: Functions.get_time()}
|
||||
},{upsert: true}, function(err, returnDocs){
|
||||
if(typeof(callback) == "function") callback();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.frontpage_lists = frontpage_lists;
|
||||
module.exports.update_frontpage = update_frontpage;
|
||||
295
server/handlers/functions.js
Normal file
@@ -0,0 +1,295 @@
|
||||
var path = require('path');
|
||||
try {
|
||||
var mongo_config = require(path.join(path.join(__dirname, '../config/'), 'mongo_config.js'));
|
||||
} catch(e) {
|
||||
console.log("Error - missing file");
|
||||
console.log("Seems you forgot to create the file mongo_config.js in /server/config/. Have a look at mongo_config.example.js.");
|
||||
process.exit();
|
||||
}
|
||||
var mongojs = require('mongojs');
|
||||
var connected_db = mongojs('mongodb://' + mongo_config.host + '/user_credentials');
|
||||
|
||||
function remove_unique_id(short_id) {
|
||||
db.collection("unique_ids").update({"_id": "unique_ids"}, {$pull: {unique_ids: short_id}}, function(err, docs) {});
|
||||
}
|
||||
|
||||
function remove_name_from_db(guid, name) {
|
||||
db.collection("user_names").update({"_id": "all_names"}, {$pull: {names: name}}, function(err, updated) {
|
||||
db.collection("user_names").remove({"guid": guid}, function(err, removed) { });
|
||||
});
|
||||
}
|
||||
|
||||
function getSession(socket) {
|
||||
try {
|
||||
/*var cookieParser = require("cookie-parser");
|
||||
var cookie = require("cookie");
|
||||
var parsedCookies = cookie.parse(socket.handshake.headers.cookie);
|
||||
return parsedCookies["_uI"];*/
|
||||
return socket.cookie_id;
|
||||
} catch(e) {
|
||||
return "empty";
|
||||
}
|
||||
}
|
||||
|
||||
function remove_from_array(array, element){
|
||||
if(Functions.contains(array, element)){
|
||||
var index = array.indexOf(element);
|
||||
if(index != -1)
|
||||
array.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function generate_channel_name(res) {
|
||||
var trying_id = uniqid.time().toLowerCase();
|
||||
db.collection("frontpage_lists").find({frontpage: {$exists: true }, "_id": trying_id }, {"_id": 1}, function(err, docs){
|
||||
if(docs.length == 0) {
|
||||
res.send(trying_id);
|
||||
return;
|
||||
}
|
||||
generate_channel_name(res);
|
||||
});
|
||||
}
|
||||
|
||||
function get_short_id(socket) {
|
||||
var new_short_id = uniqid.time().toLowerCase();
|
||||
|
||||
socket.join(new_short_id);
|
||||
socket.emit("id", new_short_id);
|
||||
}
|
||||
|
||||
function check_inlist(coll, guid, socket, offline)
|
||||
{
|
||||
|
||||
if(coll == undefined) return;
|
||||
coll = coll.replace(/ /g,'');
|
||||
if(!offline && coll != undefined){
|
||||
db.collection("connected_users").update({"_id": coll}, {$addToSet:{users: guid}}, {upsert: true}, function(err, updated) {
|
||||
if(updated.nModified > 0 || updated.upserted != undefined) {
|
||||
db.collection("connected_users").find({"_id": coll}, function(err, new_doc) {
|
||||
db.collection("frontpage_lists").update({"_id": coll}, {$set: {"viewers": new_doc[0].users.length}}, function(){
|
||||
if(new_doc[0].users == undefined || new_doc[0].users.length == undefined) {
|
||||
io.to(coll).emit("viewers", 1);
|
||||
} else {
|
||||
io.to(coll).emit("viewers", new_doc[0].users.length);
|
||||
}
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
socket.broadcast.to(coll).emit('chat', {from: docs[0].name, msg: " joined"});
|
||||
}
|
||||
});
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$addToSet: {total_users: guid + coll}}, function(err, docs){});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
db.collection("connected_users").find({"_id": coll}, function(err, new_doc) {
|
||||
io.to(coll).emit("viewers", new_doc[0].users.length);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
if(offline) {
|
||||
db.collection("connected_users").update({"_id": "offline_users"}, {$addToSet: {users: guid}}, function(err, docs){});
|
||||
} else {
|
||||
db.collection("connected_users").update({"_id": coll}, {$addToSet: {users: guid}}, function(err, docs){});
|
||||
}
|
||||
//
|
||||
if(coll != undefined && coll != "") {
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$addToSet: {total_users: guid + coll}}, function(err, docs) {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rndName(seed, len) {
|
||||
var vowels = ['a', 'e', 'i', 'o', 'u'];
|
||||
consts = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'w', 'x', 'y'];
|
||||
len = Math.floor(len);
|
||||
word = '';
|
||||
is_vowel = false;
|
||||
var arr;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (is_vowel) arr = vowels;
|
||||
else arr = consts;
|
||||
is_vowel = !is_vowel;
|
||||
word += arr[(seed[i%seed.length].charCodeAt()+i) % (arr.length-1)];
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
function removeEmojis (string) {
|
||||
//https://stackoverflow.com/a/41164278/4266467
|
||||
var regex = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g;
|
||||
return string.replace(regex, '');
|
||||
}
|
||||
|
||||
function decrypt_string(pw){
|
||||
try {
|
||||
return Buffer.from(pw, 'base64').toString('ascii')
|
||||
} catch(e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function get_time()
|
||||
{
|
||||
var d = new Date();
|
||||
var time = Math.floor(d.getTime() / 1000);
|
||||
return time;
|
||||
}
|
||||
|
||||
function contains(a, obj) {
|
||||
try{
|
||||
var i = a.length;
|
||||
while (i--) {
|
||||
if (a[i] === obj) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}catch(e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hash_pass(adminpass, hex) {
|
||||
if(hex) return crypto.createHash('sha256').update(adminpass).digest('hex');
|
||||
return crypto.createHash('sha256').update(adminpass).digest('base64');
|
||||
}
|
||||
|
||||
function setSessionAdminPass(id, adminpass, list, callback) {
|
||||
try {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
connected_db.collection(id).update({_id: list}, {$set: {adminpass: adminpass}}, {upsert: true}, function(e, d){
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function setSessionChatPass(id, name, pass, callback) {
|
||||
try {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
connected_db.collection(id).update({_id: "_chat_"}, {$set: {password: pass, name: name}}, {upsert: true}, function(e) {
|
||||
callback();
|
||||
return;
|
||||
})
|
||||
} catch(e) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionChatPass(id, callback) {
|
||||
try {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback("", "", false);
|
||||
return;
|
||||
}
|
||||
|
||||
connected_db.collection(id).find({_id: "_chat_"}, function(e, d) {
|
||||
if(d.length > 0) {
|
||||
var name = "";
|
||||
var pass = "";
|
||||
if(d[0].name != undefined) name = d[0].name;
|
||||
if(d[0].password != undefined) pass = d[0].password;
|
||||
callback(name, pass);
|
||||
return;
|
||||
} else {
|
||||
callback("", "", false);
|
||||
return;
|
||||
}
|
||||
})
|
||||
} catch(e) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function setSessionUserPass(id, userpass, list, callback) {
|
||||
try {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
connected_db.collection(id).update({_id: list}, {$set: {userpass: userpass}}, {upsert: true}, function(e, d){
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
} catch(e) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionAdminUser(id, list, callback) {
|
||||
try {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback("", "", false);
|
||||
return;
|
||||
}
|
||||
connected_db.collection(id).find({_id: list}, function(e, d) {
|
||||
var userpass = "";
|
||||
var adminpass = "";
|
||||
if(d.length > 0) {
|
||||
if(d[0].userpass != undefined) userpass = d[0].userpass;
|
||||
if(d[0].adminpass != undefined) adminpass = d[0].adminpass;
|
||||
}
|
||||
callback(userpass, adminpass, true);
|
||||
})
|
||||
} catch(e) {
|
||||
callback("", "", false);
|
||||
}
|
||||
}
|
||||
|
||||
function removeSessionChatPass(id, callback) {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
connected_db.collection(id).remove({_id: "_chat_"}, function() {
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function removeSessionAdminPass(id, channel, callback) {
|
||||
if(id == "empty" || id == undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
connected_db.collection(id).remove({_id: channel}, function() {
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.removeEmojis = removeEmojis;
|
||||
module.exports.getSessionChatPass = getSessionChatPass;
|
||||
module.exports.setSessionChatPass = setSessionChatPass;
|
||||
module.exports.removeSessionAdminPass = removeSessionAdminPass;
|
||||
module.exports.removeSessionChatPass = removeSessionChatPass;
|
||||
module.exports.setSessionAdminPass = setSessionAdminPass;
|
||||
module.exports.setSessionUserPass = setSessionUserPass;
|
||||
module.exports.getSessionAdminUser = getSessionAdminUser;
|
||||
module.exports.getSession = getSession;
|
||||
module.exports.generate_channel_name = generate_channel_name;
|
||||
module.exports.remove_unique_id = remove_unique_id;
|
||||
module.exports.remove_name_from_db = remove_name_from_db;
|
||||
module.exports.remove_from_array = remove_from_array;
|
||||
module.exports.get_short_id = get_short_id;
|
||||
module.exports.check_inlist = check_inlist;
|
||||
module.exports.rndName = rndName;
|
||||
module.exports.decrypt_string = decrypt_string;
|
||||
module.exports.get_time = get_time;
|
||||
module.exports.contains = contains;
|
||||
module.exports.hash_pass = hash_pass;
|
||||
599
server/handlers/io.js
Normal file
@@ -0,0 +1,599 @@
|
||||
var cookieParser = require("cookie-parser");
|
||||
var cookie = require("cookie");
|
||||
module.exports = function() {
|
||||
io.on('connection', function(socket){
|
||||
try {
|
||||
var parsedCookies = cookie.parse(socket.handshake.headers.cookie);
|
||||
socket.cookie_id = parsedCookies["_uI"];
|
||||
//return socket.guid;
|
||||
} catch(e) {
|
||||
socket.cookie_id = "empty";
|
||||
}
|
||||
socket.zoff_id = socket.id;
|
||||
socket.emit("get_list");
|
||||
|
||||
var guid = Functions.hash_pass(socket.handshake.headers["user-agent"] + socket.handshake.address + socket.handshake.headers["accept-language"]);
|
||||
socket.guid = guid;
|
||||
socket.on('close', function() {
|
||||
});
|
||||
|
||||
socket.on('pinging', function() {
|
||||
socket.emit("ok");
|
||||
});
|
||||
|
||||
var ping_timeout;
|
||||
var socketid = socket.zoff_id;
|
||||
var coll;
|
||||
var in_list = false;
|
||||
var name = "";
|
||||
var short_id;
|
||||
Chat.get_name(guid, {announce: false});
|
||||
var offline = false;
|
||||
var chromecast_object = false;
|
||||
|
||||
socket.emit("guid", guid);
|
||||
|
||||
socket.on('self_ping', function(msg) {
|
||||
|
||||
var channel = msg.channel;
|
||||
if(channel.indexOf("?") > -1){
|
||||
channel = channel.substring(0, channel.indexOf("?"));
|
||||
}
|
||||
channel = channel.replace(/ /g,'');
|
||||
if(offline) {
|
||||
db.collection("connected_users").update({"_id": "offline_users"}, {$addToSet: {users: guid}}, {upsert: true}, function(err, docs){});
|
||||
} else {
|
||||
db.collection("connected_users").update({"_id": channel}, {$addToSet: {users: guid}}, {upsert: true}, function(err, docs){
|
||||
db.collection("frontpage_lists").update({"_id": channel}, {$inc: {viewers: 1}}, {upsert: true}, function(){});
|
||||
});
|
||||
}
|
||||
if(channel != "" && channel != undefined) {
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$addToSet: {total_users: guid + channel}}, {upsert: true}, function(err, docs){});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("logout", function() {
|
||||
Functions.removeSessionAdminPass(Functions.getSession(socket), coll, function() {})
|
||||
});
|
||||
|
||||
socket.on('chromecast', function(msg) {
|
||||
try {
|
||||
if(typeof(msg) == "object" && msg.hasOwnProperty("guid") &&
|
||||
msg.hasOwnProperty("socket_id") && msg.hasOwnProperty("channel") && typeof(msg.guid) == "string" &&
|
||||
typeof(msg.channel) == "string" && typeof(msg.socket_id) == "string") {
|
||||
db.collection("connected_users").find({"_id": msg.channel}, function(err, connected_users_channel) {
|
||||
if(connected_users_channel.length > 0 && connected_users_channel[0].users.indexOf(msg.guid) > -1) {
|
||||
socket.cookie_id = msg.guid;
|
||||
guid = msg.guid;
|
||||
socketid = msg.socket_id;
|
||||
socket.zoff_id = socketid;
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
if(coll.indexOf("?") > -1){
|
||||
coll = coll.substring(0, coll.indexOf("?"));
|
||||
}
|
||||
in_list = true;
|
||||
chromecast_object = true;
|
||||
socket.join(coll);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("get_id", function() {
|
||||
socket.emit("id_chromecast", Functions.getSession(socket));
|
||||
});
|
||||
|
||||
socket.on("error_video", function(msg) {
|
||||
try {
|
||||
var _list = msg.channel.replace(/ /g,'');
|
||||
if(_list.length == 0) return;
|
||||
if(_list.indexOf("?") > -1){
|
||||
_list = _list.substring(0, _list.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
coll = emojiStrip(_list).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
Search.check_error_video(msg, coll);
|
||||
});
|
||||
|
||||
socket.on("get_spread", function(){
|
||||
db.collection("connected_users").find({"_id": "total_users"}, function(err, tot) {
|
||||
db.collection("connected_users").find({"_id": "offline_users"}, function(err, off) {
|
||||
db.collection("connected_users").find({"_id": {$ne: "total_users"}, "_id": {$ne: "offline_users"}}, function(err, users_list) {
|
||||
if(tot.length > 0 && off.length == 0) {
|
||||
socket.emit("spread_listeners", {offline: 0, total: tot[0].total_users.length, online_users: users_list});
|
||||
} else if(tot.length > 0 && off.length > 0){
|
||||
socket.emit("spread_listeners", {offline: off[0].users.length, total: tot[0].total_users.length, online_users: users_list});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('suggest_thumbnail', function(msg){
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
Suggestions.thumbnail(msg, coll.replace(/ /g,''), guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('suggest_description', function(msg){
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
Suggestions.description(msg, coll.replace(/ /g,''), guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on("namechange", function(msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
Chat.namechange(msg, guid, socket);
|
||||
});
|
||||
|
||||
socket.on("removename", function(msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(typeof(msg) != "object" || !msg.hasOwnProperty("channel")) {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
Chat.removename(guid, msg.channel, socket);
|
||||
});
|
||||
|
||||
socket.on("offline", function(msg){
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(!msg.hasOwnProperty('status') || !msg.hasOwnProperty('channel') ||
|
||||
typeof(msg.status) != "boolean" || typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
status: {
|
||||
expected: "boolean",
|
||||
got: msg.hasOwnProperty("status") ? typeof(msg.status) : undefined,
|
||||
},
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
var status = msg.status;
|
||||
var channel = msg.channel.replace(/ /g,'');
|
||||
if(status){
|
||||
in_list = false;
|
||||
offline = true;
|
||||
if(channel != "") coll = channel;
|
||||
if(coll !== undefined) {
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
|
||||
db.collection("connected_users").findAndModify({
|
||||
query: {"_id": coll},
|
||||
update: {$pull: {users: guid}},
|
||||
upsert: true,
|
||||
}, function(err, updated, d) {
|
||||
if(d.n == 1) {
|
||||
var num = 0;
|
||||
if(updated && updated.users) {
|
||||
num = updated.users.length;
|
||||
}
|
||||
io.to(coll).emit("viewers", num);
|
||||
db.collection("frontpage_lists").update({"_id": coll, "viewers": {$gt: 0}}, {$inc: {viewers: -1}}, function(err, docs) { });
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$pull: {total_users: guid + coll}}, function(err, docs){
|
||||
db.collection("connected_users").update({"_id": "offline_users"}, {$addToSet: {users: guid}}, function(err, docs) {
|
||||
if(docs.nModified == 1 && (coll != undefined && coll != "")) {
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$addToSet: {total_users: guid + coll}}, function(err, docs) {});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Functions.remove_name_from_db(guid, name);
|
||||
});
|
||||
}
|
||||
|
||||
Functions.remove_unique_id(short_id);
|
||||
} else {
|
||||
offline = false;
|
||||
db.collection("connected_users").update({"_id": "offline_users"}, {$pull: {users: guid}}, function(err, docs) {
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('get_history', function(msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(!msg.hasOwnProperty("channel") || !msg.hasOwnProperty("all") ||
|
||||
typeof(msg.channel) != "string" || typeof(msg.all) != "boolean") {
|
||||
var result = {
|
||||
all: {
|
||||
expected: "boolean",
|
||||
got: msg.hasOwnProperty("all") ? typeof(msg.all) : undefined,
|
||||
},
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
Chat.get_history(msg.channel.replace(/ /g,''), msg.all, socket);
|
||||
});
|
||||
|
||||
socket.on('chat', function (msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
Chat.chat(msg, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on("all,chat", function(data)
|
||||
{
|
||||
if(data.hasOwnProperty("channel") && data.channel.indexOf("?") > -1){
|
||||
var _list = data.channel.substring(0, data.channel.indexOf("?"));
|
||||
data.channel = _list;
|
||||
}
|
||||
Chat.all_chat(data, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('frontpage_lists', function(msg)
|
||||
{
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
Frontpage.frontpage_lists(msg, socket);
|
||||
});
|
||||
|
||||
socket.on('import_zoff', function(msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
ListChange.addFromOtherList(msg, guid, offline, socket);
|
||||
})
|
||||
|
||||
socket.on('now_playing', function(list, fn)
|
||||
{
|
||||
List.now_playing(list, fn, socket);
|
||||
});
|
||||
|
||||
socket.on('id', function(arr)
|
||||
{
|
||||
if(arr.hasOwnProperty("channel") && arr.channel.indexOf("?") > -1){
|
||||
var _list = arr.channel.substring(0, arr.channel.indexOf("?"));
|
||||
arr.channel = _list;
|
||||
}
|
||||
if(typeof(arr) == 'object')
|
||||
io.to(arr.id).emit(arr.id.toLowerCase(), {type: arr.type, value: arr.value});
|
||||
});
|
||||
|
||||
socket.on('list', function(msg)
|
||||
{
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
try {
|
||||
var _list = msg.channel.replace(/ /g,'');
|
||||
if(_list.length == 0) return;
|
||||
if(_list.indexOf("?") > -1){
|
||||
_list = _list.substring(0, _list.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
coll = emojiStrip(_list).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
//
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(msg.hasOwnProperty("offline") && msg.offline) {
|
||||
offline = true;
|
||||
}
|
||||
List.list(msg, guid, coll, offline, socket);
|
||||
Functions.get_short_id(socket);
|
||||
});
|
||||
|
||||
socket.on('end', function(obj)
|
||||
{
|
||||
if(obj.hasOwnProperty("channel") && obj.channel.indexOf("?") > -1){
|
||||
var _list = obj.channel.substring(0, obj.channel.indexOf("?"));
|
||||
obj.channel = _list;
|
||||
}
|
||||
if(coll === undefined) {
|
||||
try {
|
||||
coll = obj.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List.end(obj, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('addPlaylist', function(arr) {
|
||||
if(arr.hasOwnProperty("channel") && arr.channel.indexOf("?") > -1){
|
||||
var _list = arr.channel.substring(0, arr.channel.indexOf("?"));
|
||||
arr.channel = _list;
|
||||
}
|
||||
ListChange.addPlaylist(arr, guid, offline, socket);
|
||||
})
|
||||
|
||||
socket.on('add', function(arr)
|
||||
{
|
||||
if(arr.hasOwnProperty("channel") && arr.channel.indexOf("?") > -1){
|
||||
var _list = arr.channel.substring(0, arr.channel.indexOf("?"));
|
||||
arr.channel = _list;
|
||||
}
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = arr.list.replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ListChange.add_function(arr, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('delete_all', function(msg) {
|
||||
try {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListChange.delete_all(msg, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('vote', function(msg)
|
||||
{
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ListChange.voteUndecided(msg, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('password', function(inp)
|
||||
{
|
||||
if(inp.hasOwnProperty("channel") && inp.channel.indexOf("?") > -1){
|
||||
var _list = inp.channel.substring(0, inp.channel.indexOf("?"));
|
||||
inp.channel = _list;
|
||||
}
|
||||
if(coll != undefined) coll.replace(/ /g,'');
|
||||
ListSettings.password(inp, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('skip', function(list)
|
||||
{
|
||||
if(list.hasOwnProperty("channel") && list.channel.indexOf("?") > -1){
|
||||
var _list = list.channel.substring(0, list.channel.indexOf("?"));
|
||||
list.channel = _list;
|
||||
}
|
||||
List.skip(list, guid, coll.replace(/ /g,''), offline, socket);
|
||||
});
|
||||
|
||||
socket.on('conf', function(conf)
|
||||
{
|
||||
if(conf.hasOwnProperty("channel") && conf.channel.indexOf("?") > -1){
|
||||
var _list = conf.channel.substring(0, conf.channel.indexOf("?"));
|
||||
conf.channel = _list;
|
||||
}
|
||||
ListSettings.conf_function(conf, coll.replace(/ /g,''), guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('shuffle', function(msg)
|
||||
{
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ListChange.shuffle(msg, coll, guid, offline, socket);
|
||||
});
|
||||
|
||||
socket.on('change_channel', function(obj)
|
||||
{
|
||||
if(obj == undefined && coll != undefined) {
|
||||
obj.channel = coll;
|
||||
} else if(obj.hasOwnProperty("channel") && obj.channel.indexOf("?") > -1){
|
||||
var _list = obj.channel.substring(0, obj.channel.indexOf("?"));
|
||||
obj.channel = _list;
|
||||
}
|
||||
if(coll === undefined && obj !== undefined && obj.channel !== undefined){
|
||||
try {
|
||||
coll = obj.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, true);
|
||||
in_list = false;
|
||||
});
|
||||
|
||||
socket.on('disconnect', function()
|
||||
{
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
});
|
||||
|
||||
socket.on('disconnected', function()
|
||||
{
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
});
|
||||
|
||||
socket.on("left_channel", function(msg) {
|
||||
if(msg.hasOwnProperty("channel") && msg.channel.indexOf("?") > -1){
|
||||
var _list = msg.channel.substring(0, msg.channel.indexOf("?"));
|
||||
msg.channel = _list;
|
||||
}
|
||||
if(msg.hasOwnProperty("channel") && msg.channel != "" && typeof(msg.channel) == "string") {
|
||||
coll = msg.channel.replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('reconnect_failed', function()
|
||||
{
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
});
|
||||
|
||||
socket.on('connect_timeout', function()
|
||||
{
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
});
|
||||
|
||||
socket.on('error', function()
|
||||
{
|
||||
List.left_channel(coll, guid, short_id, in_list, socket, false);
|
||||
});
|
||||
|
||||
socket.on('pos', function(obj)
|
||||
{
|
||||
if(obj.hasOwnProperty("channel") && obj.channel.indexOf("?") > -1){
|
||||
var _list = obj.channel.substring(0, obj.channel.indexOf("?"));
|
||||
obj.channel = _list;
|
||||
}
|
||||
if(!obj.hasOwnProperty("channel") || typeof(obj.channel) != "string")
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = obj.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!obj.hasOwnProperty("channel") || typeof(obj.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: obj.hasOwnProperty("channel") ? typeof(obj.channel) : undefined
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: obj.hasOwnProperty("pass") ? typeof(obj.pass) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs) {
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(userpass != "" || obj.pass == undefined) {
|
||||
obj.pass = userpass;
|
||||
}
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (obj.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(obj.pass)).digest("base64")))) {
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
List.send_play(coll, socket);
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
//send_ping();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
function send_ping() {
|
||||
db.collection("connected_users").update({users: {$exists: true}}, {$set: {users: []}}, {multi: true}, function(err, docs){
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$add: {total_users: 0}}, {multi: true}, function(err, docs){
|
||||
db.collection("frontpage_lists").update({viewers: {$ne: 0}}, {$set: {"viewers": 0}}, {multi: true}, function(err, docs) {
|
||||
io.emit("self_ping");
|
||||
setTimeout(send_ping, 25000);
|
||||
});
|
||||
});
|
||||
});
|
||||
}*/
|
||||
732
server/handlers/list.js
Normal file
@@ -0,0 +1,732 @@
|
||||
var ColorThief = require('color-thief-jimp');
|
||||
var Jimp = require('jimp');
|
||||
|
||||
function now_playing(list, fn, socket) {
|
||||
if(typeof(list) !== 'string' || typeof(fn) !== 'function') {
|
||||
socket.emit('update_required');
|
||||
return;
|
||||
}
|
||||
db.collection(list).find({now_playing:true}, function(err, docs){
|
||||
if(docs.length === 0){
|
||||
fn("No song currently playing");
|
||||
return;
|
||||
}
|
||||
var title = docs[0].title;
|
||||
if(title === undefined) fn("No song currently playing");
|
||||
else fn(title);
|
||||
});
|
||||
}
|
||||
|
||||
function list(msg, guid, coll, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
|
||||
if(typeof(msg) === 'object' && msg !== undefined && msg !== null)
|
||||
{
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass, gotten) {
|
||||
if(gotten && userpass != "" && !msg.hasOwnProperty("pass")) {
|
||||
msg.pass = userpass;
|
||||
}
|
||||
if(!msg.hasOwnProperty('version') || !msg.hasOwnProperty("channel") ||
|
||||
msg.version != VERSION || msg.version == undefined ||
|
||||
typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
version: {
|
||||
expected: VERSION,
|
||||
got: msg.version,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
var pass = crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64");
|
||||
db.collection('frontpage_lists').find({"_id": coll}, function(err, frontpage_lists){
|
||||
if(frontpage_lists.length == 1) {
|
||||
db.collection(coll + "_settings").find(function(err, docs) {
|
||||
if(docs.length == 0 || (docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || docs[0].userpass == pass))) {
|
||||
if(docs.length > 0 && docs[0].hasOwnProperty('userpass') && docs[0].userpass != "" && docs[0].userpass == pass) {
|
||||
Functions.setSessionUserPass(Functions.getSession(socket), msg.pass, coll, function(){})
|
||||
socket.emit("auth_accepted", {value: true});
|
||||
}
|
||||
if(docs.length > 0 && docs[0].hasOwnProperty("adminpass") && docs[0].adminpass != "" && docs[0].adminpass == Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(adminpass), true))) {
|
||||
socket.emit("pw", true);
|
||||
}
|
||||
in_list = true;
|
||||
socket.join(coll);
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
|
||||
if(frontpage_lists.viewers != undefined){
|
||||
io.to(coll).emit("viewers", frontpage_lists.viewers);
|
||||
} else {
|
||||
io.to(coll).emit("viewers", 1);
|
||||
}
|
||||
|
||||
List.send_list(coll, socket, true, false, true);
|
||||
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
db.createCollection(coll, function(err, docs){
|
||||
db.collection(coll).createIndex({ id: 1}, {unique: true}, function(e, d) {
|
||||
var configs = {"addsongs":false, "adminpass":"", "allvideos":true, "frontpage":true, "longsongs":false, "removeplay": false, "shuffle": true, "skip": false, "skips": [], "startTime":Functions.get_time(), "views": [], "vote": false, "desc": "", userpass: "", id: "config"};
|
||||
db.collection(coll + "_settings").insert(configs, function(err, docs){
|
||||
socket.join(coll);
|
||||
List.send_list(coll, socket, true, false, true);
|
||||
db.collection("frontpage_lists").insert({"_id": coll, "count" : 0, "frontpage": true, "accessed": Functions.get_time(), "viewers": 1});
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
msg: {
|
||||
expected: "object",
|
||||
got: typeof(msg)
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
}
|
||||
}
|
||||
|
||||
function skip(list, guid, coll, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
|
||||
if(list !== undefined && list !== null && list !== "")
|
||||
{
|
||||
if(coll == undefined && list.hasOwnProperty('channel')) coll = list.channel.toLowerCase();
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = list.channel.toLowerCase().replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!list.hasOwnProperty("id") || !list.hasOwnProperty("channel") ||
|
||||
typeof(list.id) != "string" || typeof(list.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: list.hasOwnProperty("channel") ? typeof(list.channel) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: list.hasOwnProperty("pass") ? typeof(list.pass) : undefined,
|
||||
},
|
||||
userpass: {
|
||||
expected: "string",
|
||||
got: list.hasOwnProperty("userpass") ? typeof(list.userpass) : undefined,
|
||||
},
|
||||
id: {
|
||||
expected: "string",
|
||||
got: list.hasOwnProperty("id") ? typeof(list.id) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(adminpass != "" || list.pass == undefined) {
|
||||
list.pass = adminpass;
|
||||
}
|
||||
if(userpass != "" || list.userpass == undefined) {
|
||||
list.userpass = userpass;
|
||||
}
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (list.hasOwnProperty('userpass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(list.userpass)).digest("base64")))) {
|
||||
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
|
||||
adminpass = "";
|
||||
video_id = list.id;
|
||||
err = list.error;
|
||||
var error = false;
|
||||
var video_id;
|
||||
if(err != "5" && err != "100" && err != "101" && err != "150")
|
||||
{
|
||||
adminpass = list.pass;
|
||||
}else if(err == "5" || err == "100" || err == "101" || err == "150"){
|
||||
error = true;
|
||||
}
|
||||
|
||||
if(adminpass !== undefined && adminpass !== null && adminpass !== "")
|
||||
hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(adminpass),true));
|
||||
else
|
||||
hash = "";
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
|
||||
if(docs !== null && docs.length !== 0)
|
||||
{
|
||||
if(!docs[0].skip || (docs[0].adminpass == hash && docs[0].adminpass !== "") || error)
|
||||
{
|
||||
db.collection("frontpage_lists").find({"_id": coll}, function(err, frontpage_viewers){
|
||||
if((frontpage_viewers[0].viewers/2 <= docs[0].skips.length+1 && !Functions.contains(docs[0].skips, guid) && frontpage_viewers[0].viewers != 2) ||
|
||||
(frontpage_viewers[0].viewers == 2 && docs[0].skips.length+1 == 2 && !Functions.contains(docs[0].skips, guid)) ||
|
||||
(docs[0].adminpass == hash && docs[0].adminpass !== "" && docs[0].skip))
|
||||
{
|
||||
List.change_song(coll, error, video_id);
|
||||
socket.emit("toast", "skip");
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
db.collection("registered_users").find({"_id": docs[0].name}, function(err, n) {
|
||||
var icon = false;
|
||||
if(n.length > 0 && n[0].icon) {
|
||||
icon = n[0].icon;
|
||||
}
|
||||
io.to(coll).emit('chat', {from: docs[0].name, icon: icon, msg: " skipped"});
|
||||
});
|
||||
}
|
||||
});
|
||||
}else if(!Functions.contains(docs[0].skips, guid)){
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {$push:{skips:guid}}, function(err, d){
|
||||
if(frontpage_viewers[0].viewers == 2)
|
||||
to_skip = 1;
|
||||
else
|
||||
to_skip = (Math.ceil(frontpage_viewers[0].viewers/2) - docs[0].skips.length-1);
|
||||
socket.emit("toast", to_skip + " more are needed to skip!");
|
||||
socket.to(coll).emit('chat', {from: name, msg: " voted to skip"});
|
||||
});
|
||||
}else{
|
||||
socket.emit("toast", "alreadyskip");
|
||||
}
|
||||
});
|
||||
}else
|
||||
socket.emit("toast", "noskip");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
msg: {
|
||||
expected: "object",
|
||||
got: typeof(list),
|
||||
},
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
}
|
||||
}
|
||||
|
||||
function change_song(coll, error, id, callback, socket) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
var startTime = docs[0].startTime;
|
||||
if(docs !== null && docs.length !== 0)
|
||||
{
|
||||
db.collection(coll).aggregate([{
|
||||
$match:{
|
||||
views:{
|
||||
$exists: false
|
||||
},
|
||||
type:{
|
||||
$ne: "suggested"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
$sort:{
|
||||
now_playing: -1,
|
||||
votes:-1,
|
||||
added:1,
|
||||
title: 1
|
||||
}
|
||||
}, {
|
||||
$limit:2
|
||||
}], function(err, now_playing_doc){
|
||||
|
||||
if((id && id == now_playing_doc[0].id) || !id) {
|
||||
if(error){
|
||||
request('http://img.youtube.com/vi/'+now_playing_doc[0].id+'/mqdefault.jpg', function (err, response, body) {
|
||||
if (err || response.statusCode == 404) {
|
||||
db.collection(coll).remove({now_playing:true, id:id}, function(err, docs){
|
||||
var next_song;
|
||||
if(now_playing_doc.length == 2) next_song = now_playing_doc[1].id;
|
||||
List.change_song_post(coll, next_song, callback, socket);
|
||||
if(!callback) {
|
||||
io.to(coll).emit("channel", {type: "deleted", value: now_playing_doc[0].id, removed: true});
|
||||
}
|
||||
db.collection("frontpage_lists").update({_id: coll, count: {$gt: 0}}, {$inc: {count: -1}, $set:{accessed: Functions.get_time()}}, {upsert: true}, function(err, docs){});
|
||||
});
|
||||
} else {
|
||||
if((docs[0].skipped_time != undefined && docs[0].skipped_time != Functions.get_time()) || docs[0].skipped_time == undefined) {
|
||||
db.collection(coll + "_settings").update({id: "config"}, {$set: {skipped_time: Functions.get_time()}}, function(err, updated){
|
||||
db.collection(coll).update({now_playing:true, id:id}, {
|
||||
$set:{
|
||||
now_playing:false,
|
||||
votes:0,
|
||||
guids:[]
|
||||
}
|
||||
},{multi:true}, function(err, docs){
|
||||
var next_song;
|
||||
if(now_playing_doc.length == 2) next_song = now_playing_doc[1].id;
|
||||
if(docs.n >= 1) List.change_song_post(coll, next_song, callback, socket);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else if(docs[0].removeplay === true){
|
||||
db.collection(coll).remove({now_playing:true, id:id}, function(err, docs){
|
||||
var next_song;
|
||||
if(now_playing_doc.length == 2) next_song = now_playing_doc[1].id;
|
||||
List.change_song_post(coll, next_song, callback, socket);
|
||||
if(!callback) {
|
||||
io.to(coll).emit("channel", {type: "deleted", value: now_playing_doc[0].id, removed: true});
|
||||
}
|
||||
db.collection("frontpage_lists").update({_id: coll, count: {$gt: 0}}, {$inc: {count: -1}, $set:{accessed: Functions.get_time()}}, {upsert: true}, function(err, docs){});
|
||||
});
|
||||
} else {
|
||||
|
||||
if((docs[0].skipped_time != undefined && docs[0].skipped_time != Functions.get_time()) || docs[0].skipped_time == undefined) {
|
||||
db.collection(coll).update({now_playing:true, id:id}, {
|
||||
$set:{
|
||||
now_playing:false,
|
||||
votes:0,
|
||||
guids:[]
|
||||
}
|
||||
},{multi:true}, function(err, docs){
|
||||
var next_song;
|
||||
if(now_playing_doc.length == 2) next_song = now_playing_doc[1].id;
|
||||
List.change_song_post(coll, next_song, callback, socket);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(now_playing_doc[0].now_playing == true && now_playing_doc.length > 1 && now_playing_doc[1].id == id) {
|
||||
db.collection(coll).update({id: now_playing_doc[0].id}, {$set: {now_playing: false}}, function(e, d) {
|
||||
change_song(coll, error, id, callback, socket);
|
||||
})
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function change_song_post(coll, next_song, callback, socket) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll).aggregate([{
|
||||
$match:{
|
||||
now_playing:false,
|
||||
type:{
|
||||
$ne: "suggested"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
$sort:{
|
||||
votes:-1,
|
||||
added:1,
|
||||
title: 1
|
||||
}
|
||||
}, {
|
||||
$limit:2
|
||||
}], function(err, docs){
|
||||
if(docs !== null && docs.length > 0){
|
||||
var id = docs[0].id;
|
||||
if(next_song && next_song != id) {
|
||||
if((docs.length == 2 && next_song == docs[1].id)) {
|
||||
id = docs[1].id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
db.collection(coll).update({id:id},{
|
||||
$set:{
|
||||
now_playing:true,
|
||||
votes:0,
|
||||
guids:[],
|
||||
added:Functions.get_time()
|
||||
}
|
||||
}, function(err, returnDocs){
|
||||
db.collection(coll + "_settings").update({id: "config"}, {
|
||||
$set:{
|
||||
startTime:Functions.get_time(),
|
||||
skips:[]
|
||||
}
|
||||
}, function(err, returnDocs){
|
||||
db.collection(coll + "_settings").find({id: "config"}, function(err, conf){
|
||||
if(!callback) {
|
||||
io.to(coll).emit("channel", {type: "song_change", time: Functions.get_time(), remove: conf[0].removeplay});
|
||||
List.send_play(coll);
|
||||
} else {
|
||||
socket.to(coll).emit("channel", {type: "song_change", time: Functions.get_time(), remove: conf[0].removeplay});
|
||||
List.send_play(coll, socket, true);
|
||||
callback();
|
||||
}
|
||||
Frontpage.update_frontpage(coll, docs[0].id, docs[0].title);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function send_list(coll, socket, send, list_send, configs, shuffled)
|
||||
{
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll + "_settings").find({id: "config"}, function(err, _conf){
|
||||
var conf = _conf;
|
||||
if(conf.length == 0) {
|
||||
var conf = {"id": "config", "addsongs":false, "adminpass":"", "allvideos":true, "frontpage":true, "longsongs":false, "removeplay": false, "shuffle": true, "skip": false, "skips": [], "startTime":Functions.get_time(), "views": [], "vote": false, "desc": "", userpass: ""};
|
||||
db.collection(coll + "_settings").update({id: "config"}, conf, {upsert: true}, function(err, docs) {
|
||||
send_list(coll, socket, send, list_send, configs, shuffled);
|
||||
});
|
||||
} else {
|
||||
db.collection(coll).find({type: {$ne: "suggested"}}, function(err, docs)
|
||||
{
|
||||
if(docs.length > 0) {
|
||||
db.collection(coll).find({now_playing: true}, function(err, np_docs) {
|
||||
if(np_docs.length == 0) {
|
||||
db.collection(coll).aggregate([{
|
||||
$match:{
|
||||
views:{
|
||||
$exists: false
|
||||
},
|
||||
type:{
|
||||
$ne: "suggested"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
$sort:{
|
||||
now_playing: -1,
|
||||
votes:-1,
|
||||
added:1,
|
||||
title: 1
|
||||
}
|
||||
}, {
|
||||
$limit:1
|
||||
}], function(err, now_playing_doc){
|
||||
if(now_playing_doc[0].now_playing == false) {
|
||||
db.collection(coll).update({id:now_playing_doc[0].id}, {
|
||||
$set:{
|
||||
now_playing:true,
|
||||
votes:0,
|
||||
guids:[],
|
||||
added:Functions.get_time()
|
||||
}
|
||||
}, function(err, returnDocs){
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {
|
||||
$set:{
|
||||
startTime: Functions.get_time(),
|
||||
skips:[]
|
||||
}
|
||||
}, function(err, returnDocs){
|
||||
Frontpage.update_frontpage(coll, now_playing_doc[0].id, now_playing_doc[0].title);
|
||||
List.send_list(coll, socket, send, list_send, configs, shuffled);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if(np_docs.length > 1) {
|
||||
db.collection(coll).aggregate([{
|
||||
$match:{
|
||||
now_playing: true
|
||||
}
|
||||
}, {
|
||||
$sort:{
|
||||
now_playing: -1,
|
||||
votes:-1,
|
||||
added:1,
|
||||
title: 1
|
||||
}
|
||||
}], function(e, docs) {
|
||||
var real_now_playing = docs[docs.length - 1];
|
||||
db.collection(coll).update({now_playing: true, id: {$ne: real_now_playing.id}}, {$set: {now_playing: false}}, {multi: true}, function(e, d) {
|
||||
send_list(coll, socket, send, list_send, configs, shuffled);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
if(Functions.get_time()-conf[0].startTime > np_docs[0].duration){
|
||||
List.change_song(coll, false, np_docs[0].id, function() {
|
||||
List.send_list(coll, socket, send, list_send, configs, shuffled);
|
||||
}, socket);
|
||||
} else {
|
||||
if(list_send) {
|
||||
io.to(coll).emit("channel", {type: "list", playlist: docs, shuffled: shuffled});
|
||||
} else if(!list_send) {
|
||||
socket.emit("channel", {type: "list", playlist: docs, shuffled: shuffled});
|
||||
}
|
||||
if(socket === undefined && send) {
|
||||
List.send_play(coll);
|
||||
} else if(send) {
|
||||
List.send_play(coll, socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if(list_send) {
|
||||
io.to(coll).emit("channel", {type: "list", playlist: docs, shuffled: shuffled});
|
||||
} else if(!list_send) {
|
||||
socket.emit("channel", {type: "list", playlist: docs, shuffled: shuffled});
|
||||
}
|
||||
if(socket === undefined && send) {
|
||||
List.send_play(coll);
|
||||
} else if(send) {
|
||||
List.send_play(coll, socket);
|
||||
}
|
||||
}
|
||||
});
|
||||
if(configs)
|
||||
{
|
||||
if(conf.length > 0) {
|
||||
if(conf[0].adminpass !== "") conf[0].adminpass = true;
|
||||
if(conf[0].hasOwnProperty("userpass") && conf[0].userpass != "") conf[0].userpass = true;
|
||||
else conf[0].userpass = false;
|
||||
io.to(coll).emit("conf", conf);
|
||||
} else if(conf.length == 0 && docs.length > 0) {
|
||||
var conf = {"id": "config", "addsongs":false, "adminpass":"", "allvideos":true, "frontpage":true, "longsongs":false, "removeplay": false, "shuffle": true, "skip": false, "skips": [], "startTime":Functions.get_time(), "views": [], "vote": false, "desc": "", userpass: ""};
|
||||
db.collection(coll + "_settings").update({id: "config"}, conf, {upsert: true}, function(err, docs) {
|
||||
io.to(coll).emit("conf", conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if(socket){
|
||||
db.collection(coll).find({type:"suggested"}).sort({added: 1}, function(err, sugg){
|
||||
socket.emit("suggested", sugg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function end(obj, coll, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(obj) !== 'object') {
|
||||
return;
|
||||
}
|
||||
id = obj.id;
|
||||
|
||||
if(id !== undefined && id !== null && id !== "") {
|
||||
|
||||
if(!obj.hasOwnProperty("id") || !obj.hasOwnProperty("channel") ||
|
||||
typeof(obj.id) != "string" || typeof(obj.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: obj.hasOwnProperty("channel") ? typeof(obj.channel) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: obj.hasOwnProperty("pass") ? typeof(obj.pass) : undefined,
|
||||
},
|
||||
id: {
|
||||
expected: "string",
|
||||
got: obj.hasOwnProperty("id") ? typeof(obj.id) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
return;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass) {
|
||||
if(userpass != "" || obj.pass == undefined) {
|
||||
obj.pass = userpass;
|
||||
}
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (obj.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(obj.pass)).digest("base64")))) {
|
||||
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
db.collection(coll).find({now_playing:true}, function(err, np){
|
||||
if(err !== null) console.log(err);
|
||||
if(np !== null && np !== undefined && np.length == 1 && np[0].id == id){
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
var startTime = docs[0].startTime;
|
||||
if(docs[0].removeplay === true && startTime+parseInt(np[0].duration)<=Functions.get_time()+5)
|
||||
{
|
||||
db.collection(coll).remove({now_playing:true}, function(err, docs){
|
||||
List.change_song_post(coll);
|
||||
db.collection("frontpage_lists").update({_id:coll, count: {$gt: 0}}, {$inc:{count:-1}, $set:{accessed: Functions.get_time()}}, {upsert:true}, function(err, docs){});
|
||||
});
|
||||
}else{
|
||||
if(startTime+parseInt(np[0].duration)<=Functions.get_time()+5)
|
||||
{
|
||||
List.change_song(coll, false, id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
msg: {
|
||||
expected: "object",
|
||||
got: typeof(obj)
|
||||
},
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
}
|
||||
}
|
||||
|
||||
function send_play(coll, socket, broadcast) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll).find({now_playing:true}, function(err, np){
|
||||
db.collection(coll + "_settings").find(function(err, conf){
|
||||
if(err !== null) console.log(err);
|
||||
try{
|
||||
if(Functions.get_time()-conf[0].startTime > np[0].duration){
|
||||
List.change_song(coll, false, np[0].id);
|
||||
} else if(conf !== null && conf !== undefined && conf.length !== 0)
|
||||
{
|
||||
if(conf[0].adminpass !== "") conf[0].adminpass = true;
|
||||
if(conf[0].hasOwnProperty("userpass") && conf[0].userpass != "") conf[0].userpass = true;
|
||||
else conf[0].userpass = false;
|
||||
if(!np.hasOwnProperty("start")) np.start = 0;
|
||||
if(!np.hasOwnProperty("end")) np.end = np.duration;
|
||||
toSend = {np: np, conf: conf, time: Functions.get_time()};
|
||||
if(socket === undefined) {
|
||||
io.to(coll).emit("np", toSend);
|
||||
//
|
||||
List.getNextSong(coll)
|
||||
sendColor(coll, false, np[0].id);
|
||||
} else {
|
||||
sendColor(coll, socket, np[0].id);
|
||||
if(broadcast) {
|
||||
socket.to(coll).emit("np", toSend);
|
||||
return;
|
||||
}
|
||||
socket.emit("np", toSend);
|
||||
}
|
||||
}
|
||||
} catch(e){
|
||||
if(socket) {
|
||||
if(broadcast) {
|
||||
socket.to(coll).emit("np", {});
|
||||
return;
|
||||
}
|
||||
socket.emit("np", {});
|
||||
} else {
|
||||
io.to(coll).emit("np", {});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function sendColor(coll, socket, id, ajax, res) {
|
||||
if(coll != undefined && typeof(coll) == "string") {
|
||||
coll = coll.replace(/ /g,'');
|
||||
}
|
||||
var url = 'https://img.youtube.com/vi/'+id+'/mqdefault.jpg';
|
||||
Jimp.read(url).then(function (image) {
|
||||
|
||||
var c = ColorThief.getColor(image);
|
||||
if(ajax) {
|
||||
res.header({"Content-Type": "application/json"});
|
||||
res.status(200).send(c);
|
||||
return;
|
||||
} else {
|
||||
if(socket) {
|
||||
socket.emit("color", {color: c, only: true});
|
||||
} else {
|
||||
io.to(coll).emit("color", {color: c, only: false});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getNextSong(coll, callback) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll).aggregate([{
|
||||
$match:{
|
||||
views:{
|
||||
$exists: false
|
||||
},
|
||||
type:{
|
||||
$ne: "suggested"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
$sort:{
|
||||
now_playing: 1,
|
||||
votes:-1,
|
||||
added:1,
|
||||
title: 1
|
||||
}
|
||||
}, {
|
||||
$limit:1
|
||||
}], function(err, doc) {
|
||||
if(doc.length == 1) {
|
||||
io.to(coll).emit("next_song", {videoId: doc[0].id, title: doc[0].title});
|
||||
}
|
||||
if(typeof(callback) == "function") callback();
|
||||
});
|
||||
}
|
||||
|
||||
function left_channel(coll, guid, short_id, in_list, socket, change) {
|
||||
if(!coll) return;
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection("connected_users").update({"_id": coll}, {$pull: {users: guid}}, function(err, updated) {
|
||||
if(updated.nModified > 0) {
|
||||
db.collection("connected_users").find({"_id": coll}, function(err, new_doc){
|
||||
db.collection("frontpage_lists").update({"_id": coll, viewers: {$gt: 0}}, {$inc: {viewers: -1}}, function(err, doc) {
|
||||
db.collection("user_names").find({"guid": guid}, function(err, docs) {
|
||||
if(docs.length == 1) {
|
||||
io.to(coll).emit('chat', {from: docs[0].name, msg: " left"});
|
||||
}
|
||||
});
|
||||
io.to(coll).emit("viewers", new_doc[0].users.length);
|
||||
socket.leave(coll);
|
||||
});
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$pull: {total_users: guid + coll}}, function(err, updated){});
|
||||
|
||||
if(!change) {
|
||||
Functions.remove_name_from_db(guid, name);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
db.collection("connected_users").update({"_id": "offline_users"}, {$pull: {users: guid}}, function(err, updated){
|
||||
if(updated.nModified > 0) {
|
||||
db.collection("connected_users").update({"_id": "total_users"}, {$pull: {total_users: guid + coll}}, function(err, updated){});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
Functions.remove_unique_id(short_id);
|
||||
}
|
||||
|
||||
module.exports.sendColor = sendColor;
|
||||
module.exports.now_playing = now_playing;
|
||||
module.exports.list = list;
|
||||
module.exports.skip = skip;
|
||||
module.exports.change_song = change_song;
|
||||
module.exports.change_song_post = change_song_post;
|
||||
module.exports.send_list = send_list;
|
||||
module.exports.end = end;
|
||||
module.exports.send_play = send_play;
|
||||
module.exports.getNextSong = getNextSong;
|
||||
module.exports.left_channel = left_channel;
|
||||
724
server/handlers/list_change.js
Normal file
@@ -0,0 +1,724 @@
|
||||
function addFromOtherList(arr, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(arr) == "object") {
|
||||
if(!arr.hasOwnProperty("channel") || !arr.hasOwnProperty("new_channel")
|
||||
|| typeof(arr.channel) != "string" || typeof(arr.new_channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("channel") ? typeof(arr.channel) : undefined
|
||||
},
|
||||
new_channel: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("new_channel") ? typeof(arr.new_channel) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
var channel = arr.channel.replace(/ /g,'').toLowerCase();
|
||||
var new_channel = arr.new_channel.replace(/ /g, '').toLowerCase();
|
||||
db.collection("frontpage_lists").find({_id: new_channel}, function(err, fp) {
|
||||
if(fp.length == 0) {
|
||||
socket.emit("toast", "nolist");
|
||||
return;
|
||||
}
|
||||
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), channel, function(userpass, adminpass) {
|
||||
if(userpass != "" || arr.userpass == undefined) {
|
||||
arr.userpass = userpass;
|
||||
}
|
||||
if(adminpass != "" || arr.adminpass == undefined) {
|
||||
arr.adminpass = adminpass;
|
||||
}
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), new_channel, function(userpass) {
|
||||
var otheruser = "";
|
||||
if(userpass != "") {
|
||||
otheruser = userpass;
|
||||
}
|
||||
db.collection(channel).find({now_playing: true}, function(e, np) {
|
||||
|
||||
var project_object = {
|
||||
"id": 1,
|
||||
"added": 1,
|
||||
"guids": { "$literal": [] },
|
||||
"now_playing": 1,
|
||||
"title": 1,
|
||||
"votes": { "$literal": 0 },
|
||||
"start": 1,
|
||||
"duration": 1,
|
||||
"end": 1,
|
||||
"type": 1
|
||||
};
|
||||
var to_set_np = true;
|
||||
if(np.length > 0) {
|
||||
project_object.now_playing = { "$literal": false };
|
||||
to_set_np = false;
|
||||
}
|
||||
db.collection(new_channel + "_settings").find({id: "config"}, function(e, new_conf) {
|
||||
if(new_conf.length > 0 && (new_conf[0].userpass == "" || !new_conf[0].userpass || new_conf[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(otheruser)).digest("base64"))) {
|
||||
db.collection(channel + "_settings").find({id: "config"}, function(e, this_conf) {
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(arr.adminpass), true));
|
||||
if((this_conf[0].userpass == "" || !this_conf[0].userpass || this_conf[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(arr.userpass)).digest("base64"))) {
|
||||
if(((this_conf[0].addsongs === true && (hash == this_conf[0].adminpass || this_conf[0].adminpass === "")) ||
|
||||
this_conf[0].addsongs === false)) {
|
||||
db.collection(new_channel).aggregate([
|
||||
{
|
||||
"$match": { type: "video" }
|
||||
},
|
||||
{
|
||||
"$project": project_object
|
||||
}
|
||||
], function(e, docs) {
|
||||
var path = require('path');
|
||||
var mongo_config = require(path.join(path.join(__dirname, '../config/'), 'mongo_config.js'));
|
||||
var MongoClient = require('mongodb').MongoClient;
|
||||
var url = "mongodb://" + mongo_config.host + ":" + mongo_config.port + "/";
|
||||
MongoClient.connect(url, function(err, _db) {
|
||||
var dbo = _db.db(mongo_config.config);
|
||||
dbo.collection(channel).insertMany(docs, {ordered: false}, function(err, res) {
|
||||
db.collection(channel + "_settings").update({id: "config"}, {$set: {startTime: Functions.get_time()}}, function(e,d) {
|
||||
if(to_set_np) {
|
||||
var to_change = {
|
||||
_id: channel,
|
||||
count: res.nInserted != undefined ? res.nInserted : res.insertedCount,
|
||||
frontpage: true,
|
||||
accessed: Functions.get_time(),
|
||||
}
|
||||
db.collection(channel).find({now_playing: true}, function(e, np_docs) {
|
||||
to_change.id = np_docs[0].id;
|
||||
to_change.title = np_docs[0].title;
|
||||
db.collection("frontpage_lists").update({_id: channel}, {$set: to_change}, function(e, d) {
|
||||
List.send_list(channel, undefined, false, true, false);
|
||||
List.send_play(channel, undefined);
|
||||
socket.emit("toast", "addedplaylist");
|
||||
_db.close();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
db.collection("frontpage_lists").update({_id: channel}, {$inc: {count: res.nInserted != undefined ? res.nInserted : res.insertedCount}}, function(e, d) {
|
||||
List.send_list(channel, undefined, false, true, false);
|
||||
List.send_play(channel, undefined);
|
||||
socket.emit("toast", "addedplaylist");
|
||||
_db.close();
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "listhaspass");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
return;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
socket.emit("toast", "other_list_pass");
|
||||
return;
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addPlaylist(arr, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(arr) == "object") {
|
||||
if(!arr.hasOwnProperty("channel") || !arr.hasOwnProperty("songs")
|
||||
|| typeof(arr.channel) != "string" || typeof(arr.songs) != "object") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("channel") ? typeof(arr.channel) : undefined
|
||||
},
|
||||
songs: {
|
||||
expected: "object",
|
||||
got: arr.hasOwnProperty("songs") ? typeof(arr.songs) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
var channel = arr.channel.replace(/ /g,'').toLowerCase();
|
||||
db.collection("frontpage_lists").find({_id: channel}, function(err, fp) {
|
||||
if(fp.length == 0) {
|
||||
socket.emit("toast", "nolist");
|
||||
return;
|
||||
}
|
||||
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), channel, function(userpass, adminpass) {
|
||||
if(userpass != "" || arr.userpass == undefined) {
|
||||
arr.userpass = userpass;
|
||||
}
|
||||
if(adminpass != "" || arr.adminpass == undefined) {
|
||||
arr.adminpass = adminpass;
|
||||
}
|
||||
db.collection(channel).find({now_playing: true}, function(e, np) {
|
||||
var now_playing = false;
|
||||
if(np.length == 0) now_playing = true;
|
||||
db.collection(channel + "_settings").find({id: "config"}, function(e, conf) {
|
||||
if(arr.length == 0) {
|
||||
socket.emit("toast", "Empty list..");
|
||||
return;
|
||||
}
|
||||
if(conf.length > 0) {
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(arr.adminpass), true));
|
||||
if((conf[0].userpass == "" || !conf[0].userpass || conf[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(arr.userpass)).digest("base64"))) {
|
||||
if(((conf[0].addsongs === true && (hash == conf[0].adminpass || conf[0].adminpass === "")) ||
|
||||
conf[0].addsongs === false)) {
|
||||
var path = require('path');
|
||||
var mongo_config = require(path.join(path.join(__dirname, '../config/'), 'mongo_config.js'));
|
||||
var MongoClient = require('mongodb').MongoClient;
|
||||
var url = "mongodb://" + mongo_config.host + ":" + mongo_config.port + "/";
|
||||
MongoClient.connect(url, function(err, _db) {
|
||||
var dbo = _db.db(mongo_config.config);
|
||||
var number_elements = arr.songs.length + 1;
|
||||
var time = Functions.get_time() - number_elements;
|
||||
var to_set_np = now_playing;
|
||||
var bulk = dbo.collection(channel).initializeUnorderedBulkOp({useLegacyOps: true});
|
||||
for(var i = 0; i < arr.songs.length; i++) {
|
||||
var this_element = arr.songs[i];
|
||||
if(!this_element.hasOwnProperty("duration") || !this_element.hasOwnProperty("id") || !this_element.hasOwnProperty("title")) {
|
||||
continue;
|
||||
}
|
||||
this_element.added = time;
|
||||
this_element.now_playing = now_playing;
|
||||
this_element.votes = 0;
|
||||
this_element.guids = [];
|
||||
if(!this_element.hasOwnProperty("start")) this_element.start = 0;
|
||||
if(!this_element.hasOwnProperty("end")) this_element.end = this_element.duration;
|
||||
this_element.start = parseInt(this_element.start);
|
||||
this_element.end = parseInt(this_element.end);
|
||||
this_element.type = "video";
|
||||
this_element.duration = parseInt(this_element.duration);
|
||||
if(this_element.start > this_element.end) {
|
||||
this_element.start = 0;
|
||||
}
|
||||
if(now_playing) {
|
||||
now_playing = false;
|
||||
}
|
||||
bulk.insert(this_element);
|
||||
}
|
||||
bulk.execute(function(err, results) {
|
||||
db.collection(channel + "_settings").update({id: "config"}, {$set: {startTime: Functions.get_time()}}, function(e,d) {
|
||||
if(to_set_np) {
|
||||
var to_change = {
|
||||
_id: channel,
|
||||
count: results.nInserted,
|
||||
frontpage: true,
|
||||
accessed: Functions.get_time(),
|
||||
}
|
||||
db.collection(channel).find({now_playing: true}, function(e, np_docs) {
|
||||
to_change.id = np_docs[0].id;
|
||||
to_change.title = np_docs[0].title;
|
||||
db.collection("frontpage_lists").update({_id: channel}, {$set: to_change}, function(e, d) {
|
||||
List.send_list(channel, undefined, false, true, false);
|
||||
List.send_play(channel, undefined);
|
||||
socket.emit("toast", "addedplaylist");
|
||||
_db.close();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
db.collection("frontpage_lists").update({_id: channel}, {$inc: {count: results.nInserted != undefined ? results.nInserted : results.insertedCount}}, function(e, d) {
|
||||
List.send_list(channel, undefined, false, true, false);
|
||||
List.send_play(channel, undefined);
|
||||
socket.emit("toast", "addedplaylist");
|
||||
_db.close();
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "listhaspass");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
socket.emit("toast", "nolist");
|
||||
return;
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function add_function(arr, coll, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(arr) === 'object' && arr !== undefined && arr !== null && arr !== "" && !isNaN(parseInt(arr.duration)))
|
||||
{
|
||||
|
||||
if(coll == "" || coll == undefined || coll == null ||
|
||||
!arr.hasOwnProperty("start") || !arr.hasOwnProperty("end")) {
|
||||
var result = {
|
||||
start: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("start") ? typeof(arr.start) : undefined
|
||||
},
|
||||
end: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("end") ? typeof(arr.end) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var start = parseInt(arr.start);
|
||||
var end = parseInt(arr.end);
|
||||
if(start < 0) {
|
||||
socket.emit("toast", "faulty_start_end");
|
||||
return;
|
||||
}
|
||||
if(end < 0) {
|
||||
socket.emit("toast", "faulty_start_end");
|
||||
return;
|
||||
}
|
||||
if(start >= end) {
|
||||
start = 0;
|
||||
arr.duration = end - start;
|
||||
}
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(typeof(arr.id) != "string" || typeof(arr.start) != "number" ||
|
||||
typeof(arr.end) != "number" || typeof(arr.title) != "string" ||
|
||||
typeof(arr.list) != "string" || typeof(arr.duration) != "number") {
|
||||
var result = {
|
||||
start: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("start") ? typeof(arr.start) : undefined
|
||||
},
|
||||
end: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("end") ? typeof(arr.end) : undefined
|
||||
},
|
||||
title: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("title") ? typeof(arr.title) : undefined
|
||||
},
|
||||
list: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("list") ? typeof(arr.list) : undefined
|
||||
},
|
||||
duration: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("duration") ? typeof(arr.duration) : undefined
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("pass") ? typeof(arr.pass) : undefined
|
||||
},
|
||||
adminpass: {
|
||||
expected: "string",
|
||||
got: arr.hasOwnProperty("adminpass") ? typeof(arr.adminpass) : undefined
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(adminpass != "" || arr.adminpass == undefined) {
|
||||
arr.adminpass = adminpass;
|
||||
}
|
||||
if(userpass != "" || arr.userpass == undefined) {
|
||||
arr.userpass = userpass;
|
||||
}
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (arr.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(arr.pass)).digest("base64")))) {
|
||||
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
|
||||
var id = arr.id;
|
||||
var title = arr.title;
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(arr.adminpass), true));
|
||||
var duration = parseInt(arr.duration);
|
||||
/*db.collection(coll + "_settings").find(function(err, docs)
|
||||
{*/
|
||||
conf = docs;
|
||||
if(docs !== null && docs.length !== 0 && ((docs[0].addsongs === true && (hash == docs[0].adminpass || docs[0].adminpass === "")) ||
|
||||
docs[0].addsongs === false)) {
|
||||
db.collection(coll).find({id:id, type:{$ne:"suggested"}}, function(err, docs){
|
||||
if(docs !== null && docs.length === 0) {
|
||||
var guids = [guid];
|
||||
var added = Functions.get_time();
|
||||
var votes = 1;
|
||||
db.collection(coll).find({now_playing:true}, function(err, docs){
|
||||
if((docs !== null && docs.length === 0)){
|
||||
np = true;
|
||||
} else {
|
||||
np = false;
|
||||
}
|
||||
var new_song = {"added": added,"guids":guids,"id":id,"now_playing":np,"title":title,"votes":votes, "duration":duration, "start": parseInt(start), "end": parseInt(end), "type": "video"};
|
||||
db.collection(coll).update({id: id}, new_song, {upsert: true}, function(err, docs){
|
||||
new_song._id = "asd";
|
||||
if(np) {
|
||||
List.send_list(coll, undefined, false, true, false);
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {$set:{startTime: Functions.get_time()}});
|
||||
List.send_play(coll, undefined);
|
||||
Frontpage.update_frontpage(coll, id, title);
|
||||
Search.get_correct_info(new_song, coll, false);
|
||||
} else {
|
||||
io.to(coll).emit("channel", {type: "added", value: new_song});
|
||||
Search.get_correct_info(new_song, coll, true);
|
||||
}
|
||||
db.collection("frontpage_lists").update({_id:coll}, {$inc:{count:1}, $set:{accessed: Functions.get_time()}}, {upsert:true}, function(err, docs){});
|
||||
List.getNextSong(coll);
|
||||
});
|
||||
socket.emit("toast", "addedsong");
|
||||
});
|
||||
} else {
|
||||
ListChange.vote(coll, id, guid, socket);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
db.collection(coll).find({id: id}, function(err, docs) {
|
||||
if(docs.length === 0) {
|
||||
db.collection(coll).update({id: id}, {$set:{
|
||||
"added":Functions.get_time(),
|
||||
"guids": [guid],
|
||||
"id":id,
|
||||
"now_playing": false,
|
||||
"title":title,
|
||||
"votes":1,
|
||||
"duration":duration,
|
||||
"start": start,
|
||||
"end": end,
|
||||
"type":"suggested"}
|
||||
},
|
||||
{upsert:true}, function(err, docs){
|
||||
socket.emit("toast", "suggested");
|
||||
io.to(coll).emit("suggested", {id: id, title: title, duration: duration});
|
||||
});
|
||||
} else if(docs[0].now_playing === true){
|
||||
socket.emit("toast", "alreadyplay");
|
||||
} else{
|
||||
if(conf[0].vote === false) ListChange.vote(coll, id, guid, socket);
|
||||
else socket.emit("toast", "listhaspass");
|
||||
}
|
||||
});
|
||||
}
|
||||
//});
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
arr: {
|
||||
expected: "object",
|
||||
got: typeof(arr)
|
||||
},
|
||||
duration: {
|
||||
expected: "number or string that can be cast to int",
|
||||
got: arr.hasOwnProperty("duration") ? typeof(arr.duration) : undefined,
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
}
|
||||
}
|
||||
|
||||
function voteUndecided(msg, coll, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(msg) === 'object' && msg !== undefined && msg !== null){
|
||||
|
||||
if(!msg.hasOwnProperty("channel") || !msg.hasOwnProperty("id") ||
|
||||
!msg.hasOwnProperty("type") || typeof(msg.channel) != "string" ||
|
||||
typeof(msg.id) != "string" || typeof(msg.type) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
id: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("id") ? typeof(msg.id) : undefined,
|
||||
},
|
||||
type: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("type") ? typeof(msg.type) : undefined,
|
||||
},
|
||||
adminpass: {
|
||||
expected: "adminpass",
|
||||
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(adminpass != "" || msg.adminpass == undefined) {
|
||||
msg.adminpass = adminpass;
|
||||
}
|
||||
if(userpass != "" || msg.pass == undefined) {
|
||||
msg.pass = userpass;
|
||||
}
|
||||
|
||||
db.collection(coll + "_settings").find({id: "config"}, function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64")))) {
|
||||
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
|
||||
if(msg.type == "del") {
|
||||
ListChange.del(msg, socket, socketid);
|
||||
} else {
|
||||
var id = msg.id;
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass), true));
|
||||
if(docs !== null && docs.length !== 0 && ((docs[0].vote === true && (hash == docs[0].adminpass || docs[0].adminpass === "")) ||
|
||||
docs[0].vote === false)) {
|
||||
ListChange.vote(coll, id, guid, socket);
|
||||
} else {
|
||||
socket.emit("toast", "listhaspass");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
msg: {
|
||||
expected: "object",
|
||||
got: typeof(msg)
|
||||
}
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
}
|
||||
}
|
||||
|
||||
function shuffle(msg, coll, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
|
||||
|
||||
if(!msg.hasOwnProperty("channel") || typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
adminpass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
coll = msg.channel.toLowerCase().replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(adminpass != "" || msg.adminpass == undefined) {
|
||||
msg.adminpass = adminpass;
|
||||
}
|
||||
if(userpass != "" || msg.pass == undefined) {
|
||||
msg.pass = userpass;
|
||||
}
|
||||
db.collection("timeout_api").find({
|
||||
type: "shuffle",
|
||||
guid: coll,
|
||||
}, function(err, docs) {
|
||||
if(docs.length > 0) {
|
||||
var date = new Date(docs[0].createdAt);
|
||||
date.setSeconds(date.getSeconds() + 5);
|
||||
var now = new Date();
|
||||
var retry_in = (date.getTime() - now.getTime()) / 1000;
|
||||
if(retry_in > 0) {
|
||||
socket.emit("toast", "wait_longer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
var now_date = new Date();
|
||||
db.collection("timeout_api").update({type: "shuffle", guid: coll}, {
|
||||
$set: {
|
||||
"createdAt": now_date,
|
||||
type: "shuffle",
|
||||
guid: coll,
|
||||
},
|
||||
}, {upsert: true}, function(err, docs) {
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
var hash;
|
||||
if(msg.adminpass === "") hash = msg.adminpass;
|
||||
else hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass),true));
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64")))) {
|
||||
if(docs !== null && docs.length !== 0 && ((docs[0].adminpass == hash || docs[0].adminpass === "") || docs[0].shuffle === false))
|
||||
{
|
||||
db.collection(coll).find({now_playing:false}).forEach(function(err, docs){
|
||||
if(!docs){
|
||||
List.send_list(coll, undefined, false, true, false, true);
|
||||
socket.emit("toast", "shuffled");
|
||||
|
||||
return;
|
||||
}else{
|
||||
num = Math.floor(Math.random()*1000000);
|
||||
db.collection(coll).update({id:docs.id}, {$set:{added:num}});
|
||||
}
|
||||
});
|
||||
}else
|
||||
socket.emit("toast", "wrongpass");
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
|
||||
var complete = function(tot, curr){
|
||||
if(tot == curr)
|
||||
{
|
||||
List.send_list(coll, undefined, false, true, false);
|
||||
List.getNextSong(coll);
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function del(params, socket, socketid) {
|
||||
if(params.id){
|
||||
var coll = emojiStrip(params.channel).toLowerCase();
|
||||
coll = coll.replace(/_/g, "").replace(/ /g,'');
|
||||
|
||||
coll = filter.clean(coll);
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs !== null && docs.length !== 0 && docs[0].adminpass == Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(params.adminpass),true)))
|
||||
{
|
||||
db.collection(coll).find({id:params.id}, function(err, docs){
|
||||
var dont_increment = false;
|
||||
if(docs[0]){
|
||||
if(docs[0].type == "suggested"){
|
||||
dont_increment = true;
|
||||
}
|
||||
db.collection(coll).remove({id:params.id}, function(err, docs){
|
||||
socket.emit("toast", "deletesong");
|
||||
io.to(coll).emit("channel", {type:"deleted", value: params.id});
|
||||
if(!dont_increment) db.collection("frontpage_lists").update({_id: coll, count: {$gt: 0}}, {$inc: {count: -1}, $set:{accessed: Functions.get_time()}}, {upsert: true}, function(err, docs){});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function delete_all(msg, coll, guid, offline, socket) {
|
||||
var socketid = socket.zoff_id;
|
||||
if(typeof(msg) == 'object' ) {
|
||||
if(!msg.hasOwnProperty('channel') || typeof(msg.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
adminpass: {
|
||||
expected: "adminpass",
|
||||
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
if(coll == undefined) {
|
||||
coll = msg.channel;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = filter.clean(coll);
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass, gotten) {
|
||||
if(adminpass != "" || msg.adminpass == undefined) {
|
||||
msg.adminpass = adminpass;
|
||||
}
|
||||
if(userpass != "" || msg.pass == undefined) {
|
||||
msg.pass = userpass;
|
||||
}
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass),true));
|
||||
var hash_userpass = crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64");
|
||||
db.collection(coll + "_settings").find(function(err, conf) {
|
||||
if(conf.length == 1 && conf) {
|
||||
conf = conf[0];
|
||||
if(conf.adminpass == hash && conf.adminpass != "" && (conf.userpass == "" || conf.userpass == undefined || (conf.userpass != "" && conf.userpass != undefined && conf.pass == hash_userpass))) {
|
||||
db.collection(coll).remove({views: {$exists: false}}, {multi: true}, function(err, succ) {
|
||||
List.send_list(coll, false, true, true, true);
|
||||
db.collection("frontpage_lists").update({_id: coll}, {$set: {count: 0, accessed: Functions.get_time()}}, {upsert: true}, function(err, docs) {});
|
||||
socket.emit("toast", "deleted_songs");
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "listhaspass");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
msg: {
|
||||
expected: "object",
|
||||
got: typeof(msg)
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function vote(coll, id, guid, socket) {
|
||||
coll = coll.replace(/ /g,'');
|
||||
db.collection(coll).find({id:id, now_playing: false, type:"video"}, function(err, docs){
|
||||
if(docs !== null && docs.length > 0 && !Functions.contains(docs[0].guids, guid))
|
||||
{
|
||||
db.collection(coll).update({id:id}, {$inc:{votes:1}, $set:{added:Functions.get_time()}, $push :{guids: guid}}, function(err, docs)
|
||||
{
|
||||
socket.emit("toast", "voted");
|
||||
io.to(coll).emit("channel", {type: "vote", value: id, time: Functions.get_time()});
|
||||
|
||||
List.getNextSong(coll);
|
||||
});
|
||||
}else
|
||||
{
|
||||
socket.emit("toast", "alreadyvoted");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.addPlaylist = addPlaylist;
|
||||
module.exports.addFromOtherList = addFromOtherList;
|
||||
module.exports.add_function = add_function;
|
||||
module.exports.voteUndecided = voteUndecided;
|
||||
module.exports.shuffle = shuffle;
|
||||
module.exports.del = del;
|
||||
module.exports.delete_all = delete_all;
|
||||
module.exports.vote = vote;
|
||||
246
server/handlers/list_settings.js
Normal file
@@ -0,0 +1,246 @@
|
||||
function password(inp, coll, guid, offline, socket) {
|
||||
var sessionId = Functions.getSession(socket);
|
||||
if(sessionId == "") sessionId = "empty";
|
||||
if(inp !== undefined && inp !== null && inp !== "")
|
||||
{
|
||||
if(!inp.hasOwnProperty("password") || !inp.hasOwnProperty("channel") ||
|
||||
typeof(inp.password) != "string" || typeof(inp.channel) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: inp.hasOwnProperty("channel") ? typeof(iinp.channel) : undefined,
|
||||
},
|
||||
password: {
|
||||
expected: "password",
|
||||
got: inp.hasOwnProperty("password") ? typeof(inp.password) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
return;
|
||||
}
|
||||
pw = inp.password;
|
||||
try {
|
||||
coll = inp.channel;
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
uncrypted = pw;
|
||||
pw = Functions.hash_pass(Functions.decrypt_string(pw), true);
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
Functions.getSessionAdminUser(sessionId, coll, function(userpass, adminpass) {
|
||||
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs !== null && docs.length !== 0)
|
||||
{
|
||||
if(docs[0].adminpass === "" || docs[0].adminpass == Functions.hash_pass(pw))
|
||||
{
|
||||
Functions.setSessionAdminPass(sessionId, inp.password, coll, function() {
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {$set:{adminpass:Functions.hash_pass(pw)}}, function(err, docs){
|
||||
if(adminpass != pw && adminpass != "") {
|
||||
socket.emit("toast", "changedpass");
|
||||
} else {
|
||||
socket.emit("toast", "correctpass");
|
||||
}
|
||||
socket.emit("pw", true);
|
||||
});
|
||||
});
|
||||
} else if(docs[0].adminpass === "" || docs[0].adminpass == Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(adminpass), true))) {
|
||||
Functions.setSessionAdminPass(sessionId, inp.password, coll, function() {
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {$set:{adminpass:Functions.hash_pass(pw)}}, function(err, docs){
|
||||
if(adminpass != pw) {
|
||||
socket.emit("toast", "changedpass");
|
||||
}
|
||||
socket.emit("pw", true);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Functions.setSessionAdminPass(Functions.getSession(socket), "", coll, function() {
|
||||
socket.emit("toast", "wrongpass");
|
||||
socket.emit("pw", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
inp: {
|
||||
expected: "string",
|
||||
got: typeof(inpt)
|
||||
},
|
||||
};
|
||||
socket.emit('update_required', result);
|
||||
}
|
||||
}
|
||||
|
||||
function conf_function(params, coll, guid, offline, socket) {
|
||||
if(params !== undefined && params !== null && params !== "")
|
||||
{
|
||||
if(coll !== undefined) {
|
||||
try {
|
||||
coll = params.channel.replace(/ /g,'');
|
||||
if(coll.length == 0) return;
|
||||
coll = emojiStrip(coll).toLowerCase();
|
||||
coll = coll.replace(/_/g, "");
|
||||
|
||||
coll = filter.clean(coll);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(coll == "" || coll == undefined || coll == null) {
|
||||
socket.emit("update_required");
|
||||
return;
|
||||
}
|
||||
|
||||
Functions.check_inlist(coll, guid, socket, offline);
|
||||
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass, gotten) {
|
||||
if(gotten) {
|
||||
params.adminpass = adminpass;
|
||||
if(!params.userpass_changed) params.userpass = userpass;
|
||||
|
||||
}
|
||||
if(!params.hasOwnProperty('voting') || !params.hasOwnProperty('addsongs') ||
|
||||
!params.hasOwnProperty('longsongs') || !params.hasOwnProperty('frontpage') ||
|
||||
!params.hasOwnProperty('allvideos') || !params.hasOwnProperty('removeplay') ||
|
||||
!params.hasOwnProperty('adminpass') || !params.hasOwnProperty('skipping') ||
|
||||
!params.hasOwnProperty('shuffling') || !params.hasOwnProperty('channel') ||
|
||||
typeof(params.userpass) != "string" || typeof(params.adminpass) != "string" ||
|
||||
typeof(params.voting) != "boolean" || typeof(params.addsongs) != "boolean" ||
|
||||
typeof(params.longsongs) != "boolean" || typeof(params.frontpage) != "boolean" ||
|
||||
typeof(params.allvideos) != "boolean" || typeof(params.removeplay) != "boolean" ||
|
||||
typeof(params.skipping) != "boolean" || typeof(params.shuffling) != "boolean" ||
|
||||
typeof(params.userpass_changed) != "boolean") {
|
||||
var result = {
|
||||
adminpass: {
|
||||
expected: "string",
|
||||
got: params.hasOwnProperty("adminpass") ? typeof(params.adminpass) : undefined,
|
||||
},
|
||||
userpass: {
|
||||
expected: "string",
|
||||
got: params.hasOwnProperty("userpass") ? typeof(params.userpass) : undefined,
|
||||
},
|
||||
vote: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("vote") ? typeof(params.vote) : undefined,
|
||||
},
|
||||
addsongs: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("addsongs") ? typeof(params.addsongs) : undefined,
|
||||
},
|
||||
longsongs: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("longsongs") ? typeof(params.longsongs) : undefined,
|
||||
},
|
||||
frontpage: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("frontpage") ? typeof(params.frontpage) : undefined,
|
||||
},
|
||||
skipping: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("skipping") ? typeof(params.skipping) : undefined,
|
||||
},
|
||||
shuffling: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("shuffling") ? typeof(params.shuffling) : undefined,
|
||||
},
|
||||
userpass_changed: {
|
||||
expected: "boolean",
|
||||
got: params.hasOwnProperty("userpass_changed") ? typeof(params.userpass_changed) : undefined,
|
||||
}
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
return;
|
||||
}
|
||||
var voting = params.voting;
|
||||
var addsongs = params.addsongs;
|
||||
var longsongs = params.longsongs;
|
||||
var frontpage = params.frontpage;
|
||||
var allvideos = params.allvideos;
|
||||
var removeplay = params.removeplay;
|
||||
var adminpass = params.adminpass;
|
||||
var skipping = params.skipping;
|
||||
var shuffling = params.shuffling;
|
||||
var userpass = Functions.decrypt_string(params.userpass);
|
||||
|
||||
|
||||
if((!params.userpass_changed && frontpage) || (params.userpass_changed && userpass == "")) {
|
||||
userpass = "";
|
||||
} else if(params.userpass_changed && userpass != "") {
|
||||
frontpage = false;
|
||||
}
|
||||
var description = "";
|
||||
var hash;
|
||||
if(params.description) description = params.description;
|
||||
if(adminpass !== "") {
|
||||
hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(adminpass), true));
|
||||
} else {
|
||||
hash = adminpass;
|
||||
}
|
||||
if(userpass != "") {
|
||||
userpass = crypto.createHash('sha256').update(userpass).digest("base64");
|
||||
}
|
||||
db.collection(coll + "_settings").find({id: "config"}, function(err, docs){
|
||||
if(docs !== null && docs.length !== 0 && (docs[0].adminpass === "" || docs[0].adminpass == hash)) {
|
||||
var obj = {
|
||||
addsongs:addsongs,
|
||||
allvideos:allvideos,
|
||||
frontpage:frontpage,
|
||||
skip:skipping,
|
||||
vote:voting,
|
||||
removeplay:removeplay,
|
||||
shuffle:shuffling,
|
||||
longsongs:longsongs,
|
||||
adminpass:hash,
|
||||
desc: description,
|
||||
};
|
||||
if(params.userpass_changed) {
|
||||
obj["userpass"] = userpass;
|
||||
} else if (frontpage) {
|
||||
obj["userpass"] = "";
|
||||
}
|
||||
db.collection(coll + "_settings").update({ id: "config" }, {
|
||||
$set:obj
|
||||
}, function(err, docs){
|
||||
Functions.setSessionUserPass(Functions.getSession(socket), params.userpass, coll, function() {
|
||||
db.collection(coll + "_settings").find(function(err, docs){
|
||||
if(docs[0].adminpass !== "") docs[0].adminpass = true;
|
||||
if(docs[0].hasOwnProperty("userpass") && docs[0].userpass != "") docs[0].userpass = true;
|
||||
else docs[0].userpass = false;
|
||||
io.to(coll).emit("conf", docs);
|
||||
socket.emit("toast", "savedsettings");
|
||||
|
||||
db.collection("frontpage_lists").update({_id: coll}, {$set:{
|
||||
frontpage:frontpage, accessed: Functions.get_time()}
|
||||
},
|
||||
{upsert:true}, function(err, docs){});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "wrongpass");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var result = {
|
||||
params: {
|
||||
expected: "object",
|
||||
got: typeof(params),
|
||||
}
|
||||
}
|
||||
socket.emit('update_required', result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.password = password;
|
||||
module.exports.conf_function = conf_function;
|
||||
39
server/handlers/notifications.js
Normal file
@@ -0,0 +1,39 @@
|
||||
var path = require('path');
|
||||
|
||||
function requested_change(type, string, channel) {
|
||||
try {
|
||||
channel = channel.replace(/ /g,'');
|
||||
var nodemailer = require('nodemailer');
|
||||
var mailconfig = require(path.join(__dirname, '../config/mailconfig.js'));
|
||||
|
||||
let transporter = nodemailer.createTransport(mailconfig);
|
||||
|
||||
transporter.verify(function(error, success) {
|
||||
if (error) {
|
||||
return;
|
||||
} else {
|
||||
var message = "A " + type + " change was requested on <b>" + channel + "</b><br><br>New supposed value is: <br><br><b>" + string + "</b><br><br><br> \
|
||||
Go to <a href='https://admin.zoff.me/'>https://admin.zoff.me/</a> to accept or decline the request.";
|
||||
var msg = {
|
||||
from: mailconfig.from,
|
||||
to: mailconfig.notify_mail,
|
||||
subject: 'ZOFF: Requested new ' + type,
|
||||
text: message,
|
||||
html: message,
|
||||
}
|
||||
transporter.sendMail(msg, (error, info) => {
|
||||
if (error) {
|
||||
transporter.close();
|
||||
return;
|
||||
}
|
||||
transporter.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
console.log("Mail is not configured and wont work");
|
||||
console.log("Seems you forgot to create a mailconfig.js in /server/config/. Have a look at the mailconfig.example.js.");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.requested_change = requested_change;
|
||||
213
server/handlers/search.js
Normal file
@@ -0,0 +1,213 @@
|
||||
var path = require('path');
|
||||
var time_regex = /P((([0-9]*\.?[0-9]*)Y)?(([0-9]*\.?[0-9]*)M)?(([0-9]*\.?[0-9]*)W)?(([0-9]*\.?[0-9]*)D)?)?(T(([0-9]*\.?[0-9]*)H)?(([0-9]*\.?[0-9]*)M)?(([0-9]*\.?[0-9]*)S)?)?/;
|
||||
try {
|
||||
var key = require(path.join(__dirname, '../config/api_key.js'));
|
||||
} catch(e) {
|
||||
console.log("Error - missing file");
|
||||
console.log("Seems you forgot to create the file api_key.js in /server/config/. Have a look at api_key.example.js.");
|
||||
process.exit();
|
||||
}
|
||||
|
||||
function get_correct_info(song_generated, channel, broadcast, callback) {
|
||||
channel = channel.replace(/ /g,'');
|
||||
request({
|
||||
type: "GET",
|
||||
url: "https://www.googleapis.com/youtube/v3/videos?part=contentDetails,snippet,id&key="+key+"&id=" + song_generated.id,
|
||||
|
||||
}, function(error, response, body) {
|
||||
try {
|
||||
var resp = JSON.parse(body);
|
||||
if(resp.items.length == 1) {
|
||||
var duration = parseInt(durationToSeconds(resp.items[0].contentDetails.duration));
|
||||
var title = resp.items[0].snippet.localized.title;
|
||||
if(title != song_generated.title || duration < parseInt(song_generated.duration)) {
|
||||
if(title != song_generated.title) {
|
||||
song_generated.title = title;
|
||||
}
|
||||
if(duration < parseInt(song_generated.duration)) {
|
||||
song_generated.duration = duration;
|
||||
song_generated.start = 0;
|
||||
song_generated.end = duration;
|
||||
}
|
||||
db.collection(channel).update({"id": song_generated.id}, {
|
||||
$set: {
|
||||
"duration": song_generated.duration,
|
||||
"start": song_generated.start,
|
||||
"end": song_generated.end,
|
||||
"title": song_generated.title,
|
||||
}
|
||||
}, function(err, docs) {
|
||||
if(broadcast && docs.nModified == 1) {
|
||||
song_generated.new_id = song_generated.id;
|
||||
//if(song_generated.type == "video")
|
||||
if(typeof(callback) == "function") {
|
||||
callback(song_generated, true);
|
||||
} else {
|
||||
io.to(channel).emit("channel", {type: "changed_values", value: song_generated});
|
||||
}
|
||||
} else {
|
||||
if(typeof(callback) == "function") {
|
||||
callback(song_generated, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if(typeof(callback) == "function") {
|
||||
callback(song_generated, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
findSimilar(song_generated, channel, broadcast, callback)
|
||||
}
|
||||
} catch(e){
|
||||
if(typeof(callback) == "function") {
|
||||
callback({}, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function check_error_video(msg, channel) {
|
||||
if(!msg.hasOwnProperty("id") || !msg.hasOwnProperty("title") ||
|
||||
typeof(msg.id) != "string" || typeof(msg.title) != "string") {
|
||||
var result = {
|
||||
id: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("id") ? typeof(msg.id) : undefined,
|
||||
},
|
||||
title: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("title") ? typeof(msg.title) : undefined,
|
||||
},
|
||||
};
|
||||
return;
|
||||
}
|
||||
channel = channel.replace(/ /g,'');
|
||||
request({
|
||||
type: "GET",
|
||||
url: "https://www.googleapis.com/youtube/v3/videos?part=id&key="+key+"&id=" + msg.id,
|
||||
|
||||
}, function(error, response, body) {
|
||||
try {
|
||||
var resp = JSON.parse(body);
|
||||
if(resp.pageInfo.totalResults == 0) {
|
||||
findSimilar(msg, channel, true, undefined)
|
||||
}
|
||||
} catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function findSimilar(msg, channel, broadcast, callback) {
|
||||
channel = channel.replace(/ /g,'');
|
||||
var yt_url = "https://www.googleapis.com/youtube/v3/search?key="+key+"&videoEmbeddable=true&part=id&type=video&order=viewCount&safeSearch=none&maxResults=5&q=" + encodeURIComponent(msg.title);
|
||||
request({
|
||||
method: "GET",
|
||||
url: yt_url,
|
||||
}, function(error, response, body){
|
||||
var resp = JSON.parse(body);
|
||||
if(resp.items.length > 0) {
|
||||
var vid_url = "https://www.googleapis.com/youtube/v3/videos?part=contentDetails,snippet,id&key="+key+"&id=";
|
||||
for(var i = 0; i < resp.items.length; i++) {
|
||||
vid_url += resp.items[i].id.videoId + ",";
|
||||
}
|
||||
request({
|
||||
type: "GET",
|
||||
url: vid_url
|
||||
}, function(error, response, body) {
|
||||
var resp = JSON.parse(body);
|
||||
var found = false;
|
||||
var element = {};
|
||||
for(var i = 0; i < resp.items.length; i++) {
|
||||
if(similarity(resp.items[i].snippet.localized.title, msg.title) > 0.75) {
|
||||
found = true;
|
||||
element = {
|
||||
title: resp.items[i].snippet.localized.title,
|
||||
duration: parseInt(durationToSeconds(resp.items[i].contentDetails.duration)),
|
||||
id: resp.items[i].id,
|
||||
start: 0,
|
||||
end: parseInt(durationToSeconds(resp.items[i].contentDetails.duration)),
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) {
|
||||
db.collection(channel).update({"id": msg.id}, {
|
||||
$set: element
|
||||
}, function(err, docs) {
|
||||
if(docs.nModified == 1 && broadcast) {
|
||||
element.new_id = element.id;
|
||||
element.id = msg.id;
|
||||
if(!callback) {
|
||||
io.to(channel).emit("channel", {type: "changed_values", value: element});
|
||||
}
|
||||
}
|
||||
if(typeof(callback) == "function") {
|
||||
msg.title = element.title;
|
||||
msg.id = element.id;
|
||||
msg.duration = element.duration;
|
||||
msg.start = element.start;
|
||||
msg.end = element.end;
|
||||
callback(msg, true);
|
||||
}
|
||||
});
|
||||
} else if(typeof(callback) == "function") {
|
||||
callback({}, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function similarity(s1, s2) {
|
||||
var longer = s1;
|
||||
var shorter = s2;
|
||||
if (s1.length < s2.length) {
|
||||
longer = s2;
|
||||
shorter = s1;
|
||||
}
|
||||
var longerLength = longer.length;
|
||||
if (longerLength == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
|
||||
}
|
||||
|
||||
function editDistance(s1, s2) {
|
||||
s1 = s1.toLowerCase();
|
||||
s2 = s2.toLowerCase();
|
||||
|
||||
var costs = new Array();
|
||||
for (var i = 0; i <= s1.length; i++) {
|
||||
var lastValue = i;
|
||||
for (var j = 0; j <= s2.length; j++) {
|
||||
if (i == 0)
|
||||
costs[j] = j;
|
||||
else {
|
||||
if (j > 0) {
|
||||
var newValue = costs[j - 1];
|
||||
if (s1.charAt(i - 1) != s2.charAt(j - 1))
|
||||
newValue = Math.min(Math.min(newValue, lastValue),
|
||||
costs[j]) + 1;
|
||||
costs[j - 1] = lastValue;
|
||||
lastValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i > 0)
|
||||
costs[s2.length] = lastValue;
|
||||
}
|
||||
return costs[s2.length];
|
||||
}
|
||||
|
||||
function durationToSeconds(duration) {
|
||||
var matches = duration.match(time_regex);
|
||||
hours= parseInt(matches[12])||0;
|
||||
minutes= parseInt(matches[14])||0;
|
||||
seconds= parseInt(matches[16])||0;
|
||||
return hours*60*60+minutes*60+seconds;
|
||||
}
|
||||
|
||||
module.exports.check_error_video = check_error_video;
|
||||
module.exports.get_correct_info = get_correct_info;
|
||||
111
server/handlers/suggestions.js
Normal file
@@ -0,0 +1,111 @@
|
||||
function thumbnail(msg, coll, guid, offline, socket) {
|
||||
if(msg.thumbnail != undefined && msg.channel && msg.channel != undefined){
|
||||
if(typeof(msg.channel) != "string" || typeof(msg.thumbnail) != "string")
|
||||
{
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
thumbnail: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("thumbnail") ? typeof(msg.thumbnail) : undefined,
|
||||
},
|
||||
adminpass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
return;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass) {
|
||||
if(userpass != "" || msg.userpass == undefined) {
|
||||
msg.userpass = userpass;
|
||||
}
|
||||
if(adminpass != "" || msg.adminpass == undefined) {
|
||||
msg.adminpass = adminpass;
|
||||
}
|
||||
if(msg.thumbnail != "") {
|
||||
msg.thumbnail = msg.thumbnail.replace(/^https?\:\/\//i, "");
|
||||
if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail;
|
||||
}
|
||||
var channel = msg.channel.toLowerCase();
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass),true));
|
||||
db.collection(channel + "_settings").find({id: "config"}, function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64")))) {
|
||||
if(docs !== null && docs.length !== 0 && docs[0].adminpass !== "" && docs[0].adminpass == hash){
|
||||
db.collection("suggested_thumbnails").update({channel: channel}, {$set:{thumbnail: msg.thumbnail}}, {upsert:true}, function(err, docs){
|
||||
Notifications.requested_change("thumbnail", msg.thumbnail, channel);
|
||||
socket.emit("toast", "suggested_thumbnail");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "thumbnail_denied");
|
||||
}
|
||||
}
|
||||
|
||||
function description(msg, coll, guid, offline, socket) {
|
||||
if(msg.description && msg.channel && msg.description.length < 100){
|
||||
if(typeof(msg.channel) != "string" || typeof(msg.description) != "string") {
|
||||
var result = {
|
||||
channel: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
|
||||
},
|
||||
pass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("pass") ? typeof(msg.pass) : undefined,
|
||||
},
|
||||
description: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("description") ? typeof(msg.description) : undefined,
|
||||
},
|
||||
adminpass: {
|
||||
expected: "string",
|
||||
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
|
||||
},
|
||||
};
|
||||
socket.emit("update_required", result);
|
||||
return;
|
||||
}
|
||||
coll = coll.replace(/ /g,'');
|
||||
Functions.getSessionAdminUser(Functions.getSession(socket), coll, function(userpass, adminpass, gotten) {
|
||||
if(userpass != "" || msg.userpass == undefined) {
|
||||
msg.userpass = userpass;
|
||||
}
|
||||
if(adminpass != "" || msg.adminpass == undefined) {
|
||||
msg.adminpass = adminpass;
|
||||
}
|
||||
var channel = msg.channel.toLowerCase();
|
||||
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass), true));
|
||||
db.collection(channel + "_settings").find({id: "config"}, function(err, docs){
|
||||
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(msg.pass)).digest("base64")))) {
|
||||
if(docs !== null && docs.length !== 0 && docs[0].adminpass !== "" && docs[0].adminpass == hash){
|
||||
db.collection("suggested_descriptions").update({channel: channel}, {$set:{description: msg.description}}, {upsert:true}, function(err, docs){
|
||||
Notifications.requested_change("description", msg.description, channel);
|
||||
socket.emit("toast", "suggested_description");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
socket.emit("auth_required");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit("toast", "description_denied");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.thumbnail = thumbnail;
|
||||
module.exports.description = description;
|
||||
24
server/models/user.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// app/models/user.js
|
||||
// load the things we need
|
||||
var mongoose = require('mongoose');
|
||||
var bcrypt = require('bcrypt-nodejs');
|
||||
|
||||
// define the schema for our user model
|
||||
var userSchema = mongoose.Schema({
|
||||
username : String,
|
||||
password : String,
|
||||
});
|
||||
|
||||
// methods ======================
|
||||
// generating a hash
|
||||
userSchema.methods.generateHash = function(password) {
|
||||
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
|
||||
};
|
||||
|
||||
// checking if password is valid
|
||||
userSchema.methods.validPassword = function(password) {
|
||||
return bcrypt.compareSync(password, this.password);
|
||||
};
|
||||
|
||||
// create the model for users and expose it to our app
|
||||
module.exports = mongoose.model('User', userSchema);
|
||||
687
server/public/assets/admin/authenticated/js/main.js
Normal file
@@ -0,0 +1,687 @@
|
||||
var connection_options = {
|
||||
'sync disconnect on unload':true,
|
||||
'secure': true,
|
||||
'force new connection': true
|
||||
};
|
||||
var socket = io.connect(window.location.protocol + '//' + window.location.hostname + ':8080', connection_options);
|
||||
var api_token_list;
|
||||
|
||||
$(document).ready(function(){
|
||||
$('ul.tabs').tabs();
|
||||
api_token_list = $("#api_token_list").clone();
|
||||
$("#api_token_list").remove();
|
||||
loaded();
|
||||
});
|
||||
|
||||
$(document).on("click", "#refresh_all", function(e){
|
||||
e.preventDefault();
|
||||
$("#descriptions_cont").empty();
|
||||
$("#thumbnails_cont").empty();
|
||||
$(".api_token_container").remove();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/api_token",
|
||||
success: function(response) {
|
||||
if(response.length == 0) {
|
||||
if(!$(".header-api-fields").hasClass("hide")) {
|
||||
$(".header-api-fields").addClass("hide");
|
||||
}
|
||||
return;
|
||||
}
|
||||
$(".header-api-fields").removeClass("hide");
|
||||
for(var i = 0; i < response.length; i++) {
|
||||
var to_add = api_token_list.clone();
|
||||
to_add.find(".api_token_limit").val(response[i].limit);
|
||||
to_add.attr("id", response[i]._id);
|
||||
to_add.find(".api_token_name").text(response[i].name);
|
||||
to_add.find(".api_token_origin").text(response[i].origin);
|
||||
to_add.find(".api_token_usage").text(response[i].usage);
|
||||
to_add.find(".update_api_token").attr("id", response[i]._id + "-update");
|
||||
to_add.find(".api_token_limit").attr("id", response[i]._id + "-limit");
|
||||
to_add.find(".delete_api_token").attr("id", response[i]._id + "-delete");
|
||||
to_add.find(".delete_api_token").attr("data-id", response[i]._id);
|
||||
to_add.find(".update_api_token").attr("data-id", response[i]._id);
|
||||
if(response[i].active) {
|
||||
to_add.find(".check").removeClass("hide");
|
||||
} else {
|
||||
to_add.find(".uncheck").removeClass("hide");
|
||||
}
|
||||
$("#api_keys").append(to_add);
|
||||
}
|
||||
},
|
||||
error: function(err) {
|
||||
}
|
||||
});
|
||||
|
||||
if(!$(".channel_things").hasClass("hide")) {
|
||||
$(".channel_things").addClass("hide")
|
||||
}
|
||||
$(".preloader-wrapper").removeClass("hide");
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/lists",
|
||||
success: function(response){
|
||||
var output_pinned = '<option value="" disabled>Channels</option>';
|
||||
var output_delete = '<option value="" disabled>Channels</option>';
|
||||
for(var x = 0; x < response.length; x++){
|
||||
if(response[x].count > 5){
|
||||
output_pinned += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
output_delete += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
|
||||
$("#frontpage_pinned").html(output_pinned);
|
||||
$("#remove_thumbnail").html(output_delete);
|
||||
$("#remove_description").html(output_delete);
|
||||
$("#delete_list_name").html(output_delete);
|
||||
$("#delete_userpass_name").html(output_delete);
|
||||
$("#delete_channel_name").html(output_delete);
|
||||
$("select").material_select();
|
||||
$(".channel_things").removeClass("hide");
|
||||
if(!$(".preloader-wrapper").hasClass("hide")) {
|
||||
$(".preloader-wrapper").addClass("hide");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/thumbnails",
|
||||
success: function(response){
|
||||
if(response.length > 0){
|
||||
$(".thumbnails-badge").removeClass("hide");
|
||||
$(".thumbnails-badge").text(response.length);
|
||||
}
|
||||
add_to_tab("thumbnails", response);
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/descriptions",
|
||||
success: function(response){
|
||||
if(response.length > 0){
|
||||
$(".descriptions-badge").removeClass("hide");
|
||||
$(".descriptions-badge").text(response.length);
|
||||
}
|
||||
add_to_tab("descriptions", response);
|
||||
}
|
||||
});
|
||||
|
||||
$("#listeners").empty();
|
||||
socket.emit("get_spread");
|
||||
});
|
||||
|
||||
socket.on("spread_listeners", function(obj){
|
||||
$("#listeners").append("<p>Private listeners: " + obj.offline + "</p>");
|
||||
$("#listeners").append("<p>Total listeners: " + obj.total + "</p>");
|
||||
$("#listeners").append("<hr>");
|
||||
for(var x in obj.online_users){
|
||||
if(obj.online_users[x]._id != "total_users" && obj.online_users[x].hasOwnProperty("users") && obj.online_users[x].users.length > 0){
|
||||
$("#listeners").append("<p>" + obj.online_users[x]._id + ": " + obj.online_users[x].users.length + "</p>");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(!$(".channel_things").hasClass("hide")) {
|
||||
$(".channel_things").addClass("hide")
|
||||
}
|
||||
$(".preloader-wrapper").removeClass("hide");
|
||||
|
||||
$(document).on("click", ".update_api_token", function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = $(this).attr("data-id");
|
||||
var limit = $("#" + id + "-limit").val();
|
||||
var that = this;
|
||||
$(that).toggleClass("disabled");
|
||||
$("#" + id + "-delete").toggleClass("disabled");
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "api/api_token",
|
||||
data: {
|
||||
id: id,
|
||||
limit: limit,
|
||||
},
|
||||
success: function(response) {
|
||||
if(response == "OK") {
|
||||
Materialize.toast("Updated limit!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
$(that).toggleClass("disabled");
|
||||
$("#" + id + "-delete").toggleClass("disabled");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".delete_api_token", function(e) {
|
||||
e.preventDefault();
|
||||
var id = $(this).attr("data-id");
|
||||
var that = this;
|
||||
$(that).toggleClass("disabled");
|
||||
$("#" + id + "-limit").toggleClass("disabled");
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "api/api_token",
|
||||
data: {
|
||||
id: id
|
||||
},
|
||||
success: function(response) {
|
||||
if(response == "success") {
|
||||
Materialize.toast("Removed token!", 2000, "green lighten");
|
||||
$("#" + id).remove();
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
$(that).toggleClass("disabled");
|
||||
$("#" + id + "-limit").toggleClass("disabled");
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
function loaded() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/api_token",
|
||||
success: function(response) {
|
||||
if(response.length == 0) {
|
||||
if(!$(".header-api-fields").hasClass("hide")) {
|
||||
$(".header-api-fields").addClass("hide");
|
||||
}
|
||||
return;
|
||||
}
|
||||
$(".header-api-fields").removeClass("hide");
|
||||
for(var i = 0; i < response.length; i++) {
|
||||
var to_add = api_token_list.clone();
|
||||
to_add.find(".api_token_limit").val(response[i].limit);
|
||||
to_add.attr("id", response[i]._id);
|
||||
to_add.find(".api_token_name").text(response[i].name);
|
||||
to_add.find(".api_token_usage").text(response[i].usage);
|
||||
to_add.find(".api_token_origin").text(response[i].origin);
|
||||
to_add.find(".update_api_token").attr("id", response[i]._id + "-update");
|
||||
to_add.find(".api_token_limit").attr("id", response[i]._id + "-limit");
|
||||
to_add.find(".delete_api_token").attr("id", response[i]._id + "-delete");
|
||||
to_add.find(".delete_api_token").attr("data-id", response[i]._id);
|
||||
to_add.find(".update_api_token").attr("data-id", response[i]._id);
|
||||
if(response[i].active) {
|
||||
to_add.find(".check").removeClass("hide");
|
||||
} else {
|
||||
to_add.find(".uncheck").removeClass("hide");
|
||||
}
|
||||
$("#api_keys").append(to_add);
|
||||
}
|
||||
},
|
||||
error: function(err) {
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/lists",
|
||||
success: function(response){
|
||||
var output_pinned = '<option value="" disabled selected>Channels</option>';
|
||||
var output_delete = '<option value="" disabled selected>Channels</option>';
|
||||
for(var x = 0; x < response.length; x++){
|
||||
if(response[x].count > 5){
|
||||
output_pinned += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
output_delete += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
|
||||
$("#frontpage_pinned").html(output_pinned);
|
||||
$("#remove_thumbnail").html(output_delete);
|
||||
$("#remove_description").html(output_delete);
|
||||
$("#delete_list_name").html(output_delete);
|
||||
$("#delete_userpass_name").html(output_delete);
|
||||
$("#delete_channel_name").html(output_delete);
|
||||
$("select").material_select();
|
||||
|
||||
if(!$(".preloader-wrapper").hasClass("hide")) {
|
||||
$(".preloader-wrapper").addClass("hide")
|
||||
}
|
||||
$(".channel_things").removeClass("hide");
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/names",
|
||||
success: function(response) {
|
||||
for(var i = 0; i < response.length; i++) {
|
||||
var icon = "";
|
||||
if(response[i].icon && response[i].icon != "") {
|
||||
icon = "<img class='chat-icon' src='" + response[i].icon + "' alt='" + response[i]._id + "'>";
|
||||
}
|
||||
$(".names-container").append("<div class='col s12'><div class='name-chat col s3'>" + icon + response[i]._id + "</div><input type='text' class='" + response[i]._id + "_input col s7'><a class='btn green waves-effect col s2 m1 approve_name' href='#' data-name='" + response[i]._id + "'><i class='material-icons'>check</i></a></div>");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/thumbnails",
|
||||
success: function(response){
|
||||
if(response.length > 0){
|
||||
$(".thumbnails-badge").removeClass("hide");
|
||||
$(".thumbnails-badge").text(response.length);
|
||||
}
|
||||
add_to_tab("thumbnails", response);
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/descriptions",
|
||||
success: function(response){
|
||||
if(response.length > 0){
|
||||
$(".descriptions-badge").removeClass("hide");
|
||||
$(".descriptions-badge").text(response.length);
|
||||
}
|
||||
add_to_tab("descriptions", response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("click", ".approve_name", function(e) {
|
||||
var that = this;
|
||||
var name = $(that).attr("data-name");
|
||||
var value = $("." + name + "_input").val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/names",
|
||||
data: {
|
||||
icon: value,
|
||||
name: name,
|
||||
},
|
||||
success: function(resp) {
|
||||
if(resp == true) {
|
||||
Materialize.toast("Approved image!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".thumbnail_link", function(e) {
|
||||
e.preventDefault();
|
||||
window.open("https:" + this.value,'_blank');
|
||||
});
|
||||
|
||||
function add_to_tab(dest, resp){
|
||||
for(var x = 0; x < resp.length; x++){
|
||||
if(dest == "thumbnails"){
|
||||
$("#" + dest + "_cont").append("<div><div class='col s4 m3'>" + resp[x].channel + "</div><input type='text' readonly class='col s4 m6 thumbnail_link' value='" + resp[x].thumbnail + "'><a class='btn green waves-effect col s2 m1 approve_" + dest + "' href='#' data-channel='" + resp[x].channel + "'><i class='material-icons'>check</i></a><a class='btn red waves-effect col s2 m1 deny_" + dest + "' href='#' data-channel='" + resp[x].channel + "'>X</a></div>");
|
||||
} else {
|
||||
$("#" + dest + "_cont").append("<div><div class='col s4 m3'>" + resp[x].channel + "</div><input type='text' readonly class='col s4 m6' value='" + resp[x].description + "'><a class='btn green waves-effect col s2 m1 approve_" + dest + "' href='#' data-channel='" + resp[x].channel + "'><i class='material-icons'>check</i></a><a class='btn red waves-effect col s2 m1 deny_" + dest + "' href='#' data-channel='" + resp[x].channel + "'>X</a></div>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on("click", "#get_token", function(e){
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/token",
|
||||
success: function(response){
|
||||
if(response != false){
|
||||
$("#new_token").val(response.token);
|
||||
$("#get_token").toggleClass("hide");
|
||||
$("#remove_token").toggleClass("hide");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).on("click", "#get_api_token", function(e){
|
||||
e.preventDefault();
|
||||
var name = $("#new_api_token_name").val();
|
||||
if(name == "") {
|
||||
Materialize.toast("Empty name..!", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
$("#new_api_token_name").val("");
|
||||
$("#get_api_token").toggleClass("disabled");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/api_token",
|
||||
data: {
|
||||
name: name,
|
||||
},
|
||||
success: function(response){
|
||||
if(response != false){
|
||||
Materialize.toast("Gotten token", 2000, "green lighten");
|
||||
$("#new_api_token").val(response.token);
|
||||
$("#get_api_token").toggleClass("disabled");
|
||||
var to_add = api_token_list;
|
||||
to_add.attr("id", response._id);
|
||||
to_add.find(".api_token_name").text(name);
|
||||
to_add.find(".api_token_usage").text(0);
|
||||
to_add.find("#delete_api_token").attr("data-id", response._id);
|
||||
$(".channel_things").append(to_add);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).on("click", ".approve_thumbnails", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $(this).attr("data-channel");
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/approve_thumbnail",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
$(that).parent().remove();
|
||||
var length = parseInt($(".thumbnails-badge").text());
|
||||
length = length - 1;
|
||||
$(".thumbnails-badge").text(length);
|
||||
if(length <= 0 && !$(".thumbnails-badge").hasClass("hide")){
|
||||
$(".thumbnails-badge").addClass("hide");
|
||||
}
|
||||
Materialize.toast("Approved Thumbnail!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".deny_thumbnails", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $(this).attr("data-channel");
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/deny_thumbnail",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
$(that).parent().remove();
|
||||
var length = parseInt($(".thumbnails-badge").text());
|
||||
length = length - 1;
|
||||
$(".thumbnails-badge").text(length);
|
||||
if(length <= 0 && !$(".thumbnails-badge").hasClass("hide")){
|
||||
$(".thumbnails-badge").addClass("hide");
|
||||
}
|
||||
Materialize.toast("Denied thumbnail!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".approve_descriptions", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $(this).attr("data-channel");
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/approve_description",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
$(that).parent().remove();
|
||||
var length = parseInt($(".descriptions-badge").text());
|
||||
length = length - 1;
|
||||
$(".descriptions-badge").text(length);
|
||||
if(length <= 0 && !$(".descriptions-badge").hasClass("hide")){
|
||||
$(".descriptions-badge").addClass("hide");
|
||||
}
|
||||
Materialize.toast("Approved description!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".deny_descriptions", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $(this).attr("data-channel");
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/deny_description",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
$(that).parent().remove();
|
||||
var length = parseInt($(".descriptions-badge").text());
|
||||
length = length - 1;
|
||||
$(".descriptions-badge").text(length);
|
||||
if(length <= 0 && !$(".descriptions-badge").hasClass("hide")){
|
||||
$(".descriptions-badge").addClass("hide");
|
||||
}
|
||||
Materialize.toast("Denied description!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", "#remove_description_button", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $("#remove_description").val();
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/remove_description",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
Materialize.toast("Removed description!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", "#remove_thumbnail_button", function(e){
|
||||
e.preventDefault();
|
||||
var channel = $("#remove_thumbnail").val();
|
||||
if(!channel) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/remove_thumbnail",
|
||||
data: {
|
||||
channel: channel
|
||||
},
|
||||
success: function(response){
|
||||
if(response){
|
||||
Materialize.toast("Removed thumbnail!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("submit", "#delete_channel", function(e){
|
||||
e.preventDefault();
|
||||
var to_delete = $("#delete_channel_name").val();
|
||||
if(!to_delete) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
var r = confirm("Delete list " + to_delete + "?");
|
||||
if (r == true) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/delete",
|
||||
data: {
|
||||
_id: to_delete
|
||||
},
|
||||
success: function(response){
|
||||
if(response == true){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/lists",
|
||||
success: function(response){
|
||||
var output_pinned = "";
|
||||
var output_delete = "";
|
||||
for(var x = 0; x < response.length; x++){
|
||||
if(response[x].count > 5){
|
||||
output_pinned += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
output_delete += "<option class='" + response[x]._id + "' value='" + response[x]._id + "'>" + response[x]._id + "</option>";
|
||||
}
|
||||
|
||||
$("#frontpage_pinned").html(output_pinned);
|
||||
$("#delete_list_name").html(output_delete);
|
||||
$("#delete_userpass_name").html(output_delete);
|
||||
$("#delete_channel_name").html(output_delete);
|
||||
$("select").material_select();
|
||||
}
|
||||
});
|
||||
Materialize.toast("Deleted channel!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#delete_channel_button", function(e){
|
||||
e.preventDefault();
|
||||
$("#delete_channel").submit();
|
||||
});
|
||||
|
||||
$(document).on("click", "#remove_token", function(e){
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/remove_token",
|
||||
success: function(response){
|
||||
if(response != false){
|
||||
$("#new_token").val("");
|
||||
$("#get_token").toggleClass("hide");
|
||||
$("#remove_token").toggleClass("hide");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).on("submit", "#change_pinned", function(e){
|
||||
e.preventDefault();
|
||||
var to_pin = $("#frontpage_pinned").val();
|
||||
if(!to_pin) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/pinned",
|
||||
data: {
|
||||
_id: to_pin
|
||||
},
|
||||
success: function(response){
|
||||
if(response == true){
|
||||
Materialize.toast("Pinned channel!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).on("click", "#change_pinned_button", function(){
|
||||
$("#change_pinned").submit();
|
||||
});
|
||||
|
||||
$(document).on("click", "#delete_admin_button", function(){
|
||||
$("#delete_list").submit();
|
||||
});
|
||||
|
||||
$(document).on("click", "#delete_userpass_button", function(){
|
||||
$("#delete_userpass").submit();
|
||||
});
|
||||
|
||||
$(document).on("submit", "#delete_list", function(e){
|
||||
e.preventDefault();
|
||||
var to_remove_password = $("#delete_list_name").val();
|
||||
if(!to_remove_password) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/admin",
|
||||
data: {
|
||||
_id: to_remove_password
|
||||
},
|
||||
success: function(response){
|
||||
if(response == true){
|
||||
Materialize.toast("Removed admin password!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).on("submit", "#delete_userpass", function(e){
|
||||
e.preventDefault();
|
||||
var to_remove_password = $("#delete_userpass_name").val();
|
||||
if(!to_remove_password) {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/userpass",
|
||||
data: {
|
||||
_id: to_remove_password
|
||||
},
|
||||
success: function(response){
|
||||
if(response == true){
|
||||
Materialize.toast("Removed userpass password!", 2000, "green lighten");
|
||||
} else {
|
||||
Materialize.toast("Something went wrong...", 2000, "red lighten");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
socket.emit("get_spread");
|
||||
0
static/images/144x144.png → server/public/assets/admin/images/144x144.png
Normal file → Executable file
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 697 B After Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 786 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
server/public/assets/admin/images/s1.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
server/public/assets/admin/images/s2.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
server/public/assets/admin/images/s3.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
server/public/assets/admin/images/spotify.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
BIN
server/public/assets/admin/images/squareicon_small.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 980 B |
BIN
server/public/assets/admin/images/youtube.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
18
server/public/assets/admin/not_authenticated/js/main.js
Normal file
@@ -0,0 +1,18 @@
|
||||
$(document).on("click", "#login_button", function(e){
|
||||
e.preventDefault();
|
||||
$("#login_form").submit();
|
||||
})
|
||||
|
||||
$(document).ready(function(){
|
||||
if(window.location.pathname == "/signup/" || window.location.pathname == "/signup"){
|
||||
$("#login_form").prepend("<input type='text' name='token' placeholder='Token' required autocomplete='off' />");
|
||||
$("#login_form").attr("action", "/signup");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$(document).on("submit", "#login_form", function(e){
|
||||
if(this.password.value == "" || this.username.value == ""){
|
||||
e.preventDefault();
|
||||
}
|
||||
})
|
||||
512
server/public/assets/css/embed.css
Executable file
@@ -0,0 +1,512 @@
|
||||
.card-image{
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
background-position: center;
|
||||
background-size: 180%;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.side_away {
|
||||
-webkit-transition: all .3s !important;
|
||||
-moz-transition: all .3s !important;
|
||||
-o-transition: all .3s !important;
|
||||
transition: all .3s !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
cursor:pointer;
|
||||
background-color: rgba(255, 255, 255, 0.04) !important;
|
||||
}
|
||||
|
||||
#empty-channel-message {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
color: white;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.video_only {
|
||||
width:100vw !important;
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
#player{
|
||||
width:50vw;
|
||||
height: calc(100vh - 32px);
|
||||
}
|
||||
|
||||
#pageButtons{
|
||||
text-align: center;
|
||||
padding-top:15px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
#pageButtons, #pageButtons a{
|
||||
color:white !important;
|
||||
}
|
||||
|
||||
.prev_page, .next_page{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#wrapper{
|
||||
height:calc(100% - 56px);
|
||||
}
|
||||
|
||||
.prev_page_hide, .next_page_hide{
|
||||
visibility: visible !important;
|
||||
color:gray;
|
||||
}
|
||||
|
||||
.list-song {
|
||||
background-color: rgba(255, 255, 255, 0.04) !important;
|
||||
}
|
||||
|
||||
#list-song-html {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#song-title{
|
||||
display:none;
|
||||
}
|
||||
|
||||
#zoffchannel{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.list-image, .list-suggested-image{
|
||||
width: 34%;
|
||||
height: 66px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.list-image:after {
|
||||
-webkit-transition: all .3s;
|
||||
transition: all .3s;
|
||||
font-family: "Material Icons";
|
||||
content: "thumb_up";
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: white;
|
||||
font-size: -webkit-xxx-large;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0,0,0,0.8);
|
||||
opacity: 0;
|
||||
/* transition: all .1s ease; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.list-suggested-image:after {
|
||||
font-family: "Material-Design-Icons";
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\e625";/*"\e800";*/
|
||||
color:white;
|
||||
font-size:65px;
|
||||
position:absolute;
|
||||
width:100%; height:100%;
|
||||
top:0; left:0;
|
||||
background:rgba(0,0,0,0.8);
|
||||
opacity:0;
|
||||
transition: all .1s ease;
|
||||
}
|
||||
|
||||
.vote-container:hover .list-image:after, .add-suggested:hover .list-suggested-image:after {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.vote-span{
|
||||
opacity: 0.7;
|
||||
padding: 0 0 0 10px;
|
||||
color:white !important;
|
||||
}
|
||||
|
||||
.list-song{
|
||||
background-color: rgba(255, 255, 255, 0.04);
|
||||
color:white;
|
||||
font:12px Arial,sans-serif;
|
||||
-webkit-transition:height .3s;
|
||||
-moz-transition:height .3s;
|
||||
-o-transition:height .3s;
|
||||
transition:height .3s;
|
||||
height:66px;
|
||||
width: 100%;
|
||||
}
|
||||
.list-song .card-content{padding:0;}
|
||||
.list-title{
|
||||
font-size:13px !important;
|
||||
display:block;
|
||||
color:white;font-size:1em;
|
||||
text-align:left;
|
||||
padding: 0 10px 0 10px;
|
||||
line-height: 2.6rem;
|
||||
}
|
||||
.card-image{cursor:pointer}
|
||||
.card{
|
||||
margin: 2.5px 0 2.5px 0 !important;
|
||||
}
|
||||
.card:hover{
|
||||
box-shadow: 0 5px 5px 0 rgba(0,0,0,0.16), 0 5px 10px 0 rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
#playlist{
|
||||
/*background-color:grey;*/
|
||||
height:100vh;
|
||||
width:50vw;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
#zoffbutton{
|
||||
cursor:pointer;
|
||||
position: absolute;
|
||||
bottom: 35px;
|
||||
left: 10px;
|
||||
background-image: url('/assets/images/z.svg');
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-position: center;
|
||||
background-size: 135%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.progress{background-color:rgba(0,0,0,0) !important;}
|
||||
|
||||
.indeterminate {
|
||||
background-color:white !important;
|
||||
}
|
||||
|
||||
#controls
|
||||
{
|
||||
background: inherit;
|
||||
|
||||
position: relative;
|
||||
opacity:0;
|
||||
height:32px;
|
||||
width:50vw;
|
||||
color:white;
|
||||
margin-top:-5px;
|
||||
}
|
||||
|
||||
#playpause, #duration, #volume-button
|
||||
{
|
||||
float:left;
|
||||
color:white;
|
||||
}
|
||||
|
||||
#playpause, #volume-button
|
||||
{
|
||||
margin-left:10px;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
#playpause:hover, #volume-button:hover, #fullscreen:hover
|
||||
{
|
||||
color:rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
#fullscreen
|
||||
{
|
||||
float:right;
|
||||
color:white;
|
||||
margin-right:15px;
|
||||
}
|
||||
|
||||
#duration, #viewers
|
||||
{
|
||||
margin-top:5px;
|
||||
font-family:"Roboto", sans-serif;
|
||||
font-weight:300;
|
||||
margin-left:15px;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
#viewers{
|
||||
float: right;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#play, #pause, #volume-button, #fullscreen
|
||||
{
|
||||
font-size:20px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
|
||||
.ui-slider-vertical {
|
||||
width: .8em;
|
||||
height: 100px;
|
||||
}
|
||||
.ui-slider {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ui-slider-vertical .ui-slider-range-min {
|
||||
bottom: 0;
|
||||
}
|
||||
.ui-slider-vertical .ui-slider-range {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
font-size: .7em;
|
||||
display: block;
|
||||
border: 0;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.ui-slider-vertical .ui-slider-handle {
|
||||
left: -.3em;
|
||||
margin-left: 0;
|
||||
margin-bottom: -.6em;
|
||||
}
|
||||
.ui-slider .ui-slider-handle {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: default;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.ui-slider-horizontal .ui-slider-range-min {
|
||||
left: 0;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-range {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ui-slider .ui-slider-range {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
font-size: .7em;
|
||||
display: block;
|
||||
border: 0;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.ui-slider-horizontal .ui-slider-handle {
|
||||
top: -.3em;
|
||||
margin-left: -.6em;
|
||||
}
|
||||
#volume {
|
||||
border-radius: 10px;
|
||||
cursor:pointer;
|
||||
float:left;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
margin: 13px auto;
|
||||
height:5px;
|
||||
width: 75px;
|
||||
/*background-color:rgba(0, 0, 0, 0.5);*/
|
||||
background:rgba(0, 0, 0, 0.5) 50% 50% repeat-x;
|
||||
border: none;
|
||||
outline: none;
|
||||
/*border-radius: 2px;*/
|
||||
}
|
||||
|
||||
#volume.vertical {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#volume .volume-slid {
|
||||
border-radius: 10px;
|
||||
background: white;
|
||||
height: 5px;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
#volume .volume-slid.vertical {
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
border-radius: 0px;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#volume .volume-handle {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
border: 1px solid lightgrey;
|
||||
margin-top: -9.75px;
|
||||
position: absolute;
|
||||
left: 0%;
|
||||
margin-left: -5.75px;
|
||||
transition: background 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
#volume .volume-handle.vertical {
|
||||
bottom: 0%;
|
||||
display: none;
|
||||
}
|
||||
#volume .volume-handle.ui-state-active {
|
||||
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.01), 0 0 0 7px rgba(255,255,255,0.3);
|
||||
position: absolute;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 21px;
|
||||
background: #dadada;
|
||||
}
|
||||
|
||||
#toast-container{
|
||||
left:2%;
|
||||
cursor:pointer;
|
||||
width:70vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: baseline;
|
||||
/*pointer-events:none;*/
|
||||
}
|
||||
|
||||
.toast {
|
||||
word-break: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.play
|
||||
{
|
||||
background-size: auto;
|
||||
width: 55px;
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.pause
|
||||
{
|
||||
background-size: auto;
|
||||
width: 55px;
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.hide
|
||||
{
|
||||
display:none !important;
|
||||
}
|
||||
|
||||
#bar
|
||||
{
|
||||
height:100%;
|
||||
background-color:rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#pageButtons {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
#pageButtons, #pageButtons a{
|
||||
color:white !important;
|
||||
}
|
||||
|
||||
#pageNumber{
|
||||
cursor: default;
|
||||
color: white;
|
||||
padding:0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.prev_page, .next_page, .last_page, .first_page{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.prev_page_hide, .next_page_hide, .last_page_hide, .first_page_hide{
|
||||
visibility: visible !important;
|
||||
color:gray;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
.prev_page_hide, .prev_page, .first_page, .first_page_hide{
|
||||
padding:0 1px;
|
||||
}
|
||||
|
||||
.next_page_hide, .next_page, .last_page, .last_page_hide{
|
||||
padding:0 0px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.first_page, .first_page_hide {
|
||||
padding: 0 0 0 10px;
|
||||
}
|
||||
|
||||
.last_page, .last_page_hide {
|
||||
padding: 0 10px 0 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
.vote-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-duration {
|
||||
border-top-right-radius: 4px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
background: rgba(0,0,0,.7);
|
||||
color: white;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
.last_page, .last_page_hide, .first_page, .first_page_hide{
|
||||
display: none !important;
|
||||
}
|
||||
*/
|
||||
#wrapper{
|
||||
background: inherit;
|
||||
/*height: 94%;*/
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) and (min-width: 601px) {
|
||||
#toast-container {
|
||||
bottom: 90px;
|
||||
}
|
||||
}
|
||||
3795
server/public/assets/css/style.css
Executable file
6
server/public/assets/dist/lib/jquery-ui.min.js
vendored
Normal file
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 322 KiB |
BIN
server/public/assets/fonts/roboto/Roboto-Bold.woff
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Bold.woff2
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Light.woff
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Light.woff2
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Medium.woff
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Medium.woff2
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Regular.woff
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Regular.woff2
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Thin.woff
Normal file
BIN
server/public/assets/fonts/roboto/Roboto-Thin.woff2
Normal file
176
server/public/assets/html/callback.html
Executable file
@@ -0,0 +1,176 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Zoff OAuth Callback</title>
|
||||
<script type="text/javascript" src="/assets/dist/callback.min.js"></script>
|
||||
<meta charset="UTF-8"/>
|
||||
<style>
|
||||
@-webkit-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-ms-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes uil-ring-anim {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.uil-ring-css {
|
||||
background: none;
|
||||
width: 190px;
|
||||
height: 190px;
|
||||
transform: scale(1);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.uil-ring-css > div {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border: 20px solid black;
|
||||
border-radius: 50%;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
transform: rotate(-45deg);'
|
||||
-ms-animation: uil-ring-anim 1s linear infinite;
|
||||
-moz-animation: uil-ring-anim 1s linear infinite;
|
||||
-webkit-animation: uil-ring-anim 1s linear infinite;
|
||||
-o-animation: uil-ring-anim 1s linear infinite;
|
||||
animation: uil-ring-anim 1s linear infinite;
|
||||
}
|
||||
h4 {
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
font-size: 2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h4>Loading..</h4>
|
||||
<div class="uil-ring-css" >
|
||||
<div></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
server/public/assets/html/offline.html
Executable file
BIN
server/public/assets/images/GitHub_Logo.png
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
server/public/assets/images/Logo.png
Executable file
|
After Width: | Height: | Size: 36 KiB |
BIN
server/public/assets/images/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
server/public/assets/images/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
server/public/assets/images/apple-touch-icon-precomposed.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
server/public/assets/images/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
9
server/public/assets/images/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/assets/images/mstile-150x150.png"/>
|
||||
<TileColor>#2d2d2d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
BIN
server/public/assets/images/ethdonate.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
server/public/assets/images/facebook.png
Executable file
|
After Width: | Height: | Size: 697 B |