@mevdragon/vidfarm-devcli 0.19.1 → 0.20.0
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.
- package/.agents/skills/music/SKILL.md +416 -0
- package/.agents/skills/music/references/api_reference.md +519 -0
- package/.agents/skills/music/references/installation.md +65 -0
- package/.agents/skills/text-to-speech/SKILL.md +226 -0
- package/.agents/skills/text-to-speech/references/installation.md +90 -0
- package/.agents/skills/text-to-speech/references/streaming.md +307 -0
- package/.agents/skills/text-to-speech/references/voice-settings.md +115 -0
- package/.agents/skills/vidfarm-media/SKILL.md +25 -11
- package/.agents/skills/vidfarm-media/references/tts.md +41 -3
- package/SKILL.director.md +31 -13
- package/SKILL.platform.md +2 -2
- package/dist/src/app.js +129 -41
- package/dist/src/cli.js +162 -5
- package/dist/src/config.js +12 -0
- package/dist/src/devcli/clips.js +7 -2
- package/dist/src/domain.js +3 -0
- package/dist/src/editor-chat.js +4 -0
- package/dist/src/primitive-context.js +14 -0
- package/dist/src/primitive-registry.js +140 -18
- package/dist/src/reskin/chat-page.js +1 -1
- package/dist/src/reskin/inpaint-clipper-page.js +446 -205
- package/dist/src/reskin/library-page.js +7 -1
- package/dist/src/reskin/portfolio-page.js +9 -9
- package/dist/src/reskin/settings-page.js +4 -4
- package/dist/src/reskin/theme.js +1 -0
- package/dist/src/services/billing.js +5 -0
- package/dist/src/services/clip-curation/ffmpeg.js +48 -0
- package/dist/src/services/clip-curation/hunt.js +2 -0
- package/dist/src/services/clip-curation/index.js +1 -1
- package/dist/src/services/clip-curation/scan.js +29 -16
- package/dist/src/services/elevenlabs.js +222 -0
- package/dist/src/services/providers.js +216 -2
- package/dist/src/services/swipe-customize.js +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Voice Settings
|
|
2
|
+
|
|
3
|
+
Fine-tune voice characteristics for your use case.
|
|
4
|
+
|
|
5
|
+
## Parameters
|
|
6
|
+
|
|
7
|
+
| Parameter | Range | Default | Description |
|
|
8
|
+
|-----------|-------|---------|-------------|
|
|
9
|
+
| `stability` | 0.0 - 1.0 | 0.5 | How consistent the voice sounds across the generation. Lower = more emotional variation and expressiveness (but can sound erratic). Higher = steady, predictable tone. |
|
|
10
|
+
| `similarity_boost` | 0.0 - 1.0 | 0.75 | How closely to match the original voice sample. Higher sounds more like the source voice but may amplify audio artifacts or background noise from the original recording. |
|
|
11
|
+
| `style` | 0.0 - 1.0 | 0.0 | Exaggerates the unique characteristics of the voice's speaking style (v2+ and v3 models only). Higher values make the voice more "characterful" but can reduce stability. |
|
|
12
|
+
| `speed` | 0.25 - 4.0 | 1.0 | Speech speed multiplier. 1.0 = normal speed. Range is 0.25-4.0 for the REST API; the Agents Platform restricts to 0.7-1.2. |
|
|
13
|
+
| `use_speaker_boost` | boolean | true | Post-processing that enhances voice clarity and similarity to the original. Generally leave this on unless you're experiencing artifacts. |
|
|
14
|
+
|
|
15
|
+
## Python Example
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from elevenlabs import ElevenLabs
|
|
19
|
+
from elevenlabs import VoiceSettings
|
|
20
|
+
|
|
21
|
+
client = ElevenLabs()
|
|
22
|
+
|
|
23
|
+
audio = client.text_to_speech.convert(
|
|
24
|
+
text="Testing different voice settings.",
|
|
25
|
+
voice_id="JBFqnCBsd6RMkjVDRZzb",
|
|
26
|
+
model_id="eleven_v3",
|
|
27
|
+
voice_settings=VoiceSettings(
|
|
28
|
+
stability=0.5,
|
|
29
|
+
similarity_boost=0.75,
|
|
30
|
+
style=0.0,
|
|
31
|
+
use_speaker_boost=True
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## JavaScript Example
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const audio = await client.textToSpeech.convert("JBFqnCBsd6RMkjVDRZzb", {
|
|
40
|
+
text: "Testing different voice settings.",
|
|
41
|
+
modelId: "eleven_v3",
|
|
42
|
+
voiceSettings: {
|
|
43
|
+
stability: 0.5,
|
|
44
|
+
similarityBoost: 0.75,
|
|
45
|
+
style: 0.0,
|
|
46
|
+
useSpeakerBoost: true,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## cURL Example
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb" \
|
|
55
|
+
-H "xi-api-key: $ELEVENLABS_API_KEY" \
|
|
56
|
+
-H "Content-Type: application/json" \
|
|
57
|
+
-d '{
|
|
58
|
+
"text": "Testing different voice settings.",
|
|
59
|
+
"model_id": "eleven_v3",
|
|
60
|
+
"voice_settings": {
|
|
61
|
+
"stability": 0.5,
|
|
62
|
+
"similarity_boost": 0.75,
|
|
63
|
+
"style": 0.0,
|
|
64
|
+
"use_speaker_boost": true
|
|
65
|
+
}
|
|
66
|
+
}' \
|
|
67
|
+
--output output.mp3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Use Case Recommendations
|
|
71
|
+
|
|
72
|
+
### Audiobooks / Narration
|
|
73
|
+
```python
|
|
74
|
+
voice_settings=VoiceSettings(
|
|
75
|
+
stability=0.7, # Consistent tone
|
|
76
|
+
similarity_boost=0.5, # Natural variation
|
|
77
|
+
style=0.0
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Conversational / Chatbots
|
|
82
|
+
```python
|
|
83
|
+
voice_settings=VoiceSettings(
|
|
84
|
+
stability=0.4, # More expressive
|
|
85
|
+
similarity_boost=0.75,
|
|
86
|
+
style=0.3 # Slight style emphasis
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### News / Professional
|
|
91
|
+
```python
|
|
92
|
+
voice_settings=VoiceSettings(
|
|
93
|
+
stability=0.8, # Very consistent
|
|
94
|
+
similarity_boost=0.6,
|
|
95
|
+
style=0.0
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Character Voices / Drama
|
|
100
|
+
```python
|
|
101
|
+
voice_settings=VoiceSettings(
|
|
102
|
+
stability=0.3, # Highly expressive
|
|
103
|
+
similarity_boost=0.8,
|
|
104
|
+
style=0.5 # Strong style
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Tips
|
|
109
|
+
|
|
110
|
+
- **Start with defaults** and adjust incrementally
|
|
111
|
+
- **Lower stability** if voice sounds monotonous
|
|
112
|
+
- **Reduce similarity_boost** if you hear audio artifacts
|
|
113
|
+
- **Style works** with v2+, v3, and multilingual models
|
|
114
|
+
- **Test with representative text** from your actual use case
|
|
115
|
+
- **Flash models** ignore some voice settings for speed
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vidfarm-media
|
|
3
|
-
description: Media resolution for video workflows — narration TTS, voice-matched speech regeneration ("same speaker, new words"), word-level timings, BGM, SFX, and image/clip sourcing through vidfarm. Use when a video needs audio (voiceover/narration,
|
|
3
|
+
description: Media resolution for video workflows — AI music generation, narration TTS, voice-matched speech regeneration ("same speaker, new words"), transcription, word-level timings, BGM, SFX, and image/clip sourcing through vidfarm. Use when a video needs audio (music, voiceover/narration, sound effects), rewording of existing narration in the original speaker's voice, caption word timings, or media assets. ElevenLabs powers music/TTS/STT via the platform key (use_wallet_credits, wallet-billed) or the customer's own ElevenLabs key. Ships the shared audio engine scripts/audio.mjs (audio_request.json → audio_meta.json) used by faceless-explainer, product-launch-video, and general-video; documents the vidfarm music/tts/stt primitives, the keyless local Kokoro + whisper.cpp engines, and My Files / raws library / generation sourcing.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# vidfarm-media
|
|
7
7
|
|
|
8
|
-
One engine and one rule. The engine: `scripts/audio.mjs` turns a neutral `audio_request.json` into narration files, word timings, a music bed, and SFX — written into the project and described by one meta JSON. The rule: **
|
|
8
|
+
One engine and one rule. The engine: `scripts/audio.mjs` turns a neutral `audio_request.json` into narration files, word timings, a music bed, and SFX — written into the project and described by one meta JSON. The rule: **the user never signs up for a third-party media account.** Audio and images are the user's own files (My Files / raws library), generated on the user's provider keys, produced by keyless local models, OR generated through vidfarm's own ElevenLabs integration — music/TTS/STT on the platform key billed to the wallet (`use_wallet_credits`, the default) or on the customer's own ElevenLabs key. If a source would need the user to sign up somewhere else, it is not a source.
|
|
9
9
|
|
|
10
10
|
## Preflight (run once, before the brief)
|
|
11
11
|
|
|
@@ -56,27 +56,41 @@ Contract points:
|
|
|
56
56
|
- BGM is **not** loop-extended by the engine — the workflow assemblers loop-extend short beds to the video length at mount time. `bgm_pending` is never set; `scripts/wait-bgm.mjs` exists as an exit-0 shim.
|
|
57
57
|
- Mount the results per `hyperframes-core`: each `voices[].path` and (`bgm.path`, `sfx[]`) as `<audio>` tracks; `voices[].words` drive captions.
|
|
58
58
|
- **Multi-track mixing is native — keep voice, music, and SFX on SEPARATE `<audio>` layers, each at its own `data-volume`** (do NOT pre-mix them into one file). The runtime and both render paths mix all audio layers together with per-track volume honored (a real ffmpeg `amix` at render). Standard balance: **narration/voice ~1.0, BGM ~0.1–0.2** under the voice, SFX per-item (the engine already emits `sfx[].volume`, e.g. `0.35`). This is the whole reason each stem is a distinct track — the user (or a later edit) can re-level any track independently.
|
|
59
|
-
- **De-combining an original on recreate.** When you're recreating a video whose original had music + narration baked into ONE track, don't try to reproduce that single bed — you cannot stem-separate baked audio. Instead build the mix fresh: a new narration track (`tts` / `regenerate-speech`) at ~1.0 **plus** a real music file (owned / user-provided / found via `vidfarm directory search "<query> music"`) at ~0.1–0.2, as two separate `<audio>` layers, and drop the original combined track. Never fake a music layer or duplicate the voice track to stand in for music.
|
|
59
|
+
- **De-combining an original on recreate.** When you're recreating a video whose original had music + narration baked into ONE track, don't try to reproduce that single bed — you cannot stem-separate baked audio. Instead build the mix fresh: a new narration track (`tts` / `regenerate-speech`) at ~1.0 **plus** a real music file (owned / user-provided / found via `vidfarm directory search "<query> music"` / generated with `vidfarm music "<vibe>"`) at ~0.1–0.2, as two separate `<audio>` layers, and drop the original combined track. Never fake a music layer or duplicate the voice track to stand in for music.
|
|
60
60
|
|
|
61
|
-
## TTS
|
|
61
|
+
## Music, TTS & STT (ElevenLabs + BYOK)
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
**Music and speech are core primitives — use them freely.** ElevenLabs powers high-quality
|
|
64
|
+
music, narration, and transcription. The `use_wallet_credits` flag (DEFAULT **true** on the
|
|
65
|
+
music / TTS / STT primitives) runs on vidfarm's platform ElevenLabs key and bills the customer's
|
|
66
|
+
wallet. **Keep it true** for the best voices/music out of the box; only set it **false** to save
|
|
67
|
+
wallet credits or to use the customer's OWN saved ElevenLabs key (Settings → AI keys → ElevenLabs
|
|
68
|
+
is an audio-only key that does NOT count as a qualified text/image/video key). With
|
|
69
|
+
`use_wallet_credits: false` and no ElevenLabs key, TTS/STT fall back to a BYOK openai/gemini/
|
|
70
|
+
openrouter key; music always needs ElevenLabs (own key or platform).
|
|
71
|
+
|
|
72
|
+
| Need | Command / route | Notes |
|
|
64
73
|
| --- | --- | --- |
|
|
65
|
-
|
|
|
74
|
+
| **Music** (bed, beat, jingle, song, score) | `vidfarm music "upbeat lo-fi beat" --length 30` · `POST /api/v1/primitives/music/generate` | ElevenLabs. `use_wallet_credits` default true (platform key + wallet); `--own-key` = your ElevenLabs key. `music_length_ms` ≤ 300000 (5 min). Place as its own `<audio>` layer ~0.1–0.2 under narration. |
|
|
75
|
+
| **Narration** (default) | `vidfarm tts "…" --cloud` · `POST /api/v1/primitives/audio/speech` | Default = ElevenLabs on the platform key (wallet-billed). Pick a voice with `--voice <voice_id>` (browse below). `--own-key` for your ElevenLabs/BYOK key. Local-first `vidfarm tts` (no `--cloud`) still runs on your env openai/gemini key. |
|
|
76
|
+
| **List voices** | `vidfarm voices` · `GET /api/v1/primitives/audio/voices` | ElevenLabs voice catalog (voice_id, name, labels, preview_url). Default = platform account; `--own-key` / `?use_wallet_credits=false` = the customer's ElevenLabs account. **Default a sensible voice AND tell the user they can pick from many.** |
|
|
66
77
|
| Narration, zero keys | `npx hyperframes tts "…" -v af_heart --json` | Kokoro-82M, local, WAV + duration in JSON |
|
|
67
|
-
| Transcript + SRT
|
|
68
|
-
| Reword existing narration in the (approximate) original voice | `POST /api/v1/primitives/audio/regenerate-speech`
|
|
78
|
+
| **Transcript + SRT** | `vidfarm stt <file\|url> --cloud` · `POST /api/v1/primitives/audio/transcribe` | Default = ElevenLabs Scribe (native diarization + real word timestamps), wallet-billed. `--own-key`/BYOK: gemini labels speakers, openai/whisper-1 gives real word timings. |
|
|
79
|
+
| Reword existing narration in the (approximate) original voice | `POST /api/v1/primitives/audio/regenerate-speech` | Listens, profiles the speaker (needs a Gemini key), rewords, regenerates with the closest preset voice + matched style. Approximation, never a clone. Details: `references/tts.md` |
|
|
69
80
|
| Word timings, zero keys | `npx hyperframes transcribe <audio> --json` | whisper.cpp; writes word-level `transcript.json` |
|
|
70
81
|
| Captions straight into a composition | `vidfarm captions generate <dir>` | whisper-1 real timings when an OpenAI key exists; estimates otherwise |
|
|
71
82
|
|
|
72
|
-
Voices, styles, formats, and the exact word-timestamp paths:
|
|
83
|
+
Voices, styles, formats, the ElevenLabs SDK details, and the exact word-timestamp paths:
|
|
84
|
+
`references/tts.md`, plus the loadable `music` and `text-to-speech` skill packs for the raw
|
|
85
|
+
ElevenLabs API.
|
|
73
86
|
|
|
74
87
|
## BGM and SFX resolution order
|
|
75
88
|
|
|
76
89
|
1. **User file / URL** — `bgm.file` / `bgm.url` in the request; copied or downloaded into `assets/bgm/`.
|
|
77
90
|
2. **File directory** — `vidfarm directory search "<query> music"` or the legacy `vidfarm files --search` (needs `$VIDFARM_API_KEY`); the user's own library, searchable by meaning when notes are annotated.
|
|
78
|
-
3. **
|
|
79
|
-
4. **
|
|
91
|
+
3. **Generate it** — `vidfarm music "<query>" --length <sec>` (ElevenLabs; `use_wallet_credits` default) writes an mp3 you can pass back as `bgm.file`. Great when the user has no fitting track. The engine script itself has no generate mode — run `vidfarm music` yourself and feed the result in.
|
|
92
|
+
4. **Local dir** — `$MUSIC_DIR` / `$SFX_DIR` (or request `bgm.dir` / `sfx_dir`), picked by filename keyword match against the query.
|
|
93
|
+
5. **Skip, with a note** — a silent bed or a missing whoosh is fine. The meta's `notes[]` says exactly what was skipped and why.
|
|
80
94
|
|
|
81
95
|
## The file directory (My Files + raws, unified)
|
|
82
96
|
|
|
@@ -1,6 +1,44 @@
|
|
|
1
|
-
# TTS / STT
|
|
1
|
+
# Music / TTS / STT — voices, timings, formats
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Three tiers. **ElevenLabs (default)** powers music, high-quality narration, and Scribe
|
|
4
|
+
transcription — on vidfarm's platform key billed to the wallet (`use_wallet_credits: true`, the
|
|
5
|
+
default) or on the customer's OWN saved ElevenLabs key (`use_wallet_credits: false`). **BYOK
|
|
6
|
+
cloud** rides the user's own openai/gemini/openrouter key through vidfarm (TTS/STT fallback when
|
|
7
|
+
`use_wallet_credits` is false and no ElevenLabs key is present). **Keyless local** runs Kokoro-82M
|
|
8
|
+
and whisper.cpp on the user's machine with no account. There is never a sign-in step for the user.
|
|
9
|
+
|
|
10
|
+
## use_wallet_credits (default true — keep it on)
|
|
11
|
+
|
|
12
|
+
`music`, `tts` (`--cloud`), and `stt` (`--cloud`) default to ElevenLabs on the platform key,
|
|
13
|
+
billed to the customer's wallet. Recommend keeping it on for the best voices/music with zero
|
|
14
|
+
setup. Turn it OFF only to (a) save wallet credits or (b) use the customer's own ElevenLabs key.
|
|
15
|
+
With it off and no ElevenLabs key, TTS/STT fall back to a BYOK openai/gemini/openrouter key; music
|
|
16
|
+
always needs ElevenLabs. ElevenLabs in Settings → AI keys is **audio-only** — it does NOT count as
|
|
17
|
+
a qualified text/image/video key, so the user still needs an openai/gemini/openrouter key for the
|
|
18
|
+
core generation features.
|
|
19
|
+
|
|
20
|
+
## Music — `vidfarm music`
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
vidfarm music "chill lo-fi hip hop beat with jazzy piano" --length 30 --json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- ElevenLabs music generation (`POST /api/v1/primitives/music/generate`). `--length <sec>` or
|
|
27
|
+
`--length-ms`, default 30s, max 5 min. `--own-key` uses the customer's ElevenLabs key instead of
|
|
28
|
+
the wallet. Returns a durable mp3; mount it as its own `<audio>` layer ~0.1–0.2 under narration.
|
|
29
|
+
- For fine control, the primitive also accepts an ElevenLabs `composition_plan` (section-by-section
|
|
30
|
+
lyrics/style) — see the loadable `music` skill pack for the plan schema.
|
|
31
|
+
|
|
32
|
+
## Voices — `vidfarm voices`
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
vidfarm voices # platform-account voices (voice_id, name, labels, preview_url)
|
|
36
|
+
vidfarm voices --own-key # the customer's own ElevenLabs account voices
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `GET /api/v1/primitives/audio/voices` (`?use_wallet_credits=false` for the user's own key).
|
|
40
|
+
- **Default a sensible voice and TELL the user they can pick from many** — surface a few names +
|
|
41
|
+
the returned `voice_library_url`, then pass the chosen `voice_id` to `tts --voice <voice_id>`.
|
|
4
42
|
|
|
5
43
|
## Preflight
|
|
6
44
|
|
|
@@ -37,7 +75,7 @@ POST /api/v1/primitives/audio/regenerate-speech # alias: /api/v1/primitives/re
|
|
|
37
75
|
|
|
38
76
|
One job: listens to the source, profiles the dominant speaker with an audio-native Gemini call (verbatim transcript + gender/age/accent/pace/pitch/energy profile + a dense style direction + the closest preset in both voice families), rewords the transcript per `rewrite_instruction` (keeping length and rhythm) or takes the exact `text`, then regenerates via TTS with the matched preset + profiled style instructions. Result: durable `audioUrl`, `script`, `original_transcript`, `voice_profile`, `voice_match`, plus a `regenerate-speech.json` artifact.
|
|
39
77
|
|
|
40
|
-
- **Honesty rule (non-negotiable):
|
|
78
|
+
- **Honesty rule (non-negotiable): regenerate-speech is an approximation, never a clone.** It runs on OpenAI/Gemini, which expose preset voices with style steering only (no self-serve cloning of an arbitrary speaker). `voice_match` says `"approximate"` or `"none"`; always present it that way. (ElevenLabs IS wired into vidfarm for music, preset-voice TTS, and Scribe STT — but regenerate-speech does not use it for speaker cloning; treat any voice-match as an approximation.)
|
|
41
79
|
- **Voice profiling needs a saved Gemini key** (it is the audio-native LLM). With only openai/openrouter keys the job degrades gracefully: STT transcript + reword still work, but the voice is a default or explicitly passed preset and `voice_match` is `"none"`.
|
|
42
80
|
- Sources over ~40 minutes of speech must be trimmed first (same cap as `/audio/transcribe`).
|
|
43
81
|
- To swap the result into a composition: mute the original span (`/videos/mute` or the layer's volume), then place `audioUrl` as a new audio layer.
|
package/SKILL.director.md
CHANGED
|
@@ -147,7 +147,7 @@ Name the plan back in these terms ("I'll SWAP the captions and REPLACE the scene
|
|
|
147
147
|
|
|
148
148
|
**Fuel a scene REPLACE with raw clips, not expensive AI video.** A heavy scenes-axis replace needs footage; sources in cost order: (1) the director's own library — search `/raws` and `/files` (`vidfarm raws search …`, `vidfarm files --search …` / `browse_files`); (2) **HUNT new raws** out of a long-form source (podcast/VOD/webinar or any YouTube/TikTok/IG/X URL) — `vidfarm raws scan <src> --prompt "<what the new scenes need>" --aspect <canvas> [--duration N --no-text --range …]` (local-first, free compute) or the async `POST /clips/scan` / `/raws/scan`; then reuse the picks (`set_layer_media` / `vidfarm set-media` to swap in place, `add_layer`/`vidfarm place` for net-new scenes); (3) `generate_layer` / `vidfarm generate` AI generation — the **expensive last resort**, only for scenes no real clip can cover. When a big scene re-work is asked for but no footage is given, **ask for a source to hunt (or point at the raws library) before AI-generating** — see [Raws](#raws-long-form--short-form-raws) and [Generate AI media …](#generate-ai-media-and-drop-it-on-the-timeline).
|
|
149
149
|
|
|
150
|
-
**Audio is natively multi-track — overlay narration + music + SFX, each at its own volume.** A composition mixes UNLIMITED simultaneous `<audio>` layers; each sits on its own `data-track-index` and carries its own `data-volume` (0–2, default 1), and the runtime mixes them with per-track volume honored identically in the preview and the exported MP4 (a real ffmpeg `amix` of every audio layer at render). So you never need a pre-mixed file — lay **narration/voiceover at ~1.0 on one track, a music bed at ~0.1–0.2 on a separate track, and SFX on their own tracks**, each via `add_layer kind=audio` (web) / `vidfarm place --kind audio --volume …` (devcli), tuning levels later with the Inspector's Volume slider or `set_layer_media` (`volume`, `muted`). **The key move when recreating a template whose original baked music + narration into ONE audio track: rebuild it as TWO independent tracks** — a fresh narration track (`/audio/speech`, or same-voice reword via `/audio/regenerate-speech` / `vidfarm speech regenerate`) at ~1.0 and a separate real music track at ~0.1–0.2 — then mute/remove the original combined source-audio layer. This gives the director independent voice and music volume, and works around AI TTS being unable to emit narration+music in one file: you compose the mix on the timeline. Honesty: you can't un-mix / stem-separate the original's baked audio — the two tracks are a fresh narration track **plus** a real music file (owned / user-provided / `browse_files`), never a faked or duplicated voice layer.
|
|
150
|
+
**Audio is natively multi-track — overlay narration + music + SFX, each at its own volume.** A composition mixes UNLIMITED simultaneous `<audio>` layers; each sits on its own `data-track-index` and carries its own `data-volume` (0–2, default 1), and the runtime mixes them with per-track volume honored identically in the preview and the exported MP4 (a real ffmpeg `amix` of every audio layer at render). So you never need a pre-mixed file — lay **narration/voiceover at ~1.0 on one track, a music bed at ~0.1–0.2 on a separate track, and SFX on their own tracks**, each via `add_layer kind=audio` (web) / `vidfarm place --kind audio --volume …` (devcli), tuning levels later with the Inspector's Volume slider or `set_layer_media` (`volume`, `muted`). **The key move when recreating a template whose original baked music + narration into ONE audio track: rebuild it as TWO independent tracks** — a fresh narration track (`/audio/speech`, or same-voice reword via `/audio/regenerate-speech` / `vidfarm speech regenerate`) at ~1.0 and a separate real music track at ~0.1–0.2 — then mute/remove the original combined source-audio layer. This gives the director independent voice and music volume, and works around AI TTS being unable to emit narration+music in one file: you compose the mix on the timeline. Honesty: you can't un-mix / stem-separate the original's baked audio — the two tracks are a fresh narration track **plus** a real music file (owned / user-provided / `browse_files` / generated with `/api/v1/primitives/music/generate` (`vidfarm music`)), never a faked or duplicated voice layer.
|
|
151
151
|
|
|
152
152
|
## Edit in the Trackpad Editor
|
|
153
153
|
|
|
@@ -623,8 +623,10 @@ Send a stable `tracer` on export so retries are traceable and filterable in job
|
|
|
623
623
|
| `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
|
|
624
624
|
| `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
|
|
625
625
|
| `vidfarm remove-background-greenscreen <image> [--key-color #00FF00] [--tolerance 0.3] [--out <f>]` | `POST /api/v1/primitives/images/remove-background-greenscreen` (polls job) | cheap chroma-key removal of a FLAT solid background → transparent PNG (cloud primitive) |
|
|
626
|
-
| `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll) | text → narration audio
|
|
627
|
-
| `vidfarm
|
|
626
|
+
| `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll, ElevenLabs on the platform key by default, `--own-key` for yours) | text → narration audio; `--cloud --voice <voice_id>` picks an ElevenLabs voice |
|
|
627
|
+
| `vidfarm music "<prompt>" [--length <sec>] [--out <f>] [--own-key]` | `POST /api/v1/primitives/music/generate` (polls job) | prompt → music track (ElevenLabs; platform key + wallet by default, `--own-key` for yours) |
|
|
628
|
+
| `vidfarm voices [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | list ElevenLabs voices (voice_id/name/labels) for `tts --voice`; default a voice + tell the user they can choose |
|
|
629
|
+
| `vidfarm stt <file\|url> [--out <base>] [--no-diarize]` (alias: `transcribe`) | (LOCAL-FIRST: local ffmpeg demux + your own key; `--cloud` = `POST /api/v1/primitives/audio/transcribe` + poll, ElevenLabs Scribe on the platform key by default, `--own-key` for yours) | video/audio → transcript in BOTH formats: simple subtitles (txt + SRT) and multi-speaker segments (json) |
|
|
628
630
|
| `vidfarm place <dir> --src <url\|file> [--at\|--replace]` | (edits local composition.html; local files → serve disk store or temp upload) | drop media (URL **or local file**) into a gap / over a scene |
|
|
629
631
|
| `vidfarm captions generate <dir> [--style <preset>] [--audio <f>\|--srt <f>\|--text "…"]` | (LOCAL-FIRST: STT on your own key — OpenAI = real word timestamps — then edits local composition.html) | transcribe narration → animated word-by-word caption cues |
|
|
630
632
|
| `vidfarm captions style <dir> --style <preset>` / `captions list` / `captions clear` | (edits local composition.html) | restyle / inspect / remove animated captions |
|
|
@@ -978,19 +980,34 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/media/dedupe" \
|
|
|
978
980
|
}'
|
|
979
981
|
```
|
|
980
982
|
|
|
983
|
+
## Primitive: music (text → music)
|
|
984
|
+
|
|
985
|
+
Generate music (instrumental, songs with lyrics, background beds, jingles, scores) via ElevenLabs. **Default this on freely — music is a core primitive.** `use_wallet_credits` defaults **true**: it runs on vidfarm's platform ElevenLabs key and bills the customer's wallet. Recommend keeping it on; set it false only to save wallet credits or to use the customer's OWN saved ElevenLabs key.
|
|
986
|
+
|
|
987
|
+
- `POST /api/v1/primitives/music/generate` (alias: `POST /api/v1/primitives/music`)
|
|
988
|
+
- Body: `{ "tracer": "...", "payload": { "prompt": "...", "music_length_ms"?: 30000, "composition_plan"?: {...}, "model"?: "...", "use_wallet_credits"?: true }, "webhook_url"?: "..." }`
|
|
989
|
+
- `prompt` — describe the vibe/genre/instrumentation. `music_length_ms` ≤ 300000 (5 min), default 30000.
|
|
990
|
+
- `composition_plan` (optional) — ElevenLabs section-by-section plan for granular lyric/style control (see the loadable `music` skill pack).
|
|
991
|
+
- Result: durable mp3 (`primary_file_url` / `audio.file_url`). Mount as its own `<audio>` layer ~0.1–0.2 under narration.
|
|
992
|
+
- devcli: `vidfarm music "<prompt>" --length 30` (`--own-key` to use your ElevenLabs key).
|
|
993
|
+
|
|
981
994
|
## Primitive: tts (text → speech)
|
|
982
995
|
|
|
983
|
-
Generate spoken narration audio from text on the
|
|
996
|
+
Generate spoken narration audio from text. **`use_wallet_credits` defaults true** → high-quality ElevenLabs narration on vidfarm's platform key, billed to the wallet — the recommended default. Set it false to use the customer's OWN ElevenLabs key or a BYOK OpenAI/Gemini/OpenRouter key (BYOK is never wallet-billed). The **voice style is promptable** via `instructions` (tone/pacing/accent/emotion/persona). Result is a durable Vidfarm audio URL (mp3/wav) ready to drop into a composition as an audio layer.
|
|
984
997
|
|
|
985
998
|
- `POST /api/v1/primitives/audio/speech` (alias: `POST /api/v1/primitives/tts`)
|
|
986
|
-
- Body: `{ "tracer": "...", "payload": { "text": "...", "voice"?: "...", "instructions"?: "...", "provider"?: "openai" | "gemini" | "openrouter", "model"?: "...", "output_format"?: "mp3" | "wav" }, "webhook_url"?: "..." }`
|
|
999
|
+
- Body: `{ "tracer": "...", "payload": { "text": "...", "voice"?: "...", "instructions"?: "...", "use_wallet_credits"?: true, "provider"?: "openai" | "gemini" | "openrouter", "model"?: "...", "output_format"?: "mp3" | "wav" }, "webhook_url"?: "..." }`
|
|
987
1000
|
- `text` (required, ≤8000 chars) — the words to speak, verbatim. Aliases: `input`, `script`.
|
|
988
|
-
- `voice` (optional) — provider
|
|
989
|
-
- `instructions` (optional) — the voice-STYLE prompt
|
|
990
|
-
- `output_format` defaults to `mp3` (
|
|
1001
|
+
- `voice` (optional) — on the ElevenLabs path, an ElevenLabs **voice_id** (or friendly name george/sarah/…); on the BYOK path a provider preset (OpenAI `alloy`/…; Gemini `Kore`/…). **Default a sensible voice and tell the user they can pick from many** — list them with `GET /api/v1/primitives/audio/voices` (or `vidfarm voices`).
|
|
1002
|
+
- `instructions` (optional) — the voice-STYLE prompt. Aliases: `style`, `style_instructions`, `voice_style`.
|
|
1003
|
+
- `output_format` defaults to `mp3` (Gemini/ElevenLabs may answer in wav — read `audio.content_type`).
|
|
991
1004
|
- Response: standard primitive job. Poll to completion, then read `primary_file_url` / `audio.file_url`.
|
|
992
|
-
- Billing:
|
|
993
|
-
- devcli: `vidfarm tts "…" --style "…"` runs LOCAL-FIRST on your own env key (no job
|
|
1005
|
+
- Billing: wallet-billed on the platform ElevenLabs key (default); free/BYOK when `use_wallet_credits: false` and the caller's own key serves it.
|
|
1006
|
+
- devcli: `vidfarm tts "…" --style "…"` runs LOCAL-FIRST on your own env key (no job); add `--cloud` for the ElevenLabs platform job (`--own-key` = your key). `vidfarm voices` lists voices.
|
|
1007
|
+
|
|
1008
|
+
## Primitive: audio/voices (list ElevenLabs voices)
|
|
1009
|
+
|
|
1010
|
+
`GET /api/v1/primitives/audio/voices` returns the ElevenLabs voice catalog — `{ scope, default_voice_id, voice_library_url, voices: [{ voice_id, name, category, labels, description, preview_url }] }`. Default scope is vidfarm's platform account; add `?use_wallet_credits=false` to list the voices on the customer's OWN saved ElevenLabs key. Pick a `voice_id` and pass it as `voice` to `/audio/speech`. devcli: `vidfarm voices` (`--own-key` for the user's account).
|
|
994
1011
|
|
|
995
1012
|
Example:
|
|
996
1013
|
|
|
@@ -1016,13 +1033,14 @@ Transcribe speech from **any video or audio URL** on the caller's saved AI provi
|
|
|
1016
1033
|
2. **Advanced multi-speaker version** — speaker-attributed, timestamped segments (`output.segments`, each `{ speaker, start_sec, end_sec, text }`, with `output.speakers` listing the cast) for multi-narration sources like podcasts, interviews, and skits — stored durably as `transcript.json`.
|
|
1017
1034
|
|
|
1018
1035
|
- `POST /api/v1/primitives/audio/transcribe` (alias: `POST /api/v1/primitives/stt`)
|
|
1019
|
-
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "diarize"?: true, "language"?: "en", "prompt"?: "...", "provider"?: "gemini" | "openai" | "openrouter", "model"?: "...", "media_type"?: "auto" | "video" | "audio" }, "webhook_url"?: "..." }`
|
|
1036
|
+
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "diarize"?: true, "word_timestamps"?: false, "use_wallet_credits"?: true, "language"?: "en", "prompt"?: "...", "provider"?: "gemini" | "openai" | "openrouter", "model"?: "...", "media_type"?: "auto" | "video" | "audio" }, "webhook_url"?: "..." }`
|
|
1020
1037
|
- `source_url` (required) — video OR audio URL. Aliases: `source_video_url`, `source_audio_url`, `video_url`, `audio_url`, `url`. For a fork's composition, pick the right source via `GET /api/v1/compositions/:forkId/remove-video-captions` first.
|
|
1021
|
-
- `
|
|
1038
|
+
- `use_wallet_credits` (default `true`) — ElevenLabs Scribe on the platform key (native diarization + real word timestamps), billed to the wallet. Set false to use the customer's OWN ElevenLabs key or a BYOK gemini/openai/openrouter key.
|
|
1039
|
+
- `diarize` (default `true`) — attribute segments to speakers. On the BYOK path speaker labels need a **Gemini key**; OpenAI/OpenRouter degrade to a single-speaker transcript. ElevenLabs Scribe (the default) diarizes natively.
|
|
1022
1040
|
- `prompt` (optional) — domain/vocabulary hint; `language` (optional) — expected language code.
|
|
1023
1041
|
- Response: standard primitive job. Poll to completion; `output` carries both formats inline (very long transcripts set `text_truncated: true` and keep the full copy in the `transcript.json` artifact), and the artifacts `transcript.json` / `transcript.srt` / `transcript.txt` are durable URLs.
|
|
1024
1042
|
- Limits: ~40 minutes of speech per job (20MB extracted audio) — trim longer sources with `/videos/trim` or `/audio/trim` and transcribe in parts.
|
|
1025
|
-
- Billing: the
|
|
1043
|
+
- Billing: wallet-billed on the platform ElevenLabs key (default); free/BYOK when `use_wallet_credits: false`. The tiny audio-demux compute step always bills (`primitive_media_lambda`), like every media primitive.
|
|
1026
1044
|
- For the CURRENT fork, the decompose-time transcript may already exist via `video-context.json` — prefer it before paying for a new transcription; use this primitive for other URLs, multi-speaker attribution, or SRT output.
|
|
1027
1045
|
- devcli: `vidfarm stt <file|url>` runs LOCAL-FIRST (local ffmpeg + your own key); add `--cloud` to run this route instead.
|
|
1028
1046
|
|
package/SKILL.platform.md
CHANGED
|
@@ -281,9 +281,9 @@ Primitives are hosted operations that are NOT templates — they're reusable bui
|
|
|
281
281
|
- `download_media_video` — social/media URL → durable MP4
|
|
282
282
|
- `hyperframes_render` — composition HTML → MP4 (the render primitive publishing uses)
|
|
283
283
|
- `brainstorm` — LLM brainstorm route
|
|
284
|
-
- `tts` / `stt` —
|
|
284
|
+
- `music` / `tts` / `stt` — ElevenLabs audio (music, narration, transcription) with a `use_wallet_credits` flag that **defaults true**. `POST /api/v1/primitives/music/generate` (`primitive:music`) generates music from a prompt or composition plan. `POST /api/v1/primitives/audio/speech` (`primitive:tts`) turns text into narration with a promptable style (`instructions`) and an ElevenLabs `voice_id` (browse via `GET /api/v1/primitives/audio/voices` → `providers.listElevenLabsVoices`). `POST /api/v1/primitives/audio/transcribe` (`primitive:stt`) transcribes any video/audio URL (video demuxes through the primitive-media ffmpeg seam first, 64kbps mono, ~40min/20MB cap) into **two formats** — simple subtitles (text + SRT) and speaker-attributed segments. **Routing (`src/services/providers.ts` `resolveAudioAuthPlan`):** `use_wallet_credits: true` (default) → the platform `ELEVENLABS_API_KEY`, billed to the wallet (`elevenlabs_music`/`elevenlabs_tts`/`elevenlabs_stt` cost centers); `false` → the customer's own saved `elevenlabs` key (no charge), else a BYOK gemini/openai/openrouter key (TTS/STT only), else the platform key + wallet. The ElevenLabs calls live in `src/services/elevenlabs.ts`; the openai/gemini/openrouter speech calls in `src/services/speech.ts`. The `elevenlabs` provider key is **audio-only** — it never counts as a qualified text/image/video key. devcli: `vidfarm music`, `vidfarm voices`, and `vidfarm tts`/`vidfarm stt` (LOCAL-FIRST on the user's env key; `--cloud` hits ElevenLabs, `--own-key` uses the user's own key).
|
|
285
285
|
|
|
286
|
-
Directors call primitives when they want a one-off asset before feeding it into a template. Templates call primitives internally as workflow steps. Each primitive declares one of three billing modes: **WALLET** — a metered platform charge (e.g. `remove_background` / `image_remove_background` RapidAPI pass-through, `video_remove_captions` GhostCut, cloud `hyperframes_render`, the local `image_remove_background_greenscreen` chroma key,
|
|
286
|
+
Directors call primitives when they want a one-off asset before feeding it into a template. Templates call primitives internally as workflow steps. Each primitive declares one of three billing modes: **WALLET** — a metered platform charge (e.g. `remove_background` / `image_remove_background` RapidAPI pass-through, `video_remove_captions` GhostCut, cloud `hyperframes_render`, the local `image_remove_background_greenscreen` chroma key, `media_overlay`, and **platform ElevenLabs music/TTS/STT when `use_wallet_credits` is on** — the default); **BYOK** — the AI spend rides the caller's own provider key and is never wallet-billed (smart decompose, clip tagging/embedding, and music/TTS/STT run with `use_wallet_credits: false` on the user's own ElevenLabs/openai/gemini/openrouter key); or **COMPUTE-ONLY** — only the AWS Lambda / Step-Functions compute bills (clip hunts, the stt audio-demux step). So "all primitives are metered against the caller's wallet" holds only for the compute/platform legs — every BYOK AI call is excepted.
|
|
287
287
|
|
|
288
288
|
## Billing
|
|
289
289
|
|