feat: fehlende Bilder finden, Massen-Umbenennen, Bilder verkleinern, Einstellungen mitnehmen
FEHLENDE BILDER. Die Frage, die bisher niemand beantwortet hat: das Fotostudio weiss, was es aufgenommen hat, der Dienst weiss, was liegt, und die Differenz rechnet keiner aus -- weshalb "das Handy zeigt bei manchen Autos kein Bild" erst im Spiel auffaellt. POST /api/dash/fehlende nimmt ein Muster mit genau EINEM Platzhalter und eine Liste (als Array oder als Textblock aus einem Textfeld) und gibt beide Richtungen zurueck: was fehlt, und was daliegt ohne in der Liste zu stehen. Beides bedeutet etwas anderes -- fehlende sind Arbeit fuers Fotostudio, ueberzaehlige sind Platz, den man zurueckbekommt. Gesucht wird KLEINGESCHRIEBEN, weil so abgelegt wird: wer "WEAPON_SMG" hereinreicht, meint weapon_smg.png und bekaeme sonst ein "fehlt", das gelogen waere. Gemessen an echten Namen: 6 geprueft, 4 da, 2 fehlen, 985 ueberzaehlig. MASSEN-UMBENENNEN mit Vorschau. Bei 3578 Items ist eine Umbenennung von Hand keine. DIE VORSCHAU IST DER EIGENTLICHE TEIL: was hier passiert, ist dieselbe Warnung wie beim Ordner-Umbenennen -- jede alte Adresse gibt danach 404 --, nur mal tausend. Geprueft wird VOR dem ersten Schritt: Kollisionen untereinander, belegte Ziele, unzulaessige Pfade. Und EIN Problem verhindert ALLES; eine halbe Umbenennung ist der Zustand, aus dem niemand mehr herausfindet, weil die Grenze nirgends steht. Gemessen: Vorschau ueber 134 Dateien fasst nichts an; eine erzwungene Kollision gibt 409 und laesst alle drei Dateien stehen; der saubere Lauf benennt zwei um, Vorschau wandert mit, ein Eintrag im Verlauf statt zwei. BILDER BEIM ABLEGEN VERKLEINERN, je Token einstellbar. NICHT als Schutz -- das waere gemessen falsch, ein 12000x12000-PNG kostet 374 ms und 47 MB, weil libvips kachelweise arbeitet. Es geht um Plattenplatz und Ladezeit: ein Bildschirmfoto ist 3840 breit und wird auf einem Handy auf 400 gezeigt. withoutEnlargement ist dabei entscheidend, sonst blaeht die Einstellung kleine Icons auf. DABEI WAERE MIR EIN ECHTER FEHLER UNTERLAUFEN: der sha256 wird weiter oben aus dem Original gerechnet (fuer den Ersatzpfad, wenn kein X-Path kam). Wird das Bild verkleinert, ist das eine ANDERE Datei -- ein Datensatz mit dem Hash des Originals waere schlicht falsch, und jede spaetere Gegenprobe verglichen gegen etwas, das nirgends liegt. Gemessen nach der Korrektur: 3000x2000 -> 512x341, 82 KB -> 3 KB, und Datenbank und Platte stimmen in Groesse und Hash ueberein. EINSTELLUNGEN MITNEHMEN. Export als JSON, Import zurueck. GEHEIMNISSE GEHEN STANDARDMAESSIG NICHT MIT: unter den Schluesseln liegen das Discord-Client-Geheimnis und das Nextcloud-Passwort, und eine Datei, die man "mal eben" exportiert und in einen Chat zieht, ist genau der Weg, auf dem so etwas abhandenkommt. Wer sie braucht, sagt ?geheim=1 -- und der Hinweis steht dann IN der Datei, nicht nur auf dem Bildschirm, von dem er beim Weiterreichen abfaellt. Der Export mit Geheimnissen steht im Verwaltungs-Verlauf. Beim Import ueberschreiben LEERE WERTE NICHTS: eine Datei ohne Geheimnisse -- der Normalfall -- traegt sie als leere Zeichenkette, und die darf ein vorhandenes Passwort nicht loeschen. Gemessen: nach einem Import mit leerem client_secret stehen die 32 Zeichen unveraendert da. Nicht mitgenommen wird sicherung.letzte -- das ist der Bericht des letzten Laufs, kein Einstellungswert. In eine frische Anlage eingespielt behauptete er dem Wachhund eine Sicherung, die es dort nie gab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -170,6 +170,57 @@ export const sha256 = (data: Buffer) => createHash('sha256').update(data).digest
|
||||
|
||||
export const absolutePath = (path: string) => join(config.filesDir, path)
|
||||
|
||||
/** Ein Bild auf eine Hoechstkante rechnen -- oder es lassen.
|
||||
*
|
||||
* WOFUER NICHT: als Schutz vor riesigen Bildern. Das waere gemessen falsch --
|
||||
* ein 12000 x 12000 grosses PNG kostet 374 ms und 47 MB, weil libvips
|
||||
* kachelweise arbeitet und nie das ganze Bild dekodiert.
|
||||
*
|
||||
* WOFUER DANN: Plattenplatz und Ladezeit. Ein Bildschirmfoto aus dem Spiel ist
|
||||
* 3840 breit; auf einem Handy wird es auf 400 gezeigt. Die restlichen 3440
|
||||
* liegen auf der Platte und gehen bei jedem Abruf ueber die Leitung.
|
||||
*
|
||||
* `withoutEnlargement` ist entscheidend: ein 200 Pixel breites Icon wird NICHT
|
||||
* auf 2048 aufgeblasen. Sonst machte die Einstellung kleine Dateien groesser.
|
||||
*
|
||||
* Gibt den neuen Puffer zurueck, oder den alten, wenn nichts zu tun war. Wirft
|
||||
* nie -- ein Bild, das sich nicht rechnen laesst, wird abgelegt wie es kam. */
|
||||
export async function vielleichtKleiner(
|
||||
data: Buffer,
|
||||
mime: string,
|
||||
maxKante: number | null,
|
||||
): Promise<{ data: Buffer; verkleinert: false | { von: string; auf: string } }> {
|
||||
if (!maxKante || maxKante < 16) return { data, verkleinert: false }
|
||||
if (!kannVorschau(mime)) return { data, verkleinert: false }
|
||||
|
||||
try {
|
||||
const { default: sharp } = await import('sharp')
|
||||
const bild = sharp(data, { animated: false })
|
||||
const vorher = await bild.metadata()
|
||||
const groesste = Math.max(vorher.width ?? 0, vorher.height ?? 0)
|
||||
if (!groesste || groesste <= maxKante) return { data, verkleinert: false }
|
||||
|
||||
// IM URSPRUNGSFORMAT bleiben: aus einem PNG ein WebP zu machen aendert
|
||||
// die Endung nicht mit, und dann liegt eine .png da, die keine ist.
|
||||
// Die sparsame Fassung entsteht ohnehin daneben.
|
||||
const neu = await bild
|
||||
.resize({ width: maxKante, height: maxKante, fit: 'inside', withoutEnlargement: true })
|
||||
.toBuffer()
|
||||
|
||||
const nachher = await sharp(neu).metadata()
|
||||
return {
|
||||
data: neu,
|
||||
verkleinert: {
|
||||
von: `${vorher.width}x${vorher.height}`,
|
||||
auf: `${nachher.width}x${nachher.height}`,
|
||||
},
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[kleiner] ${err instanceof Error ? err.message : err}`)
|
||||
return { data, verkleinert: false }
|
||||
}
|
||||
}
|
||||
|
||||
/** Schreibt die Datei.
|
||||
*
|
||||
* Erst daneben, dann umbenennen: ein Ueberschreiben ist damit entweder ganz
|
||||
|
||||
Reference in New Issue
Block a user