@mevdragon/vidfarm-devcli 0.19.1 → 0.20.1
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/demo/dist/app.css +1 -1
- package/demo/dist/app.js +75 -75
- 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 +5 -1
- package/dist/src/frontend/file-directory.js +41 -18
- 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 +8 -4
- 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
- package/public/assets/file-directory-app.js +3 -2
|
@@ -429,7 +429,11 @@ const ttsPayloadSchema = z.preprocess((raw) => {
|
|
|
429
429
|
text: z.string().min(1).max(8000),
|
|
430
430
|
voice: z.string().min(1).max(120).optional(),
|
|
431
431
|
instructions: z.string().min(1).max(4000).optional(),
|
|
432
|
-
output_format: z.enum(["mp3", "wav"]).default("mp3")
|
|
432
|
+
output_format: z.enum(["mp3", "wav"]).default("mp3"),
|
|
433
|
+
// Default TRUE: great ElevenLabs narration out of the box on the platform key,
|
|
434
|
+
// billed to the wallet. Set false to use your own ElevenLabs key or a BYOK
|
|
435
|
+
// openai/gemini/openrouter key instead. See voice options via /audio/voices.
|
|
436
|
+
use_wallet_credits: z.boolean().default(true)
|
|
433
437
|
}).superRefine((value, ctx) => {
|
|
434
438
|
// Reject an explicit provider/voice contradiction at enqueue (400) instead
|
|
435
439
|
// of queueing a job that can only fail at the provider.
|
|
@@ -465,7 +469,36 @@ const sttPayloadSchema = z.preprocess((raw) => {
|
|
|
465
469
|
// output and downstream callers estimate word windows.
|
|
466
470
|
word_timestamps: z.boolean().default(false),
|
|
467
471
|
language: z.string().min(2).max(16).optional(),
|
|
468
|
-
prompt: z.string().min(1).max(2000).optional()
|
|
472
|
+
prompt: z.string().min(1).max(2000).optional(),
|
|
473
|
+
// Default TRUE: platform ElevenLabs Scribe (native diarization + real word
|
|
474
|
+
// timestamps), billed to the wallet. Set false to use your own ElevenLabs
|
|
475
|
+
// key or a BYOK gemini/openai/openrouter key.
|
|
476
|
+
use_wallet_credits: z.boolean().default(true)
|
|
477
|
+
}));
|
|
478
|
+
// Music generation (ElevenLabs). Prompt-based, or a hand-built composition plan.
|
|
479
|
+
const musicPayloadSchema = z.preprocess((raw) => {
|
|
480
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
481
|
+
return raw;
|
|
482
|
+
const payload = raw;
|
|
483
|
+
return {
|
|
484
|
+
...payload,
|
|
485
|
+
prompt: payload.prompt ?? payload.text ?? payload.description,
|
|
486
|
+
music_length_ms: payload.music_length_ms ?? payload.length_ms ?? payload.duration_ms
|
|
487
|
+
};
|
|
488
|
+
}, z.object({
|
|
489
|
+
prompt: z.string().min(1).max(2000).optional(),
|
|
490
|
+
// ElevenLabs music caps at 5 minutes per generation.
|
|
491
|
+
music_length_ms: z.number().int().min(3000).max(300000).default(30000),
|
|
492
|
+
// Optional ElevenLabs composition plan for granular section-by-section control.
|
|
493
|
+
composition_plan: z.record(z.string(), z.any()).optional(),
|
|
494
|
+
model: z.string().min(1).optional(),
|
|
495
|
+
output_format: z.string().min(1).optional(),
|
|
496
|
+
// Default TRUE: platform ElevenLabs key, billed to the wallet. Set false to
|
|
497
|
+
// use your own saved ElevenLabs key (no wallet charge).
|
|
498
|
+
use_wallet_credits: z.boolean().default(true)
|
|
499
|
+
}).refine((value) => Boolean(value.prompt) || Boolean(value.composition_plan), {
|
|
500
|
+
message: "Provide either a prompt or a composition_plan.",
|
|
501
|
+
path: ["prompt"]
|
|
469
502
|
}));
|
|
470
503
|
// Animated captions from speech: STT with word-level timings, paged into
|
|
471
504
|
// ready-to-apply cue layers. Either transcribe a source_url or page plain text.
|
|
@@ -714,6 +747,7 @@ const PRIMITIVE_BRAINSTORM_PRODUCT_PLACEMENT_ID = "primitive:brainstorm_product_
|
|
|
714
747
|
const PRIMITIVE_MEDIA_DEDUPE_ID = "primitive:media_dedupe";
|
|
715
748
|
const PRIMITIVE_TTS_ID = "primitive:tts";
|
|
716
749
|
const PRIMITIVE_STT_ID = "primitive:stt";
|
|
750
|
+
const PRIMITIVE_MUSIC_ID = "primitive:music";
|
|
717
751
|
const PRIMITIVE_CAPTIONS_ID = "primitive:captions";
|
|
718
752
|
const PRIMITIVE_SPEECH_REGENERATE_ID = "primitive:speech_regenerate";
|
|
719
753
|
// Gemini inline audio caps out around 20MB; at the 64kbps mono mp3 we extract,
|
|
@@ -2018,20 +2052,34 @@ const ttsPrimitive = definePrimitive({
|
|
|
2018
2052
|
text: payload.text,
|
|
2019
2053
|
voice: payload.voice,
|
|
2020
2054
|
instructions: payload.instructions,
|
|
2021
|
-
responseFormat: payload.output_format
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
//
|
|
2055
|
+
responseFormat: payload.output_format,
|
|
2056
|
+
useWalletCredits: payload.use_wallet_credits
|
|
2057
|
+
});
|
|
2058
|
+
// Route may have resolved to a different provider (e.g. platform ElevenLabs
|
|
2059
|
+
// when use_wallet_credits is on); report what actually ran.
|
|
2060
|
+
const effectiveProvider = generated.effectiveProvider;
|
|
2061
|
+
const effectiveModel = generated.effectiveModel;
|
|
2062
|
+
if (generated.billing) {
|
|
2063
|
+
await ctx.billing.record({
|
|
2064
|
+
type: "ai_generation",
|
|
2065
|
+
costUsd: generated.billing.costUsd,
|
|
2066
|
+
costCenterSlug: generated.billing.costCenterSlug,
|
|
2067
|
+
metadata: { ...generated.billing.metadata, primitive: "tts", voice: payload.voice ?? null }
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
// Gemini/ElevenLabs TTS may answer in wav regardless of the requested
|
|
2071
|
+
// format; name the artifact after what actually came back.
|
|
2025
2072
|
const extension = generated.contentType.includes("wav") ? "wav" : "mp3";
|
|
2026
2073
|
const stored = await ctx.storage.putBuffer(`speech.${extension}`, generated.bytes, {
|
|
2027
2074
|
contentType: generated.contentType,
|
|
2028
2075
|
kind: "audio",
|
|
2029
2076
|
metadata: {
|
|
2030
2077
|
primitive: "tts",
|
|
2031
|
-
provider,
|
|
2032
|
-
model,
|
|
2078
|
+
provider: effectiveProvider,
|
|
2079
|
+
model: effectiveModel,
|
|
2033
2080
|
voice: payload.voice ?? null,
|
|
2034
|
-
styleInstructions: payload.instructions ?? null
|
|
2081
|
+
styleInstructions: payload.instructions ?? null,
|
|
2082
|
+
wallet_billed: Boolean(generated.billing)
|
|
2035
2083
|
}
|
|
2036
2084
|
});
|
|
2037
2085
|
ctx.logger.progress(1, "Speech generation complete");
|
|
@@ -2044,11 +2092,72 @@ const ttsPrimitive = definePrimitive({
|
|
|
2044
2092
|
audio: {
|
|
2045
2093
|
file_url: stored.url,
|
|
2046
2094
|
content_type: generated.contentType,
|
|
2047
|
-
provider,
|
|
2048
|
-
model,
|
|
2095
|
+
provider: effectiveProvider,
|
|
2096
|
+
model: effectiveModel,
|
|
2049
2097
|
voice: payload.voice ?? null,
|
|
2050
2098
|
style_instructions: payload.instructions ?? null,
|
|
2051
|
-
text_length: payload.text.length
|
|
2099
|
+
text_length: payload.text.length,
|
|
2100
|
+
wallet_billed: Boolean(generated.billing)
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
});
|
|
2107
|
+
const musicPrimitive = definePrimitive({
|
|
2108
|
+
id: PRIMITIVE_MUSIC_ID,
|
|
2109
|
+
kind: "music",
|
|
2110
|
+
operations: {
|
|
2111
|
+
run: {
|
|
2112
|
+
description: "Generate music (instrumental or with lyrics) from a prompt or a composition plan via ElevenLabs, persisted as a reusable platform audio URL.",
|
|
2113
|
+
inputSchema: musicPayloadSchema,
|
|
2114
|
+
workflow: "run"
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
jobs: {
|
|
2118
|
+
async run(ctx, input) {
|
|
2119
|
+
const payload = musicPayloadSchema.parse(input);
|
|
2120
|
+
ctx.logger.progress(0.12, "Composing music");
|
|
2121
|
+
const generated = await ctx.providers.generateMusic({
|
|
2122
|
+
prompt: payload.prompt,
|
|
2123
|
+
musicLengthMs: payload.music_length_ms,
|
|
2124
|
+
compositionPlan: payload.composition_plan,
|
|
2125
|
+
model: payload.model,
|
|
2126
|
+
outputFormat: payload.output_format,
|
|
2127
|
+
useWalletCredits: payload.use_wallet_credits
|
|
2128
|
+
});
|
|
2129
|
+
if (generated.billing) {
|
|
2130
|
+
await ctx.billing.record({
|
|
2131
|
+
type: "ai_generation",
|
|
2132
|
+
costUsd: generated.billing.costUsd,
|
|
2133
|
+
costCenterSlug: generated.billing.costCenterSlug,
|
|
2134
|
+
metadata: { ...generated.billing.metadata, primitive: "music", prompt: payload.prompt ?? null }
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
const stored = await ctx.storage.putBuffer("music.mp3", generated.bytes, {
|
|
2138
|
+
contentType: generated.contentType,
|
|
2139
|
+
kind: "audio",
|
|
2140
|
+
metadata: {
|
|
2141
|
+
primitive: "music",
|
|
2142
|
+
provider: "elevenlabs",
|
|
2143
|
+
music_length_ms: generated.musicLengthMs,
|
|
2144
|
+
wallet_billed: Boolean(generated.billing)
|
|
2145
|
+
}
|
|
2146
|
+
});
|
|
2147
|
+
ctx.logger.progress(1, "Music generation complete");
|
|
2148
|
+
return {
|
|
2149
|
+
progress: 1,
|
|
2150
|
+
output: {
|
|
2151
|
+
files: compactUrls([stored.url]),
|
|
2152
|
+
primary_file_url: stored.url,
|
|
2153
|
+
audioUrl: stored.url,
|
|
2154
|
+
audio: {
|
|
2155
|
+
file_url: stored.url,
|
|
2156
|
+
content_type: generated.contentType,
|
|
2157
|
+
provider: "elevenlabs",
|
|
2158
|
+
music_length_ms: generated.musicLengthMs,
|
|
2159
|
+
prompt: payload.prompt ?? null,
|
|
2160
|
+
wallet_billed: Boolean(generated.billing)
|
|
2052
2161
|
}
|
|
2053
2162
|
}
|
|
2054
2163
|
};
|
|
@@ -2122,18 +2231,29 @@ const sttPrimitive = definePrimitive({
|
|
|
2122
2231
|
const payload = sttPayloadSchema.parse(input);
|
|
2123
2232
|
const { audioBytes, audioContentType, extractionMetadata } = await loadSpeechAudioForPrimitive(ctx, payload);
|
|
2124
2233
|
ctx.logger.progress(0.45, "Transcribing speech");
|
|
2125
|
-
const
|
|
2126
|
-
const
|
|
2234
|
+
const requestedProvider = payload.provider ?? (payload.word_timestamps ? "openai" : "gemini");
|
|
2235
|
+
const requestedModel = payload.model ?? defaultSpeechModelFor("stt", requestedProvider) ?? "";
|
|
2127
2236
|
const transcription = await ctx.providers.transcribeSpeech({
|
|
2128
|
-
provider,
|
|
2129
|
-
model,
|
|
2237
|
+
provider: requestedProvider,
|
|
2238
|
+
model: requestedModel,
|
|
2130
2239
|
audio: audioBytes,
|
|
2131
2240
|
contentType: audioContentType,
|
|
2132
2241
|
prompt: payload.prompt,
|
|
2133
2242
|
language: payload.language,
|
|
2134
2243
|
diarize: payload.diarize,
|
|
2135
|
-
wordTimestamps: payload.word_timestamps
|
|
2136
|
-
|
|
2244
|
+
wordTimestamps: payload.word_timestamps,
|
|
2245
|
+
useWalletCredits: payload.use_wallet_credits
|
|
2246
|
+
});
|
|
2247
|
+
const provider = transcription.effectiveProvider;
|
|
2248
|
+
const model = transcription.effectiveModel;
|
|
2249
|
+
if (transcription.billing) {
|
|
2250
|
+
await ctx.billing.record({
|
|
2251
|
+
type: "ai_generation",
|
|
2252
|
+
costUsd: transcription.billing.costUsd,
|
|
2253
|
+
costCenterSlug: transcription.billing.costCenterSlug,
|
|
2254
|
+
metadata: { ...transcription.billing.metadata, primitive: "stt", source_url: payload.source_url }
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2137
2257
|
ctx.logger.progress(0.85, "Storing transcript artifacts");
|
|
2138
2258
|
const speakers = [...new Set(transcription.segments.map((segment) => segment.speaker))];
|
|
2139
2259
|
const srt = buildSrtFromSegments(transcription.segments);
|
|
@@ -3114,6 +3234,7 @@ class PrimitiveRegistry {
|
|
|
3114
3234
|
[audioNormalizePrimitive.id, audioNormalizePrimitive],
|
|
3115
3235
|
[ttsPrimitive.id, ttsPrimitive],
|
|
3116
3236
|
[sttPrimitive.id, sttPrimitive],
|
|
3237
|
+
[musicPrimitive.id, musicPrimitive],
|
|
3117
3238
|
[captionsPrimitive.id, captionsPrimitive],
|
|
3118
3239
|
[speechRegeneratePrimitive.id, speechRegeneratePrimitive],
|
|
3119
3240
|
[hyperframesRenderPrimitive.id, hyperframesRenderPrimitive],
|
|
@@ -3521,6 +3642,7 @@ function normalizeProductPlacementPayload(raw) {
|
|
|
3521
3642
|
};
|
|
3522
3643
|
}
|
|
3523
3644
|
function preferredTextProvider(availableProviders) {
|
|
3645
|
+
// Text-only priority — the audio-only elevenlabs provider is never a candidate.
|
|
3524
3646
|
const priority = ["openai", "gemini", "openrouter", "perplexity"];
|
|
3525
3647
|
return priority.find((provider) => availableProviders.includes(provider)) ?? "openai";
|
|
3526
3648
|
}
|
|
@@ -59,8 +59,8 @@ export function renderReskinChat(input = DEFAULT_INPUT, opts = {}) {
|
|
|
59
59
|
<select class="rk-chat-mode-select" id="rk-chat-mode">
|
|
60
60
|
<option value="/chat" selected>Chat</option>
|
|
61
61
|
<option value="/tools/image">Create Image</option>
|
|
62
|
-
<option value="/tools/clipper">Clip a Video</option>
|
|
63
62
|
<option value="/editor/original/fork/new">Create Video</option>
|
|
63
|
+
<option value="/tools/clipper">Clip Media</option>
|
|
64
64
|
</select>
|
|
65
65
|
<span class="rk-chat-mode-chev" aria-hidden="true">▾</span>
|
|
66
66
|
</label>
|