updated
This commit is contained in:
parent
ae11e8e17d
commit
64faf5ced6
127
dist/app/downloadDemoFile.js
vendored
127
dist/app/downloadDemoFile.js
vendored
@ -12,6 +12,8 @@ const stream_1 = require("stream");
|
||||
const util_1 = require("util");
|
||||
const unbzip2_stream_1 = __importDefault(require("unbzip2-stream"));
|
||||
const pipe = (0, util_1.promisify)(stream_1.pipeline);
|
||||
// In-Flight-Lock pro Ziel-.dem
|
||||
const inflight = new Map();
|
||||
/**
|
||||
* Entpackt eine .bz2-Datei mithilfe von Streams nach .dem
|
||||
*/
|
||||
@ -114,54 +116,99 @@ async function downloadDemoFile(match, outputBaseDir = 'demos', onProgress) {
|
||||
const isPremier = !!lastRound?.b_switched_teams;
|
||||
const matchType = isPremier ? 'premier' : 'competitive';
|
||||
const tempDir = path_1.default.join(outputBaseDir, 'temp');
|
||||
const tempFileName = `match${appId}_${mapName}_${matchId}_${matchType}.bz2`;
|
||||
const baseName = path_1.default.parse(tempFileName).name;
|
||||
const tempFile = path_1.default.join(tempDir, tempFileName);
|
||||
const finalDir = path_1.default.join(outputBaseDir, matchDate);
|
||||
const finalFile = path_1.default.join(finalDir, `${baseName}.dem`);
|
||||
// Stabiler finaler Basisname (wichtig fürs De-Duplizieren)
|
||||
const baseFinalName = `match${appId}_${mapName}_${matchId}_${matchType}`;
|
||||
const finalFile = path_1.default.join(finalDir, `${baseFinalName}.dem`);
|
||||
const finalFileName = path_1.default.basename(finalFile);
|
||||
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
||||
fs_1.default.mkdirSync(finalDir, { recursive: true });
|
||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
||||
try {
|
||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
||||
if (!success || !fs_1.default.existsSync(tempFile) || fs_1.default.statSync(tempFile).size === 0) {
|
||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
||||
const partialFile = `${finalFile}.part`;
|
||||
// Bereits vorhanden? -> sofort zurück
|
||||
if (fs_1.default.existsSync(finalFile)) {
|
||||
console.log(`♻️ Demo existiert bereits: ${finalFileName}`);
|
||||
return finalFile;
|
||||
}
|
||||
// Parallele Requests für dieselbe Zieldatei zusammenlegen
|
||||
if (inflight.has(finalFile)) {
|
||||
return await inflight.get(finalFile);
|
||||
}
|
||||
// Eindeutiger Temp-Dateiname im temp/
|
||||
const rand = Math.random().toString(36).slice(2, 8);
|
||||
const tempFileName = `${baseFinalName}_${rand}.bz2`;
|
||||
const tempFile = path_1.default.join(tempDir, tempFileName);
|
||||
const job = (async () => {
|
||||
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
||||
fs_1.default.mkdirSync(finalDir, { recursive: true });
|
||||
// Stale .part von früheren Abbrüchen entfernen
|
||||
try {
|
||||
if (fs_1.default.existsSync(partialFile))
|
||||
fs_1.default.unlinkSync(partialFile);
|
||||
}
|
||||
catch { }
|
||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
||||
try {
|
||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
||||
if (!success || !fs_1.default.existsSync(tempFile) || fs_1.default.statSync(tempFile).size === 0) {
|
||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
||||
try {
|
||||
if (fs_1.default.existsSync(tempFile))
|
||||
fs_1.default.unlinkSync(tempFile);
|
||||
}
|
||||
catch { }
|
||||
return '';
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
throw new Error(`❌ Fehler beim Download: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
console.log(`✅ Gespeichert als ${tempFileName}`);
|
||||
const entpackZeile = `🗜️ Entpacke nach ${path_1.default.basename(partialFile)}...`;
|
||||
process.stdout.write(entpackZeile);
|
||||
try {
|
||||
await extractBz2Safe(tempFile, partialFile);
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
if (fs_1.default.existsSync(partialFile))
|
||||
fs_1.default.unlinkSync(partialFile);
|
||||
}
|
||||
catch { }
|
||||
try {
|
||||
if (fs_1.default.existsSync(tempFile))
|
||||
fs_1.default.unlinkSync(tempFile);
|
||||
}
|
||||
catch {
|
||||
console.warn(`⚠️ Konnte leere Datei nicht löschen: ${tempFileName}`);
|
||||
}
|
||||
return '';
|
||||
catch { }
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
throw new Error(`❌ Fehler beim Download: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
console.log(`✅ Gespeichert als ${tempFileName}`);
|
||||
const entpackZeile = `🗜️ Entpacke ${finalFileName}...`;
|
||||
process.stdout.write(entpackZeile);
|
||||
await extractBz2Safe(tempFile, finalFile);
|
||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||
// Max-Zeichenlänge bestimmen
|
||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||
if (!fs_1.default.existsSync(finalFile)) {
|
||||
const paddedFail = failMessage.padEnd(maxLength, ' ');
|
||||
process.stdout.write(`\r${paddedFail}\n`);
|
||||
throw new Error(failMessage);
|
||||
}
|
||||
const paddedSuccess = successMessage.padEnd(maxLength, ' ');
|
||||
process.stdout.write(`\r${paddedSuccess}\n`);
|
||||
// Aufräumen
|
||||
// Prüfen & atomar nach .dem verschieben
|
||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||
if (!fs_1.default.existsSync(partialFile)) {
|
||||
process.stdout.write(`\r${failMessage.padEnd(maxLength, ' ')}\n`);
|
||||
try {
|
||||
if (fs_1.default.existsSync(tempFile))
|
||||
fs_1.default.unlinkSync(tempFile);
|
||||
}
|
||||
catch { }
|
||||
throw new Error('Entpackung fehlgeschlagen');
|
||||
}
|
||||
fs_1.default.renameSync(partialFile, finalFile);
|
||||
process.stdout.write(`\r${successMessage.padEnd(maxLength, ' ')}\n`);
|
||||
// Aufräumen
|
||||
try {
|
||||
fs_1.default.unlinkSync(tempFile);
|
||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||
}
|
||||
catch {
|
||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||
}
|
||||
return finalFile;
|
||||
})();
|
||||
inflight.set(finalFile, job);
|
||||
try {
|
||||
fs_1.default.unlinkSync(tempFile);
|
||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||
return await job;
|
||||
}
|
||||
catch {
|
||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||
finally {
|
||||
inflight.delete(finalFile);
|
||||
}
|
||||
return finalFile;
|
||||
}
|
||||
|
||||
BIN
dist/cs2-demo-downloader-linux
vendored
BIN
dist/cs2-demo-downloader-linux
vendored
Binary file not shown.
BIN
dist/cs2-demo-downloader-macos
vendored
BIN
dist/cs2-demo-downloader-macos
vendored
Binary file not shown.
BIN
dist/cs2-demo-downloader-win.exe
vendored
BIN
dist/cs2-demo-downloader-win.exe
vendored
Binary file not shown.
16
dist/main.js
vendored
16
dist/main.js
vendored
@ -49,12 +49,16 @@ async function start() {
|
||||
console.log('✅ Download abgeschlossen');
|
||||
}
|
||||
});
|
||||
// JSON-Dateipfad erstellen
|
||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
||||
const jsonFileName = path_1.default.basename(jsonFilePath);
|
||||
// Match-Daten als JSON schreiben
|
||||
await fs_1.default.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||
// JSON nur erstellen, wenn die .dem existiert
|
||||
if (demoFilePath && fs_1.default.existsSync(demoFilePath)) {
|
||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
||||
const jsonFileName = path_1.default.basename(jsonFilePath);
|
||||
await fs_1.default.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||
}
|
||||
else {
|
||||
console.warn('⚠️ Keine Demo-Datei vorhanden – JSON wird nicht erstellt.');
|
||||
}
|
||||
// Antwort an den Client
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ success: true, path: demoFilePath }));
|
||||
|
||||
@ -8,6 +8,9 @@ import bz2 from 'unbzip2-stream';
|
||||
|
||||
const pipe = promisify(pipeline);
|
||||
|
||||
// In-Flight-Lock pro Ziel-.dem
|
||||
const inflight: Map<string, Promise<string>> = new Map();
|
||||
|
||||
/**
|
||||
* Entpackt eine .bz2-Datei mithilfe von Streams nach .dem
|
||||
*/
|
||||
@ -107,8 +110,6 @@ function downloadWithHttps(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Hauptfunktion: lädt und entpackt eine CS2-Demo (.bz2), mit Fortschrittsanzeige.
|
||||
*/
|
||||
@ -146,66 +147,92 @@ export async function downloadDemoFile(
|
||||
const matchType = isPremier ? 'premier' : 'competitive';
|
||||
|
||||
const tempDir = path.join(outputBaseDir, 'temp');
|
||||
const tempFileName = `match${appId}_${mapName}_${matchId}_${matchType}.bz2`;
|
||||
const baseName = path.parse(tempFileName).name;
|
||||
const finalDir = path.join(outputBaseDir, matchDate);
|
||||
|
||||
// Stabiler finaler Basisname (wichtig fürs De-Duplizieren)
|
||||
const baseFinalName = `match${appId}_${mapName}_${matchId}_${matchType}`;
|
||||
const finalFile = path.join(finalDir, `${baseFinalName}.dem`);
|
||||
const finalFileName = path.basename(finalFile);
|
||||
const partialFile = `${finalFile}.part`;
|
||||
|
||||
// Bereits vorhanden? -> sofort zurück
|
||||
if (fs.existsSync(finalFile)) {
|
||||
console.log(`♻️ Demo existiert bereits: ${finalFileName}`);
|
||||
return finalFile;
|
||||
}
|
||||
|
||||
// Parallele Requests für dieselbe Zieldatei zusammenlegen
|
||||
if (inflight.has(finalFile)) {
|
||||
return await inflight.get(finalFile)!;
|
||||
}
|
||||
|
||||
// Eindeutiger Temp-Dateiname im temp/
|
||||
const rand = Math.random().toString(36).slice(2, 8);
|
||||
const tempFileName = `${baseFinalName}_${rand}.bz2`;
|
||||
const tempFile = path.join(tempDir, tempFileName);
|
||||
|
||||
const finalDir = path.join(outputBaseDir, matchDate);
|
||||
const finalFile = path.join(finalDir, `${baseName}.dem`);
|
||||
const finalFileName = path.basename(finalFile)
|
||||
const job = (async () => {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
fs.mkdirSync(finalDir, { recursive: true });
|
||||
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
fs.mkdirSync(finalDir, { recursive: true });
|
||||
// Stale .part von früheren Abbrüchen entfernen
|
||||
try { if (fs.existsSync(partialFile)) fs.unlinkSync(partialFile); } catch {}
|
||||
|
||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
||||
|
||||
try {
|
||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
||||
if (!success || !fs.existsSync(tempFile) || fs.statSync(tempFile).size === 0) {
|
||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
||||
|
||||
try {
|
||||
if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile);
|
||||
} catch {
|
||||
console.warn(`⚠️ Konnte leere Datei nicht löschen: ${tempFileName}`);
|
||||
try {
|
||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
||||
if (!success || !fs.existsSync(tempFile) || fs.statSync(tempFile).size === 0) {
|
||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
||||
try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); } catch {}
|
||||
return '';
|
||||
}
|
||||
|
||||
return '';
|
||||
} catch (err) {
|
||||
throw new Error(`❌ Fehler beim Download: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`❌ Fehler beim Download: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
|
||||
console.log(`✅ Gespeichert als ${tempFileName}`);
|
||||
|
||||
console.log(`✅ Gespeichert als ${tempFileName}`);
|
||||
const entpackZeile = `🗜️ Entpacke nach ${path.basename(partialFile)}...`;
|
||||
process.stdout.write(entpackZeile);
|
||||
|
||||
const entpackZeile = `🗜️ Entpacke ${finalFileName}...`;
|
||||
process.stdout.write(entpackZeile);
|
||||
try {
|
||||
await extractBz2Safe(tempFile, partialFile);
|
||||
} catch (e) {
|
||||
try { if (fs.existsSync(partialFile)) fs.unlinkSync(partialFile); } catch {}
|
||||
try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); } catch {}
|
||||
throw e;
|
||||
}
|
||||
|
||||
await extractBz2Safe(tempFile, finalFile);
|
||||
// Prüfen & atomar nach .dem verschieben
|
||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||
|
||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||
if (!fs.existsSync(partialFile)) {
|
||||
process.stdout.write(`\r${failMessage.padEnd(maxLength, ' ')}\n`);
|
||||
try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); } catch {}
|
||||
throw new Error('Entpackung fehlgeschlagen');
|
||||
}
|
||||
|
||||
// Max-Zeichenlänge bestimmen
|
||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||
fs.renameSync(partialFile, finalFile);
|
||||
process.stdout.write(`\r${successMessage.padEnd(maxLength, ' ')}\n`);
|
||||
|
||||
if (!fs.existsSync(finalFile)) {
|
||||
const paddedFail = failMessage.padEnd(maxLength, ' ');
|
||||
process.stdout.write(`\r${paddedFail}\n`);
|
||||
throw new Error(failMessage);
|
||||
}
|
||||
// Aufräumen
|
||||
try {
|
||||
fs.unlinkSync(tempFile);
|
||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||
} catch {
|
||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||
}
|
||||
|
||||
const paddedSuccess = successMessage.padEnd(maxLength, ' ');
|
||||
process.stdout.write(`\r${paddedSuccess}\n`);
|
||||
return finalFile;
|
||||
})();
|
||||
|
||||
// Aufräumen
|
||||
inflight.set(finalFile, job);
|
||||
try {
|
||||
fs.unlinkSync(tempFile);
|
||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||
} catch {
|
||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||
return await job;
|
||||
} finally {
|
||||
inflight.delete(finalFile);
|
||||
}
|
||||
|
||||
return finalFile;
|
||||
}
|
||||
|
||||
15
src/main.ts
15
src/main.ts
@ -56,13 +56,16 @@ async function start() {
|
||||
}
|
||||
);
|
||||
|
||||
// JSON-Dateipfad erstellen
|
||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
||||
const jsonFileName = path.basename(jsonFilePath)
|
||||
// JSON nur erstellen, wenn die .dem existiert
|
||||
if (demoFilePath && fs.existsSync(demoFilePath)) {
|
||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
||||
const jsonFileName = path.basename(jsonFilePath);
|
||||
|
||||
// Match-Daten als JSON schreiben
|
||||
await fs.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||
await fs.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||
} else {
|
||||
console.warn('⚠️ Keine Demo-Datei vorhanden – JSON wird nicht erstellt.');
|
||||
}
|
||||
|
||||
// Antwort an den Client
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user