aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2025-02-09 04:25:18 +0300
committerRblSb <msrblsb@gmail.com>2025-02-09 04:25:18 +0300
commit82a6c65d46e2583883b1b01d706145386308d19e (patch)
tree64742b586df7bc1ed46941cc6da9097ee2c35e90 /src
parentd86f0c30e1726a56e670955c3b995945c1daf834 (diff)
Improve cache limit handling
Diffstat (limited to 'src')
-rw-r--r--src/Types.hx1
-rw-r--r--src/client/Main.hx6
-rw-r--r--src/server/Cache.hx108
-rw-r--r--src/server/HttpServer.hx5
-rw-r--r--src/server/Logger.hx4
-rw-r--r--src/server/Main.hx6
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);
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage