schnippo 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- schnippo-0.11.0/PKG-INFO +208 -0
- schnippo-0.11.0/README.md +177 -0
- schnippo-0.11.0/pyproject.toml +61 -0
- schnippo-0.11.0/pyproject.toml.orig +56 -0
- schnippo-0.11.0/src/schnippo/__init__.py +3 -0
- schnippo-0.11.0/src/schnippo/acoustic.py +166 -0
- schnippo-0.11.0/src/schnippo/analysis.py +183 -0
- schnippo-0.11.0/src/schnippo/audio.py +267 -0
- schnippo-0.11.0/src/schnippo/branding.py +26 -0
- schnippo-0.11.0/src/schnippo/cache.py +61 -0
- schnippo-0.11.0/src/schnippo/cache_admin.py +94 -0
- schnippo-0.11.0/src/schnippo/config.py +181 -0
- schnippo-0.11.0/src/schnippo/context.py +149 -0
- schnippo-0.11.0/src/schnippo/cutlist.py +1358 -0
- schnippo-0.11.0/src/schnippo/data/guides/01_rough_cut.md +69 -0
- schnippo-0.11.0/src/schnippo/data/guides/02_fine_cut.md +45 -0
- schnippo-0.11.0/src/schnippo/data/guides/03_broll.md +39 -0
- schnippo-0.11.0/src/schnippo/data/guides/04_polish.md +40 -0
- schnippo-0.11.0/src/schnippo/data/guides/05_motion_graphics.md +63 -0
- schnippo-0.11.0/src/schnippo/data/guides/06_sound_design.md +31 -0
- schnippo-0.11.0/src/schnippo/data/guides/07_reels.md +30 -0
- schnippo-0.11.0/src/schnippo/data/guides/design_tokens.json +124 -0
- schnippo-0.11.0/src/schnippo/data/guides/technik.md +86 -0
- schnippo-0.11.0/src/schnippo/data/sfx/LICENSES.md +54 -0
- schnippo-0.11.0/src/schnippo/data/sfx/analysis/groups.json +974 -0
- schnippo-0.11.0/src/schnippo/data/sfx/library.json +860 -0
- schnippo-0.11.0/src/schnippo/data/sfx/recorded.json +289 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/alarm_bell.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/alarm_real.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/click_mouse.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/click_mouse_sharp.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/click_mouse_soft.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/clock_timer.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/clock_timer_b.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_bell.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_bright.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_chime.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_glass.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_notify.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/ding_real.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/downer_tape.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/downer_vinyl.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/impact_boom.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/impact_hit.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/impact_soft_low.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/impact_soft_mid.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/pop_bright.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/pop_mid.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/pop_soft.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/riser_impact.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/riser_long.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/riser_short.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/riser_swell.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/swish_bright.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/swish_mid.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/swish_soft.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/tick_bright.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/tick_mid.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/tick_soft.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/typing_mech.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/typing_mech_b.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/whoosh_big.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/whoosh_end.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/whoosh_mid.wav +0 -0
- schnippo-0.11.0/src/schnippo/data/sfx/wav/whoosh_soft.wav +0 -0
- schnippo-0.11.0/src/schnippo/delivery.py +237 -0
- schnippo-0.11.0/src/schnippo/design.py +78 -0
- schnippo-0.11.0/src/schnippo/edges.py +121 -0
- schnippo-0.11.0/src/schnippo/errors.py +63 -0
- schnippo-0.11.0/src/schnippo/export.py +201 -0
- schnippo-0.11.0/src/schnippo/fcpxml.py +72 -0
- schnippo-0.11.0/src/schnippo/ffmpeg_dist.py +32 -0
- schnippo-0.11.0/src/schnippo/formats.py +149 -0
- schnippo-0.11.0/src/schnippo/formatting.py +123 -0
- schnippo-0.11.0/src/schnippo/frames.py +272 -0
- schnippo-0.11.0/src/schnippo/framing.py +25 -0
- schnippo-0.11.0/src/schnippo/glossary.py +182 -0
- schnippo-0.11.0/src/schnippo/guard.py +125 -0
- schnippo-0.11.0/src/schnippo/guides.py +48 -0
- schnippo-0.11.0/src/schnippo/jobs.py +68 -0
- schnippo-0.11.0/src/schnippo/kenburns.py +59 -0
- schnippo-0.11.0/src/schnippo/keychain.py +35 -0
- schnippo-0.11.0/src/schnippo/markers.py +80 -0
- schnippo-0.11.0/src/schnippo/media.py +247 -0
- schnippo-0.11.0/src/schnippo/mg/__init__.py +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/dm-sans-600.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/dm-sans-800.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/dm-sans-LICENSE.txt +93 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/instrument-sans-600.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/instrument-sans-700.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/instrument-sans-LICENSE.txt +93 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/inter-600.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/inter-800.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/inter-LICENSE.txt +93 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/manrope-600.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/manrope-800.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/manrope-LICENSE.txt +93 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/space-grotesk-600.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/space-grotesk-700.woff2 +0 -0
- schnippo-0.11.0/src/schnippo/mg/fonts/space-grotesk-LICENSE.txt +93 -0
- schnippo-0.11.0/src/schnippo/mg/place.py +127 -0
- schnippo-0.11.0/src/schnippo/mg/preview.py +142 -0
- schnippo-0.11.0/src/schnippo/mg/render.py +254 -0
- schnippo-0.11.0/src/schnippo/mg/templates.py +331 -0
- schnippo-0.11.0/src/schnippo/mg/tokens.py +133 -0
- schnippo-0.11.0/src/schnippo/migrate.py +38 -0
- schnippo-0.11.0/src/schnippo/models.py +130 -0
- schnippo-0.11.0/src/schnippo/onboarding.py +120 -0
- schnippo-0.11.0/src/schnippo/passes.py +188 -0
- schnippo-0.11.0/src/schnippo/prefs.py +103 -0
- schnippo-0.11.0/src/schnippo/profiles.py +163 -0
- schnippo-0.11.0/src/schnippo/projects.py +129 -0
- schnippo-0.11.0/src/schnippo/py.typed +0 -0
- schnippo-0.11.0/src/schnippo/qc.py +329 -0
- schnippo-0.11.0/src/schnippo/reels.py +238 -0
- schnippo-0.11.0/src/schnippo/reframe.py +296 -0
- schnippo-0.11.0/src/schnippo/render.py +123 -0
- schnippo-0.11.0/src/schnippo/resolve_bridge.py +1081 -0
- schnippo-0.11.0/src/schnippo/retakes.py +127 -0
- schnippo-0.11.0/src/schnippo/revise.py +479 -0
- schnippo-0.11.0/src/schnippo/script.py +79 -0
- schnippo-0.11.0/src/schnippo/segments.py +89 -0
- schnippo-0.11.0/src/schnippo/sense.py +146 -0
- schnippo-0.11.0/src/schnippo/server.py +1388 -0
- schnippo-0.11.0/src/schnippo/setup_check.py +184 -0
- schnippo-0.11.0/src/schnippo/sfx.py +380 -0
- schnippo-0.11.0/src/schnippo/sfx_profile.py +250 -0
- schnippo-0.11.0/src/schnippo/sfx_record.py +103 -0
- schnippo-0.11.0/src/schnippo/sfx_sources.py +108 -0
- schnippo-0.11.0/src/schnippo/snap.py +117 -0
- schnippo-0.11.0/src/schnippo/subtitles.py +132 -0
- schnippo-0.11.0/src/schnippo/toolinfo.py +18 -0
- schnippo-0.11.0/src/schnippo/transcribe/__init__.py +0 -0
- schnippo-0.11.0/src/schnippo/transcribe/base.py +41 -0
- schnippo-0.11.0/src/schnippo/transcribe/gladia.py +188 -0
- schnippo-0.11.0/src/schnippo/ui/card.html +289 -0
- schnippo-0.11.0/src/schnippo/ui/design.js +168 -0
- schnippo-0.11.0/src/schnippo/updates.py +71 -0
- schnippo-0.11.0/src/schnippo/workspace.py +1721 -0
schnippo-0.11.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: schnippo
|
|
3
|
+
Version: 0.11.0
|
|
4
|
+
Summary: KI-Rohschnitt für DaVinci Resolve: MCP-Server für Rohschnitte per Chat
|
|
5
|
+
Keywords: mcp,davinci resolve,video editing,rough cut,transcript
|
|
6
|
+
Author: Seitz Dominik Visuals
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: MacOS X
|
|
10
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
11
|
+
Classifier: Natural Language :: German
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Multimedia :: Video :: Non-Linear Editor
|
|
15
|
+
Requires-Dist: httpx>=0.28.1
|
|
16
|
+
Requires-Dist: mcp[cli]>=2.3.0
|
|
17
|
+
Requires-Dist: numpy>=2.5.4
|
|
18
|
+
Requires-Dist: opencv-python-headless>=4.10,<5
|
|
19
|
+
Requires-Dist: pillow>=13.0.0
|
|
20
|
+
Requires-Dist: portable-ffmpeg-full==0.5.0
|
|
21
|
+
Requires-Dist: pydantic>=2.14.2
|
|
22
|
+
Requires-Dist: python-docx>=1.2.0
|
|
23
|
+
Requires-Dist: rapidfuzz>=3.14.6
|
|
24
|
+
Requires-Dist: playwright==1.64.1 ; extra == 'motion'
|
|
25
|
+
Requires-Python: >=3.14, <3.16
|
|
26
|
+
Project-URL: Homepage, https://schnippo.com
|
|
27
|
+
Project-URL: Repository, https://github.com/Seitzdominik/schnippo
|
|
28
|
+
Project-URL: Issues, https://github.com/Seitzdominik/schnippo/issues
|
|
29
|
+
Provides-Extra: motion
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Schnippo
|
|
33
|
+
|
|
34
|
+
**KI-Rohschnitt für DaVinci Resolve.** Beta.
|
|
35
|
+
|
|
36
|
+
<!-- Bild oder GIF mit freigegebenem Material folgt: docs/bilder/schnippo.gif -->
|
|
37
|
+
|
|
38
|
+
## Was ist Schnippo?
|
|
39
|
+
|
|
40
|
+
Schnippo baut per Chat aus deinen Takes einen Rohschnitt in DaVinci Resolve Studio: Du sagst Claude, welchen Ordner es schneiden soll, und bekommst eine fertige Timeline aus deinen Originalclips. Schnippo misst Sprache, Pausen, Versprecher und Ton, Claude trifft die redaktionellen Entscheidungen, und du behältst in Resolve das letzte Wort. Auf Wunsch kommen B-Roll, Zooms, Grafiken, Sound-Effekte und Reels aus dem langen Video dazu, alles nicht-destruktiv.
|
|
41
|
+
|
|
42
|
+
## Voraussetzungen
|
|
43
|
+
|
|
44
|
+
- Mac mit Apple Silicon (Windows folgt)
|
|
45
|
+
- DaVinci Resolve Studio 21.1 oder neuer, unter Preferences → System → General „External scripting using“ auf **Local** (die kostenlose Version kann kein externes Scripting)
|
|
46
|
+
- Claude Desktop
|
|
47
|
+
- Eigener [Gladia-Key](https://www.gladia.io) für die Transkription
|
|
48
|
+
|
|
49
|
+
**Kosten:** Schnippo ist kostenlos. Gladia rechnet direkt mit dir ab, grob 0,61 USD je Stunde Audio. Vor jeder Transkription nennt Schnippo die Schätzung. Dazu kommt dein Claude-Abo.
|
|
50
|
+
|
|
51
|
+
## Installation in 4 Schritten
|
|
52
|
+
|
|
53
|
+
1. **Download:** Lade die neueste `schnippo-<version>.mcpb` von der [Releases-Seite](https://github.com/Seitzdominik/schnippo/releases).
|
|
54
|
+
2. **Doppelklick:** Die Datei öffnet sich in Claude Desktop. Bestätige die Installation.
|
|
55
|
+
3. **Key:** Trage deinen Gladia-Key in den Einstellungen der Erweiterung ein.
|
|
56
|
+
4. **Claude vollständig neu starten:** Beende Claude über die Menüleiste (Claude → Beenden). Das Fenster zu schließen reicht nicht.
|
|
57
|
+
|
|
58
|
+
Neue Versionen installierst du genauso. Schnippo weist dich darauf hin und aktualisiert sich nie selbst.
|
|
59
|
+
|
|
60
|
+
## Erster Schnitt
|
|
61
|
+
|
|
62
|
+
Starte einen neuen Chat und schreibe **„Richte mich ein“**. Das Onboarding prüft Resolve und den Key, fragt deine Vorlieben ab und legt auf Wunsch einen Projektordner an:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
2026-11-07_Mein_Film/
|
|
66
|
+
├── A-Roll/ Takes mit Sprache
|
|
67
|
+
├── B-Roll/ Bildmaterial ohne Sprache
|
|
68
|
+
├── Bilder/ Standbilder, Logos
|
|
69
|
+
├── Skript/ Skript, falls vorhanden
|
|
70
|
+
├── brief.md Worum es geht, Format, Länge, Zielgruppe
|
|
71
|
+
└── glossar.txt Namen in richtiger Schreibweise
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Spickzettel für den Chat:
|
|
75
|
+
|
|
76
|
+
| Du schreibst | Schnippo macht |
|
|
77
|
+
|---|---|
|
|
78
|
+
| „Schneide Mein_Film“ | Rough Cut als neue Timeline, danach Rückfragen zu Grenzfällen |
|
|
79
|
+
| „Weiter“ | Nächster Durchgang: Fine Cut, B-Roll, Polish |
|
|
80
|
+
| „Mach aus dem Video 3 Reels“ | Hochformat-Reels aus den stärksten Stellen |
|
|
81
|
+
| „Füge dezente Sound-Effekte hinzu“ | Eigene Spur mit Effekten |
|
|
82
|
+
| „Hilfe“ | Überblick über alles Weitere |
|
|
83
|
+
|
|
84
|
+
Jeder Durchgang bekommt eine eigene Timeline. Schnippo überschreibt nichts und verändert deine Originaldateien nie. Alle Tools stehen in der [Referenz](docs/REFERENZ.md).
|
|
85
|
+
|
|
86
|
+
## Aufnahme-Tipps
|
|
87
|
+
|
|
88
|
+
- **Für Reels in 4K filmen:** Dann bleibt beim Zuschnitt von 16:9 auf 9:16 genug Auflösung.
|
|
89
|
+
- **Versprecher:** Kurz stoppen, deutlich „nochmal“ sagen oder einmal klatschen und den ganzen Satz neu beginnen.
|
|
90
|
+
- **Bild:** Vor dem Sprechen 1 Sekunde ruhig in die Kamera schauen. Alle Takes mit derselben Framerate. B-Roll gern mit doppelter Rate, dann ist Zeitlupe möglich.
|
|
91
|
+
- **Ton:** Ruhiger Raum, 2–3 Sekunden Raumton am Anfang. Im Interview ein Mikro pro Person auf eigenem Kanal.
|
|
92
|
+
- **Namen:** Schreibweisen in die `glossar.txt`, dann stimmen sie schon im Transkript.
|
|
93
|
+
|
|
94
|
+
Mehr dazu in der [Referenz](docs/REFERENZ.md#so-nimmst-du-für-schnippo-auf), eigene Sound-Effekte aufnehmen: [docs/AUFNAHMEN.md](docs/AUFNAHMEN.md).
|
|
95
|
+
|
|
96
|
+
## Bekannte Grenzen
|
|
97
|
+
|
|
98
|
+
- **Nur Mac.** Windows folgt.
|
|
99
|
+
- **Karten in Claude Desktop:** Claude Desktop zeigt die interaktiven Karten lokaler Erweiterungen derzeit nicht an. Schnippo fragt deshalb als Text im Chat.
|
|
100
|
+
- **Gemischte Framerates in der A-Roll:** Die Timeline übernimmt die Rate des Hauptmaterials, Takes mit anderer Rate sind nicht erprobt. B-Roll darf abweichen.
|
|
101
|
+
- **Lange Videos und kleine Claude-Abos:** Ein Interview mit gut 10 Minuten Sprache braucht etwa 9.200 Token für den ganzen Text, die Übersicht etwa 620. Hochgerechnet sind das bei einer Stunde Sprache rund 50.000 Token je Durchgang, dazu kommen Kontaktabzüge. Schnippo liest lange Takes deshalb seitenweise. Bei kleinen Abos kann die Nutzungsgrenze trotzdem mitten im Schnitt erreicht werden. Die Token sind gemessen, wie schnell ein bestimmtes Abo an seine Grenze kommt, ist nicht gemessen.
|
|
102
|
+
- **Noch nicht mit echtem Material erprobt:** Dreh nach Skript, Rohmaterial über 30 Minuten, sehr leise Aufnahmen. Hochformat-Quellen sind nur mit einem einzelnen Clip erprobt.
|
|
103
|
+
- **Seltener Resolve-Absturz:** Beim Einfügen von Grafiken ist Resolve 21.1.2 in Tests mehrfach abgestürzt. Die Ursache ist nicht geklärt. Die Reihenfolge beim Bau wurde angepasst, seitdem trat der Fehler in Tests nicht mehr auf. Speichere dein Projekt vor dem Bau.
|
|
104
|
+
- **Andere KI-Modelle:** Die Schnittqualität hängt vom Modell ab. Erprobt ist Schnippo nur mit Claude, das Modell braucht Bildverständnis.
|
|
105
|
+
|
|
106
|
+
## Andere Programme
|
|
107
|
+
|
|
108
|
+
Für Claude Desktop ist die `.mcpb` der einfachste Weg. Andere MCP-fähige Programme starten Schnippo über [uv](https://docs.astral.sh/uv/) mit `uvx schnippo`. Du brauchst nur den Startbefehl und deinen `GLADIA_API_KEY`.
|
|
109
|
+
|
|
110
|
+
**Claude Code**
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
claude mcp add schnippo -e GLADIA_API_KEY=dein-key -- uvx schnippo
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Cursor** (`~/.cursor/mcp.json`)
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{"mcpServers": {"schnippo": {"command": "uvx", "args": ["schnippo"], "env": {"GLADIA_API_KEY": "dein-key"}}}}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**OpenAI Codex** (`~/.codex/config.toml`)
|
|
123
|
+
|
|
124
|
+
```toml
|
|
125
|
+
[mcp_servers.schnippo]
|
|
126
|
+
command = "uvx"
|
|
127
|
+
args = ["schnippo"]
|
|
128
|
+
env = { GLADIA_API_KEY = "dein-key" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**VS Code mit Copilot** (`.vscode/mcp.json`)
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"inputs": [{"type": "promptString", "id": "gladia", "description": "Gladia-Key", "password": true}],
|
|
136
|
+
"servers": {"schnippo": {"type": "stdio", "command": "uvx", "args": ["schnippo"], "env": {"GLADIA_API_KEY": "${input:gladia}"}}}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Den Key kannst du stattdessen im macOS-Schlüsselbund ablegen: Sag im Chat „Speichere meinen Gladia-Key“, dann öffnet sich ein Eingabefenster und der Key läuft nicht durch den Chat. Plugin für Claude: [plugin/](plugin/).
|
|
141
|
+
|
|
142
|
+
## FAQ
|
|
143
|
+
|
|
144
|
+
**Verändert Schnippo meine Dateien?** Nein. Timelines verweisen auf deine Originalclips, nichts wird neu gerendert oder gelöscht.
|
|
145
|
+
|
|
146
|
+
**Geht das mit der kostenlosen Resolve-Version?** Nein, sie unterstützt kein externes Scripting.
|
|
147
|
+
|
|
148
|
+
**Bleibt mein Key beim Update erhalten?** Ja, Claude Desktop speichert Key und Einstellungen pro Erweiterung, und die Kennung bleibt von Version zu Version gleich. Von der internen Vorversion „Resolve Cut“ musst du den Key einmal neu eintragen.
|
|
149
|
+
|
|
150
|
+
**Ich kannte Resolve Cut. Was passiert mit meinen Daten?** Schnippo übernimmt beim ersten Start Einstellungen, Glossare und Cache aus den alten Ordnern. Alte `resolve-cut-export/`-Ordner liest es weiter.
|
|
151
|
+
|
|
152
|
+
**Wie schalte ich den Update-Hinweis ab?** In den Einstellungen der Erweiterung, oder mit `SCHNIPPO_UPDATE_CHECK=0`.
|
|
153
|
+
|
|
154
|
+
## Datenschutz
|
|
155
|
+
|
|
156
|
+
Keine Telemetrie, kein Schnippo-Server. Schnippo läuft auf deinem Rechner.
|
|
157
|
+
|
|
158
|
+
| Daten | Wohin | Wann |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| Originalmedien, Cache, Einstellungen | bleiben lokal | immer |
|
|
161
|
+
| Tonspur (16 kHz, mono, ohne Metadaten) | Gladia, `api.gladia.io` | beim Transkribieren |
|
|
162
|
+
| Transkripte, Schnittkontext, Einzelbilder und Kontaktabzüge | das KI-Modell deines Chats, zum Beispiel Claude | während des Schnitts |
|
|
163
|
+
| Suchbegriff oder Beschreibung | Freesound, ElevenLabs | nur mit eigenem Key und auf Wunsch |
|
|
164
|
+
| Keine Daten, nur die Abfrage der neuesten Version | GitHub, `api.github.com` | höchstens einmal am Tag, abschaltbar |
|
|
165
|
+
|
|
166
|
+
Nutze Schnippo nur mit Material, das du diesen Diensten geben darfst.
|
|
167
|
+
|
|
168
|
+
### Privacy Policy
|
|
169
|
+
|
|
170
|
+
Vollständige Fassung: [docs/PRIVACY.md](docs/PRIVACY.md), online unter https://schnippo.com/privacy.
|
|
171
|
+
|
|
172
|
+
- **Datenerhebung:** Schnippo erhebt keine Daten und betreibt keinen eigenen Server.
|
|
173
|
+
- **Nutzung und Speicherung:** Transkripte, Analysen und Einstellungen liegen lokal in `~/Library/Caches/schnippo` und `~/.config/schnippo`.
|
|
174
|
+
- **Weitergabe an Dritte:** nur an die Dienste aus der Tabelle, mit deinem eigenen Konto.
|
|
175
|
+
- **Aufbewahrung:** Lokale Daten löschst du jederzeit mit `clear_cache` oder im Finder. Bei Gladia und deinem KI-Anbieter gelten deren Bedingungen.
|
|
176
|
+
- **Kontakt:** Seitz Dominik Visuals, https://sdv.design
|
|
177
|
+
|
|
178
|
+
## Lizenzen
|
|
179
|
+
|
|
180
|
+
Schnippo steht unter der [MIT-Lizenz](LICENSE), © Seitz Dominik Visuals. Fremde Bestandteile: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
181
|
+
|
|
182
|
+
DaVinci Resolve ist eine Marke von Blackmagic Design. Schnippo ist ein unabhängiges Projekt und nicht mit Blackmagic Design verbunden.
|
|
183
|
+
|
|
184
|
+
## Mitmachen
|
|
185
|
+
|
|
186
|
+
Fehler und Wünsche gern als [Issue](https://github.com/Seitzdominik/schnippo/issues/new/choose). Bitte hänge nie echtes Material oder Keys an.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
uv sync
|
|
190
|
+
uv run pytest
|
|
191
|
+
git config core.hooksPath .githooks
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Der Hook startet `tools/check_public.py`. Technische Erkenntnisse stehen in [docs/ENTWICKLUNG.md](docs/ENTWICKLUNG.md).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## English summary
|
|
199
|
+
|
|
200
|
+
**Schnippo** is an AI rough-cut assistant for DaVinci Resolve Studio. Tell Claude which folder to cut and get a non-destructive timeline built from your original clips, with optional B-roll, punch-ins, graphics, sound effects and reels. The interface and guides are in German; English footage works too.
|
|
201
|
+
|
|
202
|
+
**Requirements:** Apple Silicon Mac, DaVinci Resolve Studio 21.1+ with external scripting set to Local, Claude Desktop, your own Gladia key (roughly USD 0.61 per hour of audio).
|
|
203
|
+
|
|
204
|
+
**Install:** download the `.mcpb` from [Releases](https://github.com/Seitzdominik/schnippo/releases), double-click it, enter your key, then fully quit and reopen Claude from the menu bar. Say „Richte mich ein“ in a new chat. Other MCP clients can run `uvx schnippo` with `GLADIA_API_KEY` set.
|
|
205
|
+
|
|
206
|
+
**Privacy:** no telemetry and no Schnippo server. Audio goes to Gladia, transcripts and frames go to your AI model, and the update check asks GitHub once a day (optional). See [docs/PRIVACY.md](docs/PRIVACY.md).
|
|
207
|
+
|
|
208
|
+
**Licence:** MIT, © Seitz Dominik Visuals. DaVinci Resolve is a trademark of Blackmagic Design. This project is independent of Blackmagic Design.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Schnippo
|
|
2
|
+
|
|
3
|
+
**KI-Rohschnitt für DaVinci Resolve.** Beta.
|
|
4
|
+
|
|
5
|
+
<!-- Bild oder GIF mit freigegebenem Material folgt: docs/bilder/schnippo.gif -->
|
|
6
|
+
|
|
7
|
+
## Was ist Schnippo?
|
|
8
|
+
|
|
9
|
+
Schnippo baut per Chat aus deinen Takes einen Rohschnitt in DaVinci Resolve Studio: Du sagst Claude, welchen Ordner es schneiden soll, und bekommst eine fertige Timeline aus deinen Originalclips. Schnippo misst Sprache, Pausen, Versprecher und Ton, Claude trifft die redaktionellen Entscheidungen, und du behältst in Resolve das letzte Wort. Auf Wunsch kommen B-Roll, Zooms, Grafiken, Sound-Effekte und Reels aus dem langen Video dazu, alles nicht-destruktiv.
|
|
10
|
+
|
|
11
|
+
## Voraussetzungen
|
|
12
|
+
|
|
13
|
+
- Mac mit Apple Silicon (Windows folgt)
|
|
14
|
+
- DaVinci Resolve Studio 21.1 oder neuer, unter Preferences → System → General „External scripting using“ auf **Local** (die kostenlose Version kann kein externes Scripting)
|
|
15
|
+
- Claude Desktop
|
|
16
|
+
- Eigener [Gladia-Key](https://www.gladia.io) für die Transkription
|
|
17
|
+
|
|
18
|
+
**Kosten:** Schnippo ist kostenlos. Gladia rechnet direkt mit dir ab, grob 0,61 USD je Stunde Audio. Vor jeder Transkription nennt Schnippo die Schätzung. Dazu kommt dein Claude-Abo.
|
|
19
|
+
|
|
20
|
+
## Installation in 4 Schritten
|
|
21
|
+
|
|
22
|
+
1. **Download:** Lade die neueste `schnippo-<version>.mcpb` von der [Releases-Seite](https://github.com/Seitzdominik/schnippo/releases).
|
|
23
|
+
2. **Doppelklick:** Die Datei öffnet sich in Claude Desktop. Bestätige die Installation.
|
|
24
|
+
3. **Key:** Trage deinen Gladia-Key in den Einstellungen der Erweiterung ein.
|
|
25
|
+
4. **Claude vollständig neu starten:** Beende Claude über die Menüleiste (Claude → Beenden). Das Fenster zu schließen reicht nicht.
|
|
26
|
+
|
|
27
|
+
Neue Versionen installierst du genauso. Schnippo weist dich darauf hin und aktualisiert sich nie selbst.
|
|
28
|
+
|
|
29
|
+
## Erster Schnitt
|
|
30
|
+
|
|
31
|
+
Starte einen neuen Chat und schreibe **„Richte mich ein“**. Das Onboarding prüft Resolve und den Key, fragt deine Vorlieben ab und legt auf Wunsch einen Projektordner an:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
2026-11-07_Mein_Film/
|
|
35
|
+
├── A-Roll/ Takes mit Sprache
|
|
36
|
+
├── B-Roll/ Bildmaterial ohne Sprache
|
|
37
|
+
├── Bilder/ Standbilder, Logos
|
|
38
|
+
├── Skript/ Skript, falls vorhanden
|
|
39
|
+
├── brief.md Worum es geht, Format, Länge, Zielgruppe
|
|
40
|
+
└── glossar.txt Namen in richtiger Schreibweise
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Spickzettel für den Chat:
|
|
44
|
+
|
|
45
|
+
| Du schreibst | Schnippo macht |
|
|
46
|
+
|---|---|
|
|
47
|
+
| „Schneide Mein_Film“ | Rough Cut als neue Timeline, danach Rückfragen zu Grenzfällen |
|
|
48
|
+
| „Weiter“ | Nächster Durchgang: Fine Cut, B-Roll, Polish |
|
|
49
|
+
| „Mach aus dem Video 3 Reels“ | Hochformat-Reels aus den stärksten Stellen |
|
|
50
|
+
| „Füge dezente Sound-Effekte hinzu“ | Eigene Spur mit Effekten |
|
|
51
|
+
| „Hilfe“ | Überblick über alles Weitere |
|
|
52
|
+
|
|
53
|
+
Jeder Durchgang bekommt eine eigene Timeline. Schnippo überschreibt nichts und verändert deine Originaldateien nie. Alle Tools stehen in der [Referenz](docs/REFERENZ.md).
|
|
54
|
+
|
|
55
|
+
## Aufnahme-Tipps
|
|
56
|
+
|
|
57
|
+
- **Für Reels in 4K filmen:** Dann bleibt beim Zuschnitt von 16:9 auf 9:16 genug Auflösung.
|
|
58
|
+
- **Versprecher:** Kurz stoppen, deutlich „nochmal“ sagen oder einmal klatschen und den ganzen Satz neu beginnen.
|
|
59
|
+
- **Bild:** Vor dem Sprechen 1 Sekunde ruhig in die Kamera schauen. Alle Takes mit derselben Framerate. B-Roll gern mit doppelter Rate, dann ist Zeitlupe möglich.
|
|
60
|
+
- **Ton:** Ruhiger Raum, 2–3 Sekunden Raumton am Anfang. Im Interview ein Mikro pro Person auf eigenem Kanal.
|
|
61
|
+
- **Namen:** Schreibweisen in die `glossar.txt`, dann stimmen sie schon im Transkript.
|
|
62
|
+
|
|
63
|
+
Mehr dazu in der [Referenz](docs/REFERENZ.md#so-nimmst-du-für-schnippo-auf), eigene Sound-Effekte aufnehmen: [docs/AUFNAHMEN.md](docs/AUFNAHMEN.md).
|
|
64
|
+
|
|
65
|
+
## Bekannte Grenzen
|
|
66
|
+
|
|
67
|
+
- **Nur Mac.** Windows folgt.
|
|
68
|
+
- **Karten in Claude Desktop:** Claude Desktop zeigt die interaktiven Karten lokaler Erweiterungen derzeit nicht an. Schnippo fragt deshalb als Text im Chat.
|
|
69
|
+
- **Gemischte Framerates in der A-Roll:** Die Timeline übernimmt die Rate des Hauptmaterials, Takes mit anderer Rate sind nicht erprobt. B-Roll darf abweichen.
|
|
70
|
+
- **Lange Videos und kleine Claude-Abos:** Ein Interview mit gut 10 Minuten Sprache braucht etwa 9.200 Token für den ganzen Text, die Übersicht etwa 620. Hochgerechnet sind das bei einer Stunde Sprache rund 50.000 Token je Durchgang, dazu kommen Kontaktabzüge. Schnippo liest lange Takes deshalb seitenweise. Bei kleinen Abos kann die Nutzungsgrenze trotzdem mitten im Schnitt erreicht werden. Die Token sind gemessen, wie schnell ein bestimmtes Abo an seine Grenze kommt, ist nicht gemessen.
|
|
71
|
+
- **Noch nicht mit echtem Material erprobt:** Dreh nach Skript, Rohmaterial über 30 Minuten, sehr leise Aufnahmen. Hochformat-Quellen sind nur mit einem einzelnen Clip erprobt.
|
|
72
|
+
- **Seltener Resolve-Absturz:** Beim Einfügen von Grafiken ist Resolve 21.1.2 in Tests mehrfach abgestürzt. Die Ursache ist nicht geklärt. Die Reihenfolge beim Bau wurde angepasst, seitdem trat der Fehler in Tests nicht mehr auf. Speichere dein Projekt vor dem Bau.
|
|
73
|
+
- **Andere KI-Modelle:** Die Schnittqualität hängt vom Modell ab. Erprobt ist Schnippo nur mit Claude, das Modell braucht Bildverständnis.
|
|
74
|
+
|
|
75
|
+
## Andere Programme
|
|
76
|
+
|
|
77
|
+
Für Claude Desktop ist die `.mcpb` der einfachste Weg. Andere MCP-fähige Programme starten Schnippo über [uv](https://docs.astral.sh/uv/) mit `uvx schnippo`. Du brauchst nur den Startbefehl und deinen `GLADIA_API_KEY`.
|
|
78
|
+
|
|
79
|
+
**Claude Code**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
claude mcp add schnippo -e GLADIA_API_KEY=dein-key -- uvx schnippo
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Cursor** (`~/.cursor/mcp.json`)
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{"mcpServers": {"schnippo": {"command": "uvx", "args": ["schnippo"], "env": {"GLADIA_API_KEY": "dein-key"}}}}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**OpenAI Codex** (`~/.codex/config.toml`)
|
|
92
|
+
|
|
93
|
+
```toml
|
|
94
|
+
[mcp_servers.schnippo]
|
|
95
|
+
command = "uvx"
|
|
96
|
+
args = ["schnippo"]
|
|
97
|
+
env = { GLADIA_API_KEY = "dein-key" }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**VS Code mit Copilot** (`.vscode/mcp.json`)
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"inputs": [{"type": "promptString", "id": "gladia", "description": "Gladia-Key", "password": true}],
|
|
105
|
+
"servers": {"schnippo": {"type": "stdio", "command": "uvx", "args": ["schnippo"], "env": {"GLADIA_API_KEY": "${input:gladia}"}}}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Den Key kannst du stattdessen im macOS-Schlüsselbund ablegen: Sag im Chat „Speichere meinen Gladia-Key“, dann öffnet sich ein Eingabefenster und der Key läuft nicht durch den Chat. Plugin für Claude: [plugin/](plugin/).
|
|
110
|
+
|
|
111
|
+
## FAQ
|
|
112
|
+
|
|
113
|
+
**Verändert Schnippo meine Dateien?** Nein. Timelines verweisen auf deine Originalclips, nichts wird neu gerendert oder gelöscht.
|
|
114
|
+
|
|
115
|
+
**Geht das mit der kostenlosen Resolve-Version?** Nein, sie unterstützt kein externes Scripting.
|
|
116
|
+
|
|
117
|
+
**Bleibt mein Key beim Update erhalten?** Ja, Claude Desktop speichert Key und Einstellungen pro Erweiterung, und die Kennung bleibt von Version zu Version gleich. Von der internen Vorversion „Resolve Cut“ musst du den Key einmal neu eintragen.
|
|
118
|
+
|
|
119
|
+
**Ich kannte Resolve Cut. Was passiert mit meinen Daten?** Schnippo übernimmt beim ersten Start Einstellungen, Glossare und Cache aus den alten Ordnern. Alte `resolve-cut-export/`-Ordner liest es weiter.
|
|
120
|
+
|
|
121
|
+
**Wie schalte ich den Update-Hinweis ab?** In den Einstellungen der Erweiterung, oder mit `SCHNIPPO_UPDATE_CHECK=0`.
|
|
122
|
+
|
|
123
|
+
## Datenschutz
|
|
124
|
+
|
|
125
|
+
Keine Telemetrie, kein Schnippo-Server. Schnippo läuft auf deinem Rechner.
|
|
126
|
+
|
|
127
|
+
| Daten | Wohin | Wann |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| Originalmedien, Cache, Einstellungen | bleiben lokal | immer |
|
|
130
|
+
| Tonspur (16 kHz, mono, ohne Metadaten) | Gladia, `api.gladia.io` | beim Transkribieren |
|
|
131
|
+
| Transkripte, Schnittkontext, Einzelbilder und Kontaktabzüge | das KI-Modell deines Chats, zum Beispiel Claude | während des Schnitts |
|
|
132
|
+
| Suchbegriff oder Beschreibung | Freesound, ElevenLabs | nur mit eigenem Key und auf Wunsch |
|
|
133
|
+
| Keine Daten, nur die Abfrage der neuesten Version | GitHub, `api.github.com` | höchstens einmal am Tag, abschaltbar |
|
|
134
|
+
|
|
135
|
+
Nutze Schnippo nur mit Material, das du diesen Diensten geben darfst.
|
|
136
|
+
|
|
137
|
+
### Privacy Policy
|
|
138
|
+
|
|
139
|
+
Vollständige Fassung: [docs/PRIVACY.md](docs/PRIVACY.md), online unter https://schnippo.com/privacy.
|
|
140
|
+
|
|
141
|
+
- **Datenerhebung:** Schnippo erhebt keine Daten und betreibt keinen eigenen Server.
|
|
142
|
+
- **Nutzung und Speicherung:** Transkripte, Analysen und Einstellungen liegen lokal in `~/Library/Caches/schnippo` und `~/.config/schnippo`.
|
|
143
|
+
- **Weitergabe an Dritte:** nur an die Dienste aus der Tabelle, mit deinem eigenen Konto.
|
|
144
|
+
- **Aufbewahrung:** Lokale Daten löschst du jederzeit mit `clear_cache` oder im Finder. Bei Gladia und deinem KI-Anbieter gelten deren Bedingungen.
|
|
145
|
+
- **Kontakt:** Seitz Dominik Visuals, https://sdv.design
|
|
146
|
+
|
|
147
|
+
## Lizenzen
|
|
148
|
+
|
|
149
|
+
Schnippo steht unter der [MIT-Lizenz](LICENSE), © Seitz Dominik Visuals. Fremde Bestandteile: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
150
|
+
|
|
151
|
+
DaVinci Resolve ist eine Marke von Blackmagic Design. Schnippo ist ein unabhängiges Projekt und nicht mit Blackmagic Design verbunden.
|
|
152
|
+
|
|
153
|
+
## Mitmachen
|
|
154
|
+
|
|
155
|
+
Fehler und Wünsche gern als [Issue](https://github.com/Seitzdominik/schnippo/issues/new/choose). Bitte hänge nie echtes Material oder Keys an.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync
|
|
159
|
+
uv run pytest
|
|
160
|
+
git config core.hooksPath .githooks
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Der Hook startet `tools/check_public.py`. Technische Erkenntnisse stehen in [docs/ENTWICKLUNG.md](docs/ENTWICKLUNG.md).
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## English summary
|
|
168
|
+
|
|
169
|
+
**Schnippo** is an AI rough-cut assistant for DaVinci Resolve Studio. Tell Claude which folder to cut and get a non-destructive timeline built from your original clips, with optional B-roll, punch-ins, graphics, sound effects and reels. The interface and guides are in German; English footage works too.
|
|
170
|
+
|
|
171
|
+
**Requirements:** Apple Silicon Mac, DaVinci Resolve Studio 21.1+ with external scripting set to Local, Claude Desktop, your own Gladia key (roughly USD 0.61 per hour of audio).
|
|
172
|
+
|
|
173
|
+
**Install:** download the `.mcpb` from [Releases](https://github.com/Seitzdominik/schnippo/releases), double-click it, enter your key, then fully quit and reopen Claude from the menu bar. Say „Richte mich ein“ in a new chat. Other MCP clients can run `uvx schnippo` with `GLADIA_API_KEY` set.
|
|
174
|
+
|
|
175
|
+
**Privacy:** no telemetry and no Schnippo server. Audio goes to Gladia, transcripts and frames go to your AI model, and the update check asks GitHub once a day (optional). See [docs/PRIVACY.md](docs/PRIVACY.md).
|
|
176
|
+
|
|
177
|
+
**Licence:** MIT, © Seitz Dominik Visuals. DaVinci Resolve is a trademark of Blackmagic Design. This project is independent of Blackmagic Design.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "schnippo"
|
|
3
|
+
version = "0.11.0"
|
|
4
|
+
description = "KI-Rohschnitt für DaVinci Resolve: MCP-Server für Rohschnitte per Chat"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
keywords = [
|
|
8
|
+
"mcp",
|
|
9
|
+
"davinci resolve",
|
|
10
|
+
"video editing",
|
|
11
|
+
"rough cut",
|
|
12
|
+
"transcript",
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: MacOS X",
|
|
17
|
+
"Intended Audience :: End Users/Desktop",
|
|
18
|
+
"Natural Language :: German",
|
|
19
|
+
"Operating System :: MacOS",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Topic :: Multimedia :: Video :: Non-Linear Editor",
|
|
22
|
+
]
|
|
23
|
+
requires-python = ">=3.14,<3.16"
|
|
24
|
+
dependencies = [
|
|
25
|
+
"httpx>=0.28.1",
|
|
26
|
+
"mcp[cli]>=2.3.0",
|
|
27
|
+
"numpy>=2.5.4",
|
|
28
|
+
"opencv-python-headless>=4.10,<5",
|
|
29
|
+
"pillow>=13.0.0",
|
|
30
|
+
"portable-ffmpeg-full==0.5.0",
|
|
31
|
+
"pydantic>=2.14.2",
|
|
32
|
+
"python-docx>=1.2.0",
|
|
33
|
+
"rapidfuzz>=3.14.6",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[[project.authors]]
|
|
37
|
+
name = "Seitz Dominik Visuals"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://schnippo.com"
|
|
41
|
+
Repository = "https://github.com/Seitzdominik/schnippo"
|
|
42
|
+
Issues = "https://github.com/Seitzdominik/schnippo/issues"
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
motion = ["playwright==1.64.1"]
|
|
46
|
+
|
|
47
|
+
[project.scripts]
|
|
48
|
+
schnippo = "schnippo.server:main"
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["uv_build>=0.13.7,<0.14.0"]
|
|
52
|
+
build-backend = "uv_build"
|
|
53
|
+
|
|
54
|
+
[dependency-groups]
|
|
55
|
+
dev = [
|
|
56
|
+
"playwright==1.64.1",
|
|
57
|
+
"pytest>=9.2.0",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "schnippo"
|
|
3
|
+
version = "0.11.0"
|
|
4
|
+
description = "KI-Rohschnitt für DaVinci Resolve: MCP-Server für Rohschnitte per Chat"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Seitz Dominik Visuals" }
|
|
9
|
+
]
|
|
10
|
+
keywords = ["mcp", "davinci resolve", "video editing", "rough cut", "transcript"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Environment :: MacOS X",
|
|
14
|
+
"Intended Audience :: End Users/Desktop",
|
|
15
|
+
"Natural Language :: German",
|
|
16
|
+
"Operating System :: MacOS",
|
|
17
|
+
"Programming Language :: Python :: 3.14",
|
|
18
|
+
"Topic :: Multimedia :: Video :: Non-Linear Editor",
|
|
19
|
+
]
|
|
20
|
+
requires-python = ">=3.14,<3.16" # tested with Resolve 21.1.2 fusionscript: 3.14 (default via .python-version) and 3.15
|
|
21
|
+
dependencies = [
|
|
22
|
+
"httpx>=0.28.1",
|
|
23
|
+
"mcp[cli]>=2.3.0",
|
|
24
|
+
"numpy>=2.5.4",
|
|
25
|
+
"opencv-python-headless>=4.10,<5",
|
|
26
|
+
"pillow>=13.0.0",
|
|
27
|
+
"portable-ffmpeg-full==0.5.0",
|
|
28
|
+
"pydantic>=2.14.2",
|
|
29
|
+
"python-docx>=1.2.0",
|
|
30
|
+
"rapidfuzz>=3.14.6",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://schnippo.com"
|
|
35
|
+
Repository = "https://github.com/Seitzdominik/schnippo"
|
|
36
|
+
Issues = "https://github.com/Seitzdominik/schnippo/issues"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
# installed on first use of motion graphics (motion_setup), not with the extension
|
|
40
|
+
motion = ["playwright==1.64.1"]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
schnippo = "schnippo.server:main"
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["uv_build>=0.13.7,<0.14.0"]
|
|
47
|
+
build-backend = "uv_build"
|
|
48
|
+
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
dev = [
|
|
51
|
+
"playwright==1.64.1",
|
|
52
|
+
"pytest>=9.2.0",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Acoustic analysis of the 16 kHz mono WAV (numpy, no Torch).
|
|
2
|
+
|
|
3
|
+
* RMS envelope (25 ms window, 10 ms hop) in dBFS.
|
|
4
|
+
* Noise floor = low percentile of the envelope; activity threshold = floor + delta.
|
|
5
|
+
This replaces the Phase-1 rule max(-35 dB, mean-18 dB), which on a quiet recording with a very low
|
|
6
|
+
room noise put the threshold *above* half of the speech frames.
|
|
7
|
+
* Voicing per frame = peak of the normalised autocorrelation in the 75–400 Hz lag range (40 ms window).
|
|
8
|
+
Voiced sounds ("äh", "hm", vowels) have a clear periodicity, breaths/clicks do not.
|
|
9
|
+
|
|
10
|
+
Regions of activity are classified as ``voiced`` (filler candidate), ``breath`` (unvoiced, breath-like
|
|
11
|
+
duration) or ``click`` (very short transient).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import wave
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
SR = 16000
|
|
23
|
+
HOP = 160 # 10 ms
|
|
24
|
+
WIN = 400 # 25 ms
|
|
25
|
+
VWIN = 640 # 40 ms voicing window
|
|
26
|
+
LAG_MIN, LAG_MAX = SR // 400, SR // 75 # 40 .. 213 samples
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class AcousticParams:
|
|
31
|
+
floor_percentile: float = 10.0
|
|
32
|
+
delta_db: float = 25.0 # activity threshold above the noise floor
|
|
33
|
+
min_threshold_db: float = -60.0 # never below this (digital silence / heavy denoise)
|
|
34
|
+
max_threshold_db: float = -30.0
|
|
35
|
+
min_region_s: float = 0.06
|
|
36
|
+
merge_gap_s: float = 0.08
|
|
37
|
+
voiced_corr: float = 0.6 # frame is voiced if autocorrelation peak >= this
|
|
38
|
+
voiced_min_s: float = 0.15 # region needs at least this much voiced material (calibrated on real take)
|
|
39
|
+
voiced_ratio: float = 0.6 # ... and this share of its active frames voiced
|
|
40
|
+
click_max_s: float = 0.08
|
|
41
|
+
|
|
42
|
+
def key(self) -> list:
|
|
43
|
+
return [self.floor_percentile, self.delta_db, self.min_threshold_db, self.max_threshold_db, self.min_region_s,
|
|
44
|
+
self.merge_gap_s, self.voiced_corr, self.voiced_min_s, self.voiced_ratio, self.click_max_s, 2]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class Acoustic:
|
|
49
|
+
env_db: np.ndarray # per 10 ms frame
|
|
50
|
+
voicing: np.ndarray # per 10 ms frame, 0..1
|
|
51
|
+
floor_db: float
|
|
52
|
+
threshold_db: float
|
|
53
|
+
duration_s: float
|
|
54
|
+
params: AcousticParams = field(default_factory=AcousticParams)
|
|
55
|
+
|
|
56
|
+
# ---- frame helpers ----
|
|
57
|
+
def _sl(self, t0: float, t1: float) -> slice:
|
|
58
|
+
return slice(max(0, int(round(t0 * 100))), max(0, int(round(t1 * 100))))
|
|
59
|
+
|
|
60
|
+
def active(self, t0: float, t1: float) -> np.ndarray:
|
|
61
|
+
return self.env_db[self._sl(t0, t1)] > self.threshold_db
|
|
62
|
+
|
|
63
|
+
def regions(self, t0: float, t1: float) -> list[tuple[float, float]]:
|
|
64
|
+
"""Active (non-silent) sub-intervals of [t0, t1]."""
|
|
65
|
+
act = self.active(t0, t1)
|
|
66
|
+
base = int(round(t0 * 100))
|
|
67
|
+
out: list[list[float]] = []
|
|
68
|
+
i = 0
|
|
69
|
+
while i < len(act):
|
|
70
|
+
if act[i]:
|
|
71
|
+
j = i
|
|
72
|
+
while j < len(act) and act[j]:
|
|
73
|
+
j += 1
|
|
74
|
+
a, b = (base + i) / 100, (base + j) / 100
|
|
75
|
+
if out and a - out[-1][1] <= self.params.merge_gap_s:
|
|
76
|
+
out[-1][1] = b
|
|
77
|
+
else:
|
|
78
|
+
out.append([a, b])
|
|
79
|
+
i = j
|
|
80
|
+
else:
|
|
81
|
+
i += 1
|
|
82
|
+
return [(max(a, t0), min(b, t1)) for a, b in out if b - a >= self.params.min_region_s]
|
|
83
|
+
|
|
84
|
+
def classify(self, t0: float, t1: float) -> dict:
|
|
85
|
+
sl = self._sl(t0, t1)
|
|
86
|
+
env, voi = self.env_db[sl], self.voicing[sl]
|
|
87
|
+
act = env > self.threshold_db
|
|
88
|
+
voiced = (voi >= self.params.voiced_corr) & act
|
|
89
|
+
vs = float(voiced.sum()) / 100
|
|
90
|
+
ratio = float(voiced.sum()) / max(1, int(act.sum()))
|
|
91
|
+
dur = t1 - t0
|
|
92
|
+
if vs >= self.params.voiced_min_s and ratio >= self.params.voiced_ratio:
|
|
93
|
+
cls = "voiced"
|
|
94
|
+
elif dur <= self.params.click_max_s:
|
|
95
|
+
cls = "click"
|
|
96
|
+
else:
|
|
97
|
+
cls = "breath"
|
|
98
|
+
return {"class": cls, "peak_db": round(float(env.max()) if len(env) else -120.0, 1),
|
|
99
|
+
"voiced_s": round(vs, 2), "voiced_ratio": round(ratio, 2)}
|
|
100
|
+
|
|
101
|
+
def silent_runs(self, t0: float, t1: float, min_s: float) -> list[tuple[float, float]]:
|
|
102
|
+
act = self.active(t0, t1)
|
|
103
|
+
base = int(round(t0 * 100))
|
|
104
|
+
out, i = [], 0
|
|
105
|
+
while i < len(act):
|
|
106
|
+
if not act[i]:
|
|
107
|
+
j = i
|
|
108
|
+
while j < len(act) and not act[j]:
|
|
109
|
+
j += 1
|
|
110
|
+
if (j - i) / 100 >= min_s:
|
|
111
|
+
out.append(((base + i) / 100, (base + j) / 100))
|
|
112
|
+
i = j
|
|
113
|
+
else:
|
|
114
|
+
i += 1
|
|
115
|
+
return out
|
|
116
|
+
|
|
117
|
+
# ---- persistence ----
|
|
118
|
+
def save(self, path: Path) -> None:
|
|
119
|
+
np.savez_compressed(path, env=self.env_db.astype(np.float32), voi=self.voicing.astype(np.float16),
|
|
120
|
+
meta=np.array([self.floor_db, self.threshold_db, self.duration_s]),
|
|
121
|
+
key=np.array(self.params.key(), dtype=float))
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def load(cls, path: Path, params: AcousticParams) -> "Acoustic | None":
|
|
125
|
+
if not path.exists():
|
|
126
|
+
return None
|
|
127
|
+
d = np.load(path)
|
|
128
|
+
if list(d["key"]) != [float(x) for x in params.key()]:
|
|
129
|
+
return None
|
|
130
|
+
floor, thr, dur = (float(x) for x in d["meta"])
|
|
131
|
+
return cls(d["env"].astype(np.float64), d["voi"].astype(np.float64), floor, thr, dur, params)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def read_wav(path: Path) -> np.ndarray:
|
|
135
|
+
with wave.open(str(path)) as w:
|
|
136
|
+
if w.getframerate() != SR or w.getnchannels() != 1 or w.getsampwidth() != 2:
|
|
137
|
+
raise ValueError("expected 16 kHz mono 16-bit WAV")
|
|
138
|
+
return np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(np.float64) / 32768.0
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _frames(xp: np.ndarray, win: int, i0: int, i1: int) -> np.ndarray:
|
|
142
|
+
"""Frames i0..i1 (hop 10 ms) of the padded signal xp (padded by win//2 in front)."""
|
|
143
|
+
idx = np.arange(win)[None, :] + (np.arange(i0, i1) * HOP)[:, None]
|
|
144
|
+
return xp[idx]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def analyze_wav(path: Path, params: AcousticParams | None = None) -> Acoustic:
|
|
148
|
+
params = params or AcousticParams()
|
|
149
|
+
x = read_wav(path)
|
|
150
|
+
n = 1 + max(0, (len(x) - 1) // HOP)
|
|
151
|
+
xp_e = np.pad(x, (WIN // 2, WIN))
|
|
152
|
+
xp_v = np.pad(x, (VWIN // 2, VWIN))
|
|
153
|
+
env = np.empty(n)
|
|
154
|
+
voi = np.empty(n)
|
|
155
|
+
nfft, hann = 1024, np.hanning(VWIN)[None, :]
|
|
156
|
+
for i0 in range(0, n, 4096): # chunked: bounded memory for hour-long material
|
|
157
|
+
i1 = min(n, i0 + 4096)
|
|
158
|
+
fe = _frames(xp_e, WIN, i0, i1)
|
|
159
|
+
env[i0:i1] = 10 * np.log10(np.mean(fe * fe, axis=1) + 1e-12)
|
|
160
|
+
fv = _frames(xp_v, VWIN, i0, i1)
|
|
161
|
+
fv = (fv - fv.mean(axis=1, keepdims=True)) * hann
|
|
162
|
+
ac = np.fft.irfft(np.abs(np.fft.rfft(fv, nfft)) ** 2, nfft)[:, :LAG_MAX + 1]
|
|
163
|
+
voi[i0:i1] = np.clip(ac[:, LAG_MIN:LAG_MAX + 1].max(axis=1) / (ac[:, 0] + 1e-12), 0, 1)
|
|
164
|
+
floor = float(np.percentile(env, params.floor_percentile))
|
|
165
|
+
thr = float(np.clip(floor + params.delta_db, params.min_threshold_db, params.max_threshold_db))
|
|
166
|
+
return Acoustic(env, voi, floor, thr, len(x) / SR, params)
|