drop.two

drop.two

9:16 rein. 9:16 und 4:5 raus.

--- ## Warum Alle Videos werden im Format 9:16 geschnitten. Nach der Freigabe müssen sie zusätzlich als 4:5 abgelegt werden. Das Framing wird beim Schnitt bereits so angelegt, dass der 4:5-Ausschnitt funktioniert — es braucht also keine zweite Timeline, sondern nur einen sauberen, mittigen Beschnitt des fertigen Masters. Bisher passiert das über ein selbstgebautes Python-CLI. `drop.two` macht daraus ein Werkzeug, das auch Kolleginnen und Kollegen ohne Terminal benutzen können: Dateien reinziehen, Ziel prüfen, fertig. ## Was die App macht 1. Eine oder mehrere 9:16-Dateien werden ausgewählt oder per Drag & Drop übergeben. 2. Aus dem Dateinamen wird das Projekt gelesen. 3. Im Zielordner entsteht ein Unterordner mit dem Projektnamen. 4. Dort landen beide Fassungen: das unveränderte 9:16-Original und die neu gerenderte 4:5-Version. Kein Preset-Dschungel, keine Timeline, kein Export-Dialog. ## Namenskonvention ``` [Projekt]-[Format]-[Version]-[Sprache].[ext] ``` Beispiel: ``` SOMOS-9x16-v3-DE.mp4 → SOMOS/SOMOS-9x16-v3-DE.mp4 SOMOS/SOMOS-4x5-v3-DE.mp4 ``` **Parsing-Regel:** Es wird **von rechts** gelesen. Die letzten drei Segmente sind Sprache, Version und Format; alles davor ist der Projektname. Nur so bleiben Projektnamen mit eigenem Bindestrich intakt: ``` Somos-Pendant-9x16-v3-DE.mp4 └──── Projekt ────┘ └Fmt┘└Ver┘└Spr┘ ``` Für die Zielversion wird ausschließlich das Format-Segment ersetzt: `9x16` → `4x5`. Alle anderen Zeichen bleiben gleich erhalten. **Schreibweisen:** Das Format darf groß oder klein (`9x16`, `9X16`, `9×16`) und mit `-`, `_` oder Leerzeichen abgetrennt sein; die Trenner bleiben in der Zielfassung erhalten (`Kampagne_9X16_v2` → `Kampagne_4x5_v2`). Ein Rest hinter dem Format ist nicht nötig. **Kein Format im Namen:** Die App zeigt das schon beim Reinziehen und bietet „4x5 anhängen“ an. Dann wird der ganze Dateistamm zum Projekt, und `4x5` wird mit dem Trenner angehängt, den der Name schon benutzt (`Sommerfest Teaser.mov` → `Sommerfest Teaser/Sommerfest Teaser 4x5.mp4`). Ohne diese Wahl wird die Datei übersprungen. **Abbruchbedingungen** — die App verarbeitet nicht und meldet es sichtbar: - Kein `9x16` mit Projektnamen davor im Namen (Ausweg: „4x5 anhängen“, s. o.) - Quelldatei hat kein 9:16-Seitenverhältnis - Zieldatei existiert bereits (überschreiben nur nach Rückfrage) ## Crop-Logik Der 4:5-Ausschnitt behält die **volle Breite** und beschneidet oben und unten: ``` Breite bleibt unverändert Höhe = Breite × 5 ÷ 4 (auf gerade Zahl gerundet) Anker vertikal zentriert (pro Datei verschiebbar, s. u.) ``` **Ausschnitt verschieben:** In der Review-Ansicht lässt sich der 4:5-Rahmen mit der Maus nach oben oder unten ziehen. Für Clips mit Szenenwechsel gibt es **Positionsmarken**: an die Stelle spulen, *Marke setzen* (Taste `M`), Rahmen ziehen. Jede Marke gilt ab ihrem Frame bis zur nächsten, als harter Schnitt ohne Fahrt dazwischen. Tasten: Leertaste spielt ab, `←`/`→` springen um ein Frame. Der Plan gilt pro Datei und wird mit *Übernehmen* gesetzt; die Dateiliste zeigt angepasste Ausschnitte in Gold. | Quelle | Ziel | |---|---| | 1080 × 1920 | 1080 × 1350 | | 2160 × 3840 | 2160 × 2700 | **Codec-Verhalten:** Die 4:5-Fassung ist immer MP4 mit H.264 (High Profile) und AAC 128 kbit/s, das Zielformat für Feed und Meta Ads, egal ob ProRes, H.264 oder HEVC reinkommt. Die Bitrate richtet sich nach Auflösung und Framerate. Framerate und Dauer bleiben unangetastet. ## Funktionsumfang **MVP** - [x] Dateiauswahl und Drag & Drop - [x] Einstellbarer Quellordner (Dialog öffnet direkt dort) - [x] Einstellbarer Zielordner, Projekt-Unterordner wird automatisch angelegt - [x] Dateiauswahl vorsortiert nach *zuletzt geändert* - [x] Fortschrittsanzeige pro Datei und gesamt - [x] Fehlerliste statt stiller Abbrüche **Danach** - [x] Review-Ansicht: 9:16-Master mit eingeblendetem 4:5-Rahmen zur Framing-Kontrolle, scrubbar - [ ] Menu-Bar-Betrieb mit Drop-Target - [x] Vertikaler Crop-Offset pro Datei justierbar, mit Positionsmarken für Szenenwechsel - [x] In-App-Updates über Sparkle (automatisch plus „Nach Updates suchen …“ im App-Menü) - [ ] Batch-Verlauf der letzten Durchläufe - [ ] Hilfe-Bereich: klickbare Kurzanleitung zur Bedienung der App - [x] Rechtliche Angaben: Website verlinkt Impressum & Datenschutz von zielke.studio (wie Patchcord) ## Tech-Stack **Swift + SwiftUI**, macOS 13+. | Baustein | Lösung | |---|---| | Oberfläche | SwiftUI, `MenuBarExtra` für den Hintergrundbetrieb | | Video | **AVFoundation** — hardwarebeschleunigt über VideoToolbox | | Review-Player | `AVPlayer` mit Overlay | | Updates | **Sparkle 2** gegen `appcast.xml` auf eigenem Server | | Einstellungen | `@AppStorage` + Security-Scoped Bookmarks für Ordnerzugriff | | Projektdatei | **XcodeGen** (`project.yml`) — textbasiert, merge-freundlich | | Paket | Signiertes `.dmg`, ca. 5 MB | **Warum kein ffmpeg:** Der Beschnitt ist rein geometrisch. AVFoundation erledigt ihn nativ über eine `AVMutableVideoComposition`. Das spart rund 80 MB Bundle-Größe, die GPL-Frage und ein mitzulieferndes Binary. **Warum nicht Python:** py2app- und PyInstaller-Bundles sind fragil, starten träge, wiegen ein Vielfaches und machen Code-Signing sowie Notarisierung unnötig schwer. Für ein Werkzeug, das Kollegen installieren sollen, ist das der falsche Unterbau. ## Architektur ``` DropTwoCore Framework, komplett UI-frei ├── FileNameParser zerlegt und baut Dateinamen ├── VideoInspector liest Dimension, Codec, Framerate, Dauer ├── CropPlan Ausschnitt-Position(en) über die Laufzeit ├── CropEngine AVAsset → VideoComposition → Export └── JobQueue async/await, 1–2 parallele Exports DropTwo App-Target ├── Settings Pfade, Bookmarks, Presets ├── DropZone Auswahl und Drag & Drop ├── ProgressView Fortschritt und Fehlerliste └── ReviewSheet AVPlayer mit 4:5-Overlay droptwo-cli Kommandozeilen-Target gegen denselben Core ``` Der Kern kennt keine Oberfläche. Das hält ihn testbar und macht das CLI-Target zum ehrlichen Prüfstein: Was dort funktioniert, funktioniert in der App. ## Projektstruktur ``` drop.two/ ├── project.yml XcodeGen-Konfiguration ├── Sources/ │ ├── DropTwoCore/ │ ├── DropTwo/ │ └── DropTwoCLI/ ├── Tests/ │ └── DropTwoCoreTests/ ├── assets/ │ ├── icon.svg Bildmarke │ └── icon-menubar.svg Template-Variante für die Menu-Bar ├── scripts/ │ ├── build.sh │ └── release.sh └── docs/ ``` Die `.xcodeproj` wird **nicht** eingecheckt — sie entsteht aus `project.yml`. ## Einrichtung **Voraussetzungen** - Xcode (vollständig, aus dem App Store) — wegen macOS-SDK, `xcodebuild` und `notarytool` - Homebrew ```bash brew install xcodegen swiftformat git clone https://git.samuelzielke.de/samuelzielke/drop.two.git cd drop.two xcodegen generate open DropTwo.xcodeproj # nur bei Bedarf ``` **Arbeiten ohne Xcode-Oberfläche:** VS Code mit der offiziellen Swift-Extension (SourceKit-LSP) deckt Autovervollständigung, Fehleranzeige und Navigation ab. Gebaut wird per `xcodebuild`. Xcode selbst braucht es für SwiftUI-Previews, Zertifikate und Instruments. ```bash xcodebuild -scheme DropTwo -configuration Debug build xcodebuild -scheme DropTwoCoreTests test ``` ## Release Vor dem ersten Release einmalig einzurichten: 1. **Apple Developer Program** (99 €/Jahr, Anmeldung als Einzelperson). Liefert das *Developer ID Application*-Zertifikat. Ohne Signatur und Notarisierung blockiert Gatekeeper die App bei allen anderen. 2. **Sparkle-Schlüsselpaar** per `generate_keys`. Der private Schlüssel bleibt im macOS-Schlüsselbund und **niemals im Repo**; der öffentliche wandert als `SUPublicEDKey` in die `Info.plist`. 3. **Team-ID** in `project.yml` unter `DEVELOPMENT_TEAM` eintragen. > **Den privaten Sparkle-Schlüssel sichern.** Geht er verloren, lassen sich bereits installierte Kopien nie wieder aktualisieren — alle müssten neu installieren. Ablauf pro Release: ```bash xcodebuild -scheme DropTwo -configuration Release archive … codesign --deep --options runtime --sign "Developer ID Application: …" DropTwo.app create-dmg … xcrun notarytool submit drop.two-1.0.dmg --keychain-profile "notary" --wait xcrun stapler staple drop.two-1.0.dmg sign_update drop.two-1.0.dmg # Signatur in appcast.xml eintragen ``` DMG und `appcast.xml` landen anschließend auf dem eigenen Server. Der Code liegt auf Gitea, die Release-Artefakte nicht. ### Landingpage (`site/`) Die Seite unter [droptwo.zielke.studio](https://droptwo.zielke.studio) liegt in `site/` und wird nicht von `publish-release.sh` hochgeladen. Die Versionsnummer liest sie selbst aus `appcast.xml`, nach einem Release muss sie also nicht angefasst werden. Nach Änderungen an der Seite: ```bash rsync -a --chmod=D755,F644 site/index.html site/favicon.svg site/fonts site/og samuelzielke@10.0.5.223:/var/www/updates/droptwo/ ``` Schriften liegen selbst gehostet in `site/fonts/` (keine Anfragen an Google). Das Vorschaubild für geteilte Links `site/og/og-de.png` entsteht aus `og-de.html`: ```bash "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new --hide-scrollbars \ --window-size=1200,630 --screenshot="$PWD/site/og/og-de.png" "file://$PWD/site/og/og-de.html" ``` Die Übersicht aller Apps und das Statistik-Dashboard liegen im Repo `zielke-apps`. ## Umsetzungsreihenfolge Die Reihenfolge ist bewusst gewählt: 1. `FileNameParser` samt Unit-Tests — inklusive Bindestrich-Kantenfällen 2. `VideoInspector` und `CropEngine` 3. `droptwo-cli` als Prüfstein, echte Datei durchjagen, **Ausgabequalität bewerten** 4. Erst danach die Oberfläche 5. Review-Ansicht 6. Signierung, Notarisierung, Sparkle Schritt 3 entscheidet über das Projekt. Liefert AVFoundation nicht die nötige Qualität, muss das am ersten Tag auffallen — nicht nachdem die halbe App steht. ## Offene Punkte - Zielpfad-Schema bei mehreren Sprachen oder Versionen desselben Projekts - Verhalten bei bereits vorhandenem Zielordner: ergänzen oder warnen - Ob das 9:16-Original kopiert oder verschoben wird - Bitrate-Strategie bei H.264: Quellbitrate übernehmen oder fester Qualitätsfaktor ## Gestaltung Bildmarke: ein 9:16-Rahmen, darin der 4:5-Ausschnitt in voller Breite und vertikal zentriert — exakt das, was die App tut. | | | |---|---| | Dark | `#0B0B0C` | | Light | `#F5F5F4` | | Gold | `#C8A96A` | | Mid Gray | `#B8B5B0` | Gold ist der einzige Akzent und bleibt sparsam eingesetzt.