diff options
| author | RblSb <msrblsb@gmail.com> | 2020-05-12 04:05:12 +0300 |
|---|---|---|
| committer | RblSb <msrblsb@gmail.com> | 2020-05-12 04:05:12 +0300 |
| commit | 9b960a699739c17179cdb1d5c9c9cb3efa912ebe (patch) | |
| tree | bd8c58a6e282f138dcbf5fd4c0ae4e1968ffb9f6 | |
| parent | ddffbe0267cf7b69b5d49825daa4b4bfa9096e6e (diff) | |
m3u8 support
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | build-client.hxml | 1 | ||||
| -rw-r--r-- | build-server.hxml | 1 | ||||
| -rw-r--r-- | res/client.js | 53 | ||||
| -rw-r--r-- | src/client/JsApi.hx | 2 | ||||
| -rw-r--r-- | src/client/players/Raw.hx | 32 |
6 files changed, 85 insertions, 8 deletions
@@ -15,8 +15,8 @@ Default channel example: http://synctube-example.herokuapp.com/ - Reworked Modern theme
### Supported players
-- Youtube (videos and playlists)
-- Raw mp4 videos (or any other media format supported in browser)
+- Youtube (videos, streams and playlists)
+- Raw mp4 videos and m3u8 playlists (or any other media format supported in browser)
- Iframes (without sync)
### Setup
diff --git a/build-client.hxml b/build-client.hxml index 40a0df1..7d2e943 100644 --- a/build-client.hxml +++ b/build-client.hxml @@ -1,4 +1,5 @@ -lib youtubeIFramePlayer:git:https://github.com/okawa-h/youtubeIFramePlayer-externs.git
+-lib hls.js-extern:git:https://github.com/grosmar/hls.js-haxe-extern.git
-cp src
--main client.Main
-D analyzer-optimize
diff --git a/build-server.hxml b/build-server.hxml index 1cd9607..abca82d 100644 --- a/build-server.hxml +++ b/build-server.hxml @@ -3,6 +3,7 @@ -lib json2object
# Client libs for completion
-lib youtubeIFramePlayer:git:https://github.com/okawa-h/youtubeIFramePlayer-externs.git
+-lib hls.js-extern:git:https://github.com/grosmar/hls.js-haxe-extern.git
-lib utest
-cp src
--main server.Main
diff --git a/res/client.js b/res/client.js index 6d00cff..2093e37 100644 --- a/res/client.js +++ b/res/client.js @@ -2281,7 +2281,9 @@ client_players_Iframe.prototype = { } }; var client_players_Raw = function(main,player) { + this.isHlsLoaded = false; this.playAllowed = true; + this.matchName = new EReg("^(.+)\\.(.+)",""); this.playerEl = window.document.querySelector("#ytapiplayer"); this.main = main; this.player = player; @@ -2293,13 +2295,21 @@ client_players_Raw.prototype = { } ,getVideoData: function(url,callback) { var _gthis = this; - var title = HxOverrides.substr(url,url.lastIndexOf("/") + 1,null); - var matchName = new EReg("^(.+)\\.",""); - if(matchName.match(title)) { - title = matchName.matched(1); + var pos = url.lastIndexOf("/") + 1; + var title = HxOverrides.substr(url,pos,null); + if(this.matchName.match(title)) { + title = this.matchName.matched(1); } else { title = Lang.get("rawVideo"); } + var isHls = this.matchName.matched(2).indexOf("m3u8") != -1; + if(isHls && !this.isHlsLoaded) { + this.loadHlsPlugin(function() { + _gthis.getVideoData(url,callback); + return; + }); + return; + } var video = window.document.createElement("video"); video.src = url; video.onerror = function(e) { @@ -2317,12 +2327,42 @@ client_players_Raw.prototype = { return; }; client_Utils.prepend(this.playerEl,video); + if(isHls) { + this.initHlsSource(video,url); + } + } + ,loadHlsPlugin: function(callback) { + var _gthis = this; + client_JsApi.addScriptToHead("https://cdn.jsdelivr.net/npm/hls.js@latest",function() { + _gthis.isHlsLoaded = true; + callback(); + return; + }); + } + ,initHlsSource: function(video,url) { + if(!Hls.isSupported()) { + return; + } + var hls = new Hls(); + hls.loadSource(url); + hls.attachMedia(video); } ,loadVideo: function(item) { var _gthis = this; var url = this.main.tryLocalIp(item.url); + var isHls = item.url.indexOf("m3u8") != -1; + if(isHls && !this.isHlsLoaded) { + this.loadHlsPlugin(function() { + _gthis.loadVideo(item); + return; + }); + return; + } if(this.video != null) { this.video.src = url; + if(isHls) { + this.initHlsSource(this.video,url); + } this.restartControlsHider(); return; } @@ -2340,6 +2380,9 @@ client_players_Raw.prototype = { this.video.onpause = ($_=this.player,$bind($_,$_.onPause)); this.video.onratechange = ($_=this.player,$bind($_,$_.onRateChange)); this.playerEl.appendChild(this.video); + if(isHls) { + this.initHlsSource(this.video,url); + } } ,restartControlsHider: function() { var _gthis = this; @@ -3266,6 +3309,8 @@ js_Browser.createXMLHttpRequest = function() { } throw new js__$Boot_HaxeError("Unable to create XMLHttpRequest object."); }; +var js_hlsjs_HlsConfig = function() { }; +js_hlsjs_HlsConfig.__name__ = true; var js_youtube_Youtube = function() { }; js_youtube_Youtube.__name__ = true; js_youtube_Youtube.init = function(onAPIReady) { diff --git a/src/client/JsApi.hx b/src/client/JsApi.hx index 8a6315c..071dbee 100644 --- a/src/client/JsApi.hx +++ b/src/client/JsApi.hx @@ -36,7 +36,7 @@ class JsApi { } @:expose - static function addScriptToHead(url:String, ?onLoaded:()->Void):Void { + public static function addScriptToHead(url:String, ?onLoaded:()->Void):Void { var script = document.createScriptElement(); script.type = "text/javascript"; script.onload = onLoaded; diff --git a/src/client/players/Raw.hx b/src/client/players/Raw.hx index 266f0a2..d8aa57b 100644 --- a/src/client/players/Raw.hx +++ b/src/client/players/Raw.hx @@ -1,5 +1,6 @@ package client.players; +import js.hlsjs.Hls; import haxe.Timer; import js.html.Element; import js.html.VideoElement; @@ -7,15 +8,18 @@ import js.Browser.document; import client.Main.ge; import Types.VideoData; import Types.VideoItem; +using StringTools; class Raw implements IPlayer { final main:Main; final player:Player; final playerEl:Element = ge("#ytapiplayer"); + final matchName = ~/^(.+)\.(.+)/; var controlsHider:Timer; var playAllowed = true; var video:VideoElement; + var isHlsLoaded = false; public function new(main:Main, player:Player) { this.main = main; @@ -28,9 +32,13 @@ class Raw implements IPlayer { public function getVideoData(url:String, callback:(data:VideoData)->Void):Void { var title = url.substr(url.lastIndexOf("/") + 1); - final matchName = ~/^(.+)\./; if (matchName.match(title)) title = matchName.matched(1); else title = Lang.get("rawVideo"); + final isHls = matchName.matched(2).contains("m3u8"); + if (isHls && !isHlsLoaded) { + loadHlsPlugin(() -> getVideoData(url, callback)); + return; + } final video = document.createVideoElement(); video.src = url; @@ -46,12 +54,33 @@ class Raw implements IPlayer { }); } Utils.prepend(playerEl, video); + if (isHls) initHlsSource(video, url); + } + + function loadHlsPlugin(callback:()->Void):Void { + JsApi.addScriptToHead("https://cdn.jsdelivr.net/npm/hls.js@latest", () -> { + isHlsLoaded = true; + callback(); + }); + } + + function initHlsSource(video:VideoElement, url:String):Void { + if (!Hls.isSupported()) return; + final hls = new Hls(); + hls.loadSource(url); + hls.attachMedia(video); } public function loadVideo(item:VideoItem):Void { final url = main.tryLocalIp(item.url); + final isHls = item.url.contains("m3u8"); + if (isHls && !isHlsLoaded) { + loadHlsPlugin(() -> loadVideo(item)); + return; + } if (video != null) { video.src = url; + if (isHls) initHlsSource(video, url); restartControlsHider(); return; } @@ -68,6 +97,7 @@ class Raw implements IPlayer { video.onpause = player.onPause; video.onratechange = player.onRateChange; playerEl.appendChild(video); + if (isHls) initHlsSource(video, url); } function restartControlsHider():Void { |
