diff options
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/Main.hx | 62 | ||||
| -rw-r--r-- | src/client/Player.hx | 60 | ||||
| -rw-r--r-- | src/client/Utils.hx | 5 | ||||
| -rw-r--r-- | src/client/players/Raw.hx | 2 | ||||
| -rw-r--r-- | src/client/players/Vk.hx | 2 | ||||
| -rw-r--r-- | src/client/players/Youtube.hx | 6 |
6 files changed, 106 insertions, 31 deletions
diff --git a/src/client/Main.hx b/src/client/Main.hx index 62fa7de..6ec8727 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -40,6 +40,8 @@ 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 **/ + public var timeFromLastState(default, null) = 0.0; public final lastState:GetTimeEvent = { time: 0, rate: 1.0, @@ -47,6 +49,8 @@ class Main { pausedByServer: false }; + var lastStateTimeStamp = 0.0; + final clients:Array<Client> = []; var pageTitle = document.title; var config:Null<Config>; @@ -510,6 +514,10 @@ class Main { if (player.itemsLength() == 1) player.setVideo(0); case VideoLoaded: + lastState.paused = false; + lastState.pausedByServer = false; + lastState.time = 0; + updateLastStateTime(); player.setTime(0); player.play(); // try to sync leader after with GetTime events @@ -524,7 +532,9 @@ class Main { if (player.isListEmpty()) player.pause(); case Pause: + lastState.time = data.pause.time; lastState.paused = true; + updateLastStateTime(); player.setPauseIndicator(lastState.paused); updateUserList(); if (isLeader()) return; @@ -532,7 +542,9 @@ class Main { player.setTime(data.pause.time); case Play: + lastState.time = data.play.time; lastState.paused = false; + updateLastStateTime(); player.setPauseIndicator(lastState.paused); updateUserList(); if (isLeader()) return; @@ -554,6 +566,7 @@ class Main { lastState.paused = data.getTime.paused; lastState.pausedByServer = data.getTime.pausedByServer; lastState.rate = data.getTime.rate; + updateLastStateTime(); if (isPauseChanged) updateUserList(); @@ -592,6 +605,8 @@ class Main { else player.setTime(newTime); case SetTime: + lastState.time = data.setTime.time; + updateLastStateTime(); final synchThreshold = settings.synchThreshold; final newTime = data.setTime.time; final time = player.getTime(); @@ -603,6 +618,8 @@ class Main { player.setPlaybackRate(data.setRate.rate); case Rewind: + lastState.time = data.rewind.time; + updateLastStateTime(); player.setTime(data.rewind.time + 0.5); case Flashback: // server-only @@ -640,6 +657,14 @@ class Main { } } + function updateLastStateTime():Void { + if (lastStateTimeStamp == 0) { + lastStateTimeStamp = Timer.stamp(); + } + timeFromLastState = Timer.stamp() - lastStateTimeStamp; + lastStateTimeStamp = Timer.stamp(); + } + function onConnected(data:WsEvent):Void { final connected = data.connected; @@ -713,10 +738,7 @@ class Main { } ge("#requestLeaderHintButton").onclick = (e:MouseEvent) -> { window.scrollTo(0, 0); - if (Utils.isTouch()) { - ge("#leader_btn").classList.add("hint"); - Timer.delay(() -> ge("#leader_btn").classList.remove("hint"), 1000); - } + if (Utils.isTouch()) blinkLeaderButton(); } ge("#requestLeaderHintButton").onpointerenter = e -> { if (Utils.isTouch()) return; @@ -744,6 +766,11 @@ class Main { } } + public function blinkLeaderButton():Void { + ge("#leader_btn").classList.add("hint"); + Timer.delay(() -> ge("#leader_btn").classList.remove("hint"), 500); + } + function onUserGroupChanged():Void { final button:ButtonElement = cast ge("#queue_next"); if (personal.hasPermission(ChangeOrderPerm, config.permissions)) { @@ -790,7 +817,10 @@ class Main { function setConfig(config:Config):Void { this.config = config; - if (Utils.isTouch()) config.requestLeaderOnPause = false; + if (Utils.isTouch()) { + config.requestLeaderOnPause = false; + config.unpauseWithoutLeader = false; + } pageTitle = config.channelName; final login:InputElement = cast ge("#guestname"); login.maxLength = config.maxLoginLength; @@ -1063,14 +1093,7 @@ class Main { clientName: personal.name } }); - JsApi.once(SetLeader, event -> { - send({ - type: SetLeader, - setLeader: { - clientName: "" - } - }); - }); + JsApi.once(SetLeader, event -> removeLeader()); } chin.style.display = ""; @@ -1318,6 +1341,15 @@ class Main { }); } + public function removeLeader():Void { + send({ + type: SetLeader, + setLeader: { + clientName: "" + } + }); + } + public function toggleLeaderAndPause():Void { if (!isLeader()) { JsApi.once(SetLeader, event -> { @@ -1336,6 +1368,10 @@ class Main { return config.requestLeaderOnPause; } + public function hasUnpauseWithoutLeader():Bool { + return config.unpauseWithoutLeader; + } + public function getTemplateUrl():String { return config.templateUrl; } diff --git a/src/client/Player.hx b/src/client/Player.hx index 911092e..92f34e9 100644 --- a/src/client/Player.hx +++ b/src/client/Player.hx @@ -12,6 +12,7 @@ import client.players.Vk; import client.players.Youtube; import haxe.Http; import haxe.Json; +import haxe.Timer; import js.html.Audio; import js.html.Element; import js.html.InputElement; @@ -37,6 +38,9 @@ class Player { var needsVolumeReset = false; final voiceOverVolume = 0.3; + /** If player was clicked and pause/play event was not generated by browser events. **/ + public var inUserInteraction = false; + public function new(main:Main):Void { this.main = main; youtube = new Youtube(main, this); @@ -57,12 +61,18 @@ class Player { if (resizeObserver != null) { resizeObserver.observe(playerEl); } else { - final timer = new haxe.Timer(50); + final timer = new Timer(50); timer.run = () -> { if (isLoaded) return; Buttons.onViewportResize(); } } + + playerEl.addEventListener("click", e -> { + inUserInteraction = true; + // for some reason Chrome has ~300ms event delay + Timer.delay(() -> inUserInteraction = false, 350); + }, {}); } function initItemButtons():Void { @@ -249,7 +259,7 @@ class Player { el.setAttribute("name", state); final el2 = ge("#pause-indicator-portrait"); - el2.setAttribute("name", "pause"); + el2.setAttribute("name", state); var isVisible = isPause || main.hasLeader(); el2.style.display = isVisible ? "" : "none"; } @@ -262,10 +272,23 @@ class Player { public function onPlay():Void { audioTrack?.play(); + if (!isLoaded) return; + if (videoList.length == 0) return; + final hasAutoPause = main.hasLeaderOnPauseRequest(); if (!main.isLeader()) { - // paused and no leader - instant pause - if (main.lastState.paused) pause(); + // user click, so we can unpause by removing leader + // (doesn't work in Firefox because of no video click propagation) + final allowUnpause = (hasAutoPause && inUserInteraction); + if (allowUnpause || main.hasUnpauseWithoutLeader()) { + main.removeLeader(); + } else { + // paused and no leader - instant pause + if (main.lastState.paused) { + pause(); + main.blinkLeaderButton(); + } + } return; } main.send({ @@ -274,7 +297,6 @@ class Player { time: getTime() } }); - final hasAutoPause = main.hasLeaderOnPauseRequest() && videoList.length > 0; if (hasAutoPause) { // do not remove leader if user cannot request it back if (main.hasPermission(RequestLeaderPerm)) main.toggleLeader(); @@ -284,6 +306,7 @@ class Player { public function onPause():Void { audioTrack?.pause(); + if (!isLoaded) return; final item = videoList.currentItem ?? return; // do not send pause if video is ended if (getTime() >= item.duration - 0.01) return; @@ -311,7 +334,10 @@ class Player { } if (!main.isLeader()) { // no pause and no permission - instant play - if (!main.lastState.paused) play(); + if (!main.lastState.paused) { + play(); + main.blinkLeaderButton(); + } return; } // we are leader, so just send pause @@ -332,7 +358,21 @@ class Player { skipSetTime = false; return; } - if (!main.isLeader()) return; + if (videoList.length == 0) return; + if (!main.isLeader()) { + if (main.hasLeader() || main.lastState.pausedByServer) { + final off = isPaused() ? 0 : main.timeFromLastState; + final time = main.lastState.time; + final delta = Math.abs(time - getTime()); + setTime(time); + main.blinkLeaderButton(); + } else { + // we dont want to seek back here because + // after seek can happen pause that will give auto-leader, + // so seeking will work + } + return; + } main.send({ type: SetTime, setTime: { @@ -349,7 +389,11 @@ class Player { skipSetRate = false; return; } - if (!main.isLeader()) return; + if (videoList.length == 0) return; + if (!main.isLeader()) { + main.blinkLeaderButton(); + return; + } main.send({ type: SetRate, setRate: { diff --git a/src/client/Utils.hx b/src/client/Utils.hx index 717d64f..4d85697 100644 --- a/src/client/Utils.hx +++ b/src/client/Utils.hx @@ -54,11 +54,6 @@ class Utils { + Std.parseFloat(style.marginBottom)); } - public static function prepend(parent:Element, child:Element):Void { - if (parent.firstChild == null) parent.appendChild(child); - else parent.insertBefore(child, parent.firstChild); - } - public static function insertAtIndex(parent:Element, child:Element, i:Int) { if (i >= parent.children.length) parent.appendChild(child); else parent.insertBefore(child, parent.children[i]); diff --git a/src/client/players/Raw.hx b/src/client/players/Raw.hx index 01752e7..f054f14 100644 --- a/src/client/players/Raw.hx +++ b/src/client/players/Raw.hx @@ -76,7 +76,7 @@ class Raw implements IPlayer { subs: subs, }); } - Utils.prepend(playerEl, video); + playerEl.prepend(video); if (isHls) initHlsSource(video, url); } diff --git a/src/client/players/Vk.hx b/src/client/players/Vk.hx index fbcf60c..599b5eb 100644 --- a/src/client/players/Vk.hx +++ b/src/client/players/Vk.hx @@ -107,7 +107,7 @@ class Vk implements IPlayer { frameborder="0" allowfullscreen> </iframe>'.trim() ); - Utils.prepend(playerEl, tempVideo); + playerEl.prepend(tempVideo); final tempVkPlayer = createVkPlayer(tempVideo); tempVkPlayer.on("inited", () -> { callback({ diff --git a/src/client/players/Youtube.hx b/src/client/players/Youtube.hx index b30dfb3..f5fb88c 100644 --- a/src/client/players/Youtube.hx +++ b/src/client/players/Youtube.hx @@ -175,7 +175,7 @@ class Youtube implements IPlayer { } final video = document.createDivElement(); video.id = "temp-videoplayer"; - Utils.prepend(playerEl, video); + playerEl.prepend(video); var tempYoutube:YoutubePlayer = null; tempYoutube = new YoutubePlayer(video.id, { videoId: extractVideoId(url), @@ -232,12 +232,12 @@ class Youtube implements IPlayer { onReady: e -> { if (!main.isAutoplayAllowed()) e.target.mute(); isLoaded = true; - youtube.pauseVideo(); + if (main.lastState.paused) youtube.pauseVideo(); + player.onCanBePlayed(); }, onStateChange: e -> { switch (e.data) { case UNSTARTED: - player.onCanBePlayed(); case ENDED: case PLAYING: player.onPlay(); |
