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 util_1 = require("util");
|
||||||
const unbzip2_stream_1 = __importDefault(require("unbzip2-stream"));
|
const unbzip2_stream_1 = __importDefault(require("unbzip2-stream"));
|
||||||
const pipe = (0, util_1.promisify)(stream_1.pipeline);
|
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
|
* 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 isPremier = !!lastRound?.b_switched_teams;
|
||||||
const matchType = isPremier ? 'premier' : 'competitive';
|
const matchType = isPremier ? 'premier' : 'competitive';
|
||||||
const tempDir = path_1.default.join(outputBaseDir, 'temp');
|
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 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);
|
const finalFileName = path_1.default.basename(finalFile);
|
||||||
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
const partialFile = `${finalFile}.part`;
|
||||||
fs_1.default.mkdirSync(finalDir, { recursive: true });
|
// Bereits vorhanden? -> sofort zurück
|
||||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
if (fs_1.default.existsSync(finalFile)) {
|
||||||
try {
|
console.log(`♻️ Demo existiert bereits: ${finalFileName}`);
|
||||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
return finalFile;
|
||||||
if (!success || !fs_1.default.existsSync(tempFile) || fs_1.default.statSync(tempFile).size === 0) {
|
}
|
||||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
// 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 {
|
try {
|
||||||
if (fs_1.default.existsSync(tempFile))
|
if (fs_1.default.existsSync(tempFile))
|
||||||
fs_1.default.unlinkSync(tempFile);
|
fs_1.default.unlinkSync(tempFile);
|
||||||
}
|
}
|
||||||
catch {
|
catch { }
|
||||||
console.warn(`⚠️ Konnte leere Datei nicht löschen: ${tempFileName}`);
|
throw e;
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
}
|
// Prüfen & atomar nach .dem verschieben
|
||||||
catch (err) {
|
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||||
throw new Error(`❌ Fehler beim Download: ${err instanceof Error ? err.message : err}`);
|
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||||
}
|
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||||
console.log(`✅ Gespeichert als ${tempFileName}`);
|
if (!fs_1.default.existsSync(partialFile)) {
|
||||||
const entpackZeile = `🗜️ Entpacke ${finalFileName}...`;
|
process.stdout.write(`\r${failMessage.padEnd(maxLength, ' ')}\n`);
|
||||||
process.stdout.write(entpackZeile);
|
try {
|
||||||
await extractBz2Safe(tempFile, finalFile);
|
if (fs_1.default.existsSync(tempFile))
|
||||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
fs_1.default.unlinkSync(tempFile);
|
||||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
}
|
||||||
// Max-Zeichenlänge bestimmen
|
catch { }
|
||||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
throw new Error('Entpackung fehlgeschlagen');
|
||||||
if (!fs_1.default.existsSync(finalFile)) {
|
}
|
||||||
const paddedFail = failMessage.padEnd(maxLength, ' ');
|
fs_1.default.renameSync(partialFile, finalFile);
|
||||||
process.stdout.write(`\r${paddedFail}\n`);
|
process.stdout.write(`\r${successMessage.padEnd(maxLength, ' ')}\n`);
|
||||||
throw new Error(failMessage);
|
// Aufräumen
|
||||||
}
|
try {
|
||||||
const paddedSuccess = successMessage.padEnd(maxLength, ' ');
|
fs_1.default.unlinkSync(tempFile);
|
||||||
process.stdout.write(`\r${paddedSuccess}\n`);
|
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||||
// Aufräumen
|
}
|
||||||
|
catch {
|
||||||
|
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||||
|
}
|
||||||
|
return finalFile;
|
||||||
|
})();
|
||||||
|
inflight.set(finalFile, job);
|
||||||
try {
|
try {
|
||||||
fs_1.default.unlinkSync(tempFile);
|
return await job;
|
||||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
|
||||||
}
|
}
|
||||||
catch {
|
finally {
|
||||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
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');
|
console.log('✅ Download abgeschlossen');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// JSON-Dateipfad erstellen
|
// JSON nur erstellen, wenn die .dem existiert
|
||||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
if (demoFilePath && fs_1.default.existsSync(demoFilePath)) {
|
||||||
const jsonFileName = path_1.default.basename(jsonFilePath);
|
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
||||||
// Match-Daten als JSON schreiben
|
const jsonFileName = path_1.default.basename(jsonFilePath);
|
||||||
await fs_1.default.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
await fs_1.default.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
||||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn('⚠️ Keine Demo-Datei vorhanden – JSON wird nicht erstellt.');
|
||||||
|
}
|
||||||
// Antwort an den Client
|
// Antwort an den Client
|
||||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
res.end(JSON.stringify({ success: true, path: demoFilePath }));
|
res.end(JSON.stringify({ success: true, path: demoFilePath }));
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import bz2 from 'unbzip2-stream';
|
|||||||
|
|
||||||
const pipe = promisify(pipeline);
|
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
|
* 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.
|
* 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 matchType = isPremier ? 'premier' : 'competitive';
|
||||||
|
|
||||||
const tempDir = path.join(outputBaseDir, 'temp');
|
const tempDir = path.join(outputBaseDir, 'temp');
|
||||||
const tempFileName = `match${appId}_${mapName}_${matchId}_${matchType}.bz2`;
|
const finalDir = path.join(outputBaseDir, matchDate);
|
||||||
const baseName = path.parse(tempFileName).name;
|
|
||||||
|
// 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 tempFile = path.join(tempDir, tempFileName);
|
||||||
|
|
||||||
const finalDir = path.join(outputBaseDir, matchDate);
|
const job = (async () => {
|
||||||
const finalFile = path.join(finalDir, `${baseName}.dem`);
|
fs.mkdirSync(tempDir, { recursive: true });
|
||||||
const finalFileName = path.basename(finalFile)
|
fs.mkdirSync(finalDir, { recursive: true });
|
||||||
|
|
||||||
fs.mkdirSync(tempDir, { recursive: true });
|
// Stale .part von früheren Abbrüchen entfernen
|
||||||
fs.mkdirSync(finalDir, { recursive: true });
|
try { if (fs.existsSync(partialFile)) fs.unlinkSync(partialFile); } catch {}
|
||||||
|
|
||||||
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
console.log(`📥 Lade Demo von ${demoUrl}...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
const success = await downloadWithHttps(demoUrl, tempFile, onProgress);
|
||||||
if (!success || !fs.existsSync(tempFile) || fs.statSync(tempFile).size === 0) {
|
if (!success || !fs.existsSync(tempFile) || fs.statSync(tempFile).size === 0) {
|
||||||
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
console.warn(`⚠️ Download fehlgeschlagen oder Datei leer – lösche ${tempFileName}`);
|
||||||
|
try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); } catch {}
|
||||||
try {
|
return '';
|
||||||
if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile);
|
|
||||||
} catch {
|
|
||||||
console.warn(`⚠️ Konnte leere Datei nicht löschen: ${tempFileName}`);
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
return '';
|
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 ${finalFileName}...`;
|
const entpackZeile = `🗜️ Entpacke nach ${path.basename(partialFile)}...`;
|
||||||
process.stdout.write(entpackZeile);
|
process.stdout.write(entpackZeile);
|
||||||
|
|
||||||
await extractBz2Safe(tempFile, finalFile);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
// Prüfen & atomar nach .dem verschieben
|
||||||
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
const successMessage = `✅ Entpackt: ${finalFileName}`;
|
||||||
|
const failMessage = `❌ Entpackung fehlgeschlagen – Datei nicht vorhanden`;
|
||||||
|
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
||||||
|
|
||||||
// Max-Zeichenlänge bestimmen
|
if (!fs.existsSync(partialFile)) {
|
||||||
const maxLength = Math.max(entpackZeile.length, successMessage.length, failMessage.length);
|
process.stdout.write(`\r${failMessage.padEnd(maxLength, ' ')}\n`);
|
||||||
|
try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); } catch {}
|
||||||
|
throw new Error('Entpackung fehlgeschlagen');
|
||||||
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(finalFile)) {
|
fs.renameSync(partialFile, finalFile);
|
||||||
const paddedFail = failMessage.padEnd(maxLength, ' ');
|
process.stdout.write(`\r${successMessage.padEnd(maxLength, ' ')}\n`);
|
||||||
process.stdout.write(`\r${paddedFail}\n`);
|
|
||||||
throw new Error(failMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const paddedSuccess = successMessage.padEnd(maxLength, ' ');
|
// Aufräumen
|
||||||
process.stdout.write(`\r${paddedSuccess}\n`);
|
try {
|
||||||
|
fs.unlinkSync(tempFile);
|
||||||
|
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
||||||
|
} catch {
|
||||||
|
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Aufräumen
|
return finalFile;
|
||||||
|
})();
|
||||||
|
|
||||||
|
inflight.set(finalFile, job);
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(tempFile);
|
return await job;
|
||||||
console.log(`🧹 Gelöscht: ${tempFileName}`);
|
} finally {
|
||||||
} catch {
|
inflight.delete(finalFile);
|
||||||
console.log(`⚠️ Konnte temporäre Datei nicht löschen: ${tempFileName}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalFile;
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main.ts
15
src/main.ts
@ -56,13 +56,16 @@ async function start() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// JSON-Dateipfad erstellen
|
// JSON nur erstellen, wenn die .dem existiert
|
||||||
const jsonFilePath = demoFilePath.replace(/\.dem$/, '.json');
|
if (demoFilePath && fs.existsSync(demoFilePath)) {
|
||||||
const jsonFileName = path.basename(jsonFilePath)
|
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');
|
||||||
await fs.promises.writeFile(jsonFilePath, JSON.stringify(match, null, 2), 'utf-8');
|
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
||||||
console.log(`📝 Match-Daten gespeichert unter: ${jsonFileName}`);
|
} else {
|
||||||
|
console.warn('⚠️ Keine Demo-Datei vorhanden – JSON wird nicht erstellt.');
|
||||||
|
}
|
||||||
|
|
||||||
// Antwort an den Client
|
// Antwort an den Client
|
||||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user