Upload via GUI (146 Dateien)

This commit is contained in:
2026-10-05 17:20:30 +00:00
parent 384354f44e
commit 93c74f94a1
4 changed files with 445 additions and 222 deletions
+106
View File
@@ -0,0 +1,106 @@
/*
* Garderobe: gespeicherte Skins und Umhänge zum schnellen Wechseln.
*
* Mojang merkt sich keine früheren Skins – wer einen neuen hochlädt, verliert
* den alten. Die Garderobe hält Skins (samt Armform) und eigene AeroMC-Umhänge
* als PNG-Dateien im Launcher-Datenordner fest; vor jedem Skinwechsel im
* Launcher wird der bisherige Skin automatisch gesichert. Gleiche Dateien
* (gleiche Prüfsumme) werden nicht doppelt abgelegt.
*/
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const MAX_AUTOMATISCH = 20; // ältere automatische Sicherungen fliegen raus
let ordner = null;
function init(userDataDir) {
ordner = path.join(userDataDir, 'garderobe');
}
const indexDatei = () => path.join(ordner, 'garderobe.json');
function lies() {
try { return JSON.parse(fs.readFileSync(indexDatei(), 'utf8')); } catch { return []; }
}
function schreibe(liste) {
fs.mkdirSync(ordner, { recursive: true });
fs.writeFileSync(indexDatei(), JSON.stringify(liste, null, 1));
}
/** Alle Einträge, neueste zuerst, jeweils mit Bild als data:-Adresse. */
function liste() {
return lies()
.filter((e) => fs.existsSync(path.join(ordner, e.datei)))
.sort((a, b) => String(b.erstellt).localeCompare(String(a.erstellt)))
.map((e) => Object.assign({}, e, {
dataUri: 'data:image/png;base64,' + fs.readFileSync(path.join(ordner, e.datei)).toString('base64'),
}));
}
/**
* Legt einen Skin/Umhang ab. art: 'skin' | 'umhang', daten: PNG-Buffer.
* Gibt es dieselbe Datei schon, wird nur ggf. aus "automatisch" ein fester Eintrag.
*/
function hinzufuegen({ art, daten, name, variante, automatisch }) {
const hash = crypto.createHash('sha1').update(daten).digest('hex');
const eintraege = lies();
const vorhanden = eintraege.find((e) => e.art === art && e.hash === hash);
if (vorhanden) {
if (!automatisch && vorhanden.automatisch) {
vorhanden.automatisch = false;
if (name) vorhanden.name = name;
schreibe(eintraege);
}
return { eintrag: vorhanden, neu: false };
}
fs.mkdirSync(ordner, { recursive: true });
const id = Date.now().toString(36) + crypto.randomBytes(3).toString('hex');
const datei = `${art}-${id}.png`;
fs.writeFileSync(path.join(ordner, datei), daten);
const eintrag = {
id, art, hash, datei,
name: String(name || '').trim().slice(0, 40) || (art === 'skin' ? 'Skin' : 'Umhang'),
variante: art === 'skin' ? (variante === 'slim' ? 'slim' : 'classic') : undefined,
automatisch: !!automatisch,
erstellt: new Date().toISOString(),
};
eintraege.push(eintrag);
// automatische Sicherungen begrenzen (feste Einträge bleiben immer)
const auto = eintraege.filter((e) => e.automatisch).sort((a, b) => String(b.erstellt).localeCompare(String(a.erstellt)));
for (const alt of auto.slice(MAX_AUTOMATISCH)) loescheIntern(eintraege, alt.id);
schreibe(eintraege);
return { eintrag, neu: true };
}
function loescheIntern(eintraege, id) {
const i = eintraege.findIndex((e) => e.id === id);
if (i < 0) return false;
try { fs.unlinkSync(path.join(ordner, eintraege[i].datei)); } catch { /* schon weg */ }
eintraege.splice(i, 1);
return true;
}
function loeschen(id) {
const eintraege = lies();
const ok = loescheIntern(eintraege, id);
if (ok) schreibe(eintraege);
return ok;
}
function umbenennen(id, name) {
const eintraege = lies();
const e = eintraege.find((x) => x.id === id);
if (!e) return false;
e.name = String(name || '').trim().slice(0, 40) || e.name;
e.automatisch = false; // umbenannt = bewusst behalten
schreibe(eintraege);
return true;
}
/** Eintrag samt Dateipfad (zum Anziehen). */
function holen(id) {
const e = lies().find((x) => x.id === id);
return e ? Object.assign({}, e, { pfad: path.join(ordner, e.datei) }) : null;
}
module.exports = { init, liste, hinzufuegen, loeschen, umbenennen, holen };