@vanillaskyai/video 0.6.0 → 0.7.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/CHANGELOG.md +157 -0
- package/PUBLIC-API.md +123 -9
- package/README.md +69 -115
- package/dist/{bg-confetti-SHQI7ATB.js → bg-confetti-HITCGLGD.js} +1 -1
- package/dist/{bg-emoji-XYFRA63Q.js → bg-emoji-VU4UZYZN.js} +1 -1
- package/dist/{bg-media-G4XOQEAS.js → bg-media-L34PDQXJ.js} +2 -2
- package/dist/{brand-message-XHONEDNW.js → brand-message-FUJ4STFO.js} +1 -1
- package/dist/{builtin-server-YHEZ2JRF.js → builtin-server-KB7FKF6A.js} +2 -2
- package/dist/{chart-bar-HRHD57ML.js → chart-bar-EGPHJATL.js} +2 -2
- package/dist/{chart-counter-KFPV44RB.js → chart-counter-5FFWQNZO.js} +2 -2
- package/dist/{chart-progress-ring-OR2VOIHV.js → chart-progress-ring-JMXQLMLC.js} +2 -2
- package/dist/{chunk-66MNRUCR.js → chunk-2PYO6VAC.js} +51 -3
- package/dist/{chunk-ZQQQAAKP.js → chunk-2XT4MZ76.js} +33 -2
- package/dist/{chunk-PNS52FL4.js → chunk-5SLENAJW.js} +19 -6
- package/dist/{chunk-EFL34TXF.js → chunk-AITKH6QT.js} +24 -8
- package/dist/{chunk-FZHMQFG3.js → chunk-GAIOHGNR.js} +5 -21
- package/dist/{chunk-3RV4YKB3.js → chunk-JNN3EYVP.js} +51 -26
- package/dist/{chunk-HVCPEAQF.js → chunk-LKBZX7GV.js} +9 -6
- package/dist/{chunk-G66Z5CWR.js → chunk-MMUXVA47.js} +27 -3
- package/dist/{chunk-7BGAU6C3.js → chunk-MXZSDGZQ.js} +2 -1
- package/dist/{chunk-3OU7HIEB.js → chunk-RE4IMWJR.js} +15 -31
- package/dist/{chunk-FVTMYS6U.js → chunk-RGF452LL.js} +1 -1
- package/dist/{chunk-34O5BY6X.js → chunk-RXHW4EP4.js} +21 -4
- package/dist/chunk-SKRGRKHY.js +142 -0
- package/dist/{chunk-TCEHK2BW.js → chunk-YJJC4N4D.js} +2 -1
- package/dist/cli.js +438 -30
- package/dist/{compose-video-JWIEDTEJ.js → compose-video-PD6LKKRK.js} +4 -4
- package/dist/{cta-logo-JJOGK4UC.js → cta-logo-YR7LYUR5.js} +1 -1
- package/dist/{cta-media-IQ3MAWEH.js → cta-media-DQJBKUO2.js} +2 -2
- package/dist/{events-BBP30j3c.d.ts → events-B6qS1Lsb.d.ts} +2 -2
- package/dist/{incoming-call-KXF67OT2.js → incoming-call-54KQ7O5A.js} +1 -1
- package/dist/index.d.ts +89 -3
- package/dist/index.js +12 -1
- package/dist/{infographic-before-after-2I7YZVDD.js → infographic-before-after-ROS52GOW.js} +1 -1
- package/dist/{infographic-feature-list-QDM4I7XE.js → infographic-feature-list-E7MGUDAN.js} +2 -2
- package/dist/{infographic-problem-solution-FMLJCPG6.js → infographic-problem-solution-3S6NGO5N.js} +2 -2
- package/dist/{infographic-stat-row-VFTIXS5F.js → infographic-stat-row-SYB7AB7H.js} +2 -2
- package/dist/{infographic-steps-L7W24Z35.js → infographic-steps-KCE6JWE2.js} +2 -2
- package/dist/{kit-DEG3fcaL.d.ts → kit-8g46H2RZ.d.ts} +1 -1
- package/dist/{prompt-input-F3CYU2XH.js → prompt-input-MHX4O42G.js} +1 -1
- package/dist/react.d.ts +194 -6
- package/dist/react.js +1849 -40
- package/dist/{reaction-2XCLOJOK.js → reaction-SLEGR3BE.js} +2 -2
- package/dist/server.d.ts +102 -4
- package/dist/server.js +972 -27
- package/dist/{showcase-code-KHSMIPG6.js → showcase-code-2HLAVKBM.js} +2 -2
- package/dist/{showcase-phone-M4CEANIW.js → showcase-phone-Q4HEO4LH.js} +2 -2
- package/dist/{showcase-terminal-26I3DYF7.js → showcase-terminal-DL45LUFG.js} +2 -2
- package/dist/{showcase-web-TY6CZV2Z.js → showcase-web-2QZO73FZ.js} +2 -2
- package/dist/{social-milestone-TF6MX7ET.js → social-milestone-GEFUQSBF.js} +1 -1
- package/dist/{social-notification-2RJDAZSF.js → social-notification-FJ5VHVLK.js} +1 -1
- package/dist/{social-review-stack-2ICKJQG7.js → social-review-stack-KXQCKFRP.js} +1 -1
- package/dist/{social-testimonial-X7OGADA3.js → social-testimonial-JZ7CHC7S.js} +1 -1
- package/dist/{social-tweet-TK7SH2F6.js → social-tweet-UCDXM25F.js} +1 -1
- package/dist/{system-prompt-4I6Z5HK3.js → system-prompt-RRXIWDDD.js} +5 -3
- package/dist/template-catalog.js +1 -1
- package/dist/templates.d.ts +3 -3
- package/dist/test.d.ts +2 -2
- package/dist/test.js +3 -3
- package/dist/{text-stream-J6EDDJP4.js → text-stream-FW4BLBAL.js} +2 -2
- package/dist/{types-BV9IqExh.d.ts → types-2wHBqtg8.d.ts} +29 -1
- package/dist/types-DrABlRa7.d.ts +46 -0
- package/docs/agent-integration.md +49 -25
- package/docs/architecture.md +37 -22
- package/docs/branding-and-personalization.md +1 -1
- package/docs/concepts.md +14 -0
- package/docs/custom-templates.md +9 -9
- package/docs/customization.md +41 -5
- package/docs/getting-started.md +81 -84
- package/docs/media-and-audio.md +103 -166
- package/docs/persistence.md +1 -1
- package/docs/production.md +81 -92
- package/docs/prompt-and-input.md +71 -161
- package/docs/provider-integration.md +91 -49
- package/docs/responsive-orientation.md +1 -1
- package/docs/security.md +6 -5
- package/docs/streaming-protocol.md +1 -1
- package/docs/testing.md +51 -43
- package/examples/custom-template/README.md +1 -1
- package/package.json +20 -22
- package/registry/items/barChart.json +6 -4
- package/registry/items/beforeAfter.json +1 -1
- package/registry/items/bigNumber.json +4 -3
- package/registry/items/brandMessage.json +3 -2
- package/registry/items/cardList.json +6 -4
- package/registry/items/codeEditor.json +4 -3
- package/registry/items/confetti.json +1 -1
- package/registry/items/ctaLogo.json +3 -2
- package/registry/items/ctaMedia.json +4 -3
- package/registry/items/emojiBurst.json +1 -1
- package/registry/items/incomingCall.json +3 -2
- package/registry/items/media.json +4 -3
- package/registry/items/milestone.json +3 -2
- package/registry/items/notification.json +3 -2
- package/registry/items/phoneMockup.json +4 -3
- package/registry/items/problemSolution.json +4 -3
- package/registry/items/progressRing.json +4 -3
- package/registry/items/promptInput.json +3 -2
- package/registry/items/reaction.json +2 -2
- package/registry/items/reviewStack.json +3 -2
- package/registry/items/steps.json +6 -4
- package/registry/items/terminal.json +5 -4
- package/registry/items/testimonial.json +3 -2
- package/registry/items/tripleStats.json +4 -3
- package/registry/items/tweet.json +3 -2
- package/registry/items/webMockup.json +4 -3
- package/starters/video-chat/.env.example +16 -0
- package/starters/video-chat/README.md +63 -0
- package/starters/video-chat/index.html +12 -0
- package/starters/video-chat/package.json +28 -0
- package/starters/video-chat/server.ts +152 -0
- package/starters/video-chat/src/main.tsx +8 -0
- package/starters/video-chat/stock.ts +139 -0
- package/starters/video-chat/tsconfig.json +20 -0
- package/starters/video-chat/vite.config.ts +81 -0
- package/styles/video-chat.css +713 -0
- package/docs/input-and-first-scene.md +0 -64
- package/docs/integrate-nextjs.md +0 -86
- package/docs/live-channels.md +0 -149
- package/docs/use-cases.md +0 -59
- package/examples/nextjs-quickstart/.env.example +0 -2
- package/examples/nextjs-quickstart/README.md +0 -27
- package/examples/nextjs-quickstart/next-env.d.ts +0 -4
- package/examples/nextjs-quickstart/package.json +0 -25
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +0 -33
- package/examples/nextjs-quickstart/src/app/layout.tsx +0 -5
- package/examples/nextjs-quickstart/src/app/page.tsx +0 -31
- package/examples/nextjs-quickstart/tsconfig.json +0 -26
- package/examples/server-integrations/README.md +0 -20
- package/examples/server-integrations/src/ai-sdk-media.ts +0 -90
package/docs/getting-started.md
CHANGED
|
@@ -1,109 +1,106 @@
|
|
|
1
|
-
[← Documentation home](../README.md) · [
|
|
1
|
+
[← Documentation home](../README.md) · [Next: Provider integration →](provider-integration.md)
|
|
2
2
|
|
|
3
3
|
# Getting started
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The fastest VanillaSky integration is the complete, general-purpose video chat.
|
|
6
|
+
It starts with packaged templates and browser voice, then turns on optional
|
|
7
|
+
speech, stock, transcription, and generated video when their server keys exist.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
npm install @vanillaskyai/video
|
|
9
|
-
```
|
|
9
|
+
## Create the app
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Create one authenticated server route with `createVideoHandler`. Connect the
|
|
17
|
-
app-owned model through `streamText`; keep credentials, authentication, rate
|
|
18
|
-
limits, and media policy on the server:
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
import { streamText } from "ai";
|
|
22
|
-
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
23
|
-
import { videoModel } from "@/lib/video-model";
|
|
24
|
-
|
|
25
|
-
const handle = createVideoHandler({
|
|
26
|
-
// Local development only. Replace with your session check before deploying.
|
|
27
|
-
authorize: (request) => {
|
|
28
|
-
if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
|
|
29
|
-
const hostname = new URL(request.url).hostname;
|
|
30
|
-
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
31
|
-
},
|
|
32
|
-
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
33
|
-
model: videoModel,
|
|
34
|
-
system: systemPrompt,
|
|
35
|
-
prompt: userPrompt,
|
|
36
|
-
abortSignal: signal,
|
|
37
|
-
}),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export const POST = handle;
|
|
41
|
-
export const OPTIONS = handle;
|
|
11
|
+
Start in an empty folder:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @vanillaskyai/video init
|
|
42
15
|
```
|
|
43
16
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
before deploying. For literal files and commands,
|
|
48
|
-
use the tested [`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
|
|
17
|
+
Init installs the exact SDK version that ran it, creates a small application
|
|
18
|
+
shell, and installs its provider dependencies.
|
|
19
|
+
It does not copy VanillaSky's template tree. The important generated files are:
|
|
49
20
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
21
|
+
| File | Your application owns |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `src/main.tsx` | The mount point for the SDK-owned chat |
|
|
24
|
+
| `server.ts` | Provider choices and callbacks |
|
|
25
|
+
| `stock.ts` | Optional stock search policy |
|
|
26
|
+
| `vite.config.ts` | Local UI and `/api/video-chat` endpoint |
|
|
27
|
+
| `.env.local` | Ignored server-only credentials |
|
|
53
28
|
|
|
54
|
-
|
|
29
|
+
The generated browser entry is intentionally tiny:
|
|
55
30
|
|
|
56
31
|
```tsx
|
|
57
|
-
|
|
32
|
+
import { StrictMode } from "react";
|
|
33
|
+
import { createRoot } from "react-dom/client";
|
|
34
|
+
import { VideoChat } from "@vanillaskyai/video/react";
|
|
35
|
+
import "@vanillaskyai/video/video-chat.css";
|
|
36
|
+
|
|
37
|
+
createRoot(document.getElementById("root")!).render(
|
|
38
|
+
<StrictMode><VideoChat /></StrictMode>,
|
|
39
|
+
);
|
|
40
|
+
```
|
|
58
41
|
|
|
59
|
-
|
|
42
|
+
The SDK owns the responsive interface, conversation state, suggestions, voice
|
|
43
|
+
input, narration pacing, streaming player, and packaged templates. Your shell
|
|
44
|
+
stays responsible for providers, keys, authorization, limits, storage,
|
|
45
|
+
branding, and product copy.
|
|
60
46
|
|
|
61
|
-
|
|
62
|
-
const video = useVideo();
|
|
47
|
+
## Add the one required key
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
<button onClick={() => { void video.generate({ input }); }}>Generate video</button>
|
|
66
|
-
{video.error && <p role="alert">Video generation failed.</p>}
|
|
67
|
-
<VideoPlayer {...video.playerProps} />
|
|
68
|
-
</>;
|
|
69
|
-
}
|
|
70
|
-
```
|
|
49
|
+
Add the text-provider key to the generated, ignored `.env.local`:
|
|
71
50
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
51
|
+
```dotenv
|
|
52
|
+
ANTHROPIC_API_KEY=
|
|
53
|
+
```
|
|
75
54
|
|
|
76
|
-
|
|
77
|
-
|
|
55
|
+
Fill the value locally; do not expose it through a client-prefixed environment
|
|
56
|
+
variable. Then inspect the setup without calling any provider:
|
|
78
57
|
|
|
79
|
-
```
|
|
80
|
-
|
|
58
|
+
```bash
|
|
59
|
+
npx vanillasky doctor
|
|
81
60
|
```
|
|
82
61
|
|
|
83
|
-
The
|
|
84
|
-
|
|
85
|
-
reactive UI.
|
|
62
|
+
The base experience reports `templates + browser voice`. A ready text key makes
|
|
63
|
+
the chat answer. Optional keys progressively add capabilities:
|
|
86
64
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
stops at a planner length limit includes a `plan_incomplete` warning because
|
|
91
|
-
requested scenes or the ending may be missing. Provider finish reasons and
|
|
92
|
-
content-filter details remain available to the server through the `onComplete`
|
|
93
|
-
summary; surface that server-owned state separately when completeness matters
|
|
94
|
-
to your product.
|
|
65
|
+
```dotenv
|
|
66
|
+
# Optional generated speech
|
|
67
|
+
XAI_API_KEY=
|
|
95
68
|
|
|
96
|
-
|
|
97
|
-
|
|
69
|
+
# Optional generated video and voice transcription
|
|
70
|
+
FAL_KEY=
|
|
98
71
|
|
|
99
|
-
|
|
100
|
-
|
|
72
|
+
# Optional stock media
|
|
73
|
+
PEXELS_API_KEY=
|
|
101
74
|
```
|
|
102
75
|
|
|
103
|
-
|
|
104
|
-
|
|
76
|
+
Doctor reports only key names and readiness, never values. Adding or removing
|
|
77
|
+
an optional key changes the available modes after a server restart; the client
|
|
78
|
+
does not need to change.
|
|
79
|
+
|
|
80
|
+
## Run and verify
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run dev
|
|
84
|
+
```
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
86
|
+
Open the reported localhost URL. Try one explanatory question and one unrelated
|
|
87
|
+
creative request in the same conversation. Confirm each response starts with a
|
|
88
|
+
spoken hook from the response stream before the full plan is complete, holds its opening until the first
|
|
89
|
+
scene is ready, reaches its final frame, and leaves the composer ready for
|
|
90
|
+
another turn. With stock media enabled, click a welcome or follow-up card and
|
|
91
|
+
confirm its footage carries directly into that opening. With generated video
|
|
92
|
+
enabled, confirm the only choices are Templates and Full AI video and that the
|
|
93
|
+
first generated shot continues the spoken hook without repeating it.
|
|
94
|
+
|
|
95
|
+
The generated local authorization accepts localhost only. Replace it with your
|
|
96
|
+
real session check, rate limits, and usage policy before deploying.
|
|
97
|
+
|
|
98
|
+
## Continue
|
|
99
|
+
|
|
100
|
+
- Change providers or add media capabilities in [Provider integration](provider-integration.md).
|
|
101
|
+
- Brand or reshape the default experience in [Customization](customization.md).
|
|
102
|
+
- Use a fully custom UI with the headless chat hook described in
|
|
103
|
+
[Provider integration](provider-integration.md#custom-interface).
|
|
104
|
+
- Copy and edit a visual only when needed in [Custom templates](custom-templates.md).
|
|
105
|
+
- Apply production authorization and key handling from
|
|
106
|
+
[Production](production.md) and [Security](security.md).
|
package/docs/media-and-audio.md
CHANGED
|
@@ -1,198 +1,135 @@
|
|
|
1
1
|
[← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Custom templates →](custom-templates.md)
|
|
2
2
|
|
|
3
|
-
# Media and
|
|
3
|
+
# Media, voice, and audio
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
VanillaSky keeps provider choice in the application. The SDK defines small
|
|
6
|
+
server callbacks, advertises only the capabilities you configure, and keeps
|
|
7
|
+
all credentials out of React and the browser bundle.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
If a product needs spoken audio, the application must create and synchronize
|
|
11
|
-
that experience outside this contract.
|
|
9
|
+
## Two visual modes
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
`suppliedMedia`, or configure the server-only `resolveMedia` callback for
|
|
15
|
-
planner-selected backgrounds. Preload the next scene's asset before it becomes
|
|
16
|
-
active. Keep provider credentials on the server.
|
|
11
|
+
The chat exposes only two clear choices:
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
require the completed video to use it.
|
|
13
|
+
| Mode | Visual source | Required callback |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `templates` | Trusted rendered templates, optionally with stock footage | none; `searchMedia` is optional |
|
|
16
|
+
| `full` | A generated clip for every visual beat | `generateVideo` |
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
defaults. Hosting and licensing are the application's responsibility.
|
|
18
|
+
Templates are always available and are the fast, inexpensive fallback. The
|
|
19
|
+
`full` mode is advertised only when `generateVideo` exists. VanillaSky does not
|
|
20
|
+
offer a mixed mode that generates only some scenes.
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`
|
|
22
|
+
## Stock media for templates
|
|
23
|
+
|
|
24
|
+
Add `searchMedia` when template answers, the welcome screen, and follow-up
|
|
25
|
+
cards should use approved photography or footage:
|
|
32
26
|
|
|
33
27
|
```ts
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
28
|
+
createVideoChatHandler({
|
|
29
|
+
authorize: verifySession,
|
|
30
|
+
streamText: planWithYourModel,
|
|
31
|
+
generateText: runSmallTextTask,
|
|
32
|
+
searchMedia: async (query, { purpose, orientation, signal }) => {
|
|
33
|
+
const asset = await searchApprovedCatalog({
|
|
34
|
+
query,
|
|
35
|
+
purpose,
|
|
36
|
+
orientation,
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
39
|
+
return asset
|
|
40
|
+
? { url: asset.url, type: asset.type, posterUrl: asset.posterUrl }
|
|
41
|
+
: null;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
39
44
|
```
|
|
40
45
|
|
|
41
|
-
The
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
to guarantee a silent video.
|
|
46
|
+
The planner emits a short semantic keyword, not a URL. The callback returns an
|
|
47
|
+
application-approved image or video URL, and the SDK validates it before it
|
|
48
|
+
reaches a scene. Return `null` when no licensed, safe, relevant asset exists;
|
|
49
|
+
the template falls back to its built-in treatment.
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
For Pexels, keep `PEXELS_API_KEY` on the server, enforce a deadline, filter for
|
|
52
|
+
orientation, and return only validated Pexels asset domains. Licensing,
|
|
53
|
+
attribution, caching, MIME checks, and byte limits remain application-owned.
|
|
50
54
|
|
|
51
|
-
##
|
|
55
|
+
## Full generated video
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
the
|
|
55
|
-
visible and let the player's start control provide that interaction:
|
|
57
|
+
Add `generateVideo` to enable full AI video. It receives the planned visual
|
|
58
|
+
subject plus the generated look so every clip can follow the same direction:
|
|
56
59
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
```ts
|
|
61
|
+
createVideoChatHandler({
|
|
62
|
+
authorize: verifySession,
|
|
63
|
+
streamText: planWithYourModel,
|
|
64
|
+
generateText: runSmallTextTask,
|
|
65
|
+
generateVideo: async (subject, {
|
|
66
|
+
generatedLook,
|
|
67
|
+
orientation,
|
|
68
|
+
requestId,
|
|
69
|
+
scene,
|
|
70
|
+
signal,
|
|
71
|
+
}) => {
|
|
72
|
+
const asset = await generateAndStore({
|
|
73
|
+
subject,
|
|
74
|
+
generatedLook,
|
|
75
|
+
orientation,
|
|
76
|
+
requestId,
|
|
77
|
+
sceneId: scene?.id,
|
|
78
|
+
signal,
|
|
79
|
+
maxRetries: 0,
|
|
80
|
+
});
|
|
81
|
+
return asset ? { url: asset.url, type: "video" } : null;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
62
84
|
```
|
|
63
85
|
|
|
64
|
-
The
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
not clicked yet, even when the complete generated video is already ready. The
|
|
70
|
-
server keeps the generated first scene on screen for at least three more
|
|
71
|
-
seconds when the duration budget permits. After that first successful sound start,
|
|
72
|
-
replacement streams on the same mounted player autoplay the same generation
|
|
73
|
-
intro with sound and fall back to a start control if the browser blocks them.
|
|
74
|
-
|
|
75
|
-
When `VideoInput.opening` is supplied, its asset-free gradient `media` scene
|
|
76
|
-
replaces the generic generation cover as soon as it is available. It remains
|
|
77
|
-
as the static start poster until the viewer clicks, then begins the actual
|
|
78
|
-
timeline with sound; there is no additional generic pre-roll before it.
|
|
79
|
-
|
|
80
|
-
Use `playbackMode="manual"` to require the button on every run,
|
|
81
|
-
`playbackMode="muted-autoplay"` for browser-safe muted autoplay, or
|
|
82
|
-
`playbackMode="autoplay-with-sound"` to try audible autoplay immediately. The
|
|
83
|
-
lower-level `autoPlay` and `startMuted` props remain available when no playback
|
|
84
|
-
mode is set. For a chat response that should try audible autoplay without the
|
|
85
|
-
SDK generation intro, pass `opening: false`, wait to mount the player until a
|
|
86
|
-
generated scene exists, and render it with `autoPlay` and `startMuted={false}`.
|
|
87
|
-
If the browser blocks the audible start, the player returns to the first frame
|
|
88
|
-
and exposes its sound-start control.
|
|
89
|
-
|
|
90
|
-
For a saved video rendered with `<VideoPlayer video={video} loop />`, the
|
|
91
|
-
soundtrack loops too. A track shorter than the visual timeline repeats without
|
|
92
|
-
a silent gap, and the player restarts it from the beginning when the visual
|
|
93
|
-
timeline wraps. This does not bypass browser autoplay policy: use muted autoplay
|
|
94
|
-
for unattended playback and let a viewer unmute, or start audible playback from
|
|
95
|
-
a user interaction.
|
|
96
|
-
|
|
97
|
-
## Mix native clip audio with a soundtrack
|
|
98
|
-
|
|
99
|
-
Some generated or supplied scene videos contain their own synchronized
|
|
100
|
-
dialogue, effects, or ambience. Opt into that embedded track at the player:
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
<VideoPlayer
|
|
104
|
-
video={video}
|
|
105
|
-
nativeMediaAudio={{ volume: 0.85 }}
|
|
106
|
-
playbackMode="muted-autoplay"
|
|
107
|
-
/>
|
|
108
|
-
```
|
|
86
|
+
The first streamed object reserves the first generated shot while the same
|
|
87
|
+
model call continues planning later scenes. Use `requestId` and `scene.id` as
|
|
88
|
+
an idempotency key, because generated clips are billable. Keep `maxRetries: 0`
|
|
89
|
+
inside provider calls, honour `signal`, and make retries an explicit product
|
|
90
|
+
decision with a known budget.
|
|
109
91
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`video.audio.volume`. The existing sound button is their shared master mute.
|
|
113
|
-
Incoming videos may preroll visually, but remain muted until their scene is
|
|
114
|
-
active. Native media audio is off by default so existing players keep their
|
|
115
|
-
current soundtrack-only behavior.
|
|
92
|
+
VanillaSky does not depend on a video model or storage service. The application
|
|
93
|
+
owns the provider key, model, spend, generated bytes, retention, and delivery.
|
|
116
94
|
|
|
117
|
-
##
|
|
95
|
+
## Voice and transcription
|
|
118
96
|
|
|
119
|
-
`
|
|
120
|
-
|
|
121
|
-
resolved alongside the query:
|
|
97
|
+
Without `generateSpeech`, `VideoChat` uses the browser voice. Add a speech
|
|
98
|
+
callback for a consistent generated voice:
|
|
122
99
|
|
|
123
100
|
```ts
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
return
|
|
127
|
-
}
|
|
101
|
+
generateSpeech: async ({ text, signal }) => {
|
|
102
|
+
const speech = await synthesize(text, { signal });
|
|
103
|
+
return { audio: speech.bytes, mediaType: speech.mediaType };
|
|
104
|
+
},
|
|
128
105
|
```
|
|
129
106
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
107
|
+
The SDK measures or estimates each line, keeps narration synchronized with the
|
|
108
|
+
picture, and prevents a new scene from replacing speech that is still playing.
|
|
109
|
+
If generated speech fails, the interface can fall back to browser speech.
|
|
133
110
|
|
|
134
|
-
|
|
135
|
-
is
|
|
136
|
-
|
|
137
|
-
model the AI SDK supports. Install the AI SDK and whichever provider you
|
|
138
|
-
chose — for example:
|
|
111
|
+
Add `transcribe` for server-side microphone transcription when browser speech
|
|
112
|
+
recognition is unavailable. Set `maxAudioBytes`, validate the media type, and
|
|
113
|
+
apply a provider deadline.
|
|
139
114
|
|
|
140
|
-
|
|
141
|
-
npm install ai @ai-sdk/fal
|
|
142
|
-
```
|
|
115
|
+
## Native clip audio and soundtrack
|
|
143
116
|
|
|
144
|
-
|
|
117
|
+
Generated clips can contain diegetic audio. The chat player keeps that audio
|
|
118
|
+
separate from narration and uses one master mute control. Avoid generated
|
|
119
|
+
voiceover or music inside clips so it does not compete with the answer voice.
|
|
145
120
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
121
|
+
A serialized `Video` can also contain an application-owned soundtrack for
|
|
122
|
+
replay or custom playback. Soundtrack files, licenses, beat markers, volume,
|
|
123
|
+
and fade-out remain host-owned; narration and speech synchronization remain
|
|
124
|
+
the chat layer's responsibility. Browser autoplay rules still require a viewer
|
|
125
|
+
interaction before audible playback on many devices.
|
|
151
126
|
|
|
152
|
-
|
|
153
|
-
and return `null` when it passes: a scene that falls back to the brand
|
|
154
|
-
gradient is better than one that never arrives.
|
|
127
|
+
## Safety rules
|
|
155
128
|
|
|
156
|
-
|
|
129
|
+
- Keep every provider key in server-only environment variables.
|
|
130
|
+
- Never let a planner return arbitrary final media URLs.
|
|
131
|
+
- Bound query length, response size, duration, concurrency, and generated spend.
|
|
132
|
+
- Preload the next asset and keep the current visual when media is late.
|
|
133
|
+
- Return a safe fallback instead of leaving the response waiting forever.
|
|
157
134
|
|
|
158
|
-
|
|
159
|
-
built-in planner may emit a bounded semantic query for later media-capable
|
|
160
|
-
scenes. The SDK calls the application-owned resolver on the server, replaces
|
|
161
|
-
the query with the approved URL, type, and optional poster, then validates the
|
|
162
|
-
scene before emitting it. Without the callback, media intent stays hidden from
|
|
163
|
-
the planner. The first accepted generated scene remains asset-free.
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
167
|
-
|
|
168
|
-
createVideoHandler({
|
|
169
|
-
authorize: checkSession,
|
|
170
|
-
streamText: planWithYourModel,
|
|
171
|
-
resolveMedia: async (query, { preferredType, signal }) => {
|
|
172
|
-
const asset = await searchYourApprovedCatalog({ query, preferredType, signal });
|
|
173
|
-
return asset
|
|
174
|
-
? { url: asset.url, type: asset.type, posterUrl: asset.posterUrl }
|
|
175
|
-
: null;
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The resolver query is 2–80 characters and at most eight words. Return `null`
|
|
181
|
-
when no licensed, safe, relevant asset exists; media-capable templates fall
|
|
182
|
-
back to the brand gradient when their schema permits it. The browser never
|
|
183
|
-
receives `mediaKeyword`, provider keys, or raw provider metadata.
|
|
184
|
-
|
|
185
|
-
`allowMediaUrl` is an authorization hook for applications with their own custom
|
|
186
|
-
stream adapter. It validates a final URL; it does not search for, fetch, or
|
|
187
|
-
resolve media. The default 0.1 path needs no callback because every planner URL
|
|
188
|
-
must already be present in `suppliedMedia`.
|
|
189
|
-
|
|
190
|
-
Do not expose provider keys to React or allow arbitrary planner URLs. Templates
|
|
191
|
-
describe visual building blocks; the application owns media retrieval,
|
|
192
|
-
caching, licensing, and delivery.
|
|
193
|
-
|
|
194
|
-
For Pexels, keep `PEXELS_API_KEY` on the server and implement `resolveMedia`
|
|
195
|
-
with the Pexels API. Return only validated `images.pexels.com` or
|
|
196
|
-
`videos.pexels.com` results. The application remains responsible for
|
|
197
|
-
attribution, search, orientation filtering, MIME checks, timeouts, caching,
|
|
198
|
-
and fallback behavior.
|
|
135
|
+
[← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Custom templates →](custom-templates.md)
|
package/docs/persistence.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[← Documentation home](../README.md) · [Previous: Core concepts](concepts.md) · [Next:
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Core concepts](concepts.md) · [Next: Streaming protocol →](streaming-protocol.md)
|
|
2
2
|
|
|
3
3
|
# Persistence and replay
|
|
4
4
|
|