Files
ZeasyAlexandClaude Opus 5 5f6301268a Native Windows-Auslieferung statt Container
Docker faellt weg. Das Projekt hat keine npm-Abhaengigkeiten und Node fuehrt
TypeScript direkt aus - der Container schrieb nur noch drei Programme fest,
und dafuer braucht es keine Virtualisierung, sondern einen Ordner.

Der Anlass war, dass ADR-0004 zwar `docker compose pull` versprach, es aber
nie ein veroeffentlichtes Image gab. Wer installieren wollte, musste
whisper.cpp aus dem Quelltext uebersetzen.

Plattformschicht (packages/core/src/platform.ts):
- Programme werden neben der Anwendung gesucht, nicht im PATH
- Daten unter %LOCALAPPDATA%, damit Installation und Update ohne
  Administratorrechte auskommen
- Schriften mitgeliefert: ohne fontsdir setzt libass wortlos eine
  Ersatzschrift ein - gemessen 720 statt 826 Pixel Zeilenbreite

Windows-Eigenheiten, an echtem ffmpeg 8.1 durchprobiert:
- Pfade im Filtergraphen brauchen alle drei Schritte (Vorwaertsschraegstriche,
  escapter Doppelpunkt, Anfuehrungszeichen); je zwei davon reichen nicht
- `nice` gibt es nicht, ersetzt durch os.setPriority

Auslieferung:
- scripts/build-package.ts baut das Paket, laeuft auch unter Linux
- install.ps1 ohne Administratorrechte, Autostart ueber den Aufgabenplaner
- update legt neue Fassungen bereit; eingespielt wird beim naechsten Start,
  nie mitten im Stream
- ZIP lesen und schreiben ohne Abhaengigkeiten (packages/core/src/zip.ts)

Kein `irm ... | iex`-Einzeiler: git.zeasy.dev antwortete instanzweit mit einer
Umleitung auf /user/login, und `iex` fuehrte die Anmeldeseite als PowerShell
aus. Der Updater verfolgt Umleitungen jetzt nicht und hat keine erfundene
Vorgabeadresse mehr.

Ausserdem: backup.ps1 war an zwei Stellen dokumentiert, aber nie geschrieben.

ADR-0007 ersetzt ADR-0004. 84 Tests, Typpruefung sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhVSfMFdWnXKCv5JfLnArZ
2026-09-09 17:39:26 +02:00

64 lines
2.1 KiB
TypeScript

// Auswertung einer OBS-Szene gegen den 9:16-Ausschnitt.
//
// Der Anlass: In einem fertigen Clip lagen fremde Untertitel im Bild, und
// niemand wusste, woher sie kommen. Raten hilft da nicht - sie sind ins
// Streambild eingebrannt, also muss man nachsehen, welche Quelle sie malt.
// Die reine Rechnerei steht hier, damit sie pruefbar ist; das Anzeigen und
// das Reden mit OBS bleibt im Entrypoint.
import type { Rect } from '#core/config.ts';
export type Box = { x: number; y: number; w: number; h: number };
/**
* Quellenarten und Namen, hinter denen erfahrungsgemaess Untertitel stecken.
* Bewusst weit gefasst: ein Fehlalarm kostet einen Blick, eine uebersehene
* Quelle kostet jeden Clip.
*/
export const CAPTION_HINTS =
/caption|untertitel|subtitle|transcri|speech|vocal|stt|dictat|closed.?cc|(^|\W)cc(\W|$)/i;
/** Wie viel Prozent des Ausschnitts die Quelle ueberdeckt. */
export function overlapPercent(box: Box, r: Rect): number {
const w = Math.max(0, Math.min(box.x + box.w, r.x + r.w) - Math.max(box.x, r.x));
const h = Math.max(0, Math.min(box.y + box.h, r.y + r.h) - Math.max(box.y, r.y));
const area = r.w * r.h;
return area > 0 ? Math.round(((w * h) / area) * 100) : 0;
}
export type SourceVerdict = {
cam: number;
game: number;
/** Steht die Quelle im fertigen Short? */
inShort: boolean;
/** Sieht sie nach fremden Untertiteln aus? */
suspectCaption: boolean;
};
/**
* Urteil ueber eine einzelne Quelle.
*
* Ausgeblendete Quellen sind nie im Bild - egal wo sie liegen. Und eine
* Untertitelquelle ausserhalb des Ausschnitts ist kein Problem, sondern genau
* das, was docs/obs-layout.md vorschlaegt: im Stream sichtbar lassen, aus dem
* Gameplay-Band herausschieben.
*/
export function judgeSource(args: {
box: Box;
enabled: boolean;
name: string;
kind: string;
url?: string;
cam: Rect;
game: Rect;
}): SourceVerdict {
const cam = overlapPercent(args.box, args.cam);
const game = overlapPercent(args.box, args.game);
const inShort = args.enabled && (cam > 0 || game > 0);
return {
cam, game, inShort,
suspectCaption: inShort &&
CAPTION_HINTS.test(`${args.name} ${args.kind} ${args.url ?? ''}`),
};
}