perf(v5): Kompressions-Fortschritt als kleine Nachricht, Roh-Größen 30 s gemerkt
WAS: Ein Prozent-Tick von HandBrake (etwa einmal je Sekunde) geht jetzt als `kompression-stand` (nur die Warteschlange) an Haupt und Fenster; der volle Vorgangs-Stand — mit rekursivem Ordnerlauf durch jeden Roh-Ordner, fremden Ordnern und zwei statfs — kommt nur noch bei einem Phasenwechsel oder auf Zuruf. Roh-Größen werden 30 s gemerkt; Löschen, Aufräumen und der Knopf „Aktualisieren" messen frisch. WARUM: Durchsicht 12.09.2026, Fund F7 — auf einer NAS als Arbeitsordner war das ein Netz-Ordnerlauf pro Sekunde, und das Fenster zeichnete jede Sekunde komplett neu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
904e4b2ef6
commit
551f1d7b2c
@@ -213,6 +213,10 @@ function kernNachricht(nachricht: KernNachricht): void {
|
||||
case 'vorgaenge':
|
||||
stand.vorgaenge = nachricht.stand
|
||||
break
|
||||
case 'kompression-stand':
|
||||
// 5.7.0 (F7): nur die Warteschlange frisch — der Rest bleibt stehen.
|
||||
if (stand.vorgaenge !== null) stand.vorgaenge = { ...stand.vorgaenge, kompression: nachricht.kompression }
|
||||
break
|
||||
case 'kompression-ergebnis':
|
||||
meldung(`${nachricht.ok ? '' : '⚠ '}Kompression „${nachricht.titel}": ${nachricht.text}`)
|
||||
break
|
||||
|
||||
@@ -414,6 +414,9 @@ export type KernNachricht =
|
||||
/** Alle Vorgänge, die Roh-Belegung und die Kompressions-Warteschlange
|
||||
* (5.4.0) — geht auch ans Haupt (Taskleiste, Updates warten). */
|
||||
| { art: 'vorgaenge'; stand: VorgaengeStand }
|
||||
/** 5.7.0 (F7): NUR die Warteschlange — jeder Prozent-Tick der Kompression
|
||||
* kommt so, ohne den teuren vollen Vorgangs-Stand (Ordnerläufe, statfs). */
|
||||
| { art: 'kompression-stand'; kompression: VorgaengeStand['kompression'] }
|
||||
/** Eine Kompression ist zu Ende — fürs Toast und die Ereignis-Zeile. */
|
||||
| { art: 'kompression-ergebnis'; vorgangId: string; titel: string; ok: boolean; text: string }
|
||||
| { art: 'laufwerk-gesundheit'; id: string; bericht: LaufwerkBericht }
|
||||
@@ -594,6 +597,8 @@ export function istKernNachricht(wert: unknown): wert is KernNachricht {
|
||||
istObjekt((wert.stand as Record<string, unknown>).kompression) &&
|
||||
istObjekt((wert.stand as Record<string, unknown>).statistik)
|
||||
)
|
||||
case 'kompression-stand':
|
||||
return istObjekt(wert.kompression) && Array.isArray((wert.kompression as Record<string, unknown>).wartend)
|
||||
case 'kompression-ergebnis':
|
||||
return (
|
||||
istKennung(wert.vorgangId) &&
|
||||
|
||||
@@ -121,6 +121,9 @@ function start(): void {
|
||||
// 5.4.0: Die Kompression läuft OHNE Laufwerk weiter — Updates
|
||||
// warten auch auf sie, und die Taskleiste zeigt ihren Fortschritt.
|
||||
kompressionVerarbeiten(nachricht.stand.kompression.laeuft)
|
||||
} else if (nachricht.art === 'kompression-stand') {
|
||||
// 5.7.0 (F7): der leichte Fortschritts-Tick — dieselbe Wirkung.
|
||||
kompressionVerarbeiten(nachricht.kompression.laeuft)
|
||||
} else if (nachricht.art === 'kompression-ergebnis' && !ohneNebenwirkungen) {
|
||||
toast(nachricht.ok ? 'Rippy ist fertig' : 'Rippy: Kompression fehlgeschlagen', `${nachricht.titel}: ${nachricht.text}`)
|
||||
} else if (nachricht.art === 'disc-info' && nachricht.info.titel.length > 0 && !ohneNebenwirkungen) {
|
||||
|
||||
@@ -165,7 +165,7 @@ export function ordnerGroesse(pfad: string): number {
|
||||
}
|
||||
|
||||
/** Ordner unter roh\, die kein Vorgang kennt — nur zeigen, nie von selbst löschen. */
|
||||
export function fremdeOrdner(rohWurzel: string, bekannte: ReadonlySet<string>): FremderOrdner[] {
|
||||
export function fremdeOrdner(rohWurzel: string, bekannte: ReadonlySet<string>, groesse: (pfad: string) => number = ordnerGroesse): FremderOrdner[] {
|
||||
if (!existsSync(rohWurzel)) return []
|
||||
const liste: FremderOrdner[] = []
|
||||
let namen: string[]
|
||||
@@ -182,7 +182,7 @@ export function fremdeOrdner(rohWurzel: string, bekannte: ReadonlySet<string>):
|
||||
for (const name of namen) {
|
||||
const voll = join(rohWurzel, name)
|
||||
if (bekannte.has(voll) || bekannte.has(name)) continue
|
||||
liste.push({ ordner: voll, bytes: ordnerGroesse(voll) })
|
||||
liste.push({ ordner: voll, bytes: groesse(voll) })
|
||||
}
|
||||
return liste.sort((a, b) => b.bytes - a.bytes)
|
||||
}
|
||||
|
||||
@@ -115,12 +115,46 @@ export class VorgangDienst {
|
||||
}
|
||||
}
|
||||
|
||||
/** Jede Änderung der Warteschlange — Fortschritt eingeschlossen. 5.7.0
|
||||
* (Durchsicht F7): Ein Prozent-Tick ist eine KLEINE Nachricht
|
||||
* (kompression-stand); vorher löste jeder Tick (HandBrake: etwa einmal je
|
||||
* Sekunde) den vollen Vorgangs-Stand aus — mit einem Ordnerlauf durch
|
||||
* jeden Roh-Ordner, zwei statfs und einem kompletten Neuzeichnen des
|
||||
* Fensters. Der volle Stand geht nur noch bei einem Phasenwechsel. */
|
||||
private standGeaendert(laeuft: KompressionStand | null): void {
|
||||
if (laeuft !== null && this.letztePhase.get(laeuft.vorgangId) !== laeuft.phase) {
|
||||
const phasenwechsel = laeuft !== null && this.letztePhase.get(laeuft.vorgangId) !== laeuft.phase
|
||||
if (laeuft !== null && phasenwechsel) {
|
||||
this.letztePhase.set(laeuft.vorgangId, laeuft.phase)
|
||||
this.db?.vorgangAktualisieren(laeuft.vorgangId, { phase: laeuft.phase === 'legt-ab' ? 'legt-ab' : 'komprimiert' })
|
||||
}
|
||||
this.senden()
|
||||
if (phasenwechsel || laeuft === null) {
|
||||
this.senden()
|
||||
return
|
||||
}
|
||||
this.kontext.anHauptUndFenster({
|
||||
art: 'kompression-stand',
|
||||
kompression: { ...this.warteschlange.stand(), pausiert: this.warteschlange.istPausiert() },
|
||||
})
|
||||
}
|
||||
|
||||
// Roh-Größen sind teuer (rekursiver Ordnerlauf, bei einer NAS über das
|
||||
// Netz) und ändern sich nur, wenn Rippy selbst etwas anlegt oder löscht —
|
||||
// deshalb 30 s lang gemerkt (F7). Wer löscht, leert das Gedächtnis.
|
||||
private groessen = new Map<string, { bytes: number; wann: number }>()
|
||||
private static readonly GROESSEN_FRISCH_MS = 30_000
|
||||
|
||||
private groesseGemerkt(pfad: string): number {
|
||||
const jetzt = Date.now()
|
||||
const alt = this.groessen.get(pfad)
|
||||
if (alt !== undefined && jetzt - alt.wann < VorgangDienst.GROESSEN_FRISCH_MS) return alt.bytes
|
||||
const bytes = ordnerGroesse(pfad)
|
||||
this.groessen.set(pfad, { bytes, wann: jetzt })
|
||||
return bytes
|
||||
}
|
||||
|
||||
/** Nach Löschen oder Aufräumen: frisch messen. */
|
||||
private groessenVergessen(): void {
|
||||
this.groessen.clear()
|
||||
}
|
||||
|
||||
private async ergebnis(auftrag: KompressionsAuftrag, ergebnis: AblageErgebnis): Promise<void> {
|
||||
@@ -220,7 +254,7 @@ export class VorgangDienst {
|
||||
let rohGesamtBytes = 0
|
||||
const vorgaenge: VorgangEintrag[] = liste.map((v) => {
|
||||
const rohDa = v.rohGeloeschtAm === null && existsSync(v.rohOrdner)
|
||||
const rohBytes = rohDa ? ordnerGroesse(v.rohOrdner) : 0
|
||||
const rohBytes = rohDa ? this.groesseGemerkt(v.rohOrdner) : 0
|
||||
rohGesamtBytes += rohBytes
|
||||
const auftrag = auftragAusJson(v.auftrag)
|
||||
const ruhig = v.phase === 'fertig' || v.phase === 'fehler' || v.phase === 'abgebrochen'
|
||||
@@ -247,7 +281,7 @@ export class VorgangDienst {
|
||||
}
|
||||
})
|
||||
const bekannte = new Set(liste.map((v) => v.rohOrdner))
|
||||
const fremde = fremdeOrdner(this.einstellungen.rohWurzel(), bekannte)
|
||||
const fremde = fremdeOrdner(this.einstellungen.rohWurzel(), bekannte, (pfad) => this.groesseGemerkt(pfad))
|
||||
for (const f of fremde) rohGesamtBytes += f.bytes
|
||||
return {
|
||||
vorgaenge,
|
||||
@@ -259,6 +293,12 @@ export class VorgangDienst {
|
||||
}
|
||||
}
|
||||
|
||||
/** Der Knopf „Aktualisieren": frisch messen, nicht aus dem Gedächtnis. */
|
||||
neuMessen(): void {
|
||||
this.groessenVergessen()
|
||||
this.senden()
|
||||
}
|
||||
|
||||
senden(): void {
|
||||
try {
|
||||
this.kontext.anHauptUndFenster({ art: 'vorgaenge', stand: this.stand() })
|
||||
@@ -290,6 +330,7 @@ export class VorgangDienst {
|
||||
fertigeDateien: dateienAusJson(v.fertigeDateien, (g) => this.kontext.protokoll.schreiben(`${v.id}: ${g}`)),
|
||||
}))
|
||||
const ergebnis = aufraeumen(kandidaten, regel, new Date())
|
||||
if (ergebnis.geloescht.length > 0) this.groessenVergessen()
|
||||
const jetzt = new Date().toISOString()
|
||||
for (const id of ergebnis.geloescht) {
|
||||
this.db.vorgangAktualisieren(id, { rohGeloeschtAm: jetzt, rohBytes: this.db.vorgangLesen(id)?.rohBytes ?? 0 })
|
||||
@@ -321,6 +362,7 @@ export class VorgangDienst {
|
||||
}
|
||||
try {
|
||||
rmSync(v.rohOrdner, { recursive: true, force: true })
|
||||
this.groessenVergessen()
|
||||
this.db?.vorgangAktualisieren(vorgangId, { rohGeloeschtAm: new Date().toISOString() })
|
||||
this.kontext.hinweis(`Roh-Dateien von „${v.titel}" gelöscht (von Hand).`)
|
||||
} catch (fehler) {
|
||||
@@ -341,6 +383,7 @@ export class VorgangDienst {
|
||||
}
|
||||
try {
|
||||
rmSync(ordner, { recursive: true, force: true })
|
||||
this.groessenVergessen()
|
||||
this.kontext.hinweis(`Ordner „${ordner}" gelöscht (von Hand).`)
|
||||
} catch (fehler) {
|
||||
this.kontext.fehler(`„${ordner}" nicht gelöscht: ${String(fehler)}`)
|
||||
|
||||
@@ -289,7 +289,7 @@ function fensterNachricht(frage: unknown): void {
|
||||
void werkzeuge.schluesselLauf().then(() => werkzeuge.senden())
|
||||
return
|
||||
case 'vorgaenge-laden':
|
||||
vorgaenge.senden()
|
||||
vorgaenge.neuMessen()
|
||||
return
|
||||
case 'vorgang-roh-loeschen':
|
||||
vorgaenge.rohLoeschen(frage.vorgangId)
|
||||
|
||||
Reference in New Issue
Block a user