diff options
| -rw-r--r-- | build/server.js | 27 | ||||
| -rw-r--r-- | res/client.js | 74 | ||||
| -rw-r--r-- | res/css/des.css | 6 | ||||
| -rwxr-xr-x | res/img/favicon.png | bin | 171 -> 0 bytes | |||
| -rw-r--r-- | res/img/favicon.svg | 17 | ||||
| -rw-r--r-- | res/index.html | 5 | ||||
| -rw-r--r-- | res/manifest.json | 12 | ||||
| -rw-r--r-- | src/client/Buttons.hx | 180 | ||||
| -rw-r--r-- | src/client/Main.hx | 136 | ||||
| -rw-r--r-- | src/client/Player.hx | 30 | ||||
| -rw-r--r-- | src/client/Split.hx | 44 | ||||
| -rw-r--r-- | src/client/players/Iframe.hx | 4 | ||||
| -rw-r--r-- | src/client/players/Raw.hx | 8 | ||||
| -rw-r--r-- | src/client/players/Vk.hx | 4 | ||||
| -rw-r--r-- | src/client/players/Youtube.hx | 4 | ||||
| -rw-r--r-- | src/server/Logger.hx | 9 | ||||
| -rw-r--r-- | src/server/Main.hx | 19 | ||||
| -rw-r--r-- | user/README.md | 1 |
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 Binary files differdeleted file mode 100755 index 5d46d1c..0000000 --- a/res/img/favicon.png +++ /dev/null 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. |
