aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Types.hx1
-rw-r--r--src/client/Main.hx62
-rw-r--r--src/client/Player.hx60
-rw-r--r--src/client/Utils.hx5
-rw-r--r--src/client/players/Raw.hx2
-rw-r--r--src/client/players/Vk.hx2
-rw-r--r--src/client/players/Youtube.hx6
7 files changed, 107 insertions, 31 deletions
diff --git a/src/Types.hx b/src/Types.hx
index fdb3ba9..ec09b5c 100644
--- a/src/Types.hx
+++ b/src/Types.hx
@@ -32,6 +32,7 @@ typedef Config = {
totalVideoLimit:Int,
userVideoLimit:Int,
requestLeaderOnPause:Bool,
+ unpauseWithoutLeader:Bool,
localAdmins:Bool,
allowProxyIps:Bool,
localNetworkOnly:Bool,
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();
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage