aboutsummaryrefslogtreecommitdiffstats
path: root/build/server.js
diff options
context:
space:
mode:
Diffstat (limited to 'build/server.js')
-rw-r--r--build/server.js1204
1 files changed, 757 insertions, 447 deletions
diff --git a/build/server.js b/build/server.js
index ef1de33..a6b4b7e 100644
--- a/build/server.js
+++ b/build/server.js
@@ -2237,6 +2237,17 @@ Lambda.find = function(it,f) {
}
return null;
};
+Lambda.findIndex = function(it,f) {
+ var i = 0;
+ var v = $getIterator(it);
+ while(v.hasNext()) {
+ if(f(v.next())) {
+ return i;
+ }
+ ++i;
+ }
+ return -1;
+};
var haxe_IMap = function() { };
haxe_IMap.__name__ = true;
haxe_IMap.__isInterface__ = true;
@@ -2454,16 +2465,7 @@ VideoList.prototype = {
return Lambda.exists(this.items,f);
}
,findIndex: function(f) {
- var i = 0;
- var _g = 0;
- var _g1 = this.items;
- while(_g < _g1.length) {
- if(f(_g1[_g++])) {
- return i;
- }
- ++i;
- }
- return -1;
+ return Lambda.findIndex(this.items,f);
}
,addItem: function(item,atEnd) {
if(atEnd) {
@@ -3679,415 +3681,6 @@ json2object_PositionUtils.prototype = {
}
,__class__: json2object_PositionUtils
};
-var server_Cache = function(main,cacheDir) {
- this.freeSpaceBlock = 10485760;
- this.cachedFiles = [];
- this.storageLimit = 3145728 * 1024;
- this.isYtReady = false;
- this.notEnoughSpaceErrorText = "Error: Not enough free space on server or file size is out of cache storage limit.";
- this.main = main;
- this.cacheDir = cacheDir;
- server_Utils.ensureDir(cacheDir);
- this.isYtReady = this.checkYtDeps();
- if(this.isYtReady) {
- this.cleanYtInputFiles();
- }
-};
-server_Cache.__name__ = true;
-server_Cache.prototype = {
- checkYtDeps: function() {
- var ytdl;
- try {
- ytdl = require("@distube/ytdl-core");
- } catch( _g ) {
- return false;
- }
- try {
- js_node_ChildProcess.execSync("ffmpeg -version",{ stdio : "ignore", timeout : 3000});
- return true;
- } catch( _g ) {
- return false;
- }
- }
- ,cleanYtInputFiles: function() {
- var names = js_node_Fs.readdirSync(this.cacheDir);
- var _g = 0;
- while(_g < names.length) {
- var name = names[_g];
- ++_g;
- if(!StringTools.startsWith(name,"__tmp")) {
- continue;
- }
- this.remove(name);
- }
- }
- ,getCachedFiles: function() {
- return this.cachedFiles;
- }
- ,setCachedFiles: function(names) {
- this.cachedFiles.length = 0;
- var _g = 0;
- while(_g < names.length) this.cachedFiles.push(names[_g++]);
- var names = js_node_Fs.readdirSync(this.cacheDir);
- var _g = 0;
- while(_g < names.length) {
- var name = names[_g];
- ++_g;
- if(StringTools.startsWith(name,".")) {
- continue;
- }
- if(sys_FileSystem.isDirectory("" + this.cacheDir + "/" + name)) {
- continue;
- }
- if(this.cachedFiles.indexOf(name) != -1) {
- continue;
- }
- haxe_Log.trace("Remove non-tracked cache " + name,{ fileName : "src/server/Cache.hx", lineNumber : 70, className : "server.Cache", methodName : "setCachedFiles"});
- this.remove(name);
- }
- }
- ,log: function(client,msg) {
- this.main.serverMessage(client,msg);
- haxe_Log.trace(msg,{ fileName : "src/server/Cache.hx", lineNumber : 77, className : "server.Cache", methodName : "log"});
- }
- ,cacheYoutubeVideo: function(client,url,callback) {
- var _gthis = this;
- if(!this.isYtReady) {
- haxe_Log.trace("Do `npm i @distube/ytdl-core@latest` to use cache feature (you also need to install `ffmpeg` to build mp4 from downloaded audio/video tracks).",{ fileName : "src/server/Cache.hx", lineNumber : 82, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- return;
- }
- var videoId = utils_YoutubeUtils.extractVideoId(url);
- if(videoId == "") {
- this.log(client,"Error: youtube video id not found in url: " + url);
- return;
- }
- var outName = videoId + ".mp4";
- if(this.cachedFiles.indexOf(outName) != -1 && this.isFileExists(outName)) {
- callback(outName);
- return;
- }
- var inVideoName = "__tmp-video-" + videoId;
- var inAudioName = "__tmp-audio-" + videoId;
- if(this.isFileExists(inVideoName)) {
- this.log(client,"Caching " + outName + " already in progress");
- return;
- }
- var ytdl = require("@distube/ytdl-core");
- haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/Cache.hx", lineNumber : 125, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- this.main.send(client,{ type : "Progress", progress : { type : "Caching", ratio : 0, data : outName}});
- var agent = null;
- var cookiesPath = "" + this.main.userDir + "/cookies.json";
- if(sys_FileSystem.exists(cookiesPath)) {
- agent = ytdl.createAgent(JSON.parse(js_node_Fs.readFileSync(cookiesPath,{ encoding : "utf8"})));
- }
- var promise = ytdl.getInfo(url,{ agent : agent});
- promise.then(function(info) {
- haxe_Log.trace("Get info with " + info.formats.length + " formats",{ fileName : "src/server/Cache.hx", lineNumber : 143, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- var audioFormat;
- try {
- var ytdl1 = ytdl.chooseFormat;
- var _g = [];
- var _g1 = 0;
- var _g2 = info.formats;
- while(_g1 < _g2.length) {
- var v = _g2[_g1];
- ++_g1;
- var tmp = v.audioCodec;
- if(tmp != null ? StringTools.startsWith(tmp,"mp4a") : null) {
- _g.push(v);
- }
- }
- audioFormat = ytdl1(_g,{ quality : "highestaudio"});
- } catch( _g ) {
- var e = haxe_Exception.caught(_g);
- _gthis.log(client,"Error: audio format not found");
- haxe_Log.trace(e,{ fileName : "src/server/Cache.hx", lineNumber : 150, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- var _g1 = [];
- var _g2 = 0;
- var _g3 = info.formats;
- while(_g2 < _g3.length) {
- var v = _g3[_g2];
- ++_g2;
- if(v.hasAudio) {
- _g1.push(v);
- }
- }
- haxe_Log.trace(_g1,{ fileName : "src/server/Cache.hx", lineNumber : 151, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- return;
- }
- var videoFormat;
- var tmp = _gthis.getBestYoutubeVideoFormat(info.formats);
- if(tmp != null) {
- videoFormat = tmp;
- } else {
- _gthis.log(client,"Error: video format not found");
- var _g = [];
- var _g1 = 0;
- var _g2 = info.formats;
- while(_g1 < _g2.length) {
- var v = _g2[_g1];
- ++_g1;
- if(v.hasVideo) {
- _g.push(v);
- }
- }
- haxe_Log.trace(_g,{ fileName : "src/server/Cache.hx", lineNumber : 156, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- return;
- }
- var tmp = Std.parseInt(videoFormat.contentLength);
- var videoSize = tmp != null ? tmp : 0;
- var tmp = Std.parseInt(audioFormat.contentLength);
- var audioSize = tmp != null ? tmp : 0;
- var hasSpace = _gthis.removeOlderCache((videoSize + audioSize) * 2 + _gthis.freeSpaceBlock);
- if(!hasSpace) {
- videoFormat = _gthis.getBestYoutubeVideoFormat(info.formats,videoFormat.qualityLabel);
- var tmp = Std.parseInt(videoFormat.contentLength);
- var videoSize = tmp != null ? tmp : 0;
- var tmp = Std.parseInt(audioFormat.contentLength);
- var audioSize = tmp != null ? tmp : 0;
- var hasSpace = _gthis.removeOlderCache((videoSize + audioSize) * 2 + _gthis.freeSpaceBlock);
- if(!hasSpace) {
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- _gthis.log(client,_gthis.notEnoughSpaceErrorText);
- }
- if(!hasSpace) {
- return;
- }
- }
- var dlVideo = ytdl(url,{ format : videoFormat, agent : agent});
- dlVideo.pipe(js_node_Fs.createWriteStream("" + _gthis.cacheDir + "/" + inVideoName));
- dlVideo.on("error",function(err) {
- _gthis.log(client,"Error during video download: " + err);
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- });
- var dlAudio = ytdl(url,{ format : audioFormat, agent : agent});
- dlAudio.pipe(js_node_Fs.createWriteStream("" + _gthis.cacheDir + "/" + inAudioName));
- dlAudio.on("error",function(err) {
- _gthis.log(client,"Error during audio download: " + err);
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- });
- var count = 0;
- var onComplete = function(type) {
- count += 1;
- haxe_Log.trace("" + type + " track downloaded (" + count + "/2)",{ fileName : "src/server/Cache.hx", lineNumber : 197, className : "server.Cache", methodName : "cacheYoutubeVideo"});
- if(count < 2) {
- return;
- }
- if(!_gthis.isFileExists(inVideoName) || !_gthis.isFileExists(inAudioName)) {
- _gthis.log(client,"Input files not found for making final video");
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- return;
- }
- var size = js_node_Fs.statSync("" + _gthis.cacheDir + "/" + inVideoName).size;
- size += js_node_Fs.statSync("" + _gthis.cacheDir + "/" + inAudioName).size;
- var hasSpace = _gthis.removeOlderCache(size + _gthis.freeSpaceBlock);
- if(!hasSpace) {
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- _gthis.log(client,_gthis.notEnoughSpaceErrorText);
- }
- if(!hasSpace) {
- return;
- }
- var args = ("-y -i ./" + inVideoName + " -i ./" + inAudioName + " -c copy -map 0:v -map 1:a ./" + outName).split(" ");
- var $process = js_node_ChildProcess.spawn("ffmpeg",args,{ cwd : _gthis.cacheDir});
- var outputData = [];
- $process.stderr.on("data",function(data) {
- return outputData.push(data);
- });
- $process.on("close",function(code) {
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- if(code != 0) {
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- var errCodeMsg = "Error: ffmpeg closed with code " + code;
- var _g = [];
- var _g1 = 0;
- var _g2 = _gthis.main.clients;
- while(_g1 < _g2.length) {
- var v = _g2[_g1];
- ++_g1;
- if((v.group & 1 << ClientGroup.Admin._hx_index) != 0) {
- _g.push(v);
- }
- }
- var admins = _g;
- var _g = 0;
- while(_g < admins.length) {
- var client1 = admins[_g];
- ++_g;
- _gthis.log(client1,js_node_buffer_Buffer.concat(outputData).toString());
- _gthis.log(client1,errCodeMsg);
- }
- if(admins.indexOf(client) == -1) {
- _gthis.log(client,errCodeMsg);
- }
- return;
- }
- _gthis.add(outName);
- callback(outName);
- });
- };
- dlVideo.on("finish",function() {
- onComplete("Video");
- });
- dlAudio.on("finish",function() {
- onComplete("Audio");
- });
- dlVideo.on("progress",function(chunkLength,downloaded,contentLength) {
- var v = downloaded / contentLength;
- var ratio = v < 0 ? 0 : v > 1 ? 1 : v;
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Downloading", ratio : ratio}});
- });
- }).catch(function(err) {
- _gthis.remove(inVideoName);
- _gthis.remove(inAudioName);
- _gthis.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 1}});
- _gthis.log(client,"" + err);
- });
- }
- ,setStorageLimit: function(bytes) {
- var _gthis = this;
- this.storageLimit = bytes;
- var a = this.storageLimit;
- this.storageLimit = a < 0 ? 0 : a;
- this.getFreeDiskSpace(function(availSpace) {
- var a = availSpace - _gthis.freeSpaceBlock;
- var availSpace = a < 0 ? 0 : a;
- _gthis.removeOlderCache();
- var freeSpace = _gthis.getFreeSpace();
- if(availSpace < freeSpace) {
- var a = _gthis.storageLimit += availSpace - freeSpace;
- _gthis.storageLimit = a < 0 ? 0 : a;
- _gthis.removeOlderCache();
- }
- });
- }
- ,getFreeDiskSpace: function(callback) {
- 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.hx", lineNumber : 272, className : "server.Cache", methodName : "getFreeDiskSpace"});
- callback(this.storageLimit);
- return;
- }
- tmp("/",function(err,stats) {
- if(err != null) {
- haxe_Log.trace(err,{ fileName : "src/server/Cache.hx", lineNumber : 278, className : "server.Cache", methodName : "getFreeDiskSpace"});
- callback(_gthis.storageLimit);
- return;
- }
- callback(stats.bsize * stats.bavail);
- });
- }
- ,add: function(name) {
- if(this.cachedFiles.indexOf(name) == -1) {
- this.cachedFiles.unshift(name);
- }
- }
- ,remove: function(name) {
- HxOverrides.remove(this.cachedFiles,name);
- this.removeFile(name);
- }
- ,removeOlderCache: function(addFileSize) {
- if(addFileSize == null) {
- addFileSize = 0;
- }
- var space = this.getUsedSpace(addFileSize);
- while(space > this.storageLimit) {
- var tmp = this.cachedFiles.pop();
- if(tmp == null) {
- break;
- }
- this.removeFile(tmp);
- space = this.getUsedSpace(addFileSize);
- }
- return space < this.storageLimit;
- }
- ,removeFile: function(name) {
- var path = this.getFilePath(name);
- if(sys_FileSystem.exists(path)) {
- js_node_Fs.unlinkSync(path);
- }
- }
- ,getFreeFileName: function(fullName) {
- if(fullName == null) {
- fullName = "video.mp4";
- }
- var baseName = haxe_io_Path.withoutDirectory(haxe_io_Path.withoutExtension(fullName));
- var ext = haxe_io_Path.extension(fullName);
- var i = 1;
- while(true) {
- var name = "" + baseName + (i == 1 ? "" : "" + i) + "." + ext;
- if(!this.isFileExists(name)) {
- return name;
- }
- ++i;
- }
- }
- ,getFilePath: function(name) {
- return "" + this.cacheDir + "/" + name;
- }
- ,getFileUrl: function(name) {
- return "/" + haxe_io_Path.withoutDirectory(this.cacheDir) + "/" + name;
- }
- ,isFileExists: function(name) {
- return sys_FileSystem.exists(this.getFilePath(name));
- }
- ,getFreeSpace: function() {
- return this.storageLimit - this.getUsedSpace();
- }
- ,getUsedSpace: function(addFileSize) {
- if(addFileSize == null) {
- addFileSize = 0;
- }
- var total = addFileSize < 0 ? 0 : addFileSize;
- var arr = this.cachedFiles;
- var _g_i = arr.length - 1;
- while(_g_i > -1) {
- var name = arr[_g_i--];
- var path = this.getFilePath(name);
- if(!sys_FileSystem.exists(path)) {
- HxOverrides.remove(this.cachedFiles,name);
- continue;
- }
- total += js_node_Fs.statSync(path).size;
- }
- return total;
- }
- ,getBestYoutubeVideoFormat: function(formats,ignoreQuality) {
- var qPriority = [1080,720,480,360,240,144];
- var _g = 0;
- while(_g < qPriority.length) {
- var quality = "" + qPriority[_g++] + "p";
- if(quality == ignoreQuality) {
- continue;
- }
- var _g1 = 0;
- while(_g1 < formats.length) {
- var format = formats[_g1];
- ++_g1;
- if(format.videoCodec == null) {
- continue;
- }
- if(format.qualityLabel == quality) {
- return format;
- }
- }
- }
- return null;
- }
- ,__class__: server_Cache
-};
var server_ConsoleInput = function(main) {
var _g = new haxe_ds_StringMap();
_g.h["addAdmin"] = { args : ["name","password"], desc : "Adds channel admin"};
@@ -4165,7 +3758,7 @@ server_ConsoleInput.prototype = {
case "addAdmin":
var name = args[0];
var password = args[1];
- if(this.main.badNickName(name)) {
+ if(this.main.isBadClientName(name)) {
haxe_Log.trace(StringTools.replace(Lang.get("usernameError"),"$MAX","" + this.main.config.maxLoginLength),{ fileName : "src/server/ConsoleInput.hx", lineNumber : 113, className : "server.ConsoleInput", methodName : "parseLine"});
return;
}
@@ -4434,7 +4027,7 @@ server_HttpServer.prototype = {
}
});
stream.on("error",function(err) {
- haxe_Log.trace(err,{ fileName : "src/server/HttpServer.hx", lineNumber : 201, className : "server.HttpServer", methodName : "uploadFile"});
+ haxe_Log.trace(err,{ fileName : "src/server/HttpServer.hx", lineNumber : 202, className : "server.HttpServer", methodName : "uploadFile"});
res.statusCode = 500;
res.end(JSON.stringify({ info : "File write stream error."}));
var _this = _gthis.uploadingFilesSizes;
@@ -4448,7 +4041,7 @@ server_HttpServer.prototype = {
_gthis.cache.remove(name);
});
req.on("error",function(err) {
- haxe_Log.trace("Request Error:",{ fileName : "src/server/HttpServer.hx", lineNumber : 208, className : "server.HttpServer", methodName : "uploadFile", customParams : [err]});
+ haxe_Log.trace("Request Error:",{ fileName : "src/server/HttpServer.hx", lineNumber : 209, className : "server.HttpServer", methodName : "uploadFile", customParams : [err]});
stream.destroy();
res.statusCode = 500;
res.end(JSON.stringify({ info : "File request error."}));
@@ -4736,7 +4329,7 @@ var server_Main = function(opts) {
this.wsEventParser = new JsonParser_$1();
this.freeIds = [];
this.clients = [];
- this.playersCacheSupport = [];
+ this.playersCacheSupport = ["RawType"];
this.rootDir = "" + __dirname + "/..";
var _gthis = this;
this.isNoState = !opts.loadState;
@@ -4761,7 +4354,7 @@ var server_Main = function(opts) {
this.logger = new server_Logger(this.logsDir,10,this.verbose);
this.consoleInput = new server_ConsoleInput(this);
this.consoleInput.initConsoleInput();
- this.cache = new server_Cache(this,this.cacheDir);
+ this.cache = new server_cache_Cache(this,this.cacheDir);
if(this.cache.isYtReady) {
this.playersCacheSupport.push("YoutubeType");
}
@@ -4795,7 +4388,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 : 137, 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 : 138, className : "server.Main", methodName : "new"});
attempts -= 1;
_gthis.port++;
preparePort();
@@ -4822,16 +4415,16 @@ server_Main.jsonFilterNulls = function(key,value) {
server_Main.prototype = {
runServer: function() {
var _gthis = this;
- haxe_Log.trace("Local: http://" + this.localIp + ":" + this.port,{ fileName : "src/server/Main.hx", lineNumber : 150, className : "server.Main", methodName : "runServer"});
+ haxe_Log.trace("Local: http://" + this.localIp + ":" + this.port,{ fileName : "src/server/Main.hx", lineNumber : 151, className : "server.Main", methodName : "runServer"});
if(this.config.localNetworkOnly) {
- haxe_Log.trace("Global network is disabled in config",{ fileName : "src/server/Main.hx", lineNumber : 152, className : "server.Main", methodName : "runServer"});
+ haxe_Log.trace("Global network is disabled in config",{ fileName : "src/server/Main.hx", lineNumber : 153, 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: http://" + _gthis.globalIp + ":" + _gthis.port,{ fileName : "src/server/Main.hx", lineNumber : 158, className : "server.Main", methodName : "runServer"});
+ haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + _gthis.port,{ fileName : "src/server/Main.hx", lineNumber : 159, className : "server.Main", methodName : "runServer"});
});
}
var dir = "" + this.rootDir + "/res";
@@ -4916,7 +4509,7 @@ 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 : 232, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 233, className : "server.Main", methodName : "getUserConfig"});
}
config[field] = Reflect.field(customConfig,field);
}
@@ -4927,14 +4520,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 : 238, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: emote name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 239, 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 : 242, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: emote url of name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 243, className : "server.Main", methodName : "getUserConfig"});
}
emoteCopies_h[emote.image] = true;
}
@@ -4971,7 +4564,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 : 280, className : "server.Main", methodName : "saveState"});
+ haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 281, className : "server.Main", methodName : "saveState"});
var json = JSON.stringify(this.getCurrentState(),null,"\t");
js_node_Fs.writeFileSync(this.statePath,json);
this.writeUsers(this.userList);
@@ -4986,7 +4579,7 @@ server_Main.prototype = {
if(!sys_FileSystem.exists(this.statePath)) {
return;
}
- haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 304, className : "server.Main", methodName : "loadState"});
+ haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 305, 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 : [];
@@ -5008,7 +4601,7 @@ server_Main.prototype = {
}
,logError: function(type,data) {
this.cache.removeOlderCache(1048576);
- haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 328, className : "server.Main", methodName : "logError", customParams : [data]});
+ haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 329, 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;
@@ -5030,7 +4623,7 @@ server_Main.prototype = {
if(_gthis.clients.length == 0) {
return;
}
- haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 341, className : "server.Main", methodName : "initIntergationHandlers"});
+ haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 342, className : "server.Main", methodName : "initIntergationHandlers"});
js_node_Http.get(url,null,function(r) {
});
};
@@ -5049,13 +4642,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 : 362, className : "server.Main", methodName : "addAdmin"});
+ haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 363, 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 : 369, className : "server.Main", methodName : "removeAdmin"});
+ haxe_Log.trace("Admin " + name + " removed.",{ fileName : "src/server/Main.hx", lineNumber : 370, className : "server.Main", methodName : "removeAdmin"});
}
,replayLog: function(events) {
var _gthis = this;
@@ -5122,7 +4715,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 : 428, className : "server.Main", methodName : "onConnect", customParams : ["" + name + " connected (" + ip + ")"]});
+ haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 429, className : "server.Main", methodName : "onConnect", customParams : ["" + name + " connected (" + ip + ")"]});
var isAdmin = this.config.localAdmins && req.socket.localAddress == ip;
var client = new Client(ws,req,id,name,0);
client.uuid = uuid;
@@ -5136,7 +4729,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 : 445, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace(errors,{ fileName : "src/server/Main.hx", lineNumber : 446, className : "server.Main", methodName : "onConnect"});
_gthis.serverMessage(client,errors);
return;
}
@@ -5212,7 +4805,19 @@ server_Main.prototype = {
}
} else {
var _g = item.playerType;
- if(_g == "YoutubeType") {
+ 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) {
@@ -5225,7 +4830,8 @@ server_Main.prototype = {
_gthis.restartWaitTimer();
}
});
- } else {
+ break;
+ default:
var name = StringTools.replace("" + _g,"Type","");
this.serverMessage(client,"No cache support for " + name + " player.");
data.addVideo.item = item;
@@ -5318,7 +4924,7 @@ server_Main.prototype = {
if(!internal) {
return;
}
- haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 510, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " disconnected"]});
+ haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 511, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " disconnected"]});
server_Utils.sortedPush(this.freeIds,client.id);
HxOverrides.remove(this.clients,client);
this.sendClientList();
@@ -5432,7 +5038,7 @@ server_Main.prototype = {
case "Login":
var name = StringTools.trim(data.login.clientName);
var lcName = name.toLowerCase();
- if(this.badNickName(lcName)) {
+ if(this.isBadClientName(lcName)) {
this.serverMessage(client,"usernameError");
this.send(client,{ type : "LoginError"});
return;
@@ -5458,7 +5064,7 @@ server_Main.prototype = {
this.send(client,{ type : "LoginError"});
return;
}
- haxe_Log.trace(HxOverrides.dateStr(new Date()),{ fileName : "src/server/Main.hx", lineNumber : 601, 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 : 602, className : "server.Main", methodName : "onMessage", customParams : ["Client " + client.name + " logged as " + name]});
client.name = name;
client.setGroupFlag(ClientGroup.User,true);
this.checkBan(client);
@@ -5471,7 +5077,7 @@ 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 : 622, 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 : 623, 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);
break;
@@ -5801,13 +5407,13 @@ 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 : 1058, className : "server.Main", methodName : "checkBan"});
+ haxe_Log.trace("" + client.name + " ban removed",{ fileName : "src/server/Main.hx", lineNumber : 1064, className : "server.Main", methodName : "checkBan"});
this.sendClientList();
}
break;
}
}
- ,badNickName: function(name) {
+ ,isBadClientName: function(name) {
if(name.length > this.config.maxLoginLength) {
return true;
}
@@ -5907,6 +5513,11 @@ server_Main.prototype = {
}
return false;
}
+ ,hasPlaylistUrl: function(url) {
+ return this.videoList.exists(function(item) {
+ return item.url == url;
+ });
+ }
,__class__: server_Main
};
var server_Utils = function() { };
@@ -6110,6 +5721,705 @@ server_VideoTimer.prototype = {
}
,__class__: server_VideoTimer
};
+var server_cache_Cache = function(main,cacheDir) {
+ this.cachedFiles = [];
+ this.freeSpaceBlock = 10485760;
+ this.storageLimit = 3145728 * 1024;
+ this.isYtReady = false;
+ this.notEnoughSpaceErrorText = "Error: Not enough free space on server or file size is out of cache storage limit.";
+ this.main = main;
+ this.cacheDir = cacheDir;
+ server_Utils.ensureDir(cacheDir);
+ this.youtubeCache = new server_cache_YoutubeCache(main,this);
+ this.rawCache = new server_cache_RawCache(main,this);
+ this.isYtReady = this.youtubeCache.checkYtDeps();
+ if(this.isYtReady) {
+ this.youtubeCache.cleanYtInputFiles();
+ }
+};
+server_cache_Cache.__name__ = true;
+server_cache_Cache.prototype = {
+ getCachedFiles: function() {
+ return this.cachedFiles;
+ }
+ ,setCachedFiles: function(names) {
+ this.cachedFiles.length = 0;
+ var _g = 0;
+ while(_g < names.length) this.cachedFiles.push(names[_g++]);
+ var names = js_node_Fs.readdirSync(this.cacheDir);
+ var _g = 0;
+ while(_g < names.length) {
+ var name = names[_g];
+ ++_g;
+ if(StringTools.startsWith(name,".")) {
+ continue;
+ }
+ if(sys_FileSystem.isDirectory("" + this.cacheDir + "/" + name)) {
+ continue;
+ }
+ if(this.cachedFiles.indexOf(name) != -1) {
+ continue;
+ }
+ haxe_Log.trace("Remove non-tracked cache " + name,{ fileName : "src/server/cache/Cache.hx", lineNumber : 47, className : "server.cache.Cache", methodName : "setCachedFiles"});
+ this.remove(name);
+ }
+ }
+ ,log: function(client,msg) {
+ this.main.serverMessage(client,msg);
+ haxe_Log.trace(msg,{ fileName : "src/server/cache/Cache.hx", lineNumber : 54, className : "server.cache.Cache", methodName : "log"});
+ }
+ ,cacheYoutubeVideo: function(client,url,callback) {
+ this.youtubeCache.cacheYoutubeVideo(client,url,callback);
+ }
+ ,cacheRawVideo: function(client,url,callback) {
+ this.rawCache.cacheRawVideo(client,url,callback);
+ }
+ ,setStorageLimit: function(bytes) {
+ var _gthis = this;
+ this.storageLimit = bytes;
+ var a = this.storageLimit;
+ this.storageLimit = a < 0 ? 0 : a;
+ this.getFreeDiskSpace(function(availSpace) {
+ var a = availSpace - _gthis.freeSpaceBlock;
+ var availSpace = a < 0 ? 0 : a;
+ _gthis.removeOlderCache();
+ var freeSpace = _gthis.getFreeSpace();
+ if(availSpace < freeSpace) {
+ var a = _gthis.storageLimit += availSpace - freeSpace;
+ _gthis.storageLimit = a < 0 ? 0 : a;
+ _gthis.removeOlderCache();
+ }
+ });
+ }
+ ,getFreeDiskSpace: function(callback) {
+ 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 : 83, className : "server.cache.Cache", methodName : "getFreeDiskSpace"});
+ callback(this.storageLimit);
+ return;
+ }
+ tmp("/",function(err,stats) {
+ if(err != null) {
+ haxe_Log.trace(err,{ fileName : "src/server/cache/Cache.hx", lineNumber : 89, className : "server.cache.Cache", methodName : "getFreeDiskSpace"});
+ callback(_gthis.storageLimit);
+ return;
+ }
+ callback(stats.bsize * stats.bavail);
+ });
+ }
+ ,add: function(name) {
+ if(this.cachedFiles.indexOf(name) == -1) {
+ this.cachedFiles.unshift(name);
+ }
+ }
+ ,remove: function(name) {
+ HxOverrides.remove(this.cachedFiles,name);
+ this.removeFile(name);
+ }
+ ,exists: function(name) {
+ if(this.cachedFiles.indexOf(name) != -1) {
+ return this.isFileExists(name);
+ } else {
+ return false;
+ }
+ }
+ ,removeOlderCache: function(addFileSize) {
+ if(addFileSize == null) {
+ addFileSize = 0;
+ }
+ var space = this.getUsedSpace(addFileSize);
+ var arr = this.cachedFiles;
+ var _g_i = arr.length - 1;
+ while(_g_i > -1) {
+ var name = arr[_g_i--];
+ if(space <= this.storageLimit) {
+ break;
+ }
+ if(this.main.hasPlaylistUrl(this.getFileUrl(name))) {
+ continue;
+ }
+ this.remove(name);
+ space = this.getUsedSpace(addFileSize);
+ }
+ return space < this.storageLimit;
+ }
+ ,removeFile: function(name) {
+ var path = this.getFilePath(name);
+ if(sys_FileSystem.exists(path)) {
+ js_node_Fs.unlinkSync(path);
+ }
+ }
+ ,getFreeFileName: function(fullName) {
+ if(fullName == null) {
+ fullName = "video.mp4";
+ }
+ var baseName = haxe_io_Path.withoutDirectory(haxe_io_Path.withoutExtension(fullName));
+ var ext = haxe_io_Path.extension(fullName);
+ var i = 1;
+ while(true) {
+ var name = "" + baseName + (i == 1 ? "" : "" + i) + "." + ext;
+ if(!this.isFileExists(name)) {
+ return name;
+ }
+ ++i;
+ }
+ }
+ ,getFilePath: function(name) {
+ return "" + this.cacheDir + "/" + name;
+ }
+ ,getFileUrl: function(name) {
+ return "/" + haxe_io_Path.withoutDirectory(this.cacheDir) + "/" + name;
+ }
+ ,isFileExists: function(name) {
+ return sys_FileSystem.exists(this.getFilePath(name));
+ }
+ ,getFreeSpace: function() {
+ return this.storageLimit - this.getUsedSpace();
+ }
+ ,getUsedSpace: function(addFileSize) {
+ if(addFileSize == null) {
+ addFileSize = 0;
+ }
+ var total = addFileSize < 0 ? 0 : addFileSize;
+ var arr = this.cachedFiles;
+ var _g_i = arr.length - 1;
+ while(_g_i > -1) {
+ var name = arr[_g_i--];
+ var path = this.getFilePath(name);
+ if(!sys_FileSystem.exists(path)) {
+ HxOverrides.remove(this.cachedFiles,name);
+ continue;
+ }
+ total += js_node_Fs.statSync(path).size;
+ }
+ return total;
+ }
+ ,__class__: server_cache_Cache
+};
+var server_cache_RawCache = function(main,cache) {
+ this.main = main;
+ this.cache = cache;
+};
+server_cache_RawCache.__name__ = true;
+server_cache_RawCache.prototype = {
+ cacheRawVideo: function(client,url,callback) {
+ var isM3U8 = url.indexOf(".m3u8") != -1;
+ var ext = isM3U8 ? "m3u8" : "mp4";
+ var matchName = new EReg("^([^:.]+)\\.(.+)","");
+ var decodedUrl;
+ try {
+ decodedUrl = decodeURIComponent(url.split("+").join(" "));
+ } catch( _g ) {
+ decodedUrl = url;
+ }
+ 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);
+ 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}});
+ if(isM3U8) {
+ this.handleM3u8(client,url,outName,callback);
+ } else {
+ this.handleMp4(client,url,outName,callback);
+ }
+ }
+ ,handleMp4: function(client,url,outName,callback) {
+ var _gthis = this;
+ this.downloadFile(client,url,outName,function(downloaded,total) {
+ var v = downloaded / total;
+ _gthis.main.send(client,{ type : "Progress", progress : { type : "Downloading", ratio : v < 0 ? 0 : v > 1 ? 1 : v}});
+ },function() {
+ _gthis.cache.add(outName);
+ callback(outName);
+ },function(err) {
+ _gthis.log(client,"Mp4 download failed: " + err);
+ _gthis.cancelProgress(client);
+ });
+ }
+ ,handleM3u8: function(client,url,outName,callback) {
+ var _gthis = this;
+ var useProxy = true;
+ this.downloadM3u8Playlist(client,url,useProxy,function(playlist,totalSize,segments) {
+ if(useProxy) {
+ totalSize = playlist.length;
+ }
+ if(!_gthis.cache.removeOlderCache(totalSize + _gthis.cache.freeSpaceBlock)) {
+ _gthis.log(client,_gthis.cache.notEnoughSpaceErrorText);
+ _gthis.cancelProgress(client);
+ return;
+ }
+ if(useProxy) {
+ _gthis.main.send(client,{ type : "Progress", progress : { type : "Caching", ratio : 1, data : outName}});
+ js_node_Fs.writeFileSync("" + _gthis.cache.cacheDir + "/" + outName,playlist);
+ _gthis.cache.add(outName);
+ callback(outName);
+ return;
+ }
+ var activeDownloads = 0;
+ var maxParallelDownloads = 10;
+ var downloaded = 0;
+ var downloadNextBatch = null;
+ downloadNextBatch = function() {
+ var _g = 0;
+ while(_g < segments.length) {
+ var segment = [segments[_g]];
+ ++_g;
+ if(activeDownloads >= maxParallelDownloads) {
+ break;
+ }
+ if(segment[0].started) {
+ continue;
+ }
+ segment[0].started = true;
+ activeDownloads += 1;
+ haxe_Log.trace("download segment",{ fileName : "src/server/cache/RawCache.hx", lineNumber : 118, className : "server.cache.RawCache", methodName : "handleM3u8", customParams : [segment[0].i]});
+ _gthis.downloadFile(client,segment[0].url,segment[0].name,(function() {
+ return function(downloadedBytes,totalBytes) {
+ };
+ })(),(function(segment) {
+ return function() {
+ activeDownloads -= 1;
+ segment[0].completed = true;
+ downloaded += 1;
+ var progress = downloaded / segments.length;
+ _gthis.main.send(client,{ type : "Progress", progress : { type : "Downloading", ratio : 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 : 138, className : "server.cache.RawCache", methodName : "handleM3u8"});
+ js_node_Fs.writeFileSync("" + _gthis.cache.cacheDir + "/" + outName,playlist);
+ _gthis.cache.add(outName);
+ callback(outName);
+ } else {
+ downloadNextBatch();
+ }
+ };
+ })(segment),(function(segment) {
+ return function(err) {
+ activeDownloads -= 1;
+ downloaded += 1;
+ _gthis.log(client,"TS segment " + segment[0].i + " download failed: " + err);
+ _gthis.cancelProgress(client);
+ var _gthis1 = _gthis;
+ var result = new Array(segments.length);
+ var _g = 0;
+ var _g1 = segments.length;
+ while(_g < _g1) {
+ var i = _g++;
+ result[i] = segments[i].name;
+ }
+ _gthis1.cleanupFiles(result);
+ };
+ })(segment));
+ }
+ };
+ downloadNextBatch();
+ },function(err) {
+ _gthis.log(client,"M3U8 processing failed: " + err);
+ _gthis.cancelProgress(client);
+ });
+ }
+ ,request: function(url,options,callback) {
+ var httpsOptions = options != null ? options : { };
+ httpsOptions.rejectUnauthorized = false;
+ httpsOptions.headers = httpsOptions.headers != null ? httpsOptions.headers : { };
+ if(StringTools.startsWith(url,"https:")) {
+ return js_node_Https.request(url,httpsOptions,callback);
+ } else {
+ return js_node_Http.request(url,httpsOptions,callback);
+ }
+ }
+ ,downloadM3u8Playlist: function(client,url,useProxy,onSuccess,onError) {
+ var _gthis = this;
+ var req = this.request(url,{ 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.36", "Accept" : "*/*"}},function(res) {
+ if(res.statusCode >= 300 && res.statusCode < 400) {
+ var redirectUrl = res.headers["location"];
+ if(redirectUrl != null) {
+ _gthis.downloadM3u8Playlist(client,redirectUrl,useProxy,onSuccess,onError);
+ return;
+ }
+ }
+ var body = [];
+ res.on("data",function(chunk) {
+ return body.push(chunk);
+ });
+ res.on("end",function() {
+ try {
+ var buffer = js_node_buffer_Buffer.concat(body);
+ var content = buffer.toString();
+ if(!new EReg("^#EXTM3U","").match(content)) {
+ onError("Invalid M3U8 playlist");
+ return;
+ }
+ var baseUrl = url.substring(0,url.lastIndexOf("/") + 1);
+ var segments = [];
+ var lines = content.split("\n");
+ var _g_current = 0;
+ var _g_array = lines;
+ while(_g_current < _g_array.length) {
+ var _g_value = _g_array[_g_current++];
+ var _g_key = _g_current - 1;
+ var line = StringTools.trim(_g_value);
+ if(line.length == 0) {
+ continue;
+ }
+ if(StringTools.startsWith(line,"#")) {
+ continue;
+ }
+ var segmentUrl = line.indexOf("://") == -1 ? baseUrl + line : line;
+ var i = segments.length;
+ var segment = { i : i, url : segmentUrl, started : false, completed : false, name : "segment" + i + ".ts"};
+ segments.push(segment);
+ lines[_g_key] = "./" + segment.name;
+ if(useProxy) {
+ lines[_g_key] = "/proxy?url=" + segmentUrl;
+ }
+ }
+ var req = _gthis.request(segments[0].url,{ method : "GET"},function(res) {
+ var tmp = Std.parseInt(res.headers["content-length"]);
+ var totalSize = (tmp != null ? tmp : 0) * (segments.length + 1);
+ if(totalSize == 0) {
+ onError("Failed to get segment sizes: no content-length");
+ return;
+ }
+ onSuccess(lines.join("\n"),totalSize,segments);
+ });
+ req.on("error",function(err) {
+ onError("Request error: failed to get segment sizes");
+ });
+ req.end();
+ } catch( _g ) {
+ var _g1 = haxe_Exception.caught(_g);
+ onError("Playlist processing error: " + Std.string(_g1));
+ }
+ });
+ });
+ req.on("error",onError);
+ req.end();
+ }
+ ,downloadFile: function(client,url,fileName,onProgress,onComplete,onError) {
+ var file = js_node_Fs.createWriteStream("" + this.cache.cacheDir + "/" + fileName);
+ 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"]);
+ total = tmp != null ? tmp : 0;
+ var downloaded = 0;
+ res.on("data",function(chunk) {
+ downloaded += chunk.length;
+ onProgress(downloaded,total);
+ if(!file.write(chunk)) {
+ res.pause();
+ file.once("drain",function() {
+ return res.resume();
+ });
+ }
+ });
+ res.on("end",function() {
+ file.end();
+ });
+ res.on("error",function(err) {
+ file.destroy();
+ onError("Response error: " + err);
+ });
+ });
+ file.on("finish",onComplete);
+ file.on("error",function(err) {
+ req.destroy();
+ onError("File error: " + err);
+ });
+ req.on("error",function(err) {
+ file.destroy();
+ onError("Request failed: " + err);
+ });
+ req.end();
+ }
+ ,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);
+ }
+ }
+ }
+ ,log: function(client,msg) {
+ this.cache.log(client,msg);
+ }
+ ,cancelProgress: function(client) {
+ this.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 0}});
+ }
+ ,__class__: server_cache_RawCache
+};
+var server_cache_YoutubeCache = function(main,cache) {
+ this.main = main;
+ this.cache = cache;
+};
+server_cache_YoutubeCache.__name__ = true;
+server_cache_YoutubeCache.prototype = {
+ checkYtDeps: function() {
+ var ytdl;
+ try {
+ ytdl = require("@distube/ytdl-core");
+ } catch( _g ) {
+ return false;
+ }
+ try {
+ js_node_ChildProcess.execSync("ffmpeg -version",{ stdio : "ignore", timeout : 3000});
+ return true;
+ } catch( _g ) {
+ return false;
+ }
+ }
+ ,cleanYtInputFiles: function() {
+ 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,"__tmp")) {
+ continue;
+ }
+ this.cache.remove(name);
+ }
+ }
+ ,cacheYoutubeVideo: function(client,url,callback) {
+ var _gthis = this;
+ if(!this.cache.isYtReady) {
+ haxe_Log.trace("Do `npm i @distube/ytdl-core@latest` 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 : 46, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ return;
+ }
+ var videoId = utils_YoutubeUtils.extractVideoId(url);
+ if(videoId == "") {
+ this.log(client,"Error: youtube video id not found in url: " + url);
+ return;
+ }
+ var outName = videoId + ".mp4";
+ if(this.cache.exists(outName)) {
+ callback(outName);
+ return;
+ }
+ var inVideoName = "__tmp-video-" + videoId;
+ var inAudioName = "__tmp-audio-" + videoId;
+ if(this.cache.isFileExists(inVideoName)) {
+ this.log(client,"Caching " + outName + " already in progress");
+ return;
+ }
+ var ytdl = require("@distube/ytdl-core");
+ haxe_Log.trace("Caching " + url + " to " + outName + "...",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 80, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ this.main.send(client,{ type : "Progress", progress : { type : "Caching", ratio : 0, data : outName}});
+ var agent = null;
+ var cookiesPath = "" + this.main.userDir + "/cookies.json";
+ if(sys_FileSystem.exists(cookiesPath)) {
+ agent = ytdl.createAgent(JSON.parse(js_node_Fs.readFileSync(cookiesPath,{ encoding : "utf8"})));
+ }
+ var promise = ytdl.getInfo(url,{ agent : agent});
+ promise.then(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"});
+ var audioFormat;
+ try {
+ var ytdl1 = ytdl.chooseFormat;
+ var _g = [];
+ var _g1 = 0;
+ var _g2 = info.formats;
+ while(_g1 < _g2.length) {
+ var v = _g2[_g1];
+ ++_g1;
+ var tmp = v.audioCodec;
+ if(tmp != null ? StringTools.startsWith(tmp,"mp4a") : null) {
+ _g.push(v);
+ }
+ }
+ audioFormat = ytdl1(_g,{ quality : "highestaudio"});
+ } catch( _g ) {
+ var e = haxe_Exception.caught(_g);
+ _gthis.log(client,"Error: audio format not found");
+ haxe_Log.trace(e,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 105, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ var _g1 = [];
+ var _g2 = 0;
+ var _g3 = info.formats;
+ while(_g2 < _g3.length) {
+ var v = _g3[_g2];
+ ++_g2;
+ if(v.hasAudio) {
+ _g1.push(v);
+ }
+ }
+ haxe_Log.trace(_g1,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 106, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ return;
+ }
+ var videoFormat;
+ var tmp = _gthis.getBestYoutubeVideoFormat(info.formats);
+ if(tmp != null) {
+ videoFormat = tmp;
+ } else {
+ _gthis.log(client,"Error: video format not found");
+ var _g = [];
+ var _g1 = 0;
+ var _g2 = info.formats;
+ while(_g1 < _g2.length) {
+ var v = _g2[_g1];
+ ++_g1;
+ if(v.hasVideo) {
+ _g.push(v);
+ }
+ }
+ haxe_Log.trace(_g,{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 111, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ return;
+ }
+ var tmp = Std.parseInt(videoFormat.contentLength);
+ var videoSize = tmp != null ? tmp : 0;
+ var tmp = Std.parseInt(audioFormat.contentLength);
+ var audioSize = tmp != null ? tmp : 0;
+ var hasSpace = _gthis.cache.removeOlderCache((videoSize + audioSize) * 2 + _gthis.cache.freeSpaceBlock);
+ if(!hasSpace) {
+ videoFormat = _gthis.getBestYoutubeVideoFormat(info.formats,videoFormat.qualityLabel);
+ var tmp = Std.parseInt(videoFormat.contentLength);
+ var videoSize = tmp != null ? tmp : 0;
+ var tmp = Std.parseInt(audioFormat.contentLength);
+ var audioSize = tmp != null ? tmp : 0;
+ var hasSpace = _gthis.cache.removeOlderCache((videoSize + audioSize) * 2 + _gthis.cache.freeSpaceBlock);
+ if(!hasSpace) {
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ _gthis.log(client,_gthis.cache.notEnoughSpaceErrorText);
+ }
+ if(!hasSpace) {
+ return;
+ }
+ }
+ var dlVideo = ytdl(url,{ format : videoFormat, agent : agent});
+ dlVideo.pipe(js_node_Fs.createWriteStream("" + _gthis.cache.cacheDir + "/" + inVideoName));
+ dlVideo.on("error",function(err) {
+ _gthis.log(client,"Error during video download: " + err);
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ });
+ var dlAudio = ytdl(url,{ format : audioFormat, agent : agent});
+ dlAudio.pipe(js_node_Fs.createWriteStream("" + _gthis.cache.cacheDir + "/" + inAudioName));
+ dlAudio.on("error",function(err) {
+ _gthis.log(client,"Error during audio download: " + err);
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ });
+ var count = 0;
+ var onComplete = function(type) {
+ count += 1;
+ haxe_Log.trace("" + type + " track downloaded (" + count + "/2)",{ fileName : "src/server/cache/YoutubeCache.hx", lineNumber : 153, className : "server.cache.YoutubeCache", methodName : "cacheYoutubeVideo"});
+ if(count < 2) {
+ return;
+ }
+ if(!_gthis.cache.isFileExists(inVideoName) || !_gthis.cache.isFileExists(inAudioName)) {
+ _gthis.log(client,"Input files not found for making final video");
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ return;
+ }
+ var size = js_node_Fs.statSync("" + _gthis.cache.cacheDir + "/" + inVideoName).size;
+ size += js_node_Fs.statSync("" + _gthis.cache.cacheDir + "/" + inAudioName).size;
+ var hasSpace = _gthis.cache.removeOlderCache(size + _gthis.cache.freeSpaceBlock);
+ if(!hasSpace) {
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ _gthis.log(client,_gthis.cache.notEnoughSpaceErrorText);
+ }
+ if(!hasSpace) {
+ return;
+ }
+ var args = ("-y -i ./" + inVideoName + " -i ./" + inAudioName + " -c copy -map 0:v -map 1:a ./" + outName).split(" ");
+ var $process = js_node_ChildProcess.spawn("ffmpeg",args,{ cwd : _gthis.cache.cacheDir});
+ var outputData = [];
+ $process.stderr.on("data",function(data) {
+ return outputData.push(data);
+ });
+ $process.on("close",function(code) {
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ if(code != 0) {
+ _gthis.cancelProgress(client);
+ var errCodeMsg = "Error: ffmpeg closed with code " + code;
+ var _g = [];
+ var _g1 = 0;
+ var _g2 = _gthis.main.clients;
+ while(_g1 < _g2.length) {
+ var v = _g2[_g1];
+ ++_g1;
+ if((v.group & 1 << ClientGroup.Admin._hx_index) != 0) {
+ _g.push(v);
+ }
+ }
+ var admins = _g;
+ var _g = 0;
+ while(_g < admins.length) {
+ var client1 = admins[_g];
+ ++_g;
+ _gthis.log(client1,js_node_buffer_Buffer.concat(outputData).toString());
+ _gthis.log(client1,errCodeMsg);
+ }
+ if(admins.indexOf(client) == -1) {
+ _gthis.log(client,errCodeMsg);
+ }
+ return;
+ }
+ _gthis.cache.add(outName);
+ callback(outName);
+ });
+ };
+ dlVideo.on("finish",function() {
+ onComplete("Video");
+ });
+ dlAudio.on("finish",function() {
+ onComplete("Audio");
+ });
+ dlVideo.on("progress",function(chunkLength,downloaded,contentLength) {
+ var v = downloaded / contentLength;
+ var ratio = v < 0 ? 0 : v > 1 ? 1 : v;
+ _gthis.main.send(client,{ type : "Progress", progress : { type : "Downloading", ratio : ratio}});
+ });
+ }).catch(function(err) {
+ _gthis.cache.remove(inVideoName);
+ _gthis.cache.remove(inAudioName);
+ _gthis.cancelProgress(client);
+ _gthis.log(client,"" + err);
+ });
+ }
+ ,getBestYoutubeVideoFormat: function(formats,ignoreQuality) {
+ var qPriority = [1080,720,480,360,240,144];
+ var _g = 0;
+ while(_g < qPriority.length) {
+ var quality = "" + qPriority[_g++] + "p";
+ if(quality == ignoreQuality) {
+ continue;
+ }
+ var _g1 = 0;
+ while(_g1 < formats.length) {
+ var format = formats[_g1];
+ ++_g1;
+ if(format.videoCodec == null) {
+ continue;
+ }
+ if(format.qualityLabel == quality) {
+ return format;
+ }
+ }
+ }
+ return null;
+ }
+ ,log: function(client,msg) {
+ this.cache.log(client,msg);
+ }
+ ,cancelProgress: function(client) {
+ this.main.send(client,{ type : "Progress", progress : { type : "Canceled", ratio : 0}});
+ }
+ ,__class__: server_cache_YoutubeCache
+};
var sys_FileSystem = function() { };
sys_FileSystem.__name__ = true;
sys_FileSystem.exists = function(path) {
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage