|
|
<div align="center">
|
|
|
<img src="assets/icon.svg" width="120" alt="drop.two">
|
|
|
<h1>drop.two</h1>
|
|
|
<p><strong>9:16 rein. 9:16 und 4:5 raus.</strong></p>
|
|
|
</div>
|
|
|
|
|
|
---
|
|
|
|
|
|
## 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 Segmente bleiben zeichengleich erhalten.
|
|
|
|
|
|
**Abbruchbedingungen** — die App verarbeitet nicht und meldet es sichtbar:
|
|
|
|
|
|
- Dateiname entspricht nicht dem Schema (weniger als vier Segmente)
|
|
|
- Format-Segment ist nicht `9x16`
|
|
|
- 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 (Offset später konfigurierbar)
|
|
|
```
|
|
|
|
|
|
| Quelle | Ziel |
|
|
|
|---|---|
|
|
|
| 1080 × 1920 | 1080 × 1350 |
|
|
|
| 2160 × 3840 | 2160 × 2700 |
|
|
|
|
|
|
**Codec-Verhalten:** Der Quell-Codec wird erkannt und passend ausgespielt — ProRes bleibt ProRes, H.264 bleibt H.264 mit vergleichbarer Bitrate. Die Audiospur läuft per Passthrough durch, ohne Neukodierung. Framerate, Farbraum 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
|
|
|
- [ ] Vertikaler Crop-Offset pro Datei justierbar
|
|
|
- [ ] In-App-Updates über Sparkle
|
|
|
- [ ] Batch-Verlauf der letzten Durchläufe
|
|
|
|
|
|
## 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
|
|
|
├── 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.
|
|
|
|
|
|
## 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
|
|
|
- Vertikaler Offset — global, pro Projekt oder pro Datei
|
|
|
|
|
|
## 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. |