mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-05-11 06:11:15 +00:00
- Add invokeWithTimeout wrapper for TDLib API calls (2min timeout per call) - Add stuck detection to getChannelMessages: break if from_message_id doesn't advance - Add stuck detection to getTopicMessages: same protection for topic scanning - Add stuck detection to getForumTopicList: break if pagination offsets don't advance - Add max page limit (5000) to all scanning loops to prevent infinite pagination - Add mutex wait timeout (30min) to prevent indefinite blocking when holder hangs - Add cycle timeout (4h default, configurable via WORKER_CYCLE_TIMEOUT_MINUTES) - Fix end-of-page detection to use actual limit value instead of hardcoded 100 Co-authored-by: xCyanGrizzly <53275238+xCyanGrizzly@users.noreply.github.com>
77 lines
2.8 KiB
JavaScript
77 lines
2.8 KiB
JavaScript
import { execFile } from "child_process";
|
|
import { promisify } from "util";
|
|
import path from "path";
|
|
import { childLogger } from "../util/logger.js";
|
|
const execFileAsync = promisify(execFile);
|
|
const log = childLogger("rar-reader");
|
|
/**
|
|
* Parse output of `unrar l -v <file>` to extract file metadata.
|
|
* unrar automatically discovers sibling parts when they're co-located.
|
|
*/
|
|
export async function readRarContents(firstPartPath) {
|
|
try {
|
|
const { stdout } = await execFileAsync("unrar", ["l", "-v", firstPartPath], {
|
|
timeout: 30000,
|
|
maxBuffer: 10 * 1024 * 1024, // 10MB for very large archives
|
|
});
|
|
return parseUnrarOutput(stdout);
|
|
}
|
|
catch (err) {
|
|
log.warn({ err, file: firstPartPath }, "Failed to read RAR contents");
|
|
return []; // Fallback: return empty on error
|
|
}
|
|
}
|
|
/**
|
|
* Parse the tabular output of `unrar l -v`.
|
|
*
|
|
* Example output format:
|
|
* Archive: test.rar
|
|
* Details: RAR 5
|
|
*
|
|
* Attributes Size Packed Ratio Date Time CRC-32 Name
|
|
* ----------- --------- --------- ----- -------- ----- -------- ----
|
|
* ...A.... 12345 10234 83% 2024-01-15 10:30 DEADBEEF folder/file.stl
|
|
* ----------- --------- --------- ----- -------- ----- -------- ----
|
|
*/
|
|
function parseUnrarOutput(output) {
|
|
const entries = [];
|
|
const lines = output.split("\n");
|
|
let inFileList = false;
|
|
let separatorCount = 0;
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
// Detect separator lines (------- pattern)
|
|
if (/^-{5,}/.test(trimmed)) {
|
|
separatorCount++;
|
|
if (separatorCount === 1) {
|
|
inFileList = true;
|
|
}
|
|
else if (separatorCount >= 2) {
|
|
inFileList = false;
|
|
}
|
|
continue;
|
|
}
|
|
if (!inFileList)
|
|
continue;
|
|
// Parse file entry line
|
|
// Format: Attributes Size Packed Ratio Date Time CRC Name
|
|
const match = trimmed.match(/^\S+\s+(\d+)\s+(\d+)\s+\d+%\s+\S+\s+\S+\s+([0-9A-Fa-f]+)\s+(.+)$/);
|
|
if (match) {
|
|
const [, uncompressedStr, compressedStr, crc32, filePath] = match;
|
|
// Skip directory entries (typically end with / or have size 0 with dir attributes)
|
|
if (filePath.endsWith("/") || filePath.endsWith("\\"))
|
|
continue;
|
|
const ext = path.extname(filePath).toLowerCase();
|
|
entries.push({
|
|
path: filePath,
|
|
fileName: path.basename(filePath),
|
|
extension: ext ? ext.slice(1) : null,
|
|
compressedSize: BigInt(compressedStr),
|
|
uncompressedSize: BigInt(uncompressedStr),
|
|
crc32: crc32.toLowerCase(),
|
|
});
|
|
}
|
|
}
|
|
return entries;
|
|
}
|
|
//# sourceMappingURL=rar-reader.js.map
|