From 304bd1e77a5442f614aacd2b35f330777118b2cd Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Mon, 19 Oct 2026 15:32:53 -0700 Subject: merge with upstream https://github.com/RblSb/SyncTube --- build/server.js | 1116 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 868 insertions(+), 248 deletions(-) (limited to 'build/server.js') diff --git a/build/server.js b/build/server.js index 5f68d3c..5b8b03f 100644 --- a/build/server.js +++ b/build/server.js @@ -1,4 +1,4 @@ -// Generated by Haxe 4.3.7 +// Generated by Haxe 4.3.3 (function ($global) { "use strict"; var $estr = function() { return js_Boot.__string_rec(this,''); },$hxEnums = $hxEnums || {},$_; function $extend(from, fields) { @@ -71,11 +71,18 @@ ClientTools.setLeader = function(clients,name) { } }; ClientTools.hasLeader = function(clients) { + return ClientTools.getLeader(clients) != null; +}; +ClientTools.getLeader = function(clients) { var _g = 0; - while(_g < clients.length) if((clients[_g++].group & 4) != 0) { - return true; + while(_g < clients.length) { + var client = clients[_g]; + ++_g; + if((client.group & 4) != 0) { + return client; + } } - return false; + return null; }; ClientTools.getByName = function(clients,name,def) { var _g = 0; @@ -759,7 +766,7 @@ JsonParser_$237c3c85cf00342dfb7f01e6a6157d10.prototype = $extend(json2object_rea this.value = null; } ,loadJsonString: function(s,pos,variable) { - this.value = this.loadString(s,pos,variable,["RawType","YoutubeType","VkType","IframeType"],"RawType"); + this.value = this.loadString(s,pos,variable,["RawType","YoutubeType","VkType","VimeoType","IframeType"],"RawType"); } ,__class__: JsonParser_$237c3c85cf00342dfb7f01e6a6157d10 }); @@ -1060,7 +1067,7 @@ JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.__name__ = true; JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.__super__ = json2object_reader_BaseParser; JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.prototype = $extend(json2object_reader_BaseParser.prototype,{ onIncorrectType: function(pos,variable) { - this.errors.push(json2object_Error.IncorrectType(variable,"{ ?updatePlaylist : Null<{ videoList : Array }>, ?updateClients : Null<{ clients : Array }>, type : WsEventType, ?togglePlaylistLock : Null<{ isOpen : Bool }>, ?toggleItemType : Null<{ pos : Int }>, ?skipVideo : Null<{ url : String }>, ?setTime : Null<{ time : Float }>, ?setRate : Null<{ rate : Float }>, ?setNextItem : Null<{ pos : Int }>, ?setLeader : Null<{ clientName : String }>, ?serverMessage : Null<{ textId : String }>, ?rewind : Null<{ time : Float }>, ?removeVideo : Null<{ url : String }>, ?progress : Null<{ type : ProgressType, ratio : Float, ?data : Null }>, ?playItem : Null<{ pos : Int }>, ?play : Null<{ time : Float }>, ?pause : Null<{ time : Float }>, ?message : Null<{ text : String, clientName : String }>, ?logout : Null<{ oldClientName : String, clients : Array, clientName : String }>, ?login : Null<{ ?passHash : Null, ?isUnknownClient : Null, ?clients : Null>, clientName : String }>, ?kickClient : Null<{ name : String }>, ?getTime : Null, ?dump : Null<{ data : String }>, ?connected : Null<{ videoList : Array, uuid : String, playersCacheSupport : Array, itemPos : Int, isUnknownClient : Bool, isPlaylistOpen : Bool, history : Array, globalIp : String, config : Config, clients : Array, clientName : String }>, ?banClient : Null<{ time : Float, name : String }>, ?addVideo : Null<{ item : VideoItem, atEnd : Bool }> }",pos)); + this.errors.push(json2object_Error.IncorrectType(variable,"{ ?updatePlaylist : Null<{ videoList : Array }>, ?updateClients : Null<{ clients : Array }>, type : WsEventType, ?togglePlaylistLock : Null<{ isOpen : Bool }>, ?toggleItemType : Null<{ pos : Int, ?all : Null }>, ?skipVideoStats : Null<{ voted : Int, total : Int, required : Int }>, ?skipVideo : Null<{ url : String }>, ?setTime : Null<{ time : Float }>, ?setRate : Null<{ rate : Float }>, ?setNextItem : Null<{ pos : Int }>, ?setLeader : Null<{ clientName : String }>, ?serverMessage : Null<{ textId : String }>, ?rewind : Null<{ time : Float }>, ?removeVideo : Null<{ url : String }>, ?progress : Null<{ ?url : Null, type : ProgressType, ratio : Float, ?data : Null }>, ?playItem : Null<{ pos : Int }>, ?play : Null<{ time : Float }>, ?pause : Null<{ time : Float }>, ?message : Null<{ text : String, clientName : String }>, ?logout : Null<{ oldClientName : String, clients : Array, clientName : String }>, ?login : Null<{ ?passHash : Null, ?isUnknownClient : Null, ?clients : Null>, clientName : String }>, ?kickClient : Null<{ name : String }>, ?getTime : Null, ?dump : Null<{ data : String }>, ?connected : Null<{ videoList : Array, uuid : String, playersCacheSupport : Array, itemPos : Int, isUnknownClient : Bool, isPlaylistOpen : Bool, history : Array, globalIp : String, config : Config, clients : Array, clientName : String }>, ?banClient : Null<{ time : Float, name : String }>, ?addVideo : Null<{ item : VideoItem, atEnd : Bool }> }",pos)); json2object_reader_BaseParser.prototype.onIncorrectType.call(this,pos,variable); } ,loadJsonNull: function(pos,variable) { @@ -1068,7 +1075,7 @@ JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.prototype = $extend(json2object_rea } ,loadJsonObject: function(o,pos,variable) { var assigned = new haxe_ds_StringMap(); - this.objectSetupAssign(assigned,["addVideo","banClient","connected","dump","getTime","kickClient","login","logout","message","pause","play","playItem","progress","removeVideo","rewind","serverMessage","setLeader","setNextItem","setRate","setTime","skipVideo","toggleItemType","togglePlaylistLock","type","updateClients","updatePlaylist"],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,true,true]); + this.objectSetupAssign(assigned,["addVideo","banClient","connected","dump","getTime","kickClient","login","logout","message","pause","play","playItem","progress","removeVideo","rewind","serverMessage","setLeader","setNextItem","setRate","setTime","skipVideo","skipVideoStats","toggleItemType","togglePlaylistLock","type","updateClients","updatePlaylist"],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,true,true]); this.value = this.getAuto(); var _g = 0; while(_g < o.length) { @@ -1112,7 +1119,7 @@ JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.prototype = $extend(json2object_rea this.value.playItem = this.loadObjectField(($_=new JsonParser_$5696efcdbd81f0be9220dc4cfb255706(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"playItem",assigned,this.value.playItem,pos); break; case "progress": - this.value.progress = this.loadObjectField(($_=new JsonParser_$e15c2211613da80aa2992b6249949a65(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"progress",assigned,this.value.progress,pos); + this.value.progress = this.loadObjectField(($_=new JsonParser_$df6a9f7e332eb07b32b03fe169ced290(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"progress",assigned,this.value.progress,pos); break; case "removeVideo": this.value.removeVideo = this.loadObjectField(($_=new JsonParser_$bf1400338ef221d86126b3ec267cf395(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"removeVideo",assigned,this.value.removeVideo,pos); @@ -1138,8 +1145,11 @@ JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.prototype = $extend(json2object_rea case "skipVideo": this.value.skipVideo = this.loadObjectField(($_=new JsonParser_$bf1400338ef221d86126b3ec267cf395(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"skipVideo",assigned,this.value.skipVideo,pos); break; + case "skipVideoStats": + this.value.skipVideoStats = this.loadObjectField(($_=new JsonParser_$bd8ec2f9337eacbdc88803aa17adf898(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"skipVideoStats",assigned,this.value.skipVideoStats,pos); + break; case "toggleItemType": - this.value.toggleItemType = this.loadObjectField(($_=new JsonParser_$5696efcdbd81f0be9220dc4cfb255706(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"toggleItemType",assigned,this.value.toggleItemType,pos); + this.value.toggleItemType = this.loadObjectField(($_=new JsonParser_$d5567c7dc98bd09ace00c5da6aea116a(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"toggleItemType",assigned,this.value.toggleItemType,pos); break; case "togglePlaylistLock": this.value.togglePlaylistLock = this.loadObjectField(($_=new JsonParser_$e5a960b500232cc84a0caf718de13706(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"togglePlaylistLock",assigned,this.value.togglePlaylistLock,pos); @@ -1160,7 +1170,7 @@ JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9.prototype = $extend(json2object_rea this.objectErrors(assigned,pos); } ,getAuto: function() { - return { addVideo : new JsonParser_$17f62a9968afd16686d4888a98c34102([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), banClient : new JsonParser_$cceff829ad5e63207b6f78bebb03c69a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), connected : new JsonParser_$d011018d886fa55f8828ebb1e93413c3([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), dump : new JsonParser_$dda5f87e2972892b16c7b833df96706a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), getTime : new JsonParser_$feded5bdb427eb48f39e60029d5dca4a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), kickClient : new JsonParser_$b2777fc36a3f1c43a2ae7d80ba0572ca([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), login : new JsonParser_$128275a4734b926daca1924fc6a200c7([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), logout : new JsonParser_$8d3a3702ec359ae3022bce905b75550b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), message : new JsonParser_$33c34d246ae14cf949ef186cb0e1d8eb([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), pause : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), play : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), playItem : new JsonParser_$5696efcdbd81f0be9220dc4cfb255706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), progress : new JsonParser_$e15c2211613da80aa2992b6249949a65([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), removeVideo : new JsonParser_$bf1400338ef221d86126b3ec267cf395([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), rewind : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), serverMessage : new JsonParser_$d1c1e71f4452df068de6cecc104f6dd6([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setLeader : new JsonParser_$65985176838b1da92d970132a0cb6d58([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setNextItem : new JsonParser_$5696efcdbd81f0be9220dc4cfb255706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setRate : new JsonParser_$359929cb56fda98b8740cbc66ef083c4([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setTime : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), skipVideo : new JsonParser_$bf1400338ef221d86126b3ec267cf395([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), toggleItemType : new JsonParser_$5696efcdbd81f0be9220dc4cfb255706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), togglePlaylistLock : new JsonParser_$e5a960b500232cc84a0caf718de13706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), type : new JsonParser_$d8e5213783812cec0906ca233f0379dc([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), updateClients : new JsonParser_$126814d365d277a375ef70de1963f53d([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), updatePlaylist : new JsonParser_$a82c17fc41c21179c58e1d406d2b20cd([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; + return { addVideo : new JsonParser_$17f62a9968afd16686d4888a98c34102([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), banClient : new JsonParser_$cceff829ad5e63207b6f78bebb03c69a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), connected : new JsonParser_$d011018d886fa55f8828ebb1e93413c3([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), dump : new JsonParser_$dda5f87e2972892b16c7b833df96706a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), getTime : new JsonParser_$feded5bdb427eb48f39e60029d5dca4a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), kickClient : new JsonParser_$b2777fc36a3f1c43a2ae7d80ba0572ca([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), login : new JsonParser_$128275a4734b926daca1924fc6a200c7([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), logout : new JsonParser_$8d3a3702ec359ae3022bce905b75550b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), message : new JsonParser_$33c34d246ae14cf949ef186cb0e1d8eb([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), pause : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), play : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), playItem : new JsonParser_$5696efcdbd81f0be9220dc4cfb255706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), progress : new JsonParser_$df6a9f7e332eb07b32b03fe169ced290([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), removeVideo : new JsonParser_$bf1400338ef221d86126b3ec267cf395([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), rewind : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), serverMessage : new JsonParser_$d1c1e71f4452df068de6cecc104f6dd6([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setLeader : new JsonParser_$65985176838b1da92d970132a0cb6d58([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setNextItem : new JsonParser_$5696efcdbd81f0be9220dc4cfb255706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setRate : new JsonParser_$359929cb56fda98b8740cbc66ef083c4([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), setTime : new JsonParser_$6340d11111c9ffddbd7ebb0d12d7c162([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), skipVideo : new JsonParser_$bf1400338ef221d86126b3ec267cf395([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), skipVideoStats : new JsonParser_$bd8ec2f9337eacbdc88803aa17adf898([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), toggleItemType : new JsonParser_$d5567c7dc98bd09ace00c5da6aea116a([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), togglePlaylistLock : new JsonParser_$e5a960b500232cc84a0caf718de13706([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), type : new JsonParser_$d8e5213783812cec0906ca233f0379dc([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), updateClients : new JsonParser_$126814d365d277a375ef70de1963f53d([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), updatePlaylist : new JsonParser_$a82c17fc41c21179c58e1d406d2b20cd([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; } ,__class__: JsonParser_$5f812affc76e9ba3f21130cdbd3b05d9 }); @@ -1478,6 +1488,51 @@ JsonParser_$b3a4781c98609b64e4a870171c45561f.prototype = $extend(json2object_rea } ,__class__: JsonParser_$b3a4781c98609b64e4a870171c45561f }); +var JsonParser_$bd8ec2f9337eacbdc88803aa17adf898 = function(errors,putils,errorType) { + if(errorType == null) { + errorType = 0; + } + json2object_reader_BaseParser.call(this,errors,putils,errorType); +}; +JsonParser_$bd8ec2f9337eacbdc88803aa17adf898.__name__ = true; +JsonParser_$bd8ec2f9337eacbdc88803aa17adf898.__super__ = json2object_reader_BaseParser; +JsonParser_$bd8ec2f9337eacbdc88803aa17adf898.prototype = $extend(json2object_reader_BaseParser.prototype,{ + onIncorrectType: function(pos,variable) { + this.errors.push(json2object_Error.IncorrectType(variable,"{ voted : Int, total : Int, required : Int }",pos)); + json2object_reader_BaseParser.prototype.onIncorrectType.call(this,pos,variable); + } + ,loadJsonNull: function(pos,variable) { + this.value = null; + } + ,loadJsonObject: function(o,pos,variable) { + var assigned = new haxe_ds_StringMap(); + this.objectSetupAssign(assigned,["required","total","voted"],[false,false,false]); + this.value = this.getAuto(); + var _g = 0; + while(_g < o.length) { + var field = o[_g]; + ++_g; + switch(field.name) { + case "required": + this.value.required = this.loadObjectField(($_=new JsonParser_$1686a6c336b71b36d77354cea19a8b52(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"required",assigned,this.value.required,pos); + break; + case "total": + this.value.total = this.loadObjectField(($_=new JsonParser_$1686a6c336b71b36d77354cea19a8b52(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"total",assigned,this.value.total,pos); + break; + case "voted": + this.value.voted = this.loadObjectField(($_=new JsonParser_$1686a6c336b71b36d77354cea19a8b52(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"voted",assigned,this.value.voted,pos); + break; + default: + this.errors.push(json2object_Error.UnknownVariable(field.name,this.putils.convertPosition(field.namePos))); + } + } + this.objectErrors(assigned,pos); + } + ,getAuto: function() { + return { required : new JsonParser_$1686a6c336b71b36d77354cea19a8b52([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), total : new JsonParser_$1686a6c336b71b36d77354cea19a8b52([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), voted : new JsonParser_$1686a6c336b71b36d77354cea19a8b52([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; + } + ,__class__: JsonParser_$bd8ec2f9337eacbdc88803aa17adf898 +}); var JsonParser_$bf1400338ef221d86126b3ec267cf395 = function(errors,putils,errorType) { if(errorType == null) { errorType = 0; @@ -1733,6 +1788,48 @@ JsonParser_$d1c1e71f4452df068de6cecc104f6dd6.prototype = $extend(json2object_rea } ,__class__: JsonParser_$d1c1e71f4452df068de6cecc104f6dd6 }); +var JsonParser_$d5567c7dc98bd09ace00c5da6aea116a = function(errors,putils,errorType) { + if(errorType == null) { + errorType = 0; + } + json2object_reader_BaseParser.call(this,errors,putils,errorType); +}; +JsonParser_$d5567c7dc98bd09ace00c5da6aea116a.__name__ = true; +JsonParser_$d5567c7dc98bd09ace00c5da6aea116a.__super__ = json2object_reader_BaseParser; +JsonParser_$d5567c7dc98bd09ace00c5da6aea116a.prototype = $extend(json2object_reader_BaseParser.prototype,{ + onIncorrectType: function(pos,variable) { + this.errors.push(json2object_Error.IncorrectType(variable,"{ pos : Int, ?all : Null }",pos)); + json2object_reader_BaseParser.prototype.onIncorrectType.call(this,pos,variable); + } + ,loadJsonNull: function(pos,variable) { + this.value = null; + } + ,loadJsonObject: function(o,pos,variable) { + var assigned = new haxe_ds_StringMap(); + this.objectSetupAssign(assigned,["all","pos"],[true,false]); + this.value = this.getAuto(); + var _g = 0; + while(_g < o.length) { + var field = o[_g]; + ++_g; + switch(field.name) { + case "all": + this.value.all = this.loadObjectField(($_=new JsonParser_$f55acea3678203c700715b781ad1ef0c(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"all",assigned,this.value.all,pos); + break; + case "pos": + this.value.pos = this.loadObjectField(($_=new JsonParser_$1686a6c336b71b36d77354cea19a8b52(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"pos",assigned,this.value.pos,pos); + break; + default: + this.errors.push(json2object_Error.UnknownVariable(field.name,this.putils.convertPosition(field.namePos))); + } + } + this.objectErrors(assigned,pos); + } + ,getAuto: function() { + return { all : new JsonParser_$f55acea3678203c700715b781ad1ef0c([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), pos : new JsonParser_$1686a6c336b71b36d77354cea19a8b52([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; + } + ,__class__: JsonParser_$d5567c7dc98bd09ace00c5da6aea116a +}); var JsonParser_$d581436aae29874719279c60b2eb25d0 = function(errors,putils,errorType) { if(errorType == null) { errorType = 0; @@ -1860,7 +1957,7 @@ JsonParser_$d89734267d6f665b411f49f013149267.__name__ = true; JsonParser_$d89734267d6f665b411f49f013149267.__super__ = json2object_reader_BaseParser; JsonParser_$d89734267d6f665b411f49f013149267.prototype = $extend(json2object_reader_BaseParser.prototype,{ onIncorrectType: function(pos,variable) { - this.errors.push(json2object_Error.IncorrectType(variable,"{ ?voiceOverTrack : Null, url : String, title : String, ?subs : Null, playerType : PlayerType, isTemp : Bool, duration : Float, doCache : Bool, author : String }",pos)); + this.errors.push(json2object_Error.IncorrectType(variable,"{ ?voiceOverTrack : Null, url : String, title : String, ?subs : Null, playerType : PlayerType, isTemp : Bool, ?isIncomplete : Null, duration : Float, doCache : Bool, author : String }",pos)); json2object_reader_BaseParser.prototype.onIncorrectType.call(this,pos,variable); } ,loadJsonNull: function(pos,variable) { @@ -1868,7 +1965,7 @@ JsonParser_$d89734267d6f665b411f49f013149267.prototype = $extend(json2object_rea } ,loadJsonObject: function(o,pos,variable) { var assigned = new haxe_ds_StringMap(); - this.objectSetupAssign(assigned,["author","doCache","duration","isTemp","playerType","subs","title","url","voiceOverTrack"],[false,false,false,false,false,true,false,false,true]); + this.objectSetupAssign(assigned,["author","doCache","duration","isIncomplete","isTemp","playerType","subs","title","url","voiceOverTrack"],[false,false,false,true,false,false,true,false,false,true]); this.value = this.getAuto(); var _g = 0; while(_g < o.length) { @@ -1884,6 +1981,9 @@ JsonParser_$d89734267d6f665b411f49f013149267.prototype = $extend(json2object_rea case "duration": this.value.duration = this.loadObjectField(($_=new JsonParser_$22ae0e2b89e5e3d477f988cc36d3272b(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"duration",assigned,this.value.duration,pos); break; + case "isIncomplete": + this.value.isIncomplete = this.loadObjectField(($_=new JsonParser_$f55acea3678203c700715b781ad1ef0c(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"isIncomplete",assigned,this.value.isIncomplete,pos); + break; case "isTemp": this.value.isTemp = this.loadObjectField(($_=new JsonParser_$c26f15e86e3de4c398a8273272aba034(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"isTemp",assigned,this.value.isTemp,pos); break; @@ -1909,7 +2009,7 @@ JsonParser_$d89734267d6f665b411f49f013149267.prototype = $extend(json2object_rea this.objectErrors(assigned,pos); } ,getAuto: function() { - return { author : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), doCache : new JsonParser_$c26f15e86e3de4c398a8273272aba034([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), duration : new JsonParser_$22ae0e2b89e5e3d477f988cc36d3272b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), isTemp : new JsonParser_$c26f15e86e3de4c398a8273272aba034([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), playerType : new JsonParser_$237c3c85cf00342dfb7f01e6a6157d10([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), subs : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), title : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), url : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), voiceOverTrack : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; + return { author : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), doCache : new JsonParser_$c26f15e86e3de4c398a8273272aba034([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), duration : new JsonParser_$22ae0e2b89e5e3d477f988cc36d3272b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), isIncomplete : new JsonParser_$f55acea3678203c700715b781ad1ef0c([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), isTemp : new JsonParser_$c26f15e86e3de4c398a8273272aba034([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), playerType : new JsonParser_$237c3c85cf00342dfb7f01e6a6157d10([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), subs : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), title : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), url : new JsonParser_$27118326006d3829667a400ad23d5d98([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), voiceOverTrack : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; } ,__class__: JsonParser_$d89734267d6f665b411f49f013149267 }); @@ -1931,7 +2031,7 @@ JsonParser_$d8e5213783812cec0906ca233f0379dc.prototype = $extend(json2object_rea this.value = null; } ,loadJsonString: function(s,pos,variable) { - this.value = this.loadString(s,pos,variable,["Connected","Disconnected","Login","PasswordRequest","LoginError","Logout","Message","ServerMessage","Progress","UpdateClients","BanClient","KickClient","AddVideo","RemoveVideo","SkipVideo","VideoLoaded","Pause","Play","GetTime","SetTime","SetRate","Rewind","Flashback","SetLeader","PlayItem","SetNextItem","ToggleItemType","ClearChat","ClearPlaylist","ShufflePlaylist","UpdatePlaylist","TogglePlaylistLock","Dump","CrashTest"],"Connected"); + this.value = this.loadString(s,pos,variable,["Connected","Disconnected","Login","PasswordRequest","LoginError","Logout","Message","ServerMessage","Progress","UpdateClients","BanClient","KickClient","AddVideo","RemoveVideo","SkipVideo","SkipVideoStats","VideoLoaded","Pause","Play","GetTime","SetTime","SetRate","Rewind","Flashback","SetLeader","PlayItem","SetNextItem","ToggleItemType","ClearChat","ClearPlaylist","ShufflePlaylist","UpdatePlaylist","TogglePlaylistLock","Dump","CrashTest"],"Connected"); } ,__class__: JsonParser_$d8e5213783812cec0906ca233f0379dc }); @@ -1993,17 +2093,17 @@ JsonParser_$ddce6d3de223cb2759be5c48797abca5.prototype = $extend(json2object_rea } ,__class__: JsonParser_$ddce6d3de223cb2759be5c48797abca5 }); -var JsonParser_$e15c2211613da80aa2992b6249949a65 = function(errors,putils,errorType) { +var JsonParser_$df6a9f7e332eb07b32b03fe169ced290 = function(errors,putils,errorType) { if(errorType == null) { errorType = 0; } json2object_reader_BaseParser.call(this,errors,putils,errorType); }; -JsonParser_$e15c2211613da80aa2992b6249949a65.__name__ = true; -JsonParser_$e15c2211613da80aa2992b6249949a65.__super__ = json2object_reader_BaseParser; -JsonParser_$e15c2211613da80aa2992b6249949a65.prototype = $extend(json2object_reader_BaseParser.prototype,{ +JsonParser_$df6a9f7e332eb07b32b03fe169ced290.__name__ = true; +JsonParser_$df6a9f7e332eb07b32b03fe169ced290.__super__ = json2object_reader_BaseParser; +JsonParser_$df6a9f7e332eb07b32b03fe169ced290.prototype = $extend(json2object_reader_BaseParser.prototype,{ onIncorrectType: function(pos,variable) { - this.errors.push(json2object_Error.IncorrectType(variable,"{ type : ProgressType, ratio : Float, ?data : Null }",pos)); + this.errors.push(json2object_Error.IncorrectType(variable,"{ ?url : Null, type : ProgressType, ratio : Float, ?data : Null }",pos)); json2object_reader_BaseParser.prototype.onIncorrectType.call(this,pos,variable); } ,loadJsonNull: function(pos,variable) { @@ -2011,7 +2111,7 @@ JsonParser_$e15c2211613da80aa2992b6249949a65.prototype = $extend(json2object_rea } ,loadJsonObject: function(o,pos,variable) { var assigned = new haxe_ds_StringMap(); - this.objectSetupAssign(assigned,["data","ratio","type"],[true,false,false]); + this.objectSetupAssign(assigned,["data","ratio","type","url"],[true,false,false,true]); this.value = this.getAuto(); var _g = 0; while(_g < o.length) { @@ -2027,6 +2127,9 @@ JsonParser_$e15c2211613da80aa2992b6249949a65.prototype = $extend(json2object_rea case "type": this.value.type = this.loadObjectField(($_=new JsonParser_$ec6c0149f584f56bd68388d292edf21c(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"type",assigned,this.value.type,pos); break; + case "url": + this.value.url = this.loadObjectField(($_=new JsonParser_$ddce6d3de223cb2759be5c48797abca5(this.errors,this.putils,1),$bind($_,$_.loadJson)),field,"url",assigned,this.value.url,pos); + break; default: this.errors.push(json2object_Error.UnknownVariable(field.name,this.putils.convertPosition(field.namePos))); } @@ -2034,9 +2137,9 @@ JsonParser_$e15c2211613da80aa2992b6249949a65.prototype = $extend(json2object_rea this.objectErrors(assigned,pos); } ,getAuto: function() { - return { data : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), ratio : new JsonParser_$22ae0e2b89e5e3d477f988cc36d3272b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), type : new JsonParser_$ec6c0149f584f56bd68388d292edf21c([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; + return { data : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), ratio : new JsonParser_$22ae0e2b89e5e3d477f988cc36d3272b([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), type : new JsonParser_$ec6c0149f584f56bd68388d292edf21c([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1))), url : new JsonParser_$ddce6d3de223cb2759be5c48797abca5([],this.putils,0).loadJson(new hxjsonast_Json(hxjsonast_JsonValue.JNull,new hxjsonast_Position("",0,1)))}; } - ,__class__: JsonParser_$e15c2211613da80aa2992b6249949a65 + ,__class__: JsonParser_$df6a9f7e332eb07b32b03fe169ced290 }); var JsonParser_$e5a960b500232cc84a0caf718de13706 = function(errors,putils,errorType) { if(errorType == null) { @@ -2093,7 +2196,7 @@ JsonParser_$ec6c0149f584f56bd68388d292edf21c.prototype = $extend(json2object_rea this.value = null; } ,loadJsonString: function(s,pos,variable) { - this.value = this.loadString(s,pos,variable,["Caching","Downloading","Uploading","Canceled"],"Caching"); + this.value = this.loadString(s,pos,variable,["Caching","Downloading","Uploading","Canceled","Completed"],"Caching"); } ,__class__: JsonParser_$ec6c0149f584f56bd68388d292edf21c }); @@ -2519,8 +2622,14 @@ StringTools.hex = function(n,digits) { }; var _$Types_VideoItemTools = function() { }; _$Types_VideoItemTools.__name__ = true; +_$Types_VideoItemTools.isPlayable = function(item) { + if(item.isIncomplete == true && item.doCache) { + return false; + } + return true; +}; _$Types_VideoItemTools.withUrl = function(item,url) { - return { url : url, title : item.title, author : item.author, duration : item.duration, subs : item.subs, voiceOverTrack : item.voiceOverTrack, isTemp : item.isTemp, doCache : item.doCache, playerType : item.playerType}; + return { url : url, title : item.title, author : item.author, duration : item.duration, subs : item.subs, voiceOverTrack : item.voiceOverTrack, isTemp : item.isTemp, doCache : item.doCache, isIncomplete : item.isIncomplete, playerType : item.playerType}; }; var VideoList = function() { this.items = []; @@ -2547,6 +2656,9 @@ VideoList.prototype = { ,exists: function(f) { return Lambda.exists(this.items,f); } + ,find: function(f) { + return Lambda.find(this.items,f); + } ,findIndex: function(f) { return Lambda.findIndex(this.items,f); } @@ -2568,6 +2680,11 @@ VideoList.prototype = { ,toggleItemType: function(pos) { this.items[pos].isTemp = !this.items[pos].isTemp; } + ,setAllItemTypes: function(isTemp) { + var _g = 0; + var _g1 = this.items; + while(_g < _g1.length) _g1[_g++].isTemp = isTemp; + } ,removeItem: function(index) { if(index < this.pos) { this.pos--; @@ -2860,7 +2977,14 @@ var haxe_ds_IntMap = function() { haxe_ds_IntMap.__name__ = true; haxe_ds_IntMap.__interfaces__ = [haxe_IMap]; haxe_ds_IntMap.prototype = { - __class__: haxe_ds_IntMap + remove: function(key) { + if(!this.h.hasOwnProperty(key)) { + return false; + } + delete(this.h[key]); + return true; + } + ,__class__: haxe_ds_IntMap }; var haxe_ds_StringMap = function() { this.h = Object.create(null); @@ -3139,6 +3263,7 @@ hxjsonast_JObjectField.prototype = { __class__: hxjsonast_JObjectField }; var hxjsonast_Parser = function(source,filename) { + this.allowDuplicateKeys = false; this.source = source; this.filename = filename; this.pos = 0; @@ -3296,7 +3421,7 @@ hxjsonast_Parser.prototype = { var fieldStartPos = this.pos - 1; field = this.parseString(); fieldPos = new hxjsonast_Position(this.filename,fieldStartPos,this.pos); - if(Object.prototype.hasOwnProperty.call(names_h,field)) { + if(Object.prototype.hasOwnProperty.call(names_h,field) && !this.allowDuplicateKeys) { throw haxe_Exception.thrown(new hxjsonast_Error("Duplicate field name \"" + field + "\"",fieldPos)); } else { names_h[field] = true; @@ -3942,8 +4067,9 @@ server_ConsoleInput.prototype = { } ,__class__: server_ConsoleInput }; -var server__$HttpServer_HttpServerConfig = function(dir,customDir,allowLocalRequests,cache) { +var server__$HttpServer_HttpServerConfig = function(dir,customDir,allowLocalRequests,enableCORS,cache) { this.cache = null; + this.enableCORS = false; this.allowLocalRequests = false; this.customDir = null; this.dir = dir; @@ -3953,6 +4079,9 @@ var server__$HttpServer_HttpServerConfig = function(dir,customDir,allowLocalRequ if(allowLocalRequests != null) { this.allowLocalRequests = allowLocalRequests; } + if(enableCORS != null) { + this.enableCORS = enableCORS; + } if(cache != null) { this.cache = cache; } @@ -3969,6 +4098,7 @@ var server_HttpServer = function(main,config) { this.uploadingFilesSizes = new haxe_ds_StringMap(); this.CHUNK_SIZE = 5242880; this.cache = null; + this.enableCORS = false; this.allowLocalRequests = false; this.allowedLocalFiles = new haxe_ds_StringMap(); this.hasCustomRes = false; @@ -3976,6 +4106,7 @@ var server_HttpServer = function(main,config) { this.dir = config.dir; this.customDir = config.customDir; this.allowLocalRequests = config.allowLocalRequests; + this.enableCORS = config.enableCORS; this.cache = config.cache; if(this.customDir != null) { this.hasCustomRes = sys_FileSystem.exists(this.customDir); @@ -3993,6 +4124,17 @@ server_HttpServer.prototype = { } var filePath = this.getPath(this.dir,url); var ext = haxe_io_Path.extension(filePath).toLowerCase(); + if(this.enableCORS) { + res.setHeader("access-control-allow-origin","*"); + res.setHeader("access-control-allow-headers","content-type, range, content-name, x-client-uuid"); + res.setHeader("access-control-expose-headers","accept-ranges, content-encoding, content-length, content-range"); + if(req.method == "OPTIONS") { + res.setHeader("access-control-allow-methods","GET, HEAD, POST, OPTIONS"); + res.statusCode = 204; + res.end(); + return; + } + } res.setHeader("accept-ranges","bytes"); res.setHeader("content-type",this.getMimeType(ext)); if(req.method == "POST") { @@ -4170,7 +4312,6 @@ server_HttpServer.prototype = { if(Object.prototype.hasOwnProperty.call(_this.h,filePath)) { delete(_this.h[filePath]); } - this.cache.remove(name); req.destroy(); var tmp1 = ClientTools.getByName(this.main.clients,name); if(tmp1 == null) { @@ -4195,7 +4336,7 @@ server_HttpServer.prototype = { } }); stream.on("error",function(err) { - haxe_Log.trace(err,{ fileName : "src/server/HttpServer.hx", lineNumber : 277, className : "server.HttpServer", methodName : "uploadFile"}); + haxe_Log.trace(err,{ fileName : "src/server/HttpServer.hx", lineNumber : 291, className : "server.HttpServer", methodName : "uploadFile"}); tools_HttpServerTools.json(tools_HttpServerTools.status(res,500),{ info : "File write stream error."}); var _this = _gthis.uploadingFilesSizes; if(Object.prototype.hasOwnProperty.call(_this.h,filePath)) { @@ -4208,7 +4349,7 @@ server_HttpServer.prototype = { _gthis.cache.remove(name); }); req.on("error",function(err) { - haxe_Log.trace("Request Error:",{ fileName : "src/server/HttpServer.hx", lineNumber : 284, className : "server.HttpServer", methodName : "uploadFile", customParams : [err]}); + haxe_Log.trace("Request Error:",{ fileName : "src/server/HttpServer.hx", lineNumber : 298, className : "server.HttpServer", methodName : "uploadFile", customParams : [err]}); stream.destroy(); tools_HttpServerTools.json(tools_HttpServerTools.status(res,500),{ info : "File request error."}); var _this = _gthis.uploadingFilesSizes; @@ -4386,12 +4527,18 @@ server_HttpServer.prototype = { return true; } ,parseRangeHeader: function(rangeHeader,videoSize) { - var ranges = new EReg("[-=]","g").split(rangeHeader); + var ranges = new EReg("[=-]","g").split(rangeHeader); var start = Std.parseInt(ranges[1]); + var end = Std.parseInt(ranges[2]); + if(start == null) { + var tmp = end; + var a = videoSize - (tmp != null ? tmp : 0); + start = a < 0 ? 0 : a; + end = videoSize - 1; + } if(server_Utils.isOutOfRange(start,0,videoSize - 1)) { start = 0; } - var end = Std.parseInt(ranges[2]); if(end == null) { end = start + this.CHUNK_SIZE; } @@ -4614,8 +4761,8 @@ var server_Main = function(opts) { this.loadedClientsCount = 0; this.matchGuestName = new EReg("guest [0-9]+",""); this.matchHtmlChars = new EReg("[&^<>'\"]",""); + this.cacheCancelers = new haxe_ds_StringMap(); this.skipVotes = new haxe_ds_IntMap(); - this.isServerPause = false; this.flashbacks = []; this.messages = []; this.videoTimer = new server_VideoTimer(); @@ -4660,6 +4807,8 @@ var server_Main = function(opts) { this.cache = new server_cache_Cache(this,this.cacheDir); if(this.cache.isYtReady) { this.playersCacheSupport.push("YoutubeType"); + this.playersCacheSupport.push("VkType"); + this.playersCacheSupport.push("VimeoType"); } this.initIntergationHandlers(); this.loadState(); @@ -4687,7 +4836,7 @@ var server_Main = function(opts) { preparePort = function() { server_Utils.isPortFree(_gthis.port,function(isFree) { if(!isFree && attempts > 0) { - haxe_Log.trace("Warning: port " + _gthis.port + " is already in use. Changed to " + (_gthis.port + 1),{ fileName : "src/server/Main.hx", lineNumber : 159, className : "server.Main", methodName : "new"}); + haxe_Log.trace("Warning: port " + _gthis.port + " is already in use. Changed to " + (_gthis.port + 1),{ fileName : "src/server/Main.hx", lineNumber : 163, className : "server.Main", methodName : "new"}); attempts -= 1; _gthis.port++; preparePort(); @@ -4738,20 +4887,20 @@ server_Main.prototype = { var protocol = ssl == null ? "http" : "https"; var port = this.getPort(protocol); var colonPort = this.isDefaultPort() ? "" : ":" + port; - haxe_Log.trace("Local: " + protocol + "://" + this.localIp + colonPort,{ fileName : "src/server/Main.hx", lineNumber : 189, className : "server.Main", methodName : "runServer"}); + haxe_Log.trace("Local: " + protocol + "://" + this.localIp + colonPort,{ fileName : "src/server/Main.hx", lineNumber : 193, className : "server.Main", methodName : "runServer"}); if(this.config.localNetworkOnly) { - haxe_Log.trace("Global network is disabled in config",{ fileName : "src/server/Main.hx", lineNumber : 191, className : "server.Main", methodName : "runServer"}); + haxe_Log.trace("Global network is disabled in config",{ fileName : "src/server/Main.hx", lineNumber : 195, className : "server.Main", methodName : "runServer"}); } else if(!this.isNoState) { server_Utils.getGlobalIp(function(ip) { if(ip.indexOf(":") != -1) { ip = "[" + ip + "]"; } _gthis.globalIp = ip; - haxe_Log.trace("Global: " + protocol + "://" + _gthis.globalIp + colonPort,{ fileName : "src/server/Main.hx", lineNumber : 197, className : "server.Main", methodName : "runServer"}); + haxe_Log.trace("Global: " + protocol + "://" + _gthis.globalIp + colonPort,{ fileName : "src/server/Main.hx", lineNumber : 201, className : "server.Main", methodName : "runServer"}); }); } var dir = "" + this.rootDir + "/res"; - var httpServer = new server_HttpServer(this,new server__$HttpServer_HttpServerConfig(dir,"" + this.userDir + "/res",this.config.localAdmins,this.cache)); + var httpServer = new server_HttpServer(this,new server__$HttpServer_HttpServerConfig(dir,"" + this.userDir + "/res",this.config.localAdmins,this.config.enableCORS,this.cache)); Lang.init("" + dir + "/langs"); var server; if(ssl == null) { @@ -4768,7 +4917,7 @@ server_Main.prototype = { redirectHttpServer.listen(80); } catch( _g ) { var _g1 = haxe_Exception.caught(_g); - haxe_Log.trace("Cannot run http server on port 80 for https redirects:",{ fileName : "src/server/Main.hx", lineNumber : 223, className : "server.Main", methodName : "runServer", customParams : [_g1]}); + haxe_Log.trace("Cannot run http server on port 80 for https redirects:",{ fileName : "src/server/Main.hx", lineNumber : 228, className : "server.Main", methodName : "runServer", customParams : [_g1]}); } } server = js_node_Https.createServer({ key : ssl.key, cert : ssl.cert},$bind(httpServer,httpServer.serveFiles)); @@ -4805,10 +4954,10 @@ server_Main.prototype = { return { key : key, cert : cert}; } if(!sys_FileSystem.exists(config.sslKeyPemPath)) { - haxe_Log.trace("sslKeyPemPath: absolute file path not found: " + config.sslKeyPemPath,{ fileName : "src/server/Main.hx", lineNumber : 261, className : "server.Main", methodName : "getSslConfig"}); + haxe_Log.trace("sslKeyPemPath: absolute file path not found: " + config.sslKeyPemPath,{ fileName : "src/server/Main.hx", lineNumber : 266, className : "server.Main", methodName : "getSslConfig"}); } if(!sys_FileSystem.exists(config.sslCertPemPath)) { - haxe_Log.trace("sslCertPemPath: absolute file path not found: " + config.sslCertPemPath,{ fileName : "src/server/Main.hx", lineNumber : 263, className : "server.Main", methodName : "getSslConfig"}); + haxe_Log.trace("sslCertPemPath: absolute file path not found: " + config.sslCertPemPath,{ fileName : "src/server/Main.hx", lineNumber : 268, className : "server.Main", methodName : "getSslConfig"}); } return null; } @@ -4858,6 +5007,7 @@ server_Main.prototype = { if(!sys_FileSystem.exists(customPath)) { return config; } + var defaultYtDlp = config.ytDlp; var customConfig = JSON.parse(js_node_Fs.readFileSync(customPath,{ encoding : "utf8"})); var _g = 0; var _g1 = Reflect.fields(customConfig); @@ -4865,10 +5015,12 @@ server_Main.prototype = { var field = _g1[_g]; ++_g; if(Reflect.field(config,field) == null) { - haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 308, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 314, className : "server.Main", methodName : "getUserConfig"}); } config[field] = Reflect.field(customConfig,field); } + config.ytDlp.channel = config.ytDlp.channel != null ? config.ytDlp.channel : defaultYtDlp.channel; + config.ytDlp.jsRuntime = config.ytDlp.jsRuntime != null ? config.ytDlp.jsRuntime : defaultYtDlp.jsRuntime; var emoteCopies_h = Object.create(null); var _g = 0; var _g1 = config.emotes; @@ -4876,14 +5028,14 @@ server_Main.prototype = { var emote = _g1[_g]; ++_g; if(emoteCopies_h[emote.name]) { - haxe_Log.trace("Warning: emote name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 314, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: emote name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 322, className : "server.Main", methodName : "getUserConfig"}); } emoteCopies_h[emote.name] = true; if(!this.verbose) { continue; } if(emoteCopies_h[emote.image]) { - haxe_Log.trace("Warning: emote url of name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 318, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: emote url of name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 326, className : "server.Main", methodName : "getUserConfig"}); } emoteCopies_h[emote.image] = true; } @@ -4920,7 +5072,7 @@ server_Main.prototype = { js_node_Fs.writeFileSync("" + this.userDir + "/users.json",JSON.stringify({ admins : users1, bans : _g, salt : users.salt},null,"\t")); } ,saveState: function() { - haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 356, className : "server.Main", methodName : "saveState"}); + haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 364, className : "server.Main", methodName : "saveState"}); var json = JSON.stringify(this.getCurrentState(),null,"\t"); js_node_Fs.writeFileSync(this.statePath,json); this.writeUsers(this.userList); @@ -4935,7 +5087,7 @@ server_Main.prototype = { if(!sys_FileSystem.exists(this.statePath)) { return; } - haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 380, className : "server.Main", methodName : "loadState"}); + haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 388, className : "server.Main", methodName : "loadState"}); var state = JSON.parse(js_node_Fs.readFileSync(this.statePath,{ encoding : "utf8"})); state.flashbacks = state.flashbacks != null ? state.flashbacks : []; state.cachedFiles = state.cachedFiles != null ? state.cachedFiles : []; @@ -4957,7 +5109,7 @@ server_Main.prototype = { } ,logError: function(type,data) { this.cache.removeOlderCache(1048576); - haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 404, className : "server.Main", methodName : "logError", customParams : [data]}); + haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 412, className : "server.Main", methodName : "logError", customParams : [data]}); var crashesFolder = "" + this.userDir + "/crashes"; server_Utils.ensureDir(crashesFolder); var name = DateTools.format(new Date(),"%Y-%m-%d_%H_%M_%S") + "-" + type; @@ -4979,7 +5131,7 @@ server_Main.prototype = { if(_gthis.clients.length == 0) { return; } - haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 417, className : "server.Main", methodName : "initIntergationHandlers"}); + haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 425, className : "server.Main", methodName : "initIntergationHandlers"}); js_node_Http.get(url,null,function(r) { }); }; @@ -5008,13 +5160,13 @@ server_Main.prototype = { password += this.config.salt; var hash = haxe_crypto_Sha256.encode(password); this.userList.admins.push({ name : name, hash : hash}); - haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 443, className : "server.Main", methodName : "addAdmin"}); + haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 451, className : "server.Main", methodName : "addAdmin"}); } ,removeAdmin: function(name) { HxOverrides.remove(this.userList.admins,Lambda.find(this.userList.admins,function(item) { return item.name == name; })); - haxe_Log.trace("Admin " + name + " removed.",{ fileName : "src/server/Main.hx", lineNumber : 450, className : "server.Main", methodName : "removeAdmin"}); + haxe_Log.trace("Admin " + name + " removed.",{ fileName : "src/server/Main.hx", lineNumber : 458, className : "server.Main", methodName : "removeAdmin"}); } ,replayLog: function(events) { var _gthis = this; @@ -5081,7 +5233,7 @@ server_Main.prototype = { var ip = this.clientIp(req); var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length; var name = "Guest " + (id + 1); - haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 513, className : "server.Main", methodName : "onConnect", customParams : ["" + name + " connected (" + ip + ")"]}); + haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 521, className : "server.Main", methodName : "onConnect", customParams : ["" + name + " connected (" + ip + ")"]}); var remoteIp = req.socket.remoteAddress; var isAdmin = this.config.localAdmins && this.isLoopbackIp(remoteIp); var client = new Client(ws,req,id,name,0); @@ -5096,7 +5248,7 @@ server_Main.prototype = { var obj = _gthis.wsEventParser.fromJson(data.toString()); if(_gthis.wsEventParser.errors.length > 0 || _gthis.noTypeObj(obj)) { var errors = "" + ("Wrong request for type \"" + obj.type + "\":") + "\n" + json2object_ErrorUtils.convertErrorArray(_gthis.wsEventParser.errors); - haxe_Log.trace(errors,{ fileName : "src/server/Main.hx", lineNumber : 531, className : "server.Main", methodName : "onConnect"}); + haxe_Log.trace(errors,{ fileName : "src/server/Main.hx", lineNumber : 539, className : "server.Main", methodName : "onConnect"}); _gthis.serverMessage(client,errors); return; } @@ -5171,47 +5323,55 @@ server_Main.prototype = { data.addVideo.item = item; _gthis.videoList.addItem(item,data.addVideo.atEnd); _gthis.broadcast(data); - if(_gthis.videoList.items.length == 1) { + if(_gthis.videoList.items.length == 1 && _$Types_VideoItemTools.isPlayable(item)) { _gthis.restartWaitTimer(); } - } else { - var _g = item.playerType; - switch(_g) { - case "RawType": - this.cache.cacheRawVideo(client,item.url,function(name) { - item = _$Types_VideoItemTools.withUrl(item,_gthis.cache.getFileUrl(name)); - data.addVideo.item = item; - _gthis.videoList.addItem(item,data.addVideo.atEnd); - _gthis.broadcast(data); - if(_gthis.videoList.items.length == 1) { - _gthis.restartWaitTimer(); - } - }); - break; - case "YoutubeType": - this.cache.cacheYoutubeVideo(client,item.url,function(name) { - item = _$Types_VideoItemTools.withUrl(item,_gthis.cache.getFileUrl(name)); - if(item.duration > 1) { - item.duration -= 1; - } - data.addVideo.item = item; - _gthis.videoList.addItem(item,data.addVideo.atEnd); - _gthis.broadcast(data); - if(_gthis.videoList.items.length == 1) { - _gthis.restartWaitTimer(); - } - }); - break; - default: - var name = StringTools.replace("" + _g,"Type",""); - this.serverMessage(client,"No cache support for " + name + " player."); - data.addVideo.item = item; - _gthis.videoList.addItem(item,data.addVideo.atEnd); - _gthis.broadcast(data); - if(_gthis.videoList.items.length == 1) { - _gthis.restartWaitTimer(); - } + return; + } + var isYoutube = item.playerType == "YoutubeType"; + if(this.playersCacheSupport.indexOf(item.playerType) == -1) { + var name = StringTools.replace("" + item.playerType,"Type",""); + this.serverMessage(client,"No cache support for " + name + " player."); + data.addVideo.item = item; + _gthis.videoList.addItem(item,data.addVideo.atEnd); + _gthis.broadcast(data); + if(_gthis.videoList.items.length == 1 && _$Types_VideoItemTools.isPlayable(item)) { + _gthis.restartWaitTimer(); } + return; + } + var callbacks = { onResolved : function(finalUrl) { + item = _$Types_VideoItemTools.withUrl(item,finalUrl); + item.isIncomplete = true; + if(isYoutube && item.duration > 1) { + item.duration -= 1; + } + data.addVideo.item = item; + _gthis.videoList.addItem(item,data.addVideo.atEnd); + _gthis.broadcast(data); + if(_gthis.videoList.items.length == 1 && _$Types_VideoItemTools.isPlayable(item)) { + _gthis.restartWaitTimer(); + } + }, onProgress : function(ratio) { + _gthis.broadcastCacheProgress(item.url,ratio); + }, onComplete : function() { + _gthis.completeIncompleteItem(item.url); + }, onError : function() { + _gthis.removeIncompleteItem(item.url); + }, registerCancel : function(cancel) { + _gthis.cacheCancelers.h[item.url] = cancel; + }, onMetadata : function(title,duration) { + _gthis.updateIncompleteItemMeta(item.url,title,duration); + }}; + switch(item.playerType) { + case "VimeoType":case "VkType": + this.cache.cacheYtdlpVideo(client,item.url,callbacks); + break; + case "YoutubeType": + this.cache.cacheYoutubeVideo(client,item.url,callbacks); + break; + default: + this.cache.cacheRawVideo(client,item.url,callbacks); } break; case "BanClient": @@ -5257,10 +5417,10 @@ server_Main.prototype = { if(this.isPlaylistLockedFor(client)) { return; } - this.resetSkipVote(); if(!this.checkPermission(client,"removeVideo")) { return; } + this.resetSkipVote(); if(this.videoList.items.length != 0) { if(this.videoTimer.getTime() > 30) { var _this = this.videoList; @@ -5282,7 +5442,7 @@ server_Main.prototype = { tmp.stop(); } if(this.clients.length == 1 && this.videoList.items.length > 0) { - if(!this.isServerPause) { + if(!this.videoTimer.isServerPause) { if(this.videoTimer.isPaused()) { this.videoTimer.play(); } @@ -5291,28 +5451,31 @@ server_Main.prototype = { this.checkBan(client); this.send(client,{ type : "Connected", connected : { uuid : client.uuid, config : { channelName : this.config.channelName, emotes : this.config.emotes, filters : this.config.filters, isVerbose : this.config.isVerbose, maxLoginLength : this.config.maxLoginLength, maxMessageLength : this.config.maxMessageLength, permissions : this.config.permissions, port : this.config.port, requestLeaderOnPause : this.config.requestLeaderOnPause, salt : this.config.salt, serverVersion : this.config.serverVersion, templateUrl : this.config.templateUrl, totalVideoLimit : this.config.totalVideoLimit, unpauseWithoutLeader : this.config.unpauseWithoutLeader, userVideoLimit : this.config.userVideoLimit, youtubeApiKey : this.config.youtubeApiKey, youtubePlaylistLimit : this.config.youtubePlaylistLimit}, history : this.messages, isUnknownClient : true, clientName : client.name, clients : this.clientList(), videoList : this.videoList.items, isPlaylistOpen : this.videoList.isOpen, itemPos : this.videoList.pos, globalIp : this.globalIp, playersCacheSupport : this.playersCacheSupport}}); this.sendClientListExcept(client); + this.broadcastSkipVoteStats(); break; case "CrashTest": if((client.group & 8) == 0) { return; } - haxe_Log.trace("Crashing...",{ fileName : "src/server/Main.hx", lineNumber : 1073, className : "server.Main", methodName : "onMessage"}); + haxe_Log.trace("Crashing...",{ fileName : "src/server/Main.hx", lineNumber : 1132, className : "server.Main", methodName : "onMessage"}); null[1]++; break; case "Disconnected": if(!internal) { return; } - haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 596, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " disconnected"]}); + haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 605, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " disconnected"]}); server_Utils.sortedPush(this.freeIds,client.id); HxOverrides.remove(this.clients,client); this.sendClientList(); if((client.group & 4) != 0) { if(this.videoList.items.length > 0) { this.videoTimer.pause(); - this.isServerPause = true; + this.videoTimer.isServerPause = true; } } + this.skipVotes.remove(client.id); + this.updateSkipVote(); if(this.clients.length == 0) { var tmp = this.emptyRoomCallbackTimer; if(tmp != null) { @@ -5373,6 +5536,10 @@ server_Main.prototype = { return; } var _this = this.videoList; + if(_this.items[_this.pos].isIncomplete) { + return; + } + var _this = this.videoList; var maxTime = _this.items[_this.pos].duration - 0.01; if(this.videoTimer.getTime() > maxTime) { this.videoTimer.pause(); @@ -5388,7 +5555,7 @@ server_Main.prototype = { if(this.videoTimer.isPaused()) { obj.getTime.paused = true; } - if(this.isServerPause) { + if(this.videoTimer.isServerPause) { obj.getTime.pausedByServer = true; } if(this.videoTimer.getRate() != 1) { @@ -5444,12 +5611,13 @@ server_Main.prototype = { this.send(client,{ type : "LoginError"}); return; } - haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 687, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " logged as " + name]}); + haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 698, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " logged as " + name]}); client.name = name; client.setGroupFlag(ClientGroup.User,true); this.checkBan(client); this.send(client,{ type : data.type, login : { isUnknownClient : true, clientName : client.name, clients : this.clientList()}}); this.sendClientListExcept(client); + this.broadcastSkipVoteStats(); break; case "LoginError": break; @@ -5457,9 +5625,10 @@ server_Main.prototype = { var oldName = client.name; client.name = "Guest " + (this.clients.indexOf(client) + 1); client.setGroupFlag(ClientGroup.User,false); - haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 708, className : "server.Main", methodName : "onMessage", customParams : ["Client " + oldName + " logout to " + client.name]}); + haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 720, className : "server.Main", methodName : "onMessage", customParams : ["Client " + oldName + " logout to " + client.name]}); this.send(client,{ type : data.type, logout : { oldClientName : oldName, clientName : client.name, clients : this.clientList()}}); this.sendClientListExcept(client); + this.broadcastSkipVoteStats(); break; case "Message": if(!this.checkPermission(client,"writeChat")) { @@ -5511,7 +5680,6 @@ server_Main.prototype = { this.saveFlashbackTime(_this.items[_this.pos]); } this.videoTimer.setTime(data.play.time); - this.isServerPause = false; this.videoTimer.play(); this.broadcast({ type : data.type, play : data.play}); break; @@ -5519,11 +5687,11 @@ server_Main.prototype = { if(!this.checkPermission(client,"changeOrder")) { return; } - this.resetSkipVote(); var pos = data.playItem.pos; if(!this.videoList.hasItem(pos)) { return; } + this.resetSkipVote(); if(this.videoTimer.getTime() > 30) { var _this = this.videoList; this.saveFlashbackTime(_this.items[_this.pos]); @@ -5534,37 +5702,80 @@ server_Main.prototype = { this.broadcast(data); break; case "Progress": + var p = data.progress; + var url; + var tmp = p.url; + if(tmp != null) { + url = tmp; + } else { + return; + } + var index = this.videoList.findIndex(function(i) { + return i.url == url; + }); + if(index == -1) { + return; + } + var item1 = this.videoList.items[index]; + if(item1.isIncomplete != true) { + return; + } + if(item1.doCache) { + return; + } + if(item1.author != client.name) { + return; + } + switch(p.type) { + case "Canceled": + this.removeIncompleteItem(url); + break; + case "Completed": + this.completeIncompleteItem(url); + break; + default: + this.broadcast(data); + } break; case "RemoveVideo": if(this.videoList.items.length == 0) { return; } - if(this.activeSkipVoteUrl == data.removeVideo.url) { - this.resetSkipVote(); - } if(this.isPlaylistLockedFor(client)) { return; } if(!this.checkPermission(client,"removeVideo")) { return; } - var url = data.removeVideo.url; + var url1 = data.removeVideo.url; var index = this.videoList.findIndex(function(item) { - return item.url == url; + return item.url == url1; }); if(index == -1) { return; } var _this = this.videoList; - var isCurrent = _this.items[_this.pos].url == url; + var isCurrent = _this.items[_this.pos].url == url1; + if(isCurrent) { + this.resetSkipVote(); + } if(isCurrent && this.videoTimer.getTime() > 30) { var _this = this.videoList; this.saveFlashbackTime(_this.items[_this.pos]); } this.videoList.removeItem(index); + this.cancelCache(url1); this.broadcast(data); if(isCurrent && this.videoList.items.length > 0) { - this.restartWaitTimer(); + var _this = this.videoList; + if(!_$Types_VideoItemTools.isPlayable(_this.items[_this.pos])) { + this.pauseForIncompleteItem(); + } else { + this.restartWaitTimer(); + } + } + if(this.videoList.items.length == 0) { + this.videoTimer.stop(); } break; case "Rewind": @@ -5610,7 +5821,7 @@ server_Main.prototype = { this.serverMessage(client,"Client \"" + clientName + "\" not found."); return; } - this.isServerPause = false; + this.videoTimer.isServerPause = false; ClientTools.setLeader(this.clients,clientName); this.broadcast({ type : "SetLeader", setLeader : { clientName : clientName}}); if(this.videoList.items.length == 0) { @@ -5684,6 +5895,8 @@ server_Main.prototype = { } this.handleSkipVote(client,data); break; + case "SkipVideoStats": + break; case "ToggleItemType": if(this.isPlaylistLockedFor(client)) { return; @@ -5695,6 +5908,11 @@ server_Main.prototype = { if(!this.videoList.hasItem(pos)) { return; } + if(data.toggleItemType.all == true) { + this.videoList.setAllItemTypes(!this.videoList.items[pos].isTemp); + this.broadcast({ type : "UpdatePlaylist", updatePlaylist : { videoList : this.videoList.items}}); + return; + } this.videoList.toggleItemType(pos); this.broadcast(data); break; @@ -5712,7 +5930,7 @@ server_Main.prototype = { this.broadcast({ type : "UpdatePlaylist", updatePlaylist : { videoList : this.videoList.items}}); break; case "VideoLoaded": - if(this.isServerPause) { + if(this.videoTimer.isServerPause) { return; } this.prepareVideoPlayback(); @@ -5738,13 +5956,6 @@ server_Main.prototype = { ,send: function(client,data) { client.ws.send(server_Main.jsonStringify(data),null); } - ,sendByName: function(clientName,data) { - var tmp = ClientTools.getByName(this.clients,clientName); - if(tmp == null) { - return; - } - tmp.ws.send(server_Main.jsonStringify(data),null); - } ,broadcast: function(data) { var json = server_Main.jsonStringify(data); var _g = 0; @@ -5823,23 +6034,44 @@ server_Main.prototype = { return; } this.skipVotes.h[client.id] = true; - var votes = this.getSkipVoteCount(); - var required = this.getSkipVoteRequiredCount(); - this.broadcast({ type : "ServerMessage", serverMessage : { textId : "" + client.name + " voted to skip (" + votes + "/" + required + ")."}}); - if(votes >= required) { + this.updateSkipVote(); + } + ,updateSkipVote: function() { + if(this.activeSkipVoteUrl == null) { + return; + } + var tmp; + if(this.videoList.items.length != 0) { + var _this = this.videoList; + tmp = _this.items[_this.pos].url != this.activeSkipVoteUrl; + } else { + tmp = true; + } + if(tmp) { this.resetSkipVote(); - this.skipVideo(data); + return; + } + if(this.getSkipVoteCount() >= this.getSkipVoteRequiredCount()) { + this.skipVideo({ type : "SkipVideo", skipVideo : { url : this.activeSkipVoteUrl}}); + return; } + this.broadcastSkipVoteStats(); + } + ,broadcastSkipVoteStats: function() { + if(this.activeSkipVoteUrl == null) { + return; + } + this.broadcast({ type : "SkipVideoStats", skipVideoStats : { voted : this.getSkipVoteCount(), total : this.getSkipVoteEligibleClients().length, required : this.getSkipVoteRequiredCount()}}); } ,skipVideo: function(data) { if(this.videoList.items.length == 0) { return; } - this.resetSkipVote(); var _this = this.videoList; if(_this.items[_this.pos].url != data.skipVideo.url) { return; } + this.resetSkipVote(); var _this = this.videoList; var dur = _this.items[_this.pos].duration; if(this.videoTimer.getTime() > 30 && this.videoTimer.getTime() < dur - 30) { @@ -5848,10 +6080,100 @@ server_Main.prototype = { } this.videoList.skipItem(); if(this.videoList.items.length > 0) { - this.restartWaitTimer(); + var _this = this.videoList; + if(!_$Types_VideoItemTools.isPlayable(_this.items[_this.pos])) { + this.pauseForIncompleteItem(); + } else { + this.restartWaitTimer(); + } + } else { + this.videoTimer.stop(); } this.broadcast(data); } + ,pauseForIncompleteItem: function() { + this.videoTimer.stop(); + var tmp = this.waitVideoStart; + if(tmp != null) { + tmp.stop(); + } + } + ,cancelCache: function(url) { + var tmp = this.cacheCancelers.h[url]; + if(tmp == null) { + return; + } + var _this = this.cacheCancelers; + if(Object.prototype.hasOwnProperty.call(_this.h,url)) { + delete(_this.h[url]); + } + tmp(); + } + ,broadcastCacheProgress: function(url,ratio) { + this.broadcast({ type : "Progress", progress : { type : "Downloading", ratio : ratio, url : url}}); + } + ,updateIncompleteItemMeta: function(url,title,duration) { + var tmp = this.videoList.find(function(item) { + return item.url == url; + }); + if(tmp == null) { + return; + } + if(tmp.isIncomplete != true) { + return; + } + var changed = false; + if(tmp.duration <= 0 && duration > 0) { + tmp.duration = duration; + changed = true; + } + if(title != null && title.length > 0 && tmp.title.length == 0) { + tmp.title = title; + changed = true; + } + if(!changed) { + return; + } + this.broadcast({ type : "UpdatePlaylist", updatePlaylist : { videoList : this.videoList.items}}); + } + ,completeIncompleteItem: function(url) { + var _this = this.cacheCancelers; + if(Object.prototype.hasOwnProperty.call(_this.h,url)) { + delete(_this.h[url]); + } + var index = this.videoList.findIndex(function(i) { + return i.url == url; + }); + if(index == -1) { + return; + } + var item = this.videoList.items[index]; + if(item.isIncomplete != true) { + return; + } + item.isIncomplete = false; + this.broadcast({ type : "Progress", progress : { type : "Completed", ratio : 1, url : url}}); + if(this.videoList.pos == index && item.doCache) { + this.restartWaitTimer(); + } + } + ,removeIncompleteItem: function(url) { + var _this = this.cacheCancelers; + if(Object.prototype.hasOwnProperty.call(_this.h,url)) { + delete(_this.h[url]); + } + var index = this.videoList.findIndex(function(i) { + return i.url == url; + }); + if(index == -1) { + return; + } + if(this.videoList.items[index].isIncomplete != true) { + return; + } + this.videoList.removeItem(index); + this.broadcast({ type : "RemoveVideo", removeVideo : { url : url}}); + } ,checkPermission: function(client,perm) { if((client.group & 1) != 0) { this.checkBan(client); @@ -5880,7 +6202,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 : 1255, className : "server.Main", methodName : "checkBan"}); + haxe_Log.trace("" + client.name + " ban removed",{ fileName : "src/server/Main.hx", lineNumber : 1415, className : "server.Main", methodName : "checkBan"}); this.sendClientList(); } break; @@ -5933,7 +6255,6 @@ server_Main.prototype = { } this.loadedClientsCount = 0; this.broadcast({ type : "VideoLoaded"}); - this.isServerPause = false; this.videoTimer.start(); } ,saveFlashbackTime: function(item) { @@ -6064,7 +6385,7 @@ server_Utils.getGlobalIp = function(callback) { data_b += Std.string(chunk); }); r.on("end",function() { - callback(data_b); + callback(StringTools.trim(data_b)); }); }).on("error",onError).on("timeout",onError); }; @@ -6106,12 +6427,14 @@ var server_VideoTimer = function() { this.rateStartTime = 0.0; this.pauseStartTime = 0.0; this.startTime = 0.0; + this.isServerPause = false; this.isStarted = false; }; server_VideoTimer.__name__ = true; server_VideoTimer.prototype = { start: function() { this.isStarted = true; + this.isServerPause = false; var hrtime = process.hrtime(); this.startTime = hrtime[0] + hrtime[1] / 1e9; this.pauseStartTime = 0; @@ -6120,6 +6443,7 @@ server_VideoTimer.prototype = { } ,stop: function() { this.isStarted = false; + this.isServerPause = false; this.startTime = 0; this.pauseStartTime = 0; } @@ -6139,6 +6463,7 @@ server_VideoTimer.prototype = { if(!this.isStarted) { this.start(); } + this.isServerPause = false; this.startTime += this.pauseTime(); this.pauseStartTime = 0; var hrtime = process.hrtime(); @@ -6203,12 +6528,13 @@ var server_cache_Cache = function(main,cacheDir) { this.main = main; this.cacheDir = cacheDir; server_Utils.ensureDir(cacheDir); - this.youtubeCache = new server_cache_YoutubeCache(main,this); + this.ytdlpCache = new server_cache_YtdlpCache(main,this); + this.youtubeCache = new server_cache_YoutubeCache(main,this,this.ytdlpCache); this.rawCache = new server_cache_RawCache(main,this); - this.isYtReady = this.youtubeCache.checkYtDeps(); + this.isYtReady = this.ytdlpCache.checkYtDeps(); if(this.isYtReady) { - this.youtubeCache.cleanYtInputFiles(); - this.youtubeCache.checkUpdate(); + this.ytdlpCache.cleanInputFiles(); + this.ytdlpCache.checkUpdate(); } }; server_cache_Cache.__name__ = true; @@ -6237,16 +6563,16 @@ server_cache_Cache.prototype = { if(this.cachedFiles.indexOf(name) != -1) { continue; } - haxe_Log.trace("Remove untracked cache " + name,{ fileName : "src/server/cache/Cache.hx", lineNumber : 54, className : "server.cache.Cache", methodName : "removeUntrackedFiles"}); + haxe_Log.trace("Remove untracked cache " + name,{ fileName : "src/server/cache/Cache.hx", lineNumber : 56, className : "server.cache.Cache", methodName : "removeUntrackedFiles"}); this.remove(name); } } ,log: function(client,msg) { - haxe_Log.trace(msg,{ fileName : "src/server/cache/Cache.hx", lineNumber : 60, className : "server.cache.Cache", methodName : "log"}); + haxe_Log.trace(msg,{ fileName : "src/server/cache/Cache.hx", lineNumber : 62, className : "server.cache.Cache", methodName : "log"}); this.main.serverMessage(client,msg); } ,logByName: function(clientName,msg) { - haxe_Log.trace(msg,{ fileName : "src/server/cache/Cache.hx", lineNumber : 65, className : "server.cache.Cache", methodName : "logByName"}); + haxe_Log.trace(msg,{ fileName : "src/server/cache/Cache.hx", lineNumber : 67, className : "server.cache.Cache", methodName : "logByName"}); var tmp = ClientTools.getByName(this.main.clients,clientName); if(tmp == null) { return; @@ -6274,11 +6600,29 @@ server_cache_Cache.prototype = { } } } - ,cacheYoutubeVideo: function(client,url,callback) { - this.youtubeCache.cacheYoutubeVideo(client,url,callback); + ,cacheYoutubeVideo: function(client,url,callbacks) { + this.youtubeCache.cacheYoutubeVideo(client,url,callbacks); + } + ,cacheYtdlpVideo: function(client,url,callbacks) { + if(!this.isYtReady) { + this.rawCache.cacheRawVideo(client,url,callbacks); + return; + } + this.ytdlpCache.cacheVideo(client,url,callbacks); } - ,cacheRawVideo: function(client,url,callback) { - this.rawCache.cacheRawVideo(client,url,callback); + ,cacheRawVideo: function(client,url,callbacks) { + var _gthis = this; + if(!this.isYtReady) { + this.rawCache.cacheRawVideo(client,url,callbacks); + return; + } + this.ytdlpCache.checkUrlSupported(url,function(supported) { + if(supported) { + _gthis.ytdlpCache.cacheVideo(client,url,callbacks); + } else { + _gthis.rawCache.cacheRawVideo(client,url,callbacks); + } + }); } ,setStorageLimit: function(bytes) { var _gthis = this; @@ -6301,13 +6645,13 @@ server_cache_Cache.prototype = { var _gthis = this; var tmp = js_node_Fs.statfs; if(tmp == null) { - haxe_Log.trace("Warning: no fs.statfs support in current nodejs version (needs v18+)",{ fileName : "src/server/cache/Cache.hx", lineNumber : 104, className : "server.cache.Cache", methodName : "getFreeDiskSpace"}); + haxe_Log.trace("Warning: no fs.statfs support in current nodejs version (needs v18+)",{ fileName : "src/server/cache/Cache.hx", lineNumber : 125, className : "server.cache.Cache", methodName : "getFreeDiskSpace"}); callback(this.storageLimit); return; } - tmp("/",function(err,stats) { + tmp(this.cacheDir,function(err,stats) { if(err != null) { - haxe_Log.trace(err,{ fileName : "src/server/cache/Cache.hx", lineNumber : 110, className : "server.cache.Cache", methodName : "getFreeDiskSpace"}); + haxe_Log.trace(err,{ fileName : "src/server/cache/Cache.hx", lineNumber : 131, className : "server.cache.Cache", methodName : "getFreeDiskSpace"}); callback(_gthis.storageLimit); return; } @@ -6421,7 +6765,7 @@ var server_cache_RawCache = function(main,cache) { }; server_cache_RawCache.__name__ = true; server_cache_RawCache.prototype = { - cacheRawVideo: function(client,url,callback) { + cacheRawVideo: function(client,url,cb) { var isM3U8 = url.indexOf(".m3u8") != -1; var ext = isM3U8 ? "m3u8" : "mp4"; var matchName = new EReg("^([^:.]+)\\.(.+)",""); @@ -6434,49 +6778,94 @@ server_cache_RawCache.prototype = { var outName = matchName.match(HxOverrides.substr(decodedUrl,decodedUrl.lastIndexOf("/") + 1,null)) ? matchName.matched(1) + ("." + ext) : "video." + ext; outName = this.cache.getFreeFileName(outName); if(this.cache.exists(outName)) { - callback(outName); + var tmp = this.cache.getFileUrl(outName); + cb.onResolved(tmp); + cb.onComplete(); return; } - haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 46, className : "server.cache.RawCache", methodName : "cacheRawVideo"}); - this.main.send(client,{ type : "Progress", progress : { type : "Caching", ratio : 0, data : outName}}); + haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 47, className : "server.cache.RawCache", methodName : "cacheRawVideo"}); + var tmp = this.cache.getFileUrl(outName); + cb.onResolved(tmp); if(isM3U8) { - this.handleM3u8(client,url,outName,callback); + this.handleM3u8(client,url,outName,cb); } else { - this.handleMp4(client,url,outName,callback); + this.handleMp4(client,url,outName,cb); } } - ,handleMp4: function(client,url,outName,callback) { + ,handleMp4: function(client,url,outName,cb) { var _gthis = this; var clientName = client.name; + var lastSentRatio = 0.0; + var canceled = false; this.downloadFile(client,url,outName,function(downloaded,total) { + if(canceled) { + return; + } var v = downloaded / total; - _gthis.main.sendByName(clientName,{ type : "Progress", progress : { type : "Downloading", ratio : tools_MathTools.toFixed(v < 0 ? 0 : v > 1 ? 1 : v,4)}}); + var ratio = v < 0 ? 0 : v > 1 ? 1 : v; + if(ratio - lastSentRatio < 0.01 && ratio < 1) { + return; + } + lastSentRatio = ratio; + var cb1 = cb; + var tmp = tools_MathTools.toFixed(ratio,4); + cb1.onProgress(tmp); },function() { - _gthis.cache.add(outName); - callback(outName); + if(canceled) { + return; + } + _gthis.probeDuration(outName,function(duration) { + if(canceled) { + return; + } + if(duration > 0 && cb.onMetadata != null) { + cb.onMetadata(null,duration); + } + _gthis.cache.add(outName); + cb.onComplete(); + }); },function(err) { + if(canceled) { + return; + } _gthis.log(clientName,"Mp4 download failed: " + err); - _gthis.cancelProgress(clientName); + cb.onError(); + },function(cancel) { + if(cb.registerCancel == null) { + return; + } + cb.registerCancel(function() { + canceled = true; + cancel(); + }); }); } - ,handleM3u8: function(client,url,outName,callback) { + ,handleM3u8: function(client,url,outName,cb) { var _gthis = this; var clientName = client.name; var useProxy = true; + var canceled = false; + if(cb.registerCancel != null) { + cb.registerCancel(function() { + canceled = true; + }); + } this.downloadM3u8Playlist(client,url,useProxy,function(playlist,totalSize,segments) { + if(canceled) { + return; + } if(useProxy) { totalSize = playlist.length; } if(!_gthis.cache.removeOlderCache(totalSize + _gthis.cache.freeSpaceBlock)) { _gthis.log(clientName,_gthis.cache.notEnoughSpaceErrorText); - _gthis.cancelProgress(clientName); + cb.onError(); return; } if(useProxy) { - _gthis.main.sendByName(clientName,{ type : "Progress", progress : { type : "Caching", ratio : 1, data : outName}}); js_node_Fs.writeFileSync("" + _gthis.cache.cacheDir + "/" + outName,playlist); _gthis.cache.add(outName); - callback(outName); + cb.onComplete(); return; } var activeDownloads = 0; @@ -6496,7 +6885,7 @@ server_cache_RawCache.prototype = { } segment[0].started = true; activeDownloads += 1; - haxe_Log.trace("download segment",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 120, className : "server.cache.RawCache", methodName : "handleM3u8", customParams : [segment[0].i]}); + haxe_Log.trace("download segment",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 121, className : "server.cache.RawCache", methodName : "handleM3u8", customParams : [segment[0].i]}); _gthis.downloadFile(client,segment[0].url,segment[0].name,(function() { return function(downloadedBytes,totalBytes) { }; @@ -6506,12 +6895,12 @@ server_cache_RawCache.prototype = { segment[0].completed = true; downloaded += 1; var progress = downloaded / segments.length; - _gthis.main.sendByName(clientName,{ type : "Progress", progress : { type : "Downloading", ratio : progress < 0 ? 0 : progress > 1 ? 1 : progress}}); + cb.onProgress(progress < 0 ? 0 : progress > 1 ? 1 : progress); if(downloaded == segments.length) { - haxe_Log.trace("All " + downloaded + "/" + segments.length + " segments downloaded",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 140, className : "server.cache.RawCache", methodName : "handleM3u8"}); + haxe_Log.trace("All " + downloaded + "/" + segments.length + " segments downloaded",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 135, className : "server.cache.RawCache", methodName : "handleM3u8"}); js_node_Fs.writeFileSync("" + _gthis.cache.cacheDir + "/" + outName,playlist); _gthis.cache.add(outName); - callback(outName); + cb.onComplete(); } else { downloadNextBatch(); } @@ -6521,7 +6910,7 @@ server_cache_RawCache.prototype = { activeDownloads -= 1; downloaded += 1; _gthis.log(clientName,"TS segment " + segment[0].i + " download failed: " + err); - _gthis.cancelProgress(clientName); + cb.onError(); var _gthis1 = _gthis; var result = new Array(segments.length); var _g = 0; @@ -6538,7 +6927,7 @@ server_cache_RawCache.prototype = { downloadNextBatch(); },function(err) { _gthis.log(clientName,"M3U8 processing failed: " + err); - _gthis.cancelProgress(clientName); + cb.onError(); }); } ,request: function(url,options,callback) { @@ -6599,6 +6988,7 @@ server_cache_RawCache.prototype = { } var req = _gthis.request(segments[0].url,{ method : "GET"},function(res) { var tmp = Std.parseInt(res.headers["content-length"]); + res.destroy(); var totalSize = (tmp != null ? tmp : 0) * (segments.length + 1); if(totalSize == 0) { onError("Failed to get segment sizes: no content-length"); @@ -6619,8 +7009,9 @@ server_cache_RawCache.prototype = { req.on("error",onError); req.end(); } - ,downloadFile: function(client,url,fileName,onProgress,onComplete,onError) { - var file = js_node_Fs.createWriteStream("" + this.cache.cacheDir + "/" + fileName); + ,downloadFile: function(client,url,fileName,onProgress,onComplete,onError,onCancelable) { + var outPath = "" + this.cache.cacheDir + "/" + fileName; + var file = js_node_Fs.createWriteStream(outPath); var req = this.request(url,{ method : "GET", headers : { "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537." + Std.random(100), "Accept" : "*/*"}},function(res) { var total; var tmp = Std.parseInt(res.headers["content-length"]); @@ -6653,67 +7044,52 @@ server_cache_RawCache.prototype = { file.destroy(); onError("Request failed: " + err); }); + if(onCancelable != null) { + onCancelable(function() { + req.destroy(); + file.destroy(); + if(sys_FileSystem.exists(outPath)) { + js_node_Fs.unlinkSync(outPath); + } + }); + } req.end(); } + ,probeDuration: function(fileName,callback) { + js_node_ChildProcess.execFile("ffprobe",["-v","error","-show_entries","format=duration","-of","default=noprint_wrappers=1:nokey=1","" + this.cache.cacheDir + "/" + fileName],function(err,stdout,_) { + if(err != null) { + callback(0); + return; + } + var duration = parseFloat(StringTools.trim(Std.string(stdout))); + callback(isNaN(duration) ? 0 : duration); + }); + } ,cleanupFiles: function(files) { var _g = 0; while(_g < files.length) { - var file = files[_g]; - ++_g; - if(sys_FileSystem.exists(file)) { - js_node_Fs.unlinkSync(file); + var path = "" + this.cache.cacheDir + "/" + files[_g++]; + if(sys_FileSystem.exists(path)) { + js_node_Fs.unlinkSync(path); } } } ,log: function(clientName,msg) { this.cache.logByName(clientName,msg); } - ,cancelProgress: function(clientName) { - this.main.sendByName(clientName,{ type : "Progress", progress : { type : "Canceled", ratio : 0}}); - } ,__class__: server_cache_RawCache }; -var server_cache_YoutubeCache = function(main,cache) { +var server_cache_YoutubeCache = function(main,cache,ytdlp) { this.main = main; this.cache = cache; + this.ytdlp = ytdlp; }; server_cache_YoutubeCache.__name__ = true; server_cache_YoutubeCache.prototype = { - checkYtDeps: function() { - try { - js_node_ChildProcess.execSync("ffmpeg -version",{ stdio : "ignore", timeout : 5000}); - this.ytDlp = new (require('ytdlp-nodejs')).YtDlp(); - return true; - } catch( _g ) { - return false; - } - } - ,checkUpdate: function() { - this.ytDlp.execAsync("",{ updateTo : this.main.config.ytDlp.channel, onData : function(d) { - haxe_Log.trace(d,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 36, className : "server.cache.YoutubeCache", methodName : "checkUpdate"}); - }}).catch(function(e) { - haxe_Log.trace(e,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 39, className : "server.cache.YoutubeCache", methodName : "checkUpdate"}); - }); - } - ,cleanYtInputFiles: function(prefix) { - if(prefix == null) { - prefix = "__tmp"; - } - var names = js_node_Fs.readdirSync(this.cache.cacheDir); - var _g = 0; - while(_g < names.length) { - var name = names[_g]; - ++_g; - if(!StringTools.startsWith(name,prefix)) { - continue; - } - this.cache.remove(name); - } - } - ,cacheYoutubeVideo: function(client,url,callback) { + cacheYoutubeVideo: function(client,url,cb) { var _gthis = this; if(!this.cache.isYtReady) { - haxe_Log.trace("Do `npm i https://github.com/RblSb/ytdlp-nodejs` to use cache feature (you also need to install `ffmpeg` to build mp4 from downloaded audio/video tracks).",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 53, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + haxe_Log.trace("Do `npm i https://github.com/RblSb/ytdlp-nodejs` to use cache feature (you also need to install `ffmpeg` to build mp4 from downloaded audio/video tracks).",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 23, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); return; } var clientName = client.name; @@ -6722,9 +7098,13 @@ server_cache_YoutubeCache.prototype = { this.log(clientName,"Error: youtube video id not found in url: " + url); return; } + url = "https://youtu.be/" + videoId; var outName = videoId + ".mp4"; if(this.cache.exists(outName)) { - callback(outName); + var cb1 = cb; + var tmp = this.cache.getFileUrl(outName); + cb1.onResolved(tmp); + cb.onComplete(); return; } var inVideoName = "__tmp-video-" + videoId; @@ -6732,11 +7112,31 @@ server_cache_YoutubeCache.prototype = { this.log(clientName,"Caching " + outName + " already in progress"); return; } - haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 85, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); - this.main.sendByName(clientName,{ type : "Progress", progress : { type : "Caching", ratio : 0, data : outName}}); + haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 58, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + var cb1 = cb; + var tmp = this.cache.getFileUrl(outName); + cb1.onResolved(tmp); var useCookies = false; + var lastSentRatio = 0.0; + var canceled = false; + var $process = null; + if(cb.registerCancel != null) { + cb.registerCancel(function() { + canceled = true; + if($process != null) { + $process.kill(); + } + _gthis.ytdlp.cleanInputFiles(inVideoName); + }); + } var onGetInfo = function(info) { - haxe_Log.trace("Get info with " + info.formats.length + " formats",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 98, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + if(canceled) { + return; + } + if(cb.onMetadata != null) { + cb.onMetadata(info.title,info.duration); + } + haxe_Log.trace("Get info with " + info.formats.length + " formats",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 74, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); var _this = info.formats; var _g = []; var _g1 = 0; @@ -6794,7 +7194,7 @@ server_cache_YoutubeCache.prototype = { if(tmp == null) { _gthis.log(clientName,"Error: format with audio not found"); var _g = 0; - while(_g < aformats.length) haxe_Log.trace(aformats[_g++],{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 110, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + while(_g < aformats.length) haxe_Log.trace(aformats[_g++],{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 86, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); return; } var _this = info.formats; @@ -6823,7 +7223,7 @@ server_cache_YoutubeCache.prototype = { } else { _gthis.log(clientName,"Error: video format not found"); var _g1 = 0; - while(_g1 < _g.length) haxe_Log.trace(_g[_g1++],{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 120, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + while(_g1 < _g.length) haxe_Log.trace(_g[_g1++],{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 96, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); return; } var ignoreQualities = []; @@ -6847,8 +7247,8 @@ server_cache_YoutubeCache.prototype = { var tmp2 = tmp.filesize; var hasSpace = _gthis.cache.removeOlderCache(((tmp1 != null ? tmp1 : 0) + (tmp2 != null ? tmp2 : 0)) * 2 + _gthis.cache.freeSpaceBlock); if(!hasSpace) { - _gthis.cleanYtInputFiles(inVideoName); - _gthis.cancelProgress(clientName); + _gthis.ytdlp.cleanInputFiles(inVideoName); + cb.onError(); _gthis.log(clientName,_gthis.cache.notEnoughSpaceErrorText); } if(!hasSpace) { @@ -6870,13 +7270,13 @@ server_cache_YoutubeCache.prototype = { videoSizeRatio = 1; audioSizeRatio = 0; } - haxe_Log.trace(formatIds,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 153, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo", customParams : ["" + tools_MathTools.toFixed(totalSize / 1024 / 1024) + " MiB",tools_MathTools.toFixed(videoSizeRatio),tools_MathTools.toFixed(audioSizeRatio)]}); + haxe_Log.trace(formatIds,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 129, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo", customParams : ["" + tools_MathTools.toFixed(totalSize / 1024 / 1024) + " MiB",tools_MathTools.toFixed(videoSizeRatio),tools_MathTools.toFixed(audioSizeRatio)]}); var videoRatioCache = 0.0; var audioRatioCache = 0.0; - var dlVideo = _gthis.ytDlp.downloadAsync(url,{ format : formatIds, output : "" + _gthis.cache.cacheDir + "/" + inVideoName, remuxVideo : "mp4", additionalOptions : ["--no-js-runtimes","--js-runtimes",_gthis.main.config.ytDlp.jsRuntime], cookies : useCookies ? _gthis.getCookiesPathOrNull() : null, forceIpv4 : true, socketTimeout : 2, extractorRetries : 0, onProgress : function(p) { + $process = _gthis.ytdlp.download(url,{ format : formatIds, output : "" + _gthis.cache.cacheDir + "/" + inVideoName, remuxVideo : "mp4", additionalOptions : _gthis.ytdlp.ytExtraOptions(), cookies : useCookies ? _gthis.ytdlp.getCookiesPathOrNull() : null, forceIpv4 : true, socketTimeout : 2, extractorRetries : 0},{ onProgress : function(p) { var isFinished = p.status == "finished"; if(isFinished) { - haxe_Log.trace("" + haxe_io_Path.withoutDirectory(p.filename) + " format file downloaded",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 171, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + haxe_Log.trace("" + haxe_io_Path.withoutDirectory(p.filename) + " format file downloaded",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 147, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); } var ratio; if(isFinished) { @@ -6891,51 +7291,52 @@ server_cache_YoutubeCache.prototype = { audioRatioCache = ratio; } ratio = videoRatioCache * videoSizeRatio + audioRatioCache * audioSizeRatio; - _gthis.main.sendByName(clientName,{ type : "Progress", progress : { type : "Downloading", ratio : tools_MathTools.toFixed(ratio,4)}}); - }}).catch(function(err) { - _gthis.cache.logWithAdmins(client,"Error during video download: " + err); - _gthis.cleanYtInputFiles(inVideoName); - _gthis.cancelProgress(clientName); - }); - dlVideo.then(function(v) { + if(canceled) { + return; + } + if(ratio - lastSentRatio < 0.01 && ratio < 1) { + return; + } + lastSentRatio = ratio; + var cb1 = cb; + var $process = tools_MathTools.toFixed(ratio,4); + cb1.onProgress($process); + }, onComplete : function() { + if(canceled) { + _gthis.ytdlp.cleanInputFiles(inVideoName); + return; + } var tmp = _gthis.cache.findFile(function(n) { return StringTools.startsWith(n,inVideoName); }); if(tmp == null) { _gthis.cache.logWithAdmins(client,"Error: cannot find downloaded file with prefix " + inVideoName); - _gthis.cancelProgress(clientName); + cb.onError(); return; } js_node_Fs.renameSync("" + _gthis.cache.cacheDir + "/" + tmp,"" + _gthis.cache.cacheDir + "/" + outName); - _gthis.cleanYtInputFiles(inVideoName); + _gthis.ytdlp.cleanInputFiles(inVideoName); _gthis.cache.add(outName); - callback(outName); - }); + cb.onComplete(); + }, onError : function(err) { + if(canceled) { + return; + } + _gthis.cache.logWithAdmins(client,"Error during video download: " + err); + _gthis.ytdlp.cleanInputFiles(inVideoName); + cb.onError(); + }}); }; - this.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { - haxe_Log.trace(err,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 215, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); + this.ytdlp.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { + haxe_Log.trace(err,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 193, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"}); useCookies = true; - return _gthis.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { - _gthis.cleanYtInputFiles(inVideoName); - _gthis.cancelProgress(clientName); + return _gthis.ytdlp.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { + _gthis.ytdlp.cleanInputFiles(inVideoName); + cb.onError(); _gthis.log(clientName,"" + err); }); }); } - ,getInfoAsync: function(url,useCookies) { - if(useCookies == null) { - useCookies = false; - } - return this.ytDlp.getInfoAsync(url,{ cookies : useCookies ? this.getCookiesPathOrNull() : null, additionalOptions : ["--no-js-runtimes","--js-runtimes",this.main.config.ytDlp.jsRuntime]}); - } - ,getCookiesPathOrNull: function() { - var cookiesPath = "" + this.main.userDir + "/cookies.txt"; - if(sys_FileSystem.exists(cookiesPath)) { - return cookiesPath; - } else { - return null; - } - } ,getBestYoutubeVideoFormat: function(formats,ignoreQualities) { var qPriority = [1080,720,480,360,240,144]; if(ignoreQualities != null) { @@ -6984,11 +7385,230 @@ server_cache_YoutubeCache.prototype = { ,log: function(clientName,msg) { this.cache.logByName(clientName,msg); } - ,cancelProgress: function(clientName) { - this.main.sendByName(clientName,{ type : "Progress", progress : { type : "Canceled", ratio : 0}}); - } ,__class__: server_cache_YoutubeCache }; +var server_cache_YtdlpCache = function(main,cache) { + this.main = main; + this.cache = cache; +}; +server_cache_YtdlpCache.__name__ = true; +server_cache_YtdlpCache.prototype = { + checkYtDeps: function() { + try { + js_node_ChildProcess.execSync("ffmpeg -version",{ stdio : "ignore", timeout : 5000}); + this.ytDlp = new (require('ytdlp-nodejs')).YtDlp(); + return true; + } catch( _g ) { + return false; + } + } + ,checkUpdate: function() { + this.ytDlp.execAsync("",{ updateTo : this.main.config.ytDlp.channel, onData : function(d) { + haxe_Log.trace(d,{ fileName : "src/server/cache/YtdlpCache.hx", lineNumber : 43, className : "server.cache.YtdlpCache", methodName : "checkUpdate"}); + }}).catch(function(e) { + haxe_Log.trace(e,{ fileName : "src/server/cache/YtdlpCache.hx", lineNumber : 46, className : "server.cache.YtdlpCache", methodName : "checkUpdate"}); + }); + } + ,cleanInputFiles: function(prefix) { + if(prefix == null) { + prefix = "__tmp"; + } + var names = js_node_Fs.readdirSync(this.cache.cacheDir); + var _g = 0; + while(_g < names.length) { + var name = names[_g]; + ++_g; + if(!StringTools.startsWith(name,prefix)) { + continue; + } + this.cache.remove(name); + } + } + ,getInfoAsync: function(url,useCookies) { + if(useCookies == null) { + useCookies = false; + } + return this.ytDlp.getInfoAsync(url,{ cookies : useCookies ? this.getCookiesPathOrNull() : null, additionalOptions : this.ytExtraOptions()}); + } + ,getCookiesPathOrNull: function() { + var cookiesPath = "" + this.main.userDir + "/cookies.txt"; + if(sys_FileSystem.exists(cookiesPath)) { + return cookiesPath; + } else { + return null; + } + } + ,ytExtraOptions: function() { + return ["--no-js-runtimes","--js-runtimes",this.main.config.ytDlp.jsRuntime]; + } + ,checkUrlSupported: function(url,callback) { + var $process = this.ytDlp.exec(url,{ useExtractors : ["default","-generic"], simulate : true, quiet : true, socketTimeout : 5, extractorRetries : 0}); + var done = false; + $process.on("close",function(code,_signal) { + if(!done) { + done = true; + callback(code == 0); + } + }); + $process.on("error",function(_err) { + if(!done) { + done = true; + callback(false); + } + }); + return $process; + } + ,download: function(url,options,handlers) { + var $process = this.ytDlp.exec(url,options); + var stderr = ""; + var done = false; + $process.stderr.on("data",function(d) { + stderr += Std.string(d); + return stderr; + }); + $process.on("progress",function(p) { + handlers.onProgress(p); + }); + $process.on("close",function(code,_signal) { + if(done) { + return; + } + done = true; + if(code == 0) { + handlers.onComplete(); + } else { + handlers.onError("yt-dlp exited with code " + code + ": " + stderr); + } + }); + $process.on("error",function(err) { + if(done) { + return; + } + done = true; + handlers.onError("Failed to start yt-dlp process: " + Std.string(err)); + }); + return $process; + } + ,cacheVideo: function(client,url,cb) { + var _gthis = this; + var clientName = client.name; + var outName = this.deriveOutName(url); + haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/YtdlpCache.hx", lineNumber : 128, className : "server.cache.YtdlpCache", methodName : "cacheVideo"}); + var cb1 = cb; + var tmp = this.cache.getFileUrl(outName); + cb1.onResolved(tmp); + var inName = "__tmp-" + Std.random(16777215) + "-" + haxe_io_Path.withoutExtension(outName); + var canceled = false; + var $process = null; + if(cb.registerCancel != null) { + cb.registerCancel(function() { + canceled = true; + if($process != null) { + $process.kill(); + } + _gthis.cleanInputFiles(inName); + }); + } + var useCookies = false; + var lastSentRatio = 0.0; + var onGetInfo = function(info) { + if(canceled) { + return; + } + if(cb.onMetadata != null) { + cb.onMetadata(info.title,info.duration); + } + if(!_gthis.cache.removeOlderCache(_gthis.estimateSize(info) * 2 + _gthis.cache.freeSpaceBlock)) { + _gthis.cleanInputFiles(inName); + cb.onError(); + _gthis.log(clientName,_gthis.cache.notEnoughSpaceErrorText); + return; + } + $process = _gthis.download(url,{ output : "" + _gthis.cache.cacheDir + "/" + inName, remuxVideo : "mp4", additionalOptions : _gthis.ytExtraOptions(), cookies : useCookies ? _gthis.getCookiesPathOrNull() : null, forceIpv4 : true, socketTimeout : 5, extractorRetries : 0},{ onProgress : function(p) { + if(canceled) { + return; + } + var ratio; + if(p.status == "finished") { + ratio = 1.0; + } else { + var v = p.downloaded / p.total; + ratio = v < 0 ? 0 : v > 1 ? 1 : v; + } + if(ratio - lastSentRatio < 0.01 && ratio < 1) { + return; + } + lastSentRatio = ratio; + var cb1 = cb; + var $process = tools_MathTools.toFixed(ratio,4); + cb1.onProgress($process); + }, onComplete : function() { + if(canceled) { + _gthis.cleanInputFiles(inName); + return; + } + var tmp = _gthis.cache.findFile(function(n) { + return StringTools.startsWith(n,inName); + }); + if(tmp == null) { + _gthis.cache.logWithAdmins(client,"Error: cannot find downloaded file with prefix " + inName); + cb.onError(); + return; + } + js_node_Fs.renameSync("" + _gthis.cache.cacheDir + "/" + tmp,"" + _gthis.cache.cacheDir + "/" + outName); + _gthis.cleanInputFiles(inName); + _gthis.cache.add(outName); + cb.onComplete(); + }, onError : function(err) { + if(canceled) { + return; + } + _gthis.cache.logWithAdmins(client,"Error during video download: " + err); + _gthis.cleanInputFiles(inName); + cb.onError(); + }}); + }; + this.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { + if(canceled) { + return; + } + useCookies = true; + _gthis.getInfoAsync(url,useCookies).then(onGetInfo).catch(function(err) { + _gthis.cleanInputFiles(inName); + cb.onError(); + _gthis.log(clientName,"" + err); + }); + }); + } + ,deriveOutName: function(url) { + var decoded; + try { + decoded = decodeURIComponent(url.split("+").join(" ")); + } catch( _g ) { + decoded = url; + } + var last = HxOverrides.substr(decoded,decoded.lastIndexOf("/") + 1,null); + var q = last.indexOf("?"); + if(q != -1) { + last = HxOverrides.substr(last,0,q); + } + last = haxe_io_Path.withoutExtension(last); + if(last == "") { + last = "video"; + } + return this.cache.getFreeFileName("" + last + ".mp4"); + } + ,estimateSize: function(info) { + var d = info; + var tmp = d.filesize_approx; + var tmp1 = tmp != null ? tmp : d.filesize; + return (tmp1 != null ? tmp1 : 0) | 0; + } + ,log: function(clientName,msg) { + this.cache.logByName(clientName,msg); + } + ,__class__: server_cache_YtdlpCache +}; var sys_FileSystem = function() { }; sys_FileSystem.__name__ = true; sys_FileSystem.exists = function(path) { -- cgit v1.2.3