diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Types.hx | 1 | ||||
| -rw-r--r-- | src/client/Main.hx | 6 | ||||
| -rw-r--r-- | src/server/Cache.hx | 108 | ||||
| -rw-r--r-- | src/server/HttpServer.hx | 5 | ||||
| -rw-r--r-- | src/server/Logger.hx | 4 | ||||
| -rw-r--r-- | src/server/Main.hx | 6 |
6 files changed, 101 insertions, 29 deletions
diff --git a/src/Types.hx b/src/Types.hx index ac2599c..2dfe899 100644 --- a/src/Types.hx +++ b/src/Types.hx @@ -117,6 +117,7 @@ enum abstract ProgressType(String) { var Caching; var Downloading; var Uploading; + var Canceled; } @:using(Types.VideoItemTools) diff --git a/src/client/Main.hx b/src/client/Main.hx index 5c4b28d..72a619a 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -327,7 +327,8 @@ class Main { if (url.startsWith("/")) { final host = Browser.location.hostname; final port = Browser.location.port; - url = '$protocol//$host:$port$url'; + final colonPort = port.length > 0 ? ':$port' : port; + url = '$protocol//$host$colonPort$url'; } if (!url.startsWith("http")) url = '$protocol//$url'; @@ -667,6 +668,9 @@ class Main { '$caching $name'; case Downloading: Lang.get("downloading"); case Uploading: Lang.get("uploading"); + case Canceled: + hideDynamicChin(); + return; } final percent = (data.ratio * 100).toFixed(1); var text = '$text...'; diff --git a/src/server/Cache.hx b/src/server/Cache.hx index d772648..cfd5d88 100644 --- a/src/server/Cache.hx +++ b/src/server/Cache.hx @@ -2,6 +2,7 @@ package server; import haxe.io.Path; import js.lib.Promise; +import js.node.Buffer; import js.node.ChildProcess; import js.node.Fs.Fs; import js.node.stream.Readable; @@ -9,16 +10,16 @@ import sys.FileSystem; import utils.YoutubeUtils; class Cache { - final main:Main; - final cacheDir:String; - - public final cachedFiles:Array<String> = []; + public final notEnoughSpaceErrorText = "Error: Not enough free space on server or file size is out of cache storage limit."; public final isYtReady = false; /** In bytes **/ public var storageLimit(default, null) = 3 * 1024 * 1024 * 1024; + final main:Main; + final cacheDir:String; + final cachedFiles:Array<String> = []; final freeSpaceBlock = 10 * 1024 * 1024; // 10MB public function new(main:Main, cacheDir:String) { @@ -51,6 +52,24 @@ class Cache { } } + public function getCachedFiles():Array<String> { + return cachedFiles; + } + + public function setCachedFiles(names:Array<String>) { + cachedFiles.resize(0); + for (name in names) cachedFiles.push(name); + + final names = FileSystem.readDirectory(cacheDir); + for (name in names) { + if (name.startsWith(".")) continue; + if (FileSystem.isDirectory('$cacheDir/$name')) continue; + if (cachedFiles.contains(name)) continue; + trace('Remove non-tracked cache $name'); + remove(name); + } + } + function log(client:Client, msg:String):Void { main.serverMessage(client, msg); trace(msg); @@ -77,6 +96,15 @@ class Cache { remove(inVideoName); remove(inAudioName); } + inline function cancelProgress():Void { + main.send(client, { + type: Progress, + progress: { + type: Canceled, + ratio: 1 + } + }); + } if (isFileExists(inVideoName)) { log(client, 'Caching $outName already in progress'); return; @@ -117,6 +145,7 @@ class Cache { dlVideo.on("error", err -> { log(client, "Error during video download: " + err); removeInputFiles(); + cancelProgress(); }); final dlAudio:Readable<Dynamic> = ytdl(url, { @@ -126,6 +155,7 @@ class Cache { dlAudio.on("error", err -> { log(client, "Error during audio download: " + err); removeInputFiles(); + cancelProgress(); }); var count = 0; @@ -134,26 +164,40 @@ class Cache { trace('$type track downloaded ($count/2)'); if (count < 2) return; if (!isFileExists(inVideoName) || !isFileExists(inAudioName)) { + log(client, "Input files not found for making final video"); removeInputFiles(); + cancelProgress(); return; } var size = FileSystem.stat('$cacheDir/$inVideoName').size; size += FileSystem.stat('$cacheDir/$inAudioName').size; // clean some space for full mp4 - removeOlderCache(size + freeSpaceBlock); + final hasSpace = removeOlderCache(size + freeSpaceBlock); + if (!hasSpace) { + removeInputFiles(); + cancelProgress(); + log(client, notEnoughSpaceErrorText); + return; + } final args = '-y -i ./$inVideoName -i ./$inAudioName -c copy -map 0:v -map 1:a ./$outName'.split(" "); final process = ChildProcess.spawn("ffmpeg", args, { cwd: cacheDir, - stdio: "ignore" + // stdio: "ignore" }); - // process.stderr.on('data', (data) -> { - // trace('FFmpeg stderr: ${data}'); - // }); + final outputData:Array<Buffer> = []; + process.stderr.on("data", (data) -> outputData.push(data)); process.on("close", (code:Int) -> { removeInputFiles(); if (code != 0) { - log(client, 'Error: ffmpeg closed with code $code'); + cancelProgress(); + final errCodeMsg = 'Error: ffmpeg closed with code $code'; + final admins = main.clients.filter(client -> client.isAdmin); + for (client in admins) { + log(client, Buffer.concat(outputData).toString()); + log(client, errCodeMsg); + } + if (!admins.contains(client)) log(client, errCodeMsg); return; } add(outName); @@ -163,18 +207,28 @@ class Cache { } dlVideo.on("finish", () -> onComplete("Video")); dlAudio.on("finish", () -> onComplete("Audio")); + inline function checkEnoughSpace(contentLength:Int):Void { + final hasSpace = removeOlderCache(contentLength + freeSpaceBlock); + if (!hasSpace) { + dlVideo.destroy(); + dlAudio.destroy(); + removeInputFiles(); + cancelProgress(); + main.serverMessage(client, notEnoughSpaceErrorText); + } + } var isAudioStart = true; dlAudio.on("progress", (chunkLength:Int, downloaded:Int, contentLength:Int) -> { if (isAudioStart) { isAudioStart = false; - removeOlderCache(contentLength); + checkEnoughSpace(contentLength); } }); var isVideoStart = true; dlVideo.on("progress", (chunkLength:Int, downloaded:Int, contentLength:Int) -> { if (isVideoStart) { isVideoStart = false; - removeOlderCache(contentLength); + checkEnoughSpace(contentLength); } final ratio = (downloaded / contentLength).clamp(0, 1); main.send(client, { @@ -187,6 +241,7 @@ class Cache { }); }).catchError(err -> { removeInputFiles(); + cancelProgress(); log(client, "" + err); }); } @@ -194,13 +249,8 @@ class Cache { public function setStorageLimit(bytes:Int) { storageLimit = cast bytes; storageLimit = storageLimit.limitMin(0); - final statfs = (Fs : Dynamic).statfs ?? return; - statfs("/", (err, stats) -> { - if (err != null) { - trace(err); - return; - } - final availSpace = (stats.bsize * stats.bavail - freeSpaceBlock).limitMin(0); + getFreeDiskSpace(availSpace -> { + final availSpace = (availSpace - freeSpaceBlock).limitMin(0); removeOlderCache(); final freeSpace = getFreeSpace(); if (availSpace < freeSpace) { @@ -212,6 +262,22 @@ class Cache { }); } + public function getFreeDiskSpace(callback:(availSpace:Int) -> Void):Void { + final statfs = (Fs : Dynamic).statfs ?? { + trace("Warning: no fs.statfs support in current nodejs version (needs v18+)"); + callback(storageLimit); + return; + } + statfs("/", (err, stats) -> { + if (err != null) { + trace(err); + callback(storageLimit); + return; + } + callback(stats.bsize * stats.bavail); + }); + } + public function add(name:String) { if (!cachedFiles.contains(name)) { cachedFiles.unshift(name); @@ -223,13 +289,15 @@ class Cache { removeFile(name); } - public function removeOlderCache(addFileSize = 0):Void { + /** Returns `true` if there is enough space to save `addFileSize` bytes. **/ + public function removeOlderCache(addFileSize = 0):Bool { var space = getUsedSpace(addFileSize); while (space > storageLimit) { final name = cachedFiles.pop() ?? break; removeFile(name); space = getUsedSpace(addFileSize); } + return space < storageLimit; } function removeFile(name:String):Void { diff --git a/src/server/HttpServer.hx b/src/server/HttpServer.hx index 346aac1..8f0b56e 100644 --- a/src/server/HttpServer.hx +++ b/src/server/HttpServer.hx @@ -170,15 +170,14 @@ class HttpServer { cache.removeOlderCache(size); } if (cache.getFreeSpace() < size) { - final errText = "Error: Not enough free space on server or file size is out of cache storage limit."; end(413, { // Payload Too Large - info: errText, + info: cache.notEnoughSpaceErrorText, errorId: "freeSpace", }); cache.remove(name); req.destroy(); final client = main.clients.getByName(name) ?? return; - main.serverMessage(client, errText); + main.serverMessage(client, cache.notEnoughSpaceErrorText); return; } diff --git a/src/server/Logger.hx b/src/server/Logger.hx index 42d382d..f22c9d9 100644 --- a/src/server/Logger.hx +++ b/src/server/Logger.hx @@ -38,7 +38,7 @@ class Logger { public function saveLog():Void { if (logs.length == 0) return; Utils.ensureDir(folder); - removeOldestLog(folder); + removeOldestLog(); final name = DateTools.format(Date.now(), "%Y-%m-%d_%H_%M_%S"); File.saveContent('$folder/$name.json', Main.jsonStringify(getLogs(), "\t")); } @@ -47,7 +47,7 @@ class Logger { return logs; } - function removeOldestLog(folder:String):Void { + public function removeOldestLog():Void { final names = FileSystem.readDirectory(folder).filter(name -> { if (FileSystem.isDirectory('$folder/$name')) return false; if (name.startsWith(".")) return false; diff --git a/src/server/Main.hx b/src/server/Main.hx index 644a8b1..7d78879 100644 --- a/src/server/Main.hx +++ b/src/server/Main.hx @@ -293,7 +293,7 @@ class Main { paused: videoTimer.isPaused() }, flashbacks: flashbacks, - cachedFiles: cache.cachedFiles + cachedFiles: cache.getCachedFiles() } } @@ -315,8 +315,7 @@ class Main { flashbacks.resize(0); for (flashback in state.flashbacks) flashbacks.push(flashback); - cache.cachedFiles.resize(0); - for (name in state.cachedFiles) cache.cachedFiles.push(name); + cache.setCachedFiles(state.cachedFiles); videoTimer.start(); videoTimer.setTime(state.timer.time); @@ -324,6 +323,7 @@ class Main { } function logError(type:String, data:Dynamic):Void { + cache.removeOlderCache(1024 * 1024); trace(type, data); final crashesFolder = '$rootDir/user/crashes'; Utils.ensureDir(crashesFolder); |
