aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2025-01-29 20:37:50 +0300
committerRblSb <msrblsb@gmail.com>2025-01-29 20:42:01 +0300
commit382f9b2ebedca905028341825350a0fa69d88673 (patch)
treef627cd270d893452c13f3d0916ea75673c39607e
parent0592564264fff57ccfd9677957196951f9f1c6cf (diff)
Cleanup
- PWA support (needs https) - Optimize websocket responses - Support `/ad` for youtube cache - New icon!
-rw-r--r--build/server.js27
-rw-r--r--res/client.js74
-rw-r--r--res/css/des.css6
-rwxr-xr-xres/img/favicon.pngbin171 -> 0 bytes
-rw-r--r--res/img/favicon.svg17
-rw-r--r--res/index.html5
-rw-r--r--res/manifest.json12
-rw-r--r--src/client/Buttons.hx180
-rw-r--r--src/client/Main.hx136
-rw-r--r--src/client/Player.hx30
-rw-r--r--src/client/Split.hx44
-rw-r--r--src/client/players/Iframe.hx4
-rw-r--r--src/client/players/Raw.hx8
-rw-r--r--src/client/players/Vk.hx4
-rw-r--r--src/client/players/Youtube.hx4
-rw-r--r--src/server/Logger.hx9
-rw-r--r--src/server/Main.hx19
-rw-r--r--user/README.md1
18 files changed, 329 insertions, 251 deletions
diff --git a/build/server.js b/build/server.js
index ac708e8..28d861d 100644
--- a/build/server.js
+++ b/build/server.js
@@ -4529,17 +4529,11 @@ server_Logger.prototype = {
server_Utils.ensureDir(this.folder);
this.removeOldestLog(this.folder);
var name = DateTools.format(new Date(),"%Y-%m-%d_%H_%M_%S");
- js_node_Fs.writeFileSync("" + this.folder + "/" + name + ".json",JSON.stringify(this.getLogs(),$bind(this,this.filterNulls),"\t"));
+ js_node_Fs.writeFileSync("" + this.folder + "/" + name + ".json",server_Main.jsonStringify(this.getLogs(),"\t"));
}
,getLogs: function() {
return this.logs;
}
- ,filterNulls: function(key,value) {
- if(value == null) {
- return undefined;
- }
- return value;
- }
,removeOldestLog: function(folder) {
var _gthis = this;
var _this = js_node_Fs.readdirSync(folder);
@@ -4671,6 +4665,15 @@ server_Main.__name__ = true;
server_Main.main = function() {
new server_Main({ loadState : true});
};
+server_Main.jsonStringify = function(data,space) {
+ return JSON.stringify(data,server_Main.jsonFilterNulls,space);
+};
+server_Main.jsonFilterNulls = function(key,value) {
+ if(value == null) {
+ return undefined;
+ }
+ return value;
+};
server_Main.prototype = {
runServer: function() {
var _gthis = this;
@@ -5205,7 +5208,7 @@ server_Main.prototype = {
var client1 = _this[i];
result[i] = { name : client1.name, id : client1.id, ip : _gthis.clientIp(client1.req), isBanned : (client1.group & 1) != 0, isAdmin : (client1.group & 8) != 0, isLeader : (client1.group & 4) != 0, isUser : (client1.group & 2) != 0};
}
- var json = JSON.stringify({ state : data1, clients : result, logs : this.logger.getLogs()},($_=this.logger,$bind($_,$_.filterNulls)),"\t");
+ var json = server_Main.jsonStringify({ state : data1, clients : result, logs : this.logger.getLogs()},"\t");
this.send(client,{ type : "Dump", dump : { data : json}});
break;
case "Flashback":
@@ -5557,16 +5560,16 @@ server_Main.prototype = {
this.send(client,{ type : "ServerMessage", serverMessage : { textId : textId}});
}
,send: function(client,data) {
- client.ws.send(JSON.stringify(data),null);
+ client.ws.send(server_Main.jsonStringify(data),null);
}
,broadcast: function(data) {
- var json = JSON.stringify(data);
+ var json = server_Main.jsonStringify(data);
var _g = 0;
var _g1 = this.clients;
while(_g < _g1.length) _g1[_g++].ws.send(json,null);
}
,broadcastExcept: function(skipped,data) {
- var json = JSON.stringify(data);
+ var json = server_Main.jsonStringify(data);
var _g = 0;
var _g1 = this.clients;
while(_g < _g1.length) {
@@ -5627,7 +5630,7 @@ server_Main.prototype = {
client.setGroupFlag(ClientGroup.Banned,!isOutdated);
if(isOutdated) {
HxOverrides.remove(this.userList.bans,ban);
- haxe_Log.trace("" + client.name + " ban removed",{ fileName : "src/server/Main.hx", lineNumber : 1045, className : "server.Main", methodName : "checkBan"});
+ haxe_Log.trace("" + client.name + " ban removed",{ fileName : "src/server/Main.hx", lineNumber : 1056, className : "server.Main", methodName : "checkBan"});
this.sendClientList();
}
break;
diff --git a/res/client.js b/res/client.js
index ac32132..9629980 100644
--- a/res/client.js
+++ b/res/client.js
@@ -556,8 +556,12 @@ client_Buttons.init = function(main) {
if(client_Buttons.settings.isSwapped) {
client_Buttons.swapPlayerAndChat();
}
- client_Buttons.initSplit();
- client_Buttons.setSplitSize(client_Buttons.settings.chatSize);
+ var tmp = client_Buttons.split;
+ if(tmp != null) {
+ tmp.destroy();
+ }
+ client_Buttons.split = new client_Split(client_Buttons.settings);
+ client_Buttons.split.setSize(client_Buttons.settings.chatSize);
client_Buttons.initChatInputs(main);
var _g = 0;
var _g1 = client_Buttons.settings.checkboxes;
@@ -801,14 +805,14 @@ client_Buttons.init = function(main) {
var request = window.fetch("/upload",{ method : "POST", headers : { "content-name" : haxe_io_Path.withoutExtension(name), "client-name" : main.personal.name}, body : buffer});
request.then(function(e) {
return e.json().then(function(data) {
- haxe_Log.trace(data.info,{ fileName : "src/client/Buttons.hx", lineNumber : 274, className : "client.Buttons", methodName : "init"});
+ haxe_Log.trace(data.info,{ fileName : "src/client/Buttons.hx", lineNumber : 276, className : "client.Buttons", methodName : "init"});
if(data.errorId == null) {
return;
}
main.serverMessage(data.info,true,false);
});
}).catch(function(err) {
- haxe_Log.trace(err,{ fileName : "src/client/Buttons.hx", lineNumber : 279, className : "client.Buttons", methodName : "init"});
+ haxe_Log.trace(err,{ fileName : "src/client/Buttons.hx", lineNumber : 281, className : "client.Buttons", methodName : "init"});
return haxe_Timer.delay(function() {
main.hideDynamicChin();
},500);
@@ -897,29 +901,6 @@ client_Buttons.swapPlayerAndChat = function() {
sizes.reverse();
window.document.body.style.gridTemplateColumns = sizes.join(" ");
};
-client_Buttons.initSplit = function() {
- if(client_Buttons.split != null) {
- client_Buttons.split.destroy();
- }
- client_Buttons.split = new Split({ columnGutters : [{ element : window.document.querySelector(".gutter"), track : 1}], minSize : 200, snapOffset : 0, onDragEnd : client_Buttons.saveSplitSize});
-};
-client_Buttons.setSplitSize = function(chatSize) {
- if(chatSize < 200) {
- return;
- }
- var sizes = window.document.body.style.gridTemplateColumns.split(" ");
- sizes[client_Buttons.settings.isSwapped ? 0 : sizes.length - 1] = "" + chatSize + "px";
- window.document.body.style.gridTemplateColumns = sizes.join(" ");
-};
-client_Buttons.saveSplitSize = function() {
- var sizes = window.document.body.style.gridTemplateColumns.split(" ");
- if(client_Buttons.settings.isSwapped) {
- sizes.reverse();
- }
- var tmp = parseFloat(sizes[sizes.length - 1]);
- client_Buttons.settings.chatSize = tmp;
- client_Settings.write(client_Buttons.settings);
-};
client_Buttons.initTextButtons = function(main) {
window.document.querySelector("#synchThresholdBtn").onclick = function(e) {
var secs = client_Buttons.settings.synchThreshold + 1;
@@ -3321,10 +3302,14 @@ client_Player.prototype = {
if(tmp == null) {
return;
}
- if(!this.youtube.isSupportedLink(tmp.url)) {
- return;
+ var itemUrl = tmp.url;
+ if(!this.youtube.isSupportedLink(itemUrl)) {
+ itemUrl = StringTools.replace(itemUrl,"/cache/","youtu.be/");
+ if(!this.youtube.isSupportedLink(itemUrl)) {
+ return;
+ }
}
- var http = new haxe_http_HttpJs("https://sponsor.ajay.app/api/skipSegments?videoID=" + this.youtube.extractVideoId(tmp.url));
+ var http = new haxe_http_HttpJs("https://sponsor.ajay.app/api/skipSegments?videoID=" + this.youtube.extractVideoId(itemUrl));
http.onData = function(text) {
var json;
try {
@@ -3345,7 +3330,7 @@ client_Player.prototype = {
}
};
http.onError = function(msg) {
- haxe_Log.trace(msg,{ fileName : "src/client/Player.hx", lineNumber : 652, className : "client.Player", methodName : "skipAd"});
+ haxe_Log.trace(msg,{ fileName : "src/client/Player.hx", lineNumber : 656, className : "client.Player", methodName : "skipAd"});
};
http.request();
}
@@ -3443,6 +3428,33 @@ client_Settings.write = function(data) {
}
client_Settings.storage.setItem("data",JSON.stringify(data));
};
+var client_Split = function(settings) {
+ this.settings = settings;
+ this.split = new Split({ columnGutters : [{ element : window.document.querySelector(".gutter"), track : 1}], minSize : 200, snapOffset : 0, onDragEnd : $bind(this,this.saveSize)});
+};
+client_Split.__name__ = true;
+client_Split.prototype = {
+ setSize: function(chatSize) {
+ if(chatSize < 200) {
+ return;
+ }
+ var sizes = window.document.body.style.gridTemplateColumns.split(" ");
+ sizes[this.settings.isSwapped ? 0 : sizes.length - 1] = "" + chatSize + "px";
+ window.document.body.style.gridTemplateColumns = sizes.join(" ");
+ }
+ ,saveSize: function() {
+ var sizes = window.document.body.style.gridTemplateColumns.split(" ");
+ if(this.settings.isSwapped) {
+ sizes.reverse();
+ }
+ var tmp = parseFloat(sizes[sizes.length - 1]);
+ this.settings.chatSize = tmp;
+ client_Settings.write(this.settings);
+ }
+ ,destroy: function() {
+ this.split.destroy();
+ }
+};
var client_Utils = function() { };
client_Utils.__name__ = true;
client_Utils.nativeTrace = function(msg,infos) {
diff --git a/res/css/des.css b/res/css/des.css
index d6cd5ed..f047714 100644
--- a/res/css/des.css
+++ b/res/css/des.css
@@ -761,6 +761,12 @@ footer#footer {
background-color: var(--background-chat);
}
+@media only screen and (orientation: portrait) {
+ #swapLayoutBtn {
+ display: none !important;
+ }
+}
+
/* Message buffer */
#messagebuffer {
diff --git a/res/img/favicon.png b/res/img/favicon.png
deleted file mode 100755
index 5d46d1c..0000000
--- a/res/img/favicon.png
+++ /dev/null
Binary files differ
diff --git a/res/img/favicon.svg b/res/img/favicon.svg
new file mode 100644
index 0000000..ffed1d5
--- /dev/null
+++ b/res/img/favicon.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Pixelmator Pro 3.6.13 -->
+<svg width="601" height="601" viewBox="0 0 601 601" xmlns="http://www.w3.org/2000/svg">
+ <g id="Group">
+ <linearGradient id="linearGradient1" x1="507.329206" y1="478.444028" x2="93.671347" y2="122.680956" gradientUnits="userSpaceOnUse">
+ <stop offset="0" stop-color="#3478f6" stop-opacity="1"/>
+ <stop offset="1" stop-color="#53b6f9" stop-opacity="1"/>
+ </linearGradient>
+ <path id="Path-copy-2" fill="url(#linearGradient1)" stroke="#ffffff" stroke-width="15" visibility="hidden" d="M 593 262 C 592.394104 205.032471 569.920532 151.067017 530 110.999969 C 489.952332 70.743286 436.905975 49 380 49 L 220.999985 49 C 103.492493 49.02179 8 144.523254 8 262 C 8 379.486938 103.492493 474.968018 220.999985 475 L 338 475 C 341.091156 475 343.184906 476.902557 344 478 C 345.983307 480.652161 345.313141 483.945984 345 485 C 343.76651 489.377197 340.077087 495.119568 334 505 C 329.319885 512.750732 324.919159 519.611023 324 521 C 317.44046 530.839294 315.555847 539.69342 318 545 C 319.838196 549 324.77179 551.143799 332 552 C 334.444153 552.281189 337.781982 552 341 552 C 342.691864 552 344.558411 552.114258 346 552 C 360.999451 550.715088 381.311218 548.111694 405 541 C 439.530579 530.617737 488.246277 508.916565 528 463 C 570.531616 414.023193 593.125671 346.53949 593 267 Z"/>
+ <linearGradient id="linearGradient2" x1="507.328733" y1="478.404761" x2="93.671267" y2="122.674209" gradientUnits="userSpaceOnUse">
+ <stop offset="0" stop-color="#7234f6" stop-opacity="1"/>
+ <stop offset="1" stop-color="#f253f9" stop-opacity="1"/>
+ </linearGradient>
+ <path id="Path-copy-5" fill="url(#linearGradient2)" stroke="#ffffff" stroke-width="15" d="M 592.977661 261.792419 C 592.37207 204.854401 570.094177 151.445831 530.194336 111.399597 C 490.16745 71.163757 436.842316 49 379.965881 49 L 221.011749 49 C 103.56514 49.02179 8 144.565125 8 261.980988 C 8 379.407043 103.56514 474.971069 221.011749 475.003021 L 338.480103 475.003021 C 341.569672 475.003021 343.198975 476.307861 344.013672 477.404724 C 345.995972 480.055511 345.119568 483.773438 344.80658 484.826904 C 343.57373 489.201843 340.462463 495.287201 334.38855 505.162506 C 329.710876 512.90918 325.36795 519.434387 324.449249 520.822632 C 317.893127 530.65686 315.638824 538.987366 318.081696 544.291199 C 319.918945 548.289063 324.532715 550.753601 331.757172 551.609375 C 334.200073 551.890442 337.582031 552.078979 340.798401 552.078979 C 342.48938 552.078979 344.117584 552.026428 345.558411 551.91217 C 360.55011 550.62793 380.803467 548.246826 404.479919 541.138794 C 438.992615 530.761902 488.457092 508.765045 528.190247 462.872284 C 570.699768 413.920868 593.125122 346.03125 592.999451 266.53299 Z M 554.079956 262.177521"/>
+ <path id="Path-copy-3" fill="#ffffff" stroke="none" d="M 220 180.192719 L 220 344.807281 C 220 364.413269 227.833878 375.072723 241.407272 375.072723 C 247.942627 375.072723 255.72879 373.220764 265.029083 367.690918 L 405.28363 285.752716 C 416.846191 278.966034 423 271.16394 423 262.86908 C 423 254.574249 416.846191 246.033997 405.28363 239.247284 L 265.029083 158.047272 C 255.72879 152.517365 247.942627 149.927277 241.407272 149.927277 C 227.833878 149.927277 220 160.5867 220 180.192719 Z"/>
+ </g>
+</svg>
diff --git a/res/index.html b/res/index.html
index 4c06632..dfcbcd8 100644
--- a/res/index.html
+++ b/res/index.html
@@ -5,8 +5,11 @@
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <link rel="manifest" href="manifest.json">
<title>SyncTube</title>
- <link rel="icon" type="image/png" href="img/favicon.png" />
+ <link rel="icon" href="img/favicon.svg" type="image/svg+xml">
<link id="usertheme" href="css/des.css" rel="stylesheet">
<link id="customcss" href="css/custom.css" rel="stylesheet">
<script type="module" src="https://cdn.jsdelivr.net/npm/ionicons@7.4.0/dist/ionicons/ionicons.esm.js"></script>
diff --git a/res/manifest.json b/res/manifest.json
new file mode 100644
index 0000000..1bdb671
--- /dev/null
+++ b/res/manifest.json
@@ -0,0 +1,12 @@
+{
+ "name": "SyncTube",
+ "display": "standalone",
+ "start_url": "/",
+ "icons": [
+ {
+ "src": "img/favicon.svg",
+ "type": "image/svg+xml",
+ "sizes": "any"
+ }
+ ]
+}
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
index 334debc..99e12cb 100644
--- a/src/client/Buttons.hx
+++ b/src/client/Buttons.hx
@@ -2,7 +2,7 @@ package client;
import Types.UploadResponse;
import Types.WsEvent;
-import client.Main.ge;
+import client.Main.getEl;
import haxe.Timer;
import haxe.io.Path;
import js.Browser.document;
@@ -15,28 +15,30 @@ import js.html.TransitionEvent;
import js.html.VisualViewport;
class Buttons {
- static inline var CHAT_MIN_SIZE = 200;
static var split:Split;
static var settings:ClientSettings;
public static function init(main:Main):Void {
settings = main.settings;
if (settings.isSwapped) swapPlayerAndChat();
- initSplit();
- setSplitSize(settings.chatSize);
+
+ split?.destroy();
+ split = new Split(settings);
+ split.setSize(settings.chatSize);
+
initChatInputs(main);
for (item in settings.checkboxes) {
if (item.checked == null) continue;
- final checkbox:InputElement = ge('#${item.id}') ?? continue;
+ final checkbox:InputElement = getEl('#${item.id}') ?? continue;
checkbox.checked = item.checked;
}
- final passIcon = ge("#guestpass_icon");
+ final passIcon = getEl("#guestpass_icon");
passIcon.onclick = e -> {
final icon = passIcon.firstElementChild;
final isOpen = icon.getAttribute("name") == "eye-off";
- final pass:InputElement = cast ge("#guestpass");
+ final pass:InputElement = getEl("#guestpass");
if (isOpen) {
pass.type = "password";
icon.setAttribute("name", "eye");
@@ -46,10 +48,10 @@ class Buttons {
}
}
- final smilesBtn = ge("#smilesbtn");
+ final smilesBtn = getEl("#smilesbtn");
smilesBtn.onclick = e -> {
- final wrap = ge("#smiles-wrap");
- final list = ge("#smiles-list");
+ final wrap = getEl("#smiles-wrap");
+ final list = getEl("#smiles-list");
if (list.children.length == 0) return;
final isActive = smilesBtn.classList.toggle("active");
if (isActive) {
@@ -70,22 +72,22 @@ class Buttons {
}
}
- final scrollToChatEndBtn = ge("#scroll-to-chat-end");
+ final scrollToChatEndBtn = getEl("#scroll-to-chat-end");
scrollToChatEndBtn.onclick = e -> {
main.scrollChatToEnd();
main.hideScrollToChatEndBtn();
}
// hide scroll button when chat is scrolled to the end
- final msgBuf = ge("#messagebuffer");
+ final msgBuf = getEl("#messagebuffer");
msgBuf.onscroll = e -> {
if (!main.isInChatEnd(1)) return;
main.hideScrollToChatEndBtn();
}
- ge("#clearchatbtn").onclick = e -> {
+ getEl("#clearchatbtn").onclick = e -> {
if (main.isAdmin()) main.send({type: ClearChat});
}
- final userList = ge("#userlist");
+ final userList = getEl("#userlist");
userList.onclick = e -> {
if (!main.isAdmin()) return;
var el:Element = cast e.target;
@@ -105,12 +107,12 @@ class Buttons {
});
}
- final userlistToggle = ge("#userlisttoggle");
+ final userlistToggle = getEl("#userlisttoggle");
userlistToggle.onclick = e -> {
final icon = userlistToggle.firstElementChild;
final isHidden = icon.getAttribute("name") == "chevron-forward";
- final wrap = ge("#userlist-wrap");
- final style = ge("#userlist").style;
+ final wrap = getEl("#userlist-wrap");
+ final style = getEl("#userlist").style;
if (isHidden) {
icon.setAttribute("name", "chevron-down");
style.display = "";
@@ -126,19 +128,19 @@ class Buttons {
settings.isUserListHidden = !isHidden;
Settings.write(settings);
}
- ge("#usercount").onclick = userlistToggle.onclick;
+ getEl("#usercount").onclick = userlistToggle.onclick;
if (settings.isUserListHidden) userlistToggle.onclick();
else {
- final wrap = ge("#userlist-wrap");
+ final wrap = getEl("#userlist-wrap");
final list = wrap.firstElementChild;
wrap.style.height = "15vh";
}
// enable animation after page loads
Timer.delay(() -> {
- ge("#userlist-wrap").style.transition = "200ms";
+ getEl("#userlist-wrap").style.transition = "200ms";
}, 0);
- final toggleSynch = ge("#togglesynch");
+ final toggleSynch = getEl("#togglesynch");
toggleSynch.onclick = e -> {
final icon = toggleSynch.firstElementChild;
if (main.isSyncActive) {
@@ -153,21 +155,21 @@ class Buttons {
main.send({type: UpdatePlaylist});
}
}
- final mediaRefresh = ge("#mediarefresh");
+ final mediaRefresh = getEl("#mediarefresh");
mediaRefresh.onclick = e -> {
main.refreshPlayer();
}
- final fullscreenBtn = ge("#fullscreenbtn");
+ final fullscreenBtn = getEl("#fullscreenbtn");
fullscreenBtn.onclick = e -> {
if ((Utils.isTouch() || main.isVerbose()) && !Utils.hasFullscreen()) {
window.scrollTo(0, 0);
Utils.requestFullscreen(document.documentElement);
} else {
- Utils.requestFullscreen(ge("#ytapiplayer"));
+ Utils.requestFullscreen(getEl("#ytapiplayer"));
}
}
initPageFullscreen();
- final getPlaylist = ge("#getplaylist");
+ final getPlaylist = getEl("#getplaylist");
getPlaylist.onclick = e -> {
final text = main.getPlaylistLinks().join(",");
Utils.copyToClipboard(text);
@@ -177,18 +179,18 @@ class Buttons {
icon.setAttribute("name", "link");
}, 2000);
}
- final clearPlaylist = ge("#clearplaylist");
+ final clearPlaylist = getEl("#clearplaylist");
clearPlaylist.onclick = e -> {
if (!window.confirm(Lang.get("clearPlaylistConfirm"))) return;
main.send({type: ClearPlaylist});
}
- final shufflePlaylist = ge("#shuffleplaylist");
+ final shufflePlaylist = getEl("#shuffleplaylist");
shufflePlaylist.onclick = e -> {
if (!window.confirm(Lang.get("shufflePlaylistConfirm"))) return;
main.send({type: ShufflePlaylist});
}
- final lockPlaylist = ge("#lockplaylist");
+ final lockPlaylist = getEl("#lockplaylist");
lockPlaylist.onclick = e -> {
if (!main.hasPermission(LockPlaylistPerm)) return;
if (main.isPlaylistOpen) {
@@ -199,36 +201,36 @@ class Buttons {
});
}
- final showMediaUrl = ge("#showmediaurl");
+ final showMediaUrl = getEl("#showmediaurl");
showMediaUrl.onclick = e -> {
final isOpen = showPlayerGroup(showMediaUrl);
if (isOpen) Timer.delay(() -> {
- ge("#addfromurl").scrollIntoView();
- ge("#mediaurl").focus();
+ getEl("#addfromurl").scrollIntoView();
+ getEl("#mediaurl").focus();
}, 100);
}
- final showCustomEmbed = ge("#showcustomembed");
+ final showCustomEmbed = getEl("#showcustomembed");
showCustomEmbed.onclick = e -> {
final isOpen = showPlayerGroup(showCustomEmbed);
if (isOpen) Timer.delay(() -> {
- ge("#customembed").scrollIntoView();
- ge("#customembed-title").focus();
+ getEl("#customembed").scrollIntoView();
+ getEl("#customembed-title").focus();
}, 100);
}
- final mediaUrl:InputElement = cast ge("#mediaurl");
+ final mediaUrl:InputElement = getEl("#mediaurl");
mediaUrl.oninput = () -> {
final url = mediaUrl.value;
final playerType = main.getLinkPlayerType(url);
final isSingle = main.isSingleVideoUrl(url);
final isSingleRawVideo = url != "" && playerType == RawType && isSingle;
- ge("#mediatitleblock").style.display = isSingleRawVideo ? "" : "none";
- ge("#subsurlblock").style.display = isSingleRawVideo ? "" : "none";
- ge("#voiceoverblock").style.display = (url.length > 0 && isSingle) ? "" : "none";
+ getEl("#mediatitleblock").style.display = isSingleRawVideo ? "" : "none";
+ getEl("#subsurlblock").style.display = isSingleRawVideo ? "" : "none";
+ getEl("#voiceoverblock").style.display = (url.length > 0 && isSingle) ? "" : "none";
final showCache = isSingle && main.playersCacheSupport.contains(playerType);
- ge("#cache-on-server").parentElement.style.display = showCache ? "" : "none";
- final panel = ge("#addfromurl");
+ getEl("#cache-on-server").parentElement.style.display = showCache ? "" : "none";
+ final panel = getEl("#addfromurl");
final oldH = panel.style.height; // save for animation
panel.style.height = ""; // to calculate height from content
final newH = Utils.outerHeight(panel) + "px";
@@ -237,12 +239,12 @@ class Buttons {
}
mediaUrl.onfocus = mediaUrl.oninput;
- ge("#insert_template").onclick = e -> {
+ getEl("#insert_template").onclick = e -> {
mediaUrl.value = main.getTemplateUrl();
mediaUrl.focus();
}
- ge("#mediaurl-upload").onclick = e -> {
+ getEl("#mediaurl-upload").onclick = e -> {
Utils.browseFile((buffer, name) -> {
if (name == null || name.length == 0) name = "video";
@@ -288,7 +290,7 @@ class Buttons {
final data = event.progress;
if (data.type != Uploading) return;
if (data.data == null) return;
- final input:InputElement = ge("#mediaurl");
+ final input:InputElement = getEl("#mediaurl");
input.value = data.data;
JsApi.off(Progress, onStartUpload);
}
@@ -296,24 +298,24 @@ class Buttons {
});
}
- final showOptions = ge("#showoptions");
+ final showOptions = getEl("#showoptions");
showOptions.onclick = e -> {
final isActive = toggleGroup(showOptions);
- ge("#optionsPanel").style.opacity = isActive ? "1" : "0";
+ getEl("#optionsPanel").style.opacity = isActive ? "1" : "0";
Timer.delay(() -> {
if (showOptions.classList.contains("active") != isActive) return;
- ge("#optionsPanel").classList.toggle("collapse", !isActive);
+ getEl("#optionsPanel").classList.toggle("collapse", !isActive);
}, isActive ? 0 : 200);
}
- final exitBtn = ge("#exitBtn");
+ final exitBtn = getEl("#exitBtn");
exitBtn.onclick = e -> {
showOptions.onclick();
if (main.isUser()) main.send({type: Logout});
- else ge("#guestname").focus();
+ else getEl("#guestname").focus();
}
- final swapLayoutBtn = ge("#swapLayoutBtn");
+ final swapLayoutBtn = getEl("#swapLayoutBtn");
swapLayoutBtn.onclick = e -> {
swapPlayerAndChat();
Settings.write(settings);
@@ -332,7 +334,7 @@ class Buttons {
static function toggleGroup(el:Element):Bool {
el.classList.toggle("collapsed");
- final target = ge(el.dataset.target);
+ final target = getEl(el.dataset.target);
final isClosed = target.classList.toggle("collapse");
if (isClosed) {
target.style.height = "0";
@@ -347,42 +349,14 @@ class Buttons {
}
static function swapPlayerAndChat():Void {
- settings.isSwapped = ge("body").classList.toggle("swap");
+ settings.isSwapped = getEl("body").classList.toggle("swap");
final sizes = document.body.style.gridTemplateColumns.split(" ");
sizes.reverse();
document.body.style.gridTemplateColumns = sizes.join(" ");
}
- static function initSplit():Void {
- if (split != null) split.destroy();
- split = new Split({
- columnGutters: [{
- element: ge(".gutter"),
- track: 1,
- }],
- minSize: 200,
- snapOffset: 0,
- onDragEnd: saveSplitSize
- });
- }
-
- static function setSplitSize(chatSize:Float):Void {
- if (chatSize < CHAT_MIN_SIZE) return;
- final sizes = document.body.style.gridTemplateColumns.split(" ");
- final chatId = settings.isSwapped ? 0 : sizes.length - 1;
- sizes[chatId] = '${chatSize}px';
- document.body.style.gridTemplateColumns = sizes.join(" ");
- }
-
- static function saveSplitSize():Void {
- final sizes = document.body.style.gridTemplateColumns.split(" ");
- if (settings.isSwapped) sizes.reverse();
- settings.chatSize = Std.parseFloat(sizes[sizes.length - 1]);
- Settings.write(settings);
- }
-
public static function initTextButtons(main:Main):Void {
- final synchThresholdBtn = ge("#synchThresholdBtn");
+ final synchThresholdBtn = getEl("#synchThresholdBtn");
synchThresholdBtn.onclick = e -> {
var secs = settings.synchThreshold + 1;
if (secs > 5) secs = 1;
@@ -391,7 +365,7 @@ class Buttons {
}
updateSynchThresholdBtn();
- final hotkeysBtn = ge("#hotkeysBtn");
+ final hotkeysBtn = getEl("#hotkeysBtn");
hotkeysBtn.onclick = e -> {
settings.hotkeysEnabled = !settings.hotkeysEnabled;
Settings.write(settings);
@@ -399,7 +373,7 @@ class Buttons {
}
updateHotkeysBtn();
- final removeBtn = ge("#removePlayerBtn");
+ final removeBtn = getEl("#removePlayerBtn");
removeBtn.onclick = e -> {
final isActive = main.toggleVideoElement();
if (isActive) {
@@ -408,7 +382,7 @@ class Buttons {
removeBtn.innerText = Lang.get("restorePlayer");
}
}
- final setVideoUrlBtn = ge("#setVideoUrlBtn");
+ final setVideoUrlBtn = getEl("#setVideoUrlBtn");
setVideoUrlBtn.onclick = e -> {
final src = window.prompt(Lang.get("setVideoUrlPrompt"));
if (src.trim() == "") { // reset to default url
@@ -417,7 +391,7 @@ class Buttons {
}
JsApi.setVideoSrc(src);
}
- final selectLocalVideoBtn = ge("#selectLocalVideoBtn");
+ final selectLocalVideoBtn = getEl("#selectLocalVideoBtn");
selectLocalVideoBtn.onclick = e -> {
Utils.browseFileUrl((url, name) -> {
JsApi.setVideoSrc(url);
@@ -426,11 +400,11 @@ class Buttons {
}
public static function initHotkeys(main:Main, player:Player):Void {
- ge("#mediarefresh").title += " (Alt-R)";
- ge("#voteskip").title += " (Alt-S)";
- ge("#getplaylist").title += " (Alt-C)";
- ge("#fullscreenbtn").title += " (Alt-F)";
- ge("#leader_btn").title += " (Alt-L)";
+ getEl("#mediarefresh").title += " (Alt-R)";
+ getEl("#voteskip").title += " (Alt-S)";
+ getEl("#getplaylist").title += " (Alt-C)";
+ getEl("#fullscreenbtn").title += " (Alt-F)";
+ getEl("#leader_btn").title += " (Alt-L)";
window.onkeydown = (e:KeyboardEvent) -> {
if (!settings.hotkeysEnabled) return;
final target:Element = cast e.target;
@@ -440,13 +414,13 @@ class Buttons {
if (!e.altKey) return;
switch (key) {
case R:
- ge("#mediarefresh").onclick();
+ getEl("#mediarefresh").onclick();
case S:
- ge("#voteskip").onclick();
+ getEl("#voteskip").onclick();
case C:
- ge("#getplaylist").onclick();
+ getEl("#getplaylist").onclick();
case F:
- ge("#fullscreenbtn").onclick();
+ getEl("#fullscreenbtn").onclick();
case L:
main.toggleLeader();
case P:
@@ -469,17 +443,17 @@ class Buttons {
static function updateSynchThresholdBtn():Void {
final text = Lang.get("synchThreshold");
final secs = settings.synchThreshold;
- ge("#synchThresholdBtn").innerText = '$text: ${secs}s';
+ getEl("#synchThresholdBtn").innerText = '$text: ${secs}s';
}
static function updateHotkeysBtn():Void {
final text = Lang.get("hotkeys");
final state = settings.hotkeysEnabled ? Lang.get("on") : Lang.get("off");
- ge("#hotkeysBtn").innerText = '$text: $state';
+ getEl("#hotkeysBtn").innerText = '$text: $state';
}
static function initChatInputs(main:Main):Void {
- final guestName:InputElement = cast ge("#guestname");
+ final guestName:InputElement = getEl("#guestname");
guestName.onkeydown = e -> {
if (e.keyCode == KeyCode.Return) {
main.guestLogin(guestName.value);
@@ -487,7 +461,7 @@ class Buttons {
}
}
- final guestPass:InputElement = cast ge("#guestpass");
+ final guestPass:InputElement = getEl("#guestpass");
guestPass.onkeydown = e -> {
if (e.keyCode == KeyCode.Return) {
main.userLogin(guestName.value, guestPass.value);
@@ -496,14 +470,14 @@ class Buttons {
}
}
- final chatline:InputElement = cast ge("#chatline");
+ final chatline:InputElement = getEl("#chatline");
chatline.onfocus = e -> {
if (Utils.isIOS()) {
// final startY = window.scrollY;
final startY = 0;
Timer.delay(() -> {
window.scrollBy(0, -(window.scrollY - startY));
- ge("#video").scrollTop = 0;
+ getEl("#video").scrollTop = 0;
main.scrollChatToEnd();
}, 100);
} else if (Utils.isTouch()) {
@@ -528,8 +502,8 @@ class Buttons {
return true;
});
final checkboxes:Array<InputElement> = [
- ge("#add-temp"),
- ge("#cache-on-server"),
+ getEl("#add-temp"),
+ getEl("#cache-on-server"),
];
for (checkbox in checkboxes) {
checkbox.addEventListener("change", () -> {
@@ -545,10 +519,10 @@ class Buttons {
public static function onViewportResize():Void {
final viewport = getVisualViewport() ?? return;
final isPortrait = window.innerHeight > window.innerWidth;
- final playerH = ge("#ytapiplayer").offsetHeight;
+ final playerH = getEl("#ytapiplayer").offsetHeight;
var h = viewport.height - playerH;
if (!isPortrait) h = viewport.height;
- ge("#chat").style.height = '${h}px';
+ getEl("#chat").style.height = '${h}px';
}
static inline function getVisualViewport():Null<VisualViewport> {
diff --git a/src/client/Main.hx b/src/client/Main.hx
index 61b3c3a..84f6838 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -38,7 +38,7 @@ class Main {
public var isPlaylistOpen(default, null) = true;
public var playersCacheSupport(default, null):Array<PlayerType> = [];
public var showingServerPause(default, null) = false;
- /** How much time passed from last GetTime **/
+ /** How much time passed since lastState.time update **/
public var timeFromLastState(default, null) = 0.0;
public final lastState:GetTimeEvent = {
time: 0,
@@ -63,7 +63,7 @@ class Main {
var onTimeGet:Timer;
var onBlinkTab:Null<Timer>;
var gotFirstPageInteraction = false;
- var msgBuf = ge("#messagebuffer");
+ var msgBuf = getEl("#messagebuffer");
static function main():Void {
instance = new Main();
@@ -189,14 +189,14 @@ class Main {
function initListeners():Void {
Buttons.init(this);
- final leaderBtn = ge("#leader_btn");
+ final leaderBtn = getEl("#leader_btn");
leaderBtn.onclick = toggleLeader;
leaderBtn.oncontextmenu = (e:MouseEvent) -> {
toggleLeaderAndPause();
e.preventDefault();
}
- final voteSkip = ge("#voteskip");
+ final voteSkip = getEl("#voteskip");
voteSkip.onclick = e -> {
if (Utils.isTouch() && !window.confirm(Lang.get("skipItemConfirm"))) return;
if (player.isListEmpty()) return;
@@ -210,29 +210,29 @@ class Main {
});
}
- ge("#queue_next").onclick = e -> addVideoUrl(false);
- ge("#queue_end").onclick = e -> addVideoUrl(true);
- new InputWithHistory(cast ge("#mediaurl"), settings.latestLinks, 10, value -> {
+ getEl("#queue_next").onclick = e -> addVideoUrl(false);
+ getEl("#queue_end").onclick = e -> addVideoUrl(true);
+ new InputWithHistory(getEl("#mediaurl"), settings.latestLinks, 10, value -> {
addVideoUrl(true);
return false;
});
- ge("#mediatitle").onkeydown = (e:KeyboardEvent) -> {
+ getEl("#mediatitle").onkeydown = (e:KeyboardEvent) -> {
if (e.keyCode == KeyCode.Return) addVideoUrl(true);
}
- new InputWithHistory(cast ge("#subsurl"), settings.latestSubs, 10, value -> {
+ new InputWithHistory(getEl("#subsurl"), settings.latestSubs, 10, value -> {
addVideoUrl(true);
return false;
});
- ge("#ce_queue_next").onclick = e -> addIframe(false);
- ge("#ce_queue_end").onclick = e -> addIframe(true);
- ge("#customembed-title").onkeydown = (e:KeyboardEvent) -> {
+ getEl("#ce_queue_next").onclick = e -> addIframe(false);
+ getEl("#ce_queue_end").onclick = e -> addIframe(true);
+ getEl("#customembed-title").onkeydown = (e:KeyboardEvent) -> {
if (e.keyCode == KeyCode.Return) {
addIframe(true);
e.preventDefault();
}
}
- ge("#customembed-content").onkeydown = ge("#customembed-title").onkeydown;
+ getEl("#customembed-content").onkeydown = getEl("#customembed-title").onkeydown;
}
public inline function isUser():Bool {
@@ -274,11 +274,11 @@ class Main {
}
function addVideoUrl(atEnd:Bool):Void {
- final mediaUrl:InputElement = cast ge("#mediaurl");
- final subsUrl:InputElement = cast ge("#subsurl");
- final checkboxTemp:InputElement = cast ge("#addfromurl .add-temp");
+ final mediaUrl:InputElement = getEl("#mediaurl");
+ final subsUrl:InputElement = getEl("#subsurl");
+ final checkboxTemp:InputElement = getEl("#addfromurl .add-temp");
final isTemp = checkboxTemp.checked;
- final checkboxCache:InputElement = cast ge("#cache-on-server");
+ final checkboxCache:InputElement = getEl("#cache-on-server");
final doCache = checkboxCache.checked
&& checkboxCache.parentElement.style.display != "none";
final url = mediaUrl.value;
@@ -364,14 +364,14 @@ class Main {
}
function addIframe(atEnd:Bool):Void {
- final iframeCode:InputElement = cast ge("#customembed-content");
+ final iframeCode:InputElement = getEl("#customembed-content");
final iframe = iframeCode.value;
if (iframe.length == 0) return;
iframeCode.value = "";
- final mediaTitle:InputElement = cast ge("#customembed-title");
+ final mediaTitle:InputElement = getEl("#customembed-title");
final title = mediaTitle.value;
mediaTitle.value = "";
- final checkbox:InputElement = cast ge("#customembed .add-temp");
+ final checkbox:InputElement = getEl("#customembed .add-temp");
final isTemp = checkbox.checked;
final obj:VideoDataRequest = {
url: iframe,
@@ -699,7 +699,7 @@ class Main {
} else {
onLogin(connected.clients, connected.clientName);
}
- final guestName:InputElement = cast ge("#guestname");
+ final guestName:InputElement = getEl("#guestname");
var name = settings.name;
if (name.length == 0) name = guestName.value;
final hash = settings.hash;
@@ -743,30 +743,30 @@ class Main {
serverMessage(text, false, false);
- ge("#addVideosHintButton").onclick = e -> {
- final addBtn = ge("#showmediaurl");
+ getEl("#addVideosHintButton").onclick = e -> {
+ final addBtn = getEl("#showmediaurl");
addBtn.scrollIntoView();
Timer.delay(() -> {
- if (!ge("#addfromurl").classList.contains("collapse")) {
- ge("#mediaurl").focus();
+ if (!getEl("#addfromurl").classList.contains("collapse")) {
+ getEl("#mediaurl").focus();
return;
}
addBtn.onclick();
}, 300);
}
- ge("#requestLeaderHintButton").onclick = (e:MouseEvent) -> {
+ getEl("#requestLeaderHintButton").onclick = (e:MouseEvent) -> {
window.scrollTo(0, 0);
if (Utils.isTouch()) blinkLeaderButton();
}
- ge("#requestLeaderHintButton").onpointerenter = e -> {
+ getEl("#requestLeaderHintButton").onpointerenter = e -> {
if (Utils.isTouch()) return;
- ge("#leader_btn").classList.add("hint");
+ getEl("#leader_btn").classList.add("hint");
}
- ge("#requestLeaderHintButton").onpointerleave = e -> {
- ge("#leader_btn").classList.remove("hint");
+ getEl("#requestLeaderHintButton").onpointerleave = e -> {
+ getEl("#leader_btn").classList.remove("hint");
}
if (Utils.isAndroid()) {
- ge("#openInApp").onclick = e -> {
+ getEl("#openInApp").onclick = e -> {
var isRedirected = false;
window.addEventListener("blur", e -> isRedirected = true, {once: true});
window.setTimeout(function() {
@@ -777,26 +777,26 @@ class Main {
return false;
}
}
- ge("#hideHintList").onclick = e -> {
- ge("#hideHintList").parentElement.remove();
+ getEl("#hideHintList").onclick = e -> {
+ getEl("#hideHintList").parentElement.remove();
settings.showHintList = false;
Settings.write(settings);
}
}
public function blinkLeaderButton():Void {
- ge("#leader_btn").classList.add("hint");
- Timer.delay(() -> ge("#leader_btn").classList.remove("hint"), 500);
+ getEl("#leader_btn").classList.add("hint");
+ Timer.delay(() -> getEl("#leader_btn").classList.remove("hint"), 500);
}
function onUserGroupChanged():Void {
- final button:ButtonElement = cast ge("#queue_next");
+ final button:ButtonElement = getEl("#queue_next");
if (personal.hasPermission(ChangeOrderPerm, config.permissions)) {
button.disabled = false;
} else {
button.disabled = true;
}
- final adminMenu = ge("#adminMenu");
+ final adminMenu = getEl("#adminMenu");
if (isAdmin()) adminMenu.style.display = "";
else adminMenu.style.display = "none";
}
@@ -840,9 +840,9 @@ class Main {
config.unpauseWithoutLeader = false;
}
pageTitle = config.channelName;
- final login:InputElement = cast ge("#guestname");
+ final login:InputElement = getEl("#guestname");
login.maxLength = config.maxLoginLength;
- final form:InputElement = cast ge("#chatline");
+ final form:InputElement = getEl("#chatline");
form.maxLength = config.maxMessageLength;
filters.resize(0);
@@ -860,14 +860,14 @@ class Main {
replace: '$1<$tag class="channel-emote" src="${emote.image}" title="${emote.name}"/>'
});
}
- ge("#smilesbtn").classList.remove("active");
- final smilesWrap = ge("#smiles-wrap");
+ getEl("#smilesbtn").classList.remove("active");
+ final smilesWrap = getEl("#smiles-wrap");
smilesWrap.style.display = "none";
- final smilesList = ge("#smiles-list");
+ final smilesList = getEl("#smiles-list");
smilesList.onclick = (e:MouseEvent) -> {
final el:Element = cast e.target;
if (el == smilesList) return;
- final form:InputElement = cast ge("#chatline");
+ final form:InputElement = getEl("#chatline");
form.value += ' ${el.title}';
form.focus();
}
@@ -892,25 +892,25 @@ class Main {
}
function showGuestLoginPanel():Void {
- ge("#guestlogin").style.display = "";
- ge("#guestpassword").style.display = "none";
- ge("#chatbox").style.display = "none";
- ge("#exitBtn").textContent = Lang.get("login");
+ getEl("#guestlogin").style.display = "";
+ getEl("#guestpassword").style.display = "none";
+ getEl("#chatbox").style.display = "none";
+ getEl("#exitBtn").textContent = Lang.get("login");
}
function hideGuestLoginPanel():Void {
- ge("#guestlogin").style.display = "none";
- ge("#guestpassword").style.display = "none";
- ge("#chatbox").style.display = "";
- ge("#exitBtn").textContent = Lang.get("exit");
+ getEl("#guestlogin").style.display = "none";
+ getEl("#guestpassword").style.display = "none";
+ getEl("#chatbox").style.display = "";
+ getEl("#exitBtn").textContent = Lang.get("exit");
}
function showGuestPasswordPanel():Void {
- ge("#guestlogin").style.display = "none";
- ge("#chatbox").style.display = "none";
- ge("#guestpassword").style.display = "";
- (cast ge("#guestpass") : InputElement).type = "password";
- ge("#guestpass_icon").setAttribute("name", "eye");
+ getEl("#guestlogin").style.display = "none";
+ getEl("#chatbox").style.display = "none";
+ getEl("#guestpassword").style.display = "";
+ (getEl("#guestpass") : InputElement).type = "password";
+ getEl("#guestpass_icon").setAttribute("name", "eye");
}
function updateClients(newClients:Array<ClientData>):Void {
@@ -982,7 +982,7 @@ class Main {
}
function updateUserList():Void {
- final userCount = ge("#usercount");
+ final userCount = getEl("#usercount");
userCount.textContent = clients.length + " " + Lang.get("online");
document.title = getPageTitle();
@@ -995,7 +995,7 @@ class Main {
if (client.isAdmin) klass += " userlist_owner";
list.add('<span class="$klass">${client.name}</span></div>');
}
- final userlist = ge("#userlist");
+ final userlist = getEl("#userlist");
userlist.innerHTML = list.toString();
}
@@ -1076,13 +1076,13 @@ class Main {
}
public function showScrollToChatEndBtn():Void {
- final btn = ge("#scroll-to-chat-end");
+ final btn = getEl("#scroll-to-chat-end");
btn.style.display = "";
Timer.delay(() -> btn.style.opacity = "1", 0);
}
public function hideScrollToChatEndBtn():Void {
- final btn = ge("#scroll-to-chat-end");
+ final btn = getEl("#scroll-to-chat-end");
if (btn.style.opacity == "0") return;
btn.style.opacity = "0";
btn.addEventListener("transitionend", e -> {
@@ -1091,7 +1091,7 @@ class Main {
}
public function showProgressInfo(text:String):Void {
- final chin = ge("#dynamic-chin");
+ final chin = getEl("#dynamic-chin");
var div = chin.querySelector("#progress-info");
if (div == null) {
div = document.createDivElement();
@@ -1105,7 +1105,7 @@ class Main {
public function showServerUnpause():Void {
if (showingServerPause) return;
showingServerPause = true;
- final chin = ge("#dynamic-chin");
+ final chin = getEl("#dynamic-chin");
chin.innerHTML = "";
final div = document.createDivElement();
@@ -1131,7 +1131,7 @@ class Main {
}
function showDynamicChin():Void {
- final chin = ge("#dynamic-chin");
+ final chin = getEl("#dynamic-chin");
if (chin.style.display == "") return;
chin.style.display = "";
chin.style.transition = "none";
@@ -1153,7 +1153,7 @@ class Main {
public function hideDynamicChin():Void {
showingServerPause = false;
- final chin = ge("#dynamic-chin");
+ final chin = getEl("#dynamic-chin");
final h = chin.clientHeight;
chin.style.height = '${h}px';
Timer.delay(() -> {
@@ -1171,7 +1171,7 @@ class Main {
function onChatImageLoaded(e:Event):Void {
scrollChatToEnd();
(cast e.target : Element).onload = null;
- final btn = ge("#scroll-to-chat-end");
+ final btn = getEl("#scroll-to-chat-end");
btn.style.opacity = "0";
btn.style.display = "none";
}
@@ -1337,13 +1337,13 @@ class Main {
}
function setLeaderButton(flag:Bool):Void {
- final leaderBtn = ge("#leader_btn");
+ final leaderBtn = getEl("#leader_btn");
leaderBtn.classList.toggle("success-bg", flag);
}
function setPlaylistLock(isOpen:Bool):Void {
isPlaylistOpen = isOpen;
- final lockPlaylist = ge("#lockplaylist");
+ final lockPlaylist = getEl("#lockplaylist");
final icon = lockPlaylist.firstElementChild;
if (isOpen) {
lockPlaylist.title = Lang.get("playlistOpen");
@@ -1439,7 +1439,7 @@ class Main {
}
@:generic
- public static inline function ge<T:Element>(id:String):T {
+ public static inline function getEl<T:Element>(id:String):T {
return cast document.querySelector(id);
}
}
diff --git a/src/client/Player.hx b/src/client/Player.hx
index 92f34e9..e5ea87c 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -4,7 +4,7 @@ import Types.PlayerType;
import Types.VideoData;
import Types.VideoDataRequest;
import Types.VideoItem;
-import client.Main.ge;
+import client.Main.getEl;
import client.players.Iframe;
import client.players.Raw;
import client.players.Streamable;
@@ -24,15 +24,15 @@ class Player {
final iframePlayer:IPlayer;
final rawPlayer:IPlayer;
final videoList = new VideoList();
- final videoItemsEl = ge("#queue");
- final playerEl = ge("#ytapiplayer");
+ final videoItemsEl = getEl("#queue");
+ final playerEl = getEl("#ytapiplayer");
var player:Null<IPlayer>;
var isLoaded = false;
var skipSetTime = false;
var skipSetRate = false;
var streamable:Streamable;
- final voiceOverInput:InputElement = cast ge("#voiceoverurl");
+ final voiceOverInput:InputElement = getEl("#voiceoverurl");
var audioTrack:Null<Audio>;
var isAudioTrackLoaded = false;
var needsVolumeReset = false;
@@ -76,7 +76,7 @@ class Player {
}
function initItemButtons():Void {
- final queue = ge("#queue");
+ final queue = getEl("#queue");
queue.onclick = e -> {
final btn:Element = cast e.target;
final item = btn.parentElement.parentElement;
@@ -192,7 +192,7 @@ class Player {
onCanBePlayed();
}
JsApi.fireVideoChangeEvents(item);
- ge("#currenttitle").textContent = item.title;
+ getEl("#currenttitle").textContent = item.title;
}
function setExternalAudioTrack(item:VideoItem):Void {
@@ -248,17 +248,17 @@ class Player {
public function removeVideo():Void {
JsApi.fireVideoRemoveEvents(videoList.currentItem);
player.removeVideo();
- ge("#currenttitle").textContent = Lang.get("nothingPlaying");
+ getEl("#currenttitle").textContent = Lang.get("nothingPlaying");
setPauseIndicator(false);
}
public function setPauseIndicator(isPause:Bool):Void {
if (!main.isSyncActive) return;
final state = isPause ? "pause" : "play";
- final el = ge("#pause-indicator");
+ final el = getEl("#pause-indicator");
el.setAttribute("name", state);
- final el2 = ge("#pause-indicator-portrait");
+ final el2 = getEl("#pause-indicator-portrait");
el2.setAttribute("name", state);
var isVisible = isPause || main.hasLeader();
el2.style.display = isVisible ? "" : "none";
@@ -484,8 +484,8 @@ class Player {
}
function updateCounters():Void {
- ge("#plcount").textContent = '${videoList.length} ${Lang.get("videos")}';
- ge("#pllength").textContent = totalDuration();
+ getEl("#plcount").textContent = '${videoList.length} ${Lang.get("videos")}';
+ getEl("#pllength").textContent = totalDuration();
}
public function getItems():Array<VideoItem> {
@@ -625,8 +625,12 @@ class Player {
public function skipAd():Void {
final item = videoList.currentItem ?? return;
- if (!youtube.isSupportedLink(item.url)) return;
- final id = youtube.extractVideoId(item.url);
+ var itemUrl = item.url;
+ if (!youtube.isSupportedLink(itemUrl)) {
+ itemUrl = itemUrl.replace("/cache/", "youtu.be/");
+ if (!youtube.isSupportedLink(itemUrl)) return;
+ }
+ final id = youtube.extractVideoId(itemUrl);
final url = 'https://sponsor.ajay.app/api/skipSegments?videoID=$id';
final http = new Http(url);
http.onData = text -> {
diff --git a/src/client/Split.hx b/src/client/Split.hx
index 66055d0..102fbc6 100644
--- a/src/client/Split.hx
+++ b/src/client/Split.hx
@@ -1,7 +1,49 @@
package client;
+import client.Main.getEl;
+import js.Browser.document;
+
@:native("Split")
-extern class Split {
+private extern class JsSplit {
function new(options:Any):Void;
function destroy(?immediate:Bool = true):Void;
}
+
+class Split {
+ static inline var CHAT_MIN_SIZE = 200;
+
+ final settings:ClientSettings;
+ final split:JsSplit;
+
+ public function new(settings:ClientSettings) {
+ this.settings = settings;
+ split = new JsSplit({
+ columnGutters: [{
+ element: getEl(".gutter"),
+ track: 1,
+ }],
+ minSize: CHAT_MIN_SIZE,
+ snapOffset: 0,
+ onDragEnd: saveSize
+ });
+ }
+
+ public function setSize(chatSize:Float):Void {
+ if (chatSize < CHAT_MIN_SIZE) return;
+ final sizes = document.body.style.gridTemplateColumns.split(" ");
+ final chatId = settings.isSwapped ? 0 : sizes.length - 1;
+ sizes[chatId] = '${chatSize}px';
+ document.body.style.gridTemplateColumns = sizes.join(" ");
+ }
+
+ function saveSize():Void {
+ final sizes = document.body.style.gridTemplateColumns.split(" ");
+ if (settings.isSwapped) sizes.reverse();
+ settings.chatSize = Std.parseFloat(sizes[sizes.length - 1]);
+ Settings.write(settings);
+ }
+
+ public function destroy():Void {
+ split.destroy();
+ }
+}
diff --git a/src/client/players/Iframe.hx b/src/client/players/Iframe.hx
index 6614f9d..000bf8a 100644
--- a/src/client/players/Iframe.hx
+++ b/src/client/players/Iframe.hx
@@ -4,14 +4,14 @@ import Types.PlayerType;
import Types.VideoData;
import Types.VideoDataRequest;
import Types.VideoItem;
-import client.Main.ge;
+import client.Main.getEl;
import js.Browser.document;
import js.html.Element;
class Iframe implements IPlayer {
final main:Main;
final player:Player;
- final playerEl:Element = ge("#ytapiplayer");
+ final playerEl:Element = getEl("#ytapiplayer");
var video:Element;
public function new(main:Main, player:Player) {
diff --git a/src/client/players/Raw.hx b/src/client/players/Raw.hx
index 10a54e8..e3337ae 100644
--- a/src/client/players/Raw.hx
+++ b/src/client/players/Raw.hx
@@ -4,7 +4,7 @@ import Types.PlayerType;
import Types.VideoData;
import Types.VideoDataRequest;
import Types.VideoItem;
-import client.Main.ge;
+import client.Main.getEl;
import haxe.Timer;
import js.Browser.document;
import js.Browser;
@@ -17,9 +17,9 @@ import js.html.VideoElement;
class Raw implements IPlayer {
final main:Main;
final player:Player;
- final playerEl:Element = ge("#ytapiplayer");
- final titleInput:InputElement = cast ge("#mediatitle");
- final subsInput:InputElement = cast ge("#subsurl");
+ final playerEl:Element = getEl("#ytapiplayer");
+ final titleInput:InputElement = getEl("#mediatitle");
+ final subsInput:InputElement = getEl("#subsurl");
final matchName = ~/^(.+)\.(.+)/;
var controlsHider:Timer;
var playAllowed = true;
diff --git a/src/client/players/Vk.hx b/src/client/players/Vk.hx
index 599b5eb..99643de 100644
--- a/src/client/players/Vk.hx
+++ b/src/client/players/Vk.hx
@@ -4,7 +4,7 @@ import Types.PlayerType;
import Types.VideoData;
import Types.VideoDataRequest;
import Types.VideoItem;
-import client.Main.ge;
+import client.Main.getEl;
import haxe.Constraints.Function;
import js.Browser.document;
import js.html.Element;
@@ -41,7 +41,7 @@ private extern class VkPlayer {
class Vk implements IPlayer {
final main:Main;
final player:Player;
- final playerEl:Element = ge("#ytapiplayer");
+ final playerEl:Element = getEl("#ytapiplayer");
var video:Element;
var vkPlayer:VkPlayer;
var isLoaded = false;
diff --git a/src/client/players/Youtube.hx b/src/client/players/Youtube.hx
index f5fb88c..7c851fb 100644
--- a/src/client/players/Youtube.hx
+++ b/src/client/players/Youtube.hx
@@ -4,7 +4,7 @@ import Types.PlayerType;
import Types.VideoData;
import Types.VideoDataRequest;
import Types.VideoItem;
-import client.Main.ge;
+import client.Main.getEl;
import haxe.Http;
import haxe.Json;
import js.Browser.document;
@@ -20,7 +20,7 @@ class Youtube implements IPlayer {
final urlVideoId = "?part=snippet&fields=nextPageToken,items(snippet/resourceId/videoId)";
final main:Main;
final player:Player;
- final playerEl:Element = ge("#ytapiplayer");
+ final playerEl:Element = getEl("#ytapiplayer");
var apiKey:String;
var video:Element;
var youtube:YoutubePlayer;
diff --git a/src/server/Logger.hx b/src/server/Logger.hx
index bf06968..42d382d 100644
--- a/src/server/Logger.hx
+++ b/src/server/Logger.hx
@@ -40,20 +40,13 @@ class Logger {
Utils.ensureDir(folder);
removeOldestLog(folder);
final name = DateTools.format(Date.now(), "%Y-%m-%d_%H_%M_%S");
- File.saveContent('$folder/$name.json', Json.stringify(getLogs(), filterNulls, "\t"));
+ File.saveContent('$folder/$name.json', Main.jsonStringify(getLogs(), "\t"));
}
public function getLogs():Array<ServerEvent> {
return logs;
}
- public function filterNulls(key:Any, value:Any):Any {
- #if js
- if (value == null) return js.Lib.undefined;
- #end
- return value;
- }
-
function removeOldestLog(folder:String):Void {
final names = FileSystem.readDirectory(folder).filter(name -> {
if (FileSystem.isDirectory('$folder/$name')) return false;
diff --git a/src/server/Main.hx b/src/server/Main.hx
index 4f0fd03..d5c0800 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -940,7 +940,7 @@ class Main {
}),
logs: logger.getLogs()
}
- final json = Json.stringify(data, logger.filterNulls, "\t");
+ final json = jsonStringify(data, "\t");
send(client, {
type: Dump,
dump: {
@@ -984,23 +984,34 @@ class Main {
}
public function send(client:Client, data:WsEvent):Void {
- client.ws.send(Json.stringify(data), null);
+ client.ws.send(jsonStringify(data), null);
}
public function broadcast(data:WsEvent):Void {
- final json = Json.stringify(data);
+ final json = jsonStringify(data);
for (client in clients)
client.ws.send(json, null);
}
public function broadcastExcept(skipped:Client, data:WsEvent):Void {
- final json = Json.stringify(data);
+ final json = jsonStringify(data);
for (client in clients) {
if (client == skipped) continue;
client.ws.send(json, null);
}
}
+ public static function jsonStringify(data:Any, ?space:String):String {
+ return Json.stringify(data, jsonFilterNulls, space);
+ }
+
+ static function jsonFilterNulls(key:Any, value:Any):Any {
+ #if js
+ if (value == null) return js.Lib.undefined;
+ #end
+ return value;
+ }
+
function skipVideo(data:WsEvent):Void {
if (videoList.length == 0) return;
final item = videoList.currentItem;
diff --git a/user/README.md b/user/README.md
index 59ccb4b..3173f54 100644
--- a/user/README.md
+++ b/user/README.md
@@ -21,3 +21,4 @@ Server has input commands, for example, to set admin users. Simple enter anythin
- `users.json` - Admin names with password hashes and random channel-specific salt. Do not share this file!
- `crashes/` - Latest error logs, when the server crashes.
- `logs/` - Latest activity logs, saved when the server shuts down.
+- `cache/` - Folder with video cache and uploads. You can change `cacheStorageLimitGiB` in config to limit it.
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage