mulmocast 2.17.0 → 2.19.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.
Files changed (62) hide show
  1. package/README.md +110 -38
  2. package/assets/schemas/mulmo_script.json +100 -0
  3. package/lib/actions/audio.js +4 -1
  4. package/lib/actions/avatar.js +4 -1
  5. package/lib/actions/captions.js +1 -1
  6. package/lib/actions/image_agents.d.ts +1 -0
  7. package/lib/actions/image_agents.js +2 -2
  8. package/lib/actions/images.d.ts +2 -0
  9. package/lib/actions/images.js +1 -0
  10. package/lib/actions/pdf.js +23 -2
  11. package/lib/agents/add_bgm_agent.d.ts +13 -1
  12. package/lib/agents/add_bgm_agent.js +77 -4
  13. package/lib/agents/movie_genai_agent.d.ts +24 -0
  14. package/lib/agents/movie_genai_agent.js +56 -43
  15. package/lib/cli/bin.js +0 -0
  16. package/lib/cli/commands/bundle/builder.d.ts +2 -0
  17. package/lib/cli/commands/html/builder.d.ts +2 -0
  18. package/lib/cli/commands/markdown/builder.d.ts +2 -0
  19. package/lib/cli/commands/viewer/builder.d.ts +2 -0
  20. package/lib/cli/common.d.ts +2 -0
  21. package/lib/cli/common.js +5 -0
  22. package/lib/cli/helpers.js +3 -1
  23. package/lib/mcp/server.js +0 -0
  24. package/lib/methods/mulmo_studio_context.d.ts +6 -0
  25. package/lib/methods/mulmo_studio_context.js +26 -0
  26. package/lib/types/provider2agent.d.ts +1 -4
  27. package/lib/types/provider2agent.js +27 -24
  28. package/lib/types/schema.d.ts +64 -0
  29. package/lib/types/schema.js +42 -0
  30. package/lib/types/type.d.ts +4 -1
  31. package/lib/utils/context.d.ts +32 -0
  32. package/lib/utils/estimate_usage.js +6 -5
  33. package/lib/utils/gemini_omni_video.d.ts +49 -0
  34. package/lib/utils/gemini_omni_video.js +36 -0
  35. package/lib/utils/html_render.d.ts +6 -5
  36. package/lib/utils/html_render.js +37 -30
  37. package/lib/utils/image_plugins/chart.js +2 -1
  38. package/lib/utils/image_plugins/html_tailwind.js +5 -4
  39. package/lib/utils/image_plugins/markdown.js +2 -1
  40. package/lib/utils/image_plugins/mermaid.js +2 -1
  41. package/lib/utils/image_plugins/slide.js +2 -1
  42. package/lib/utils/image_plugins/text_slide.js +2 -1
  43. package/lib/utils/image_plugins/vision.js +7 -0
  44. package/lib/utils/jinglescript.d.ts +12 -0
  45. package/lib/utils/jinglescript.js +63 -0
  46. package/lib/utils/render_network_guard.d.ts +2 -0
  47. package/lib/utils/render_network_guard.js +36 -0
  48. package/lib/utils/render_network_policy.d.ts +15 -0
  49. package/lib/utils/render_network_policy.js +85 -0
  50. package/package.json +5 -4
  51. package/scripts/test/test_genai.json +10 -9
  52. package/scripts/test/test_genai_movie.json +1 -1
  53. package/scripts/test/test_image_references.json +3 -2
  54. package/scripts/test/test_jinglescript_sound_effects.json +101 -0
  55. package/scripts/test/test_movie_references.json +2 -1
  56. package/scripts/test/test_sound_effects.json +64 -0
  57. package/scripts/test/test_sound_effects_animation.json +441 -0
  58. package/scripts/test/test_veo31_lite.json +3 -3
  59. package/scripts/test/test_captions_2.json~ +0 -278
  60. package/scripts/test/test_en_portrait.json~ +0 -31
  61. package/scripts/test/test_slide_image_ref_gemini_en.json~ +0 -289
  62. package/scripts/test/test_vertexai.json~ +0 -21
@@ -0,0 +1,441 @@
1
+ {
2
+ "$mulmocast": {
3
+ "version": "1.1"
4
+ },
5
+ "lang": "ja",
6
+ "title": "かぞえてみよう(効果音とアニメーションの同期サンプル)",
7
+ "description": "html_tailwind animations and beat sound effects (JingleScript scores and a URL) placed on the same times; no image generation.",
8
+ "audioParams": {
9
+ "bgmVolume": 0.04
10
+ },
11
+ "beats": [
12
+ {
13
+ "text": "こんにちは! きょうは、いっしょに かずを かぞえてみよう!",
14
+ "image": {
15
+ "type": "html_tailwind",
16
+ "html": [
17
+ "<div class='h-full w-full flex items-center justify-center bg-gradient-to-b from-sky-300 to-sky-100 overflow-hidden'>",
18
+ " <h1 id='title' class='text-8xl font-black text-orange-500 drop-shadow-lg' style='transform:translateY(-500px)'>かぞえてみよう!</h1>",
19
+ "</div>"
20
+ ],
21
+ "script": [
22
+ "const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
23
+ "function render(frame, totalFrames, fps) {",
24
+ " const t = frame / fps;",
25
+ " // Falls from 0.3 s and lands at 0.8 s (the 'hit' cue of the jingle), then bounces back from a squash.",
26
+ " const y = -500 * (1 - Easing.easeIn(seg(t, 0.3, 0.8)));",
27
+ " const scale = t < 0.8 ? 1 : 1.25 - 0.25 * Easing.easeOut(seg(t, 0.8, 1.1));",
28
+ " document.getElementById('title').style.transform = `translateY(${y}px) scale(${scale})`;",
29
+ "}"
30
+ ],
31
+ "animation": true
32
+ },
33
+ "soundEffects": [
34
+ {
35
+ "source": {
36
+ "kind": "jinglescript",
37
+ "score": {
38
+ "format": "jinglescript/1",
39
+ "title": "Title lands",
40
+ "tempo": 120,
41
+ "length": {
42
+ "seconds": 3
43
+ },
44
+ "cues": {
45
+ "hit": {
46
+ "seconds": 0.8
47
+ }
48
+ },
49
+ "tracks": [
50
+ {
51
+ "instrument": "whoosh",
52
+ "gain": -4,
53
+ "notes": [
54
+ {
55
+ "end": "hit",
56
+ "len": {
57
+ "seconds": 0.5
58
+ },
59
+ "variant": "fast"
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "instrument": "impact",
65
+ "gain": -6,
66
+ "notes": [
67
+ {
68
+ "at": "hit",
69
+ "variant": "soft"
70
+ }
71
+ ]
72
+ },
73
+ {
74
+ "instrument": "marimba",
75
+ "notes": [
76
+ {
77
+ "at": "hit",
78
+ "pitch": ["C5", "E5", "G5", "C6"],
79
+ "vel": 1.0
80
+ },
81
+ {
82
+ "at": "hit+1",
83
+ "pitch": "C6",
84
+ "vel": 0.35
85
+ }
86
+ ]
87
+ },
88
+ {
89
+ "instrument": "marimba",
90
+ "notes": [
91
+ {
92
+ "at": "hit",
93
+ "pitch": ["C3", "G3"],
94
+ "vel": 0.9
95
+ }
96
+ ]
97
+ }
98
+ ]
99
+ }
100
+ },
101
+ "volume": 0.8
102
+ }
103
+ ]
104
+ },
105
+ {
106
+ "text": "ほしが でてくるよ。ひとつ、ふたつ、みっつ! ぜんぶで みっつ だね。",
107
+ "image": {
108
+ "type": "html_tailwind",
109
+ "html": [
110
+ "<div class='h-full w-full flex flex-col items-center justify-center bg-indigo-950'>",
111
+ " <div class='flex gap-24'>",
112
+ " <div class='flex flex-col items-center'>",
113
+ " <div id='star0' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
114
+ " <div id='num0' class='text-7xl font-black text-white mt-6' style='opacity:0'>1</div>",
115
+ " </div>",
116
+ " <div class='flex flex-col items-center'>",
117
+ " <div id='star1' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
118
+ " <div id='num1' class='text-7xl font-black text-white mt-6' style='opacity:0'>2</div>",
119
+ " </div>",
120
+ " <div class='flex flex-col items-center'>",
121
+ " <div id='star2' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
122
+ " <div id='num2' class='text-7xl font-black text-white mt-6' style='opacity:0'>3</div>",
123
+ " </div>",
124
+ " </div>",
125
+ "</div>"
126
+ ],
127
+ "script": [
128
+ "const times = [0.8, 1.6, 2.4];",
129
+ "const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
130
+ "function render(frame, totalFrames, fps) {",
131
+ " const t = frame / fps;",
132
+ " times.forEach((at, i) => {",
133
+ " // Pops out at its time (overshooting to 1.25), then settles to 1.",
134
+ " const scale = t < at ? 0 : t < at + 0.12 ? 1.25 * Easing.easeOut(seg(t, at, at + 0.12)) : 1.25 - 0.25 * Easing.easeInOut(seg(t, at + 0.12, at + 0.3));",
135
+ " document.getElementById(`star${i}`).style.transform = `scale(${scale})`;",
136
+ " const shown = Easing.easeOut(seg(t, at, at + 0.25));",
137
+ " const num = document.getElementById(`num${i}`);",
138
+ " num.style.opacity = shown;",
139
+ " num.style.transform = `translateY(${20 * (1 - shown)}px)`;",
140
+ " });",
141
+ "}"
142
+ ],
143
+ "animation": true
144
+ },
145
+ "soundEffects": [
146
+ {
147
+ "source": {
148
+ "kind": "jinglescript",
149
+ "score": {
150
+ "format": "jinglescript/1",
151
+ "title": "Three pops",
152
+ "tempo": 120,
153
+ "length": {
154
+ "seconds": 3.2
155
+ },
156
+ "tracks": [
157
+ {
158
+ "instrument": "pop",
159
+ "reverb": false,
160
+ "notes": [
161
+ {
162
+ "at": {
163
+ "seconds": 0.8
164
+ },
165
+ "pitch": "E6",
166
+ "vel": 0.85
167
+ },
168
+ {
169
+ "at": {
170
+ "seconds": 1.6
171
+ },
172
+ "pitch": "G6",
173
+ "vel": 0.85
174
+ },
175
+ {
176
+ "at": {
177
+ "seconds": 2.4
178
+ },
179
+ "pitch": "C7",
180
+ "vel": 0.85
181
+ }
182
+ ]
183
+ }
184
+ ]
185
+ }
186
+ }
187
+ }
188
+ ]
189
+ },
190
+ {
191
+ "text": "ボールが はねるよ。ぽーん、ぽーん、ぽーん。なんかい はねたかな?",
192
+ "image": {
193
+ "type": "html_tailwind",
194
+ "html": [
195
+ "<div class='h-full w-full relative bg-gradient-to-b from-amber-50 to-amber-100 overflow-hidden'>",
196
+ " <div class='absolute left-0 right-0 bottom-0 h-24 bg-green-500'></div>",
197
+ " <div id='ball' class='absolute w-32 h-32 rounded-full bg-gradient-to-br from-rose-400 to-red-600 shadow-xl' style='left:calc(50% - 64px); bottom:96px'></div>",
198
+ "</div>"
199
+ ],
200
+ "script": [
201
+ "const landings = [0.6, 1.2, 1.8];",
202
+ "const floor = 96, peak = 520; // (not `top`: that is window.top)",
203
+ "function render(frame, totalFrames, fps) {",
204
+ " const t = frame / fps;",
205
+ " let h = 0;",
206
+ " if (t < landings[0]) {",
207
+ " const u = t / landings[0];",
208
+ " h = peak * (1 - u * u); // falling from the top",
209
+ " } else {",
210
+ " for (let k = 1; k < landings.length; k++) {",
211
+ " if (t < landings[k]) {",
212
+ " const u = (t - landings[k - 1]) / (landings[k] - landings[k - 1]);",
213
+ " h = peak * Math.pow(0.5, k) * 4 * u * (1 - u); // smaller arc after each bounce",
214
+ " break;",
215
+ " }",
216
+ " }",
217
+ " }",
218
+ " const ball = document.getElementById('ball');",
219
+ " ball.style.bottom = `${floor + h}px`;",
220
+ " // Squash for a moment on each landing.",
221
+ " const squash = landings.some((l) => t >= l && t < l + 0.08);",
222
+ " ball.style.transform = squash ? 'scale(1.25, 0.75) translateY(10px)' : 'none';",
223
+ "}"
224
+ ],
225
+ "animation": true
226
+ },
227
+ "soundEffects": [
228
+ {
229
+ "source": {
230
+ "kind": "url",
231
+ "url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
232
+ },
233
+ "startAt": 0.6,
234
+ "volume": 0.9
235
+ },
236
+ {
237
+ "source": {
238
+ "kind": "url",
239
+ "url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
240
+ },
241
+ "startAt": 1.2,
242
+ "volume": 0.7
243
+ },
244
+ {
245
+ "source": {
246
+ "kind": "url",
247
+ "url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
248
+ },
249
+ "startAt": 1.8,
250
+ "volume": 0.5
251
+ }
252
+ ]
253
+ },
254
+ {
255
+ "text": "とけいの はりが うごくよ。チク、タク、チク、タク……チーン! いっしゅう したね。",
256
+ "image": {
257
+ "type": "html_tailwind",
258
+ "html": [
259
+ "<div class='h-full w-full flex items-center justify-center bg-teal-100'>",
260
+ " <svg id='clock' viewBox='0 0 200 200' class='w-96 h-96'>",
261
+ " <circle cx='100' cy='100' r='92' fill='white' stroke='#0f766e' stroke-width='10'/>",
262
+ " <g id='marks'></g>",
263
+ " <line id='hand' x1='100' y1='100' x2='100' y2='30' stroke='#e11d48' stroke-width='8' stroke-linecap='round'/>",
264
+ " <circle cx='100' cy='100' r='8' fill='#0f766e'/>",
265
+ " </svg>",
266
+ "</div>"
267
+ ],
268
+ "script": [
269
+ "const marks = document.getElementById('marks');",
270
+ "for (let i = 0; i < 6; i++) {",
271
+ " const a = (i * 60 * Math.PI) / 180;",
272
+ " marks.innerHTML += `<circle cx='${100 + 74 * Math.sin(a)}' cy='${100 - 74 * Math.cos(a)}' r='6' fill='#14b8a6'/>`;",
273
+ "}",
274
+ "function render(frame, totalFrames, fps) {",
275
+ " const t = frame / fps;",
276
+ " // One step every 0.5 s: the tick at 0.5 ... 2.5 s, the full turn (and the chime) at 3.0 s.",
277
+ " const steps = Math.min(Math.floor(t / 0.5 + 1e-6), 6);",
278
+ " document.getElementById('hand').setAttribute('transform', `rotate(${steps * 60} 100 100)`);",
279
+ " const shaking = t >= 3.0 && t < 3.6;",
280
+ " const angle = shaking ? 8 * Math.sin((t - 3.0) * 40) * (1 - (t - 3.0) / 0.6) : 0;",
281
+ " document.getElementById('clock').style.transform = `rotate(${angle}deg)`;",
282
+ "}"
283
+ ],
284
+ "animation": true
285
+ },
286
+ "soundEffects": [
287
+ {
288
+ "source": {
289
+ "kind": "jinglescript",
290
+ "score": {
291
+ "format": "jinglescript/1",
292
+ "title": "Clock turn",
293
+ "tempo": 120,
294
+ "length": {
295
+ "seconds": 4.5
296
+ },
297
+ "cues": {
298
+ "first": {
299
+ "seconds": 0.5
300
+ },
301
+ "hit": {
302
+ "seconds": 3.0
303
+ }
304
+ },
305
+ "tracks": [
306
+ {
307
+ "instrument": "clock",
308
+ "reverb": false,
309
+ "notes": [
310
+ {
311
+ "at": "first",
312
+ "variant": ["tick", "tock"],
313
+ "vel": 0.8,
314
+ "repeat": {
315
+ "every": 1,
316
+ "count": 5
317
+ }
318
+ }
319
+ ]
320
+ },
321
+ {
322
+ "instrument": "glockenspiel",
323
+ "notes": [
324
+ {
325
+ "at": "hit",
326
+ "pitch": ["C6", "E6", "G6"],
327
+ "vel": 0.9
328
+ },
329
+ {
330
+ "at": "hit+1",
331
+ "pitch": "C7",
332
+ "vel": 0.4
333
+ }
334
+ ]
335
+ }
336
+ ]
337
+ }
338
+ }
339
+ }
340
+ ]
341
+ },
342
+ {
343
+ "text": "さいごの もんだい。ほしは いくつ でたかな? そう、みっつ! せいかい! よくできました!",
344
+ "image": {
345
+ "type": "html_tailwind",
346
+ "html": [
347
+ "<div class='h-full w-full flex items-center justify-center bg-yellow-50'>",
348
+ " <div id='stamp' class='w-[420px] h-[420px] rounded-full border-[28px] border-red-500 flex items-center justify-center' style='opacity:0'>",
349
+ " <span class='text-8xl font-black text-red-500'>せいかい</span>",
350
+ " </div>",
351
+ "</div>"
352
+ ],
353
+ "script": [
354
+ "const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
355
+ "function render(frame, totalFrames, fps) {",
356
+ " const t = frame / fps;",
357
+ " // Comes down from 0.7 s and hits the paper at 1.0 s, with a small rebound.",
358
+ " const down = Easing.easeIn(seg(t, 0.7, 1.0));",
359
+ " const rebound = t < 1.0 ? 0 : t < 1.08 ? 0.08 * seg(t, 1.0, 1.08) : 0.08 * (1 - Easing.easeInOut(seg(t, 1.08, 1.25)));",
360
+ " const stamp = document.getElementById('stamp');",
361
+ " stamp.style.opacity = seg(t, 0.7, 0.8);",
362
+ " stamp.style.transform = `scale(${3 - 2 * down + rebound}) rotate(${-25 + 17 * down}deg)`;",
363
+ "}"
364
+ ],
365
+ "animation": true
366
+ },
367
+ "soundEffects": [
368
+ {
369
+ "source": {
370
+ "kind": "jinglescript",
371
+ "score": {
372
+ "format": "jinglescript/1",
373
+ "title": "Correct!",
374
+ "tempo": 132,
375
+ "length": {
376
+ "seconds": 4
377
+ },
378
+ "cues": {
379
+ "hit": {
380
+ "seconds": 1.0
381
+ }
382
+ },
383
+ "tracks": [
384
+ {
385
+ "instrument": "impact",
386
+ "gain": -3,
387
+ "notes": [
388
+ {
389
+ "at": "hit",
390
+ "variant": "hard"
391
+ }
392
+ ]
393
+ },
394
+ {
395
+ "instrument": "marimba",
396
+ "notes": [
397
+ {
398
+ "at": "hit+1",
399
+ "pitch": "C5",
400
+ "vel": 0.8
401
+ },
402
+ {
403
+ "at": "hit+1.5",
404
+ "pitch": "E5",
405
+ "vel": 0.85
406
+ },
407
+ {
408
+ "at": "hit+2",
409
+ "pitch": "G5",
410
+ "vel": 0.9
411
+ },
412
+ {
413
+ "at": "hit+2.5",
414
+ "pitch": ["C5", "E5", "G5", "C6"],
415
+ "vel": 1.0
416
+ }
417
+ ]
418
+ },
419
+ {
420
+ "instrument": "clap",
421
+ "gain": -6,
422
+ "notes": [
423
+ {
424
+ "at": "hit+2.5",
425
+ "variant": "group",
426
+ "repeat": {
427
+ "every": 0.5,
428
+ "count": 6
429
+ }
430
+ }
431
+ ]
432
+ }
433
+ ]
434
+ }
435
+ },
436
+ "volume": 0.8
437
+ }
438
+ ]
439
+ }
440
+ ]
441
+ }
@@ -5,13 +5,13 @@
5
5
  "canvasSize": { "width": 1280, "height": 720 },
6
6
  "beats": [
7
7
  {
8
- "id": "google_genai_lite",
9
- "text": "Google GenAI veo-3.1-lite-generate-preview text-to-video",
8
+ "id": "google_genai_omni",
9
+ "text": "Google GenAI gemini-omni-1.1-flash text-to-video",
10
10
  "duration": 4,
11
11
  "moviePrompt": "A calm ocean wave rolling onto a sandy beach at golden hour",
12
12
  "movieParams": {
13
13
  "provider": "google",
14
- "model": "veo-3.1-lite-generate-preview"
14
+ "model": "gemini-omni-1.1-flash"
15
15
  }
16
16
  },
17
17
  {