From f84fdc40ba817b6a2d907484b1e1500197ceeafe Mon Sep 17 00:00:00 2001 From: RblSb Date: Sun, 12 Jan 2025 19:35:56 +0300 Subject: External audiotrack support This works as voice over if video also has audio, changing video volume to 0.3. Also improve autoplay by playing videos muted and unmute on first page click. There is no mute if you use Firefox and allow autoplay on page (navigator.getAutoplayPolicy check). --- src/client/Buttons.hx | 1 + src/client/IPlayer.hx | 4 ++ src/client/Main.hx | 29 +++++++++- src/client/Player.hx | 124 ++++++++++++++++++++++++++++++++++++++++-- src/client/players/Iframe.hx | 15 ++++- src/client/players/Raw.hx | 19 ++++++- src/client/players/Youtube.hx | 26 ++++++++- 7 files changed, 205 insertions(+), 13 deletions(-) (limited to 'src/client') diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx index de3aa27..c3581e2 100644 --- a/src/client/Buttons.hx +++ b/src/client/Buttons.hx @@ -218,6 +218,7 @@ class Buttons { && main.isSingleVideoLink(value); ge("#mediatitleblock").style.display = isRawSingleVideo ? "" : "none"; ge("#subsurlblock").style.display = isRawSingleVideo ? "" : "none"; + ge("#voiceoverblock").style.display = value.length > 0 ? "" : "none"; final panel = ge("#addfromurl"); final oldH = panel.style.height; // save for animation panel.style.height = ""; // to calculate height from content diff --git a/src/client/IPlayer.hx b/src/client/IPlayer.hx index 903902e..032ac97 100644 --- a/src/client/IPlayer.hx +++ b/src/client/IPlayer.hx @@ -12,8 +12,12 @@ interface IPlayer { function isVideoLoaded():Bool; function play():Void; function pause():Void; + function isPaused():Bool; function getTime():Float; function setTime(time:Float):Void; function getPlaybackRate():Float; function setPlaybackRate(rate:Float):Void; + function getVolume():Float; + function setVolume(volume:Float):Void; + function unmute():Void; } diff --git a/src/client/Main.hx b/src/client/Main.hx index 2850c75..248e205 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -48,6 +48,7 @@ class Main { final player:Player; var onTimeGet:Timer; var onBlinkTab:Null; + var gotFirstPageInteraction = false; static function main():Void { new Main(); @@ -94,6 +95,17 @@ class Main { openWebSocket(); }); JsApi.init(this, player); + + document.addEventListener("click", onFirstInteraction); + } + + function onFirstInteraction():Void { + if (gotFirstPageInteraction) return; + if (!player.isVideoLoaded()) return; + gotFirstPageInteraction = true; + player.unmute(); + player.play(); + document.removeEventListener("click", onFirstInteraction); } function settingsPatcher(data:Any, version:Int):Any { @@ -319,6 +331,7 @@ class Main { duration: data.duration, isTemp: isTemp, subs: data.subs, + voiceOverTrack: data.voiceOverTrack, isIframe: data.isIframe == true }, atEnd: atEnd @@ -526,8 +539,11 @@ class Main { } if (player.isVideoLoaded()) forceSyncNextTick = false; if (player.getDuration() <= player.getTime() + synchThreshold) return; - if (!data.getTime.paused) player.play(); - else player.pause(); + if (player.isPaused()) { + if (!data.getTime.paused) player.play(); + } else { + if (data.getTime.paused) player.pause(); + } player.setPauseIndicator(!data.getTime.paused); if (Math.abs(time - newTime) < synchThreshold) return; // +0.5s for buffering @@ -1199,6 +1215,15 @@ class Main { return config.youtubePlaylistLimit; } + public function isAutoplayAllowed():Bool { + final navigator:{ + getAutoplayPolicy:(type:String) -> Bool + } = cast Browser.navigator; + if (navigator.getAutoplayPolicy != null) return + navigator.getAutoplayPolicy("mediaelement"); + return gotFirstPageInteraction; + } + public function isVerbose():Bool { return config.isVerbose; } diff --git a/src/client/Player.hx b/src/client/Player.hx index bc64053..f40c34c 100644 --- a/src/client/Player.hx +++ b/src/client/Player.hx @@ -10,7 +10,9 @@ import client.players.Streamable; import client.players.Youtube; import haxe.Http; import haxe.Json; +import js.html.Audio; import js.html.Element; +import js.html.InputElement; class Player { final main:Main; @@ -25,13 +27,21 @@ class Player { var isLoaded = false; var skipSetTime = false; var skipSetRate = false; + var streamable:Streamable; + + final voiceOverInput:InputElement = cast ge("#voiceoverurl"); + var audioTrack:Null