Make rendering failure-safe and project editing reproducible

This commit is contained in:
Mikei386
2026-09-13 09:20:37 +02:00
parent c530fe025a
commit 73961f6afe
13 changed files with 1019 additions and 212 deletions
+109
View File
@@ -0,0 +1,109 @@
# Funktionsanalyse GrowthLapse
> Historische Bestandsaufnahme vor Version 0.3.0. Umgesetzte Korrekturen und aktuelle Grenzen stehen in [VERBESSERUNGEN.md](VERBESSERUNGEN.md).
Stand: 13. September 2026, Commit `c530fe0`. Grundlage ist die Prüfung der aktiven Quellcodepfade. Die unten angegebenen Szenarien sind aus dem Code hergeleitet, nicht durch vollständige UI-/Video-Tests reproduziert. Es wurden keine Laufzeitfunktionen geändert.
## Funktionsumfang und Ablauf
1. **Import:** Dateien eines Ordners einlesen, Ausgabe ausschließen, Dauer und Abmessungen mit FFprobe ermitteln, sortieren und Varianten auswählen.
2. **Segmentplanung:** Ausschnitt aus Mitte/Offset oder anhand von Gesicht-/Augenbewertung wählen. Segmentlänge auf Quelldauer begrenzen.
3. **Verarbeitung:** Optional stabilisieren, Gesicht ausrichten, Geschwindigkeit und Format normalisieren. Audio entfernen oder zeitlich anpassen; bei fehlendem Audio eine stille Spur erzeugen.
4. **Zusammenfügen:** Ohne Übergänge Stream-Copy; mit Übergängen Video-Xfade und optional Audio-Crossfade.
5. **Nachbearbeitung:** Projekt speichern/laden, Segmentstarts und Varianten ändern, Clips umsortieren und Cache wiederverwenden.
Die Funktionen decken einen persönlichen Wachstums-Zeitraffer gut ab. Der größte Verbesserungsbedarf liegt bei verlässlicher Nachbearbeitung und Erhaltung bereits erstellter Ergebnisse.
## Priorisierte Befunde
### P1: Ausgabeziel und Projekt laufen beim erneuten Rendern auseinander
**Szenario:** Projekt A rendern, in der Oberfläche Ausgabe B wählen, „Änderungen neu rendern“ ausführen.
**Befund:** `VideoProcessor.swift:243` verwendet das neue Ziel und erstellt dessen Arbeitsordner. Bei Zeile 275 wird aber das alte Projekt unverändert übernommen. Dessen `outputFilePath`, `cacheDirectoryPath` und Clip-Pfade zeigen weiter auf A; `saveProject` bei Zeile 1638 speichert entsprechend bei A. Auch die gespeicherten Zielabmessungen werden bei Formatänderungen nicht aktualisiert.
**Folge:** Video B und gespeichertes Projekt passen nicht zusammen. Bei fehlendem alten Cache können Schreibzugriffe auf die alten Clip-Pfade scheitern, obwohl der neue Cache angelegt wurde.
**Vorschlag:** Einen expliziten „Projekt speichern unter“-Ablauf einführen. Ausgabe, Cache, Clip-Pfade und Zielabmessungen gemeinsam aktualisieren; vorhandene gültige Clips kontrolliert übernehmen.
**Abnahmetest:** A nach B exportieren, B-Projekt laden und einen Clip erneut rendern. A bleibt unverändert, alle B-Verweise sind konsistent.
### P1: Abbruch und Fehler können vorhandene Ergebnisse oder Cache vernichten
**Befund:** `handleCancellation` (`VideoProcessor.swift:2054`) löscht `temporaryDirectory` vollständig, auch wenn dies der bestehende Review-Cache ist. Ein neuer Render löscht den alten Cache bereits bei `createWorkingDirectory` (Zeile 1469), vor erfolgreichem Import. FFmpeg schreibt mit `-y` direkt ins Ausgabeziel; `replaceOutput` (Zeile 1650) löscht zunächst das alte Ergebnis und kopiert danach.
**Folge:** Ein abgebrochener Nachbearbeitungsversuch entfernt auch unveränderte Cache-Clips. Ein Fehler während der Endausgabe kann ein zuvor gutes Video durch eine unvollständige Datei ersetzen.
**Vorschlag:** Neue oder geänderte Clips und Endausgabe zunächst in einen separaten Arbeitsbereich schreiben, nach Erfolg übernehmen. Bei Abbruch nur Dateien des aktuellen Durchlaufs entfernen. Bereits bestätigte Ergebnisse bis zur erfolgreichen Übernahme erhalten.
**Abnahmetest:** Ein vorhandenes Ergebnis samt Cache sichern, Neurender während Normalisierung und Endausgabe abbrechen sowie einen Schreibfehler provozieren. Das bisherige Ergebnis bleibt abspielbar und unveränderte Cache-Clips bleiben erhalten.
### P1: Cache-Löschung vertraut ungeprüften Projektpfaden
**Befund:** `loadProject` (`VideoProcessor.swift:160`) decodiert externe JSON-Pfade ohne Verzeichnisvalidierung. „Ergebnis bestätigen“ entfernt bei Zeile 191 rekursiv genau `cacheDirectoryPath`.
**Folge:** Eine falsch bearbeitete oder fremde Projektdatei kann ein anderes beschreibbares Verzeichnis als Cache deklarieren. Die Löschaktion ist nicht auf einen von GrowthLapse erzeugten Cache begrenzt.
**Vorschlag:** Cache-Verzeichnis über eine eigene Kennung und validierte Projektzuordnung absichern; Pfade einschließlich Symlinks prüfen. Ungültige Projekte vor Übernahme ablehnen. Löschfehler anzeigen, statt anschließend immer Erfolg zu protokollieren.
**Abnahmetest:** Ein Projekt verweist auf einen separaten Testordner mit einer Markerdatei. Laden oder Löschen wird abgelehnt; Marker bleibt erhalten. Ausschließlich mit temporären Testverzeichnissen prüfen.
### P2: Globale Segmentparameter werden bei Nachbearbeitung ignoriert
**Szenario:** Nach dem ersten Render die Ausschnittlänge von 8 auf 4 Sekunden ändern und erneut rendern.
**Befund:** `normalizeVideos` (`VideoProcessor.swift:915`) verwendet weiterhin `projectClip.segmentLength`. `makeProject` und `initialSegmentStart` laufen nur für neue Projekte. Der Fingerprint (Zeile 1580) enthält weder Ausschnittlänge noch Offset oder automatische Segmentwahl.
**Folge:** Die Oberfläche suggeriert die Übernahme geänderter Einstellungen, die Segmentplanung bleibt jedoch unverändert. Auch die globale Variantenwahl wird beim Neurender nicht erneut angewandt.
**Vorschlag:** Einstellungen als „nur für neuen Import“ kennzeichnen oder eine ausdrückliche Aktion „Segmente neu berechnen“ anbieten. Manuelle Clip-Korrekturen dabei gezielt erhalten oder nach Auswahl ersetzen.
**Abnahmetest:** Globale Änderung verändert die geplanten Clips nachvollziehbar oder die Oberfläche erklärt eindeutig, warum sie nicht gilt.
### P2: Eingebrannte Clipnummern bleiben nach Umsortieren veraltet
**Befund:** `reorderedProject` (`VideoProcessor.swift:1603`) ändert `index`, setzt aber nicht `needsRender`. Der Fingerprint berücksichtigt keine Reihenfolge. `normalizeVideos` verwendet gültige Cache-Clips unverändert; darin ist die Nummer bereits eingebrannt.
**Folge:** Nach geänderter Sortierung stimmen Nummer im Video und Position in der Projektliste nicht mehr überein.
**Vorschlag:** Bei aktivierten Nummern verschobene Clips invalidieren oder Nummern erst beim finalen Export einblenden.
**Abnahmetest:** Zwei Clips mit Nummern rendern, Reihenfolge wechseln, neu rendern. Sichtbare Nummern folgen der neuen Position.
### P2: STOP erreicht laufende Bildanalysen nicht direkt
**Befund:** `BestSegmentAnalyzer.detectBestSegment` (Zeile 2459) und `FaceAlignmentAnalyzer.detect` (Zeile 2605) starten `Task.detached` und warten auf `.value`. Ein Handle zur expliziten Weiterleitung des Abbruchs fehlt. Die Abbruchprüfungen laufen innerhalb des eigenständigen Tasks.
**Folge:** STOP kann während einer umfangreichen Analyse bis zu deren Ende warten, obwohl der äußere Render-Task bereits abgebrochen ist.
**Vorschlag:** Analyse-Task per Cancellation-Handler explizit abbrechen und CancellationError nicht als gewöhnlichen Analyse-Fallback behandeln.
**Abnahmetest:** Lange Analyse abbrechen; messen, dass nach Abschluss der gerade laufenden Einzeloperation keine weiteren Frames analysiert werden.
### P2: Projekte speichern keine vollständigen Render-Einstellungen
**Befund:** `GrowthLapseProject` speichert Clipdaten und Fingerprint, aber keinen vollständigen Einstellungssatz. `ContentView.swift:620` lädt nur Ausgabe- und Eingabepfad ins aktuelle Einstellungsmodell.
**Folge:** Projekt A nach Arbeit an B laden und neu rendern kann A mit den globalen Einstellungen von B erzeugen. Ein Fingerprint erkennt Unterschiede, stellt aber die ursprünglichen Werte nicht wieder her.
**Vorschlag:** Versioniertes Projektschema mit Render-Einstellungen; Migration alter Dateien mit sichtbarem Hinweis. Werkzeugsuche und andere reine Rechnerpräferenzen separat halten.
**Abnahmetest:** Zwei Projekte mit verschiedenen FPS, Audio- und Ausgabeformaten abwechselnd laden; jedes stellt seine eigenen Einstellungen wieder her.
## Weitere Verbesserungen
- **Vorabprüfung:** Filter und Encoder vor dem Rendern vollständig prüfen. VidStab-Fallback darf Deshake nur wählen, wenn vorhanden (`VideoProcessor.swift:568`).
- **Cache-Gültigkeit:** Quellgröße und Änderungszeit oder einen Inhaltshash erfassen. Ein extern ersetztes Video am selben Pfad invalidiert aktuell keinen Cache automatisch.
- **Projektvalidierung:** Leere Cliplisten, ungültige Längen, fehlende Quellen und inkonsistente Pfade ablehnen. `videos[0]` in Zeile 271 setzt eine nicht leere Liste voraus; die Oberfläche verhindert nicht alle Wege mit einem leeren geladenen Projekt.
- **Prozessausgabe:** Den Pipe-Reader bis EOF auslesen und Exit sowie Ausgabe synchron zusammenführen (`FFmpegService.swift:137`). Ob heute Ausgabe verloren geht, muss ein gezielter Stresstest klären.
- **Skalierung:** Xfade öffnet alle Clips in einem Prozess (`VideoProcessor.swift:1387`). Mit 10/50/100 Clips Speicherbedarf und Laufzeit messen, dann gegebenenfalls blockweise rendern. Ein Ressourcenproblem ist hier noch nicht gemessen.
- **Bedienung:** Importliste vor dem Rendern, geschätzte Enddauer, Vergleich Original/Ergebnis, sichtbare Kennzeichnung gecachter und neu zu rendernder Clips. Gesichtsauswahl bei mehreren Personen wäre eine sinnvolle Erweiterung.
- **Portabilität:** Relative Projektpfade und eine Funktion zum Wiederfinden verschobener Quellen ergänzen.
## Empfohlene Reihenfolge
1. Ausgabe und Cache gegen Abbruch/Fehler schützen, geladene Pfade validieren.
2. Projektziele und Einstellungen konsistent machen; Segment-Neuberechnung und Cache-Invalidierung klären.
3. STOP für Analysen, Werkzeug-Vorabprüfung und Prozessausgabe absichern.
4. Automatisierte Integrationstests mit synthetischen Videos ergänzen: ein/zwei Clips, Audio gemischt, Übergänge an/aus, Formatwechsel, Neurender, Speichern/Laden und Abbruch. Dauer, Auflösung, FPS und Audiospuren per FFprobe prüfen.
5. Erst danach größere Komfortfunktionen oder das deaktivierte Foto-Morphing ausbauen.