mulmocast 2.3.0 → 2.3.2
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/README.md +99 -43
- package/lib/actions/audio.js +9 -5
- package/lib/actions/bundle.js +2 -2
- package/lib/actions/image_agents.js +5 -3
- package/lib/cli/commands/audio/builder.d.ts +2 -0
- package/lib/cli/commands/bundle/builder.d.ts +2 -0
- package/lib/cli/commands/html/builder.d.ts +2 -0
- package/lib/cli/commands/image/builder.d.ts +2 -0
- package/lib/cli/commands/markdown/builder.d.ts +2 -0
- package/lib/cli/commands/movie/builder.d.ts +2 -0
- package/lib/cli/commands/pdf/builder.d.ts +2 -0
- package/lib/cli/commands/translate/builder.d.ts +2 -0
- package/lib/cli/common.d.ts +2 -0
- package/lib/cli/common.js +6 -0
- package/lib/cli/helpers.d.ts +1 -0
- package/lib/cli/helpers.js +7 -4
- package/lib/methods/mulmo_studio_context.js +3 -0
- package/lib/types/type.d.ts +2 -0
- package/lib/utils/file.d.ts +2 -0
- package/lib/utils/file.js +8 -4
- package/lib/utils/mulmo_config.d.ts +5 -5
- package/lib/utils/mulmo_config.js +7 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ Here is the "Hello World" in MulmoScript.
|
|
|
59
59
|
```JSON
|
|
60
60
|
{
|
|
61
61
|
"$mulmocast": {
|
|
62
|
-
"version": "1.
|
|
62
|
+
"version": "1.1"
|
|
63
63
|
},
|
|
64
64
|
"beats": [
|
|
65
65
|
{ "text": "Hello World" }
|
|
@@ -247,6 +247,8 @@ Top-level keys are applied as **defaults** (script values take precedence). Use
|
|
|
247
247
|
|
|
248
248
|
Priority chain: `config (defaults)` < `template/style` < `script` < `config.override` < `presentationStyle (-p)`
|
|
249
249
|
|
|
250
|
+
> **Note**: `kind: "path"` entries in `mulmo.config.json` are resolved relative to the **script file directory**, not the config file location. This is consistent with all other path resolution in MulmoScript.
|
|
251
|
+
|
|
250
252
|
Verify the merged result with:
|
|
251
253
|
```bash
|
|
252
254
|
mulmo tool info merged --script <script.json>
|
|
@@ -499,8 +501,6 @@ https://github.com/receptron/mulmocast-cli/tree/main/scripts
|
|
|
499
501
|
|
|
500
502
|
CLI Usage
|
|
501
503
|
|
|
502
|
-
|
|
503
|
-
|
|
504
504
|
```
|
|
505
505
|
mulmo <command> [options]
|
|
506
506
|
|
|
@@ -511,6 +511,7 @@ Commands:
|
|
|
511
511
|
mulmo movie <file> Generate movie file
|
|
512
512
|
mulmo pdf <file> Generate PDF files
|
|
513
513
|
mulmo markdown <file> Generate markdown files
|
|
514
|
+
mulmo bundle <file> Generate bundle files
|
|
514
515
|
mulmo html <file> Generate html files
|
|
515
516
|
mulmo tool <command> Generate Mulmo script and other tools
|
|
516
517
|
|
|
@@ -529,13 +530,19 @@ Positionals:
|
|
|
529
530
|
file Mulmo Script File [string] [required]
|
|
530
531
|
|
|
531
532
|
Options:
|
|
532
|
-
--version
|
|
533
|
-
-v, --verbose
|
|
534
|
-
-h, --help
|
|
535
|
-
-o, --outdir
|
|
536
|
-
-b, --basedir
|
|
537
|
-
-l, --lang
|
|
538
|
-
|
|
533
|
+
--version Show version number [boolean]
|
|
534
|
+
-v, --verbose verbose log [boolean] [required] [default: false]
|
|
535
|
+
-h, --help Show help [boolean]
|
|
536
|
+
-o, --outdir output dir [string]
|
|
537
|
+
-b, --basedir base dir [string]
|
|
538
|
+
-l, --lang target language
|
|
539
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
540
|
+
"pt", "ar", "hi"]
|
|
541
|
+
-f, --force Force regenerate [boolean] [default: false]
|
|
542
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
543
|
+
[boolean] [default: false]
|
|
544
|
+
--backup create backup media file [boolean] [default: false]
|
|
545
|
+
-p, --presentationStyle Presentation Style [string]
|
|
539
546
|
```
|
|
540
547
|
|
|
541
548
|
```
|
|
@@ -552,8 +559,13 @@ Options:
|
|
|
552
559
|
-h, --help Show help [boolean]
|
|
553
560
|
-o, --outdir output dir [string]
|
|
554
561
|
-b, --basedir base dir [string]
|
|
555
|
-
-l, --lang target language
|
|
562
|
+
-l, --lang target language
|
|
563
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
564
|
+
"pt", "ar", "hi"]
|
|
556
565
|
-f, --force Force regenerate [boolean] [default: false]
|
|
566
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
567
|
+
[boolean] [default: false]
|
|
568
|
+
--backup create backup media file [boolean] [default: false]
|
|
557
569
|
-p, --presentationStyle Presentation Style [string]
|
|
558
570
|
-a, --audiodir Audio output directory [string]
|
|
559
571
|
```
|
|
@@ -572,8 +584,13 @@ Options:
|
|
|
572
584
|
-h, --help Show help [boolean]
|
|
573
585
|
-o, --outdir output dir [string]
|
|
574
586
|
-b, --basedir base dir [string]
|
|
575
|
-
-l, --lang target language
|
|
587
|
+
-l, --lang target language
|
|
588
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
589
|
+
"pt", "ar", "hi"]
|
|
576
590
|
-f, --force Force regenerate [boolean] [default: false]
|
|
591
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
592
|
+
[boolean] [default: false]
|
|
593
|
+
--backup create backup media file [boolean] [default: false]
|
|
577
594
|
-p, --presentationStyle Presentation Style [string]
|
|
578
595
|
-i, --imagedir Image output directory [string]
|
|
579
596
|
```
|
|
@@ -592,12 +609,19 @@ Options:
|
|
|
592
609
|
-h, --help Show help [boolean]
|
|
593
610
|
-o, --outdir output dir [string]
|
|
594
611
|
-b, --basedir base dir [string]
|
|
595
|
-
-l, --lang target language
|
|
612
|
+
-l, --lang target language
|
|
613
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
614
|
+
"pt", "ar", "hi"]
|
|
596
615
|
-f, --force Force regenerate [boolean] [default: false]
|
|
616
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
617
|
+
[boolean] [default: false]
|
|
618
|
+
--backup create backup media file [boolean] [default: false]
|
|
597
619
|
-p, --presentationStyle Presentation Style [string]
|
|
598
620
|
-a, --audiodir Audio output directory [string]
|
|
599
621
|
-i, --imagedir Image output directory [string]
|
|
600
|
-
-c, --caption Video captions
|
|
622
|
+
-c, --caption Video captions
|
|
623
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
624
|
+
"pt", "ar", "hi"]
|
|
601
625
|
```
|
|
602
626
|
|
|
603
627
|
```
|
|
@@ -614,9 +638,13 @@ Options:
|
|
|
614
638
|
-h, --help Show help [boolean]
|
|
615
639
|
-o, --outdir output dir [string]
|
|
616
640
|
-b, --basedir base dir [string]
|
|
617
|
-
-l, --lang target language
|
|
641
|
+
-l, --lang target language
|
|
642
|
+
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
643
|
+
"pt", "ar", "hi"]
|
|
618
644
|
-f, --force Force regenerate [boolean] [default: false]
|
|
619
|
-
|
|
645
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
646
|
+
[boolean] [default: false]
|
|
647
|
+
--backup create backup media file [boolean] [default: false]
|
|
620
648
|
-p, --presentationStyle Presentation Style [string]
|
|
621
649
|
-i, --imagedir Image output directory [string]
|
|
622
650
|
--pdf_mode PDF mode
|
|
@@ -643,6 +671,9 @@ Options:
|
|
|
643
671
|
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
644
672
|
"pt", "ar", "hi"]
|
|
645
673
|
-f, --force Force regenerate [boolean] [default: false]
|
|
674
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
675
|
+
[boolean] [default: false]
|
|
676
|
+
--backup create backup media file [boolean] [default: false]
|
|
646
677
|
-p, --presentationStyle Presentation Style [string]
|
|
647
678
|
--image_width Image width (e.g., 400px, 50%, auto) [string]
|
|
648
679
|
```
|
|
@@ -665,6 +696,9 @@ Options:
|
|
|
665
696
|
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
666
697
|
"pt", "ar", "hi"]
|
|
667
698
|
-f, --force Force regenerate [boolean] [default: false]
|
|
699
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
700
|
+
[boolean] [default: false]
|
|
701
|
+
--backup create backup media file [boolean] [default: false]
|
|
668
702
|
-p, --presentationStyle Presentation Style [string]
|
|
669
703
|
--image_width Image width (e.g., 400px, 50%, auto) [string]
|
|
670
704
|
```
|
|
@@ -687,6 +721,8 @@ Options:
|
|
|
687
721
|
[string] [choices: "en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it",
|
|
688
722
|
"pt", "ar", "hi"]
|
|
689
723
|
-f, --force Force regenerate [boolean] [default: false]
|
|
724
|
+
-g, --grouped Output all files under output/<basename>/ directory
|
|
725
|
+
[boolean] [default: false]
|
|
690
726
|
--backup create backup media file [boolean] [default: false]
|
|
691
727
|
-p, --presentationStyle Presentation Style [string]
|
|
692
728
|
```
|
|
@@ -697,11 +733,16 @@ mulmo tool <command>
|
|
|
697
733
|
Generate Mulmo script and other tools
|
|
698
734
|
|
|
699
735
|
Commands:
|
|
700
|
-
mulmo tool scripting
|
|
701
|
-
mulmo tool
|
|
702
|
-
mulmo tool
|
|
703
|
-
mulmo tool
|
|
704
|
-
mulmo tool
|
|
736
|
+
mulmo tool scripting Generate mulmocast script
|
|
737
|
+
mulmo tool prompt Dump prompt from template
|
|
738
|
+
mulmo tool schema Dump mulmocast schema
|
|
739
|
+
mulmo tool story_to_script <file> Generate Mulmo script from story
|
|
740
|
+
mulmo tool whisper <file> Process file with whisper
|
|
741
|
+
mulmo tool complete <file> Complete MulmoScript with schema defaults
|
|
742
|
+
and optional style
|
|
743
|
+
mulmo tool info [category] Show available options (styles, bgm,
|
|
744
|
+
templates, voices, images, movies, llm,
|
|
745
|
+
themes, config, merged)
|
|
705
746
|
|
|
706
747
|
Options:
|
|
707
748
|
--version Show version number [boolean]
|
|
@@ -726,15 +767,15 @@ Options:
|
|
|
726
767
|
-i, --interactive Generate script in interactive mode with user prompts
|
|
727
768
|
[boolean]
|
|
728
769
|
-t, --template Template name to use
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
770
|
+
[string] [choices: "akira_comic", "ani", "business", "characters",
|
|
771
|
+
"children_book", "coding", "comic_strips", "documentary", "drslump_comic",
|
|
772
|
+
"ghibli_comic", "ghibli_comic_strips", "ghost_comic", "html", "image_prompt",
|
|
773
|
+
"leda", "onepiece_comic", "portrait_movie", "realistic_movie",
|
|
774
|
+
"sensei_and_taro", "shorts", "sifi_story", "trailer", "vision"]
|
|
734
775
|
-c, --cache cache dir [string]
|
|
735
776
|
-s, --script script filename [string] [default: "script"]
|
|
736
777
|
--llm llm
|
|
737
|
-
|
|
778
|
+
[string] [choices: "openai", "anthropic", "gemini", "groq", "mock"]
|
|
738
779
|
--llm_model llm model [string]
|
|
739
780
|
```
|
|
740
781
|
|
|
@@ -753,12 +794,15 @@ Options:
|
|
|
753
794
|
-o, --outdir output dir [string]
|
|
754
795
|
-b, --basedir base dir [string]
|
|
755
796
|
-t, --template Template name to use
|
|
756
|
-
|
|
757
|
-
|
|
797
|
+
[string] [choices: "akira_comic", "ani", "business", "characters",
|
|
798
|
+
"children_book", "coding", "comic_strips", "documentary", "drslump_comic",
|
|
799
|
+
"ghibli_comic", "ghibli_comic_strips", "ghost_comic", "html", "image_prompt",
|
|
800
|
+
"leda", "onepiece_comic", "portrait_movie", "realistic_movie",
|
|
801
|
+
"sensei_and_taro", "shorts", "sifi_story", "trailer", "vision"]
|
|
758
802
|
-s, --script script filename [string] [default: "script"]
|
|
759
803
|
--beats_per_scene beats per scene [number] [default: 3]
|
|
760
804
|
--llm llm
|
|
761
|
-
|
|
805
|
+
[string] [choices: "openai", "anthropic", "gemini", "groq", "mock"]
|
|
762
806
|
--llm_model llm model [string]
|
|
763
807
|
--mode story to script generation mode
|
|
764
808
|
[string] [choices: "step_wise", "one_step"] [default: "step_wise"]
|
|
@@ -774,8 +818,11 @@ Options:
|
|
|
774
818
|
-v, --verbose verbose log [boolean] [required] [default: false]
|
|
775
819
|
-h, --help Show help [boolean]
|
|
776
820
|
-t, --template Template name to use
|
|
777
|
-
|
|
778
|
-
|
|
821
|
+
[string] [choices: "akira_comic", "ani", "business", "characters",
|
|
822
|
+
"children_book", "coding", "comic_strips", "documentary", "drslump_comic",
|
|
823
|
+
"ghibli_comic", "ghibli_comic_strips", "ghost_comic", "html", "image_prompt",
|
|
824
|
+
"leda", "onepiece_comic", "portrait_movie", "realistic_movie",
|
|
825
|
+
"sensei_and_taro", "shorts", "sifi_story", "trailer", "vision"]
|
|
779
826
|
```
|
|
780
827
|
|
|
781
828
|
```
|
|
@@ -792,18 +839,23 @@ Options:
|
|
|
792
839
|
```
|
|
793
840
|
mulmo tool complete <file>
|
|
794
841
|
|
|
795
|
-
Complete
|
|
842
|
+
Complete MulmoScript with schema defaults and optional style
|
|
796
843
|
|
|
797
844
|
Positionals:
|
|
798
|
-
file Input beats file path (JSON)
|
|
845
|
+
file Input beats file path (JSON) [string] [required]
|
|
799
846
|
|
|
800
847
|
Options:
|
|
801
|
-
--version Show version number
|
|
802
|
-
-v, --verbose verbose log
|
|
803
|
-
-h, --help Show help
|
|
804
|
-
-o, --output Output file path (default: <file>_completed.json)
|
|
805
|
-
-t, --template Template name to apply
|
|
806
|
-
|
|
848
|
+
--version Show version number [boolean]
|
|
849
|
+
-v, --verbose verbose log [boolean] [required] [default: false]
|
|
850
|
+
-h, --help Show help [boolean]
|
|
851
|
+
-o, --output Output file path (default: <file>_completed.json) [string]
|
|
852
|
+
-t, --template Template name to apply
|
|
853
|
+
[string] [choices: "akira_comic", "ani", "business", "characters",
|
|
854
|
+
"children_book", "coding", "comic_strips", "documentary", "drslump_comic",
|
|
855
|
+
"ghibli_comic", "ghibli_comic_strips", "ghost_comic", "html", "image_prompt",
|
|
856
|
+
"leda", "onepiece_comic", "portrait_movie", "realistic_movie",
|
|
857
|
+
"sensei_and_taro", "shorts", "sifi_story", "trailer", "vision"]
|
|
858
|
+
-s, --style Style name or file path (.json) [string]
|
|
807
859
|
|
|
808
860
|
Examples:
|
|
809
861
|
# Complete minimal script with schema defaults
|
|
@@ -822,17 +874,21 @@ Examples:
|
|
|
822
874
|
```
|
|
823
875
|
mulmo tool info [category]
|
|
824
876
|
|
|
825
|
-
Show available options
|
|
877
|
+
Show available options (styles, bgm, templates, voices, images, movies, llm,
|
|
878
|
+
themes, config, merged)
|
|
826
879
|
|
|
827
880
|
Positionals:
|
|
828
881
|
category Category to show info for
|
|
829
|
-
|
|
882
|
+
[string] [choices: "styles", "bgm", "templates", "voices", "images", "movies",
|
|
883
|
+
"llm", "themes", "config", "merged"]
|
|
830
884
|
|
|
831
885
|
Options:
|
|
832
886
|
--version Show version number [boolean]
|
|
833
887
|
-v, --verbose verbose log [boolean] [required] [default: false]
|
|
834
888
|
-h, --help Show help [boolean]
|
|
835
|
-
-F, --format Output format
|
|
889
|
+
-F, --format Output format
|
|
890
|
+
[string] [choices: "text", "json", "yaml"] [default: "text"]
|
|
891
|
+
-S, --script Script file path (required for 'merged' category) [string]
|
|
836
892
|
|
|
837
893
|
Examples:
|
|
838
894
|
# Show all available categories
|
package/lib/actions/audio.js
CHANGED
|
@@ -5,7 +5,7 @@ import { fileWriteAgent } from "@graphai/vanilla_node_agents";
|
|
|
5
5
|
import { ttsOpenaiAgent, ttsGoogleAgent, ttsGeminiAgent, ttsElevenlabsAgent, ttsKotodamaAgent, addBGMAgent, combineAudioFilesAgent, mediaMockAgent, } from "../agents/index.js";
|
|
6
6
|
import { text2SpeechProviderSchema } from "../types/index.js";
|
|
7
7
|
import { fileCacheAgentFilter } from "../utils/filters.js";
|
|
8
|
-
import { getAudioArtifactFilePath, getAudioFilePath, getOutputStudioFilePath, resolveDirPath, defaultBGMPath, mkdir, writingMessage } from "../utils/file.js";
|
|
8
|
+
import { getAudioArtifactFilePath, getAudioFilePath, getGroupedAudioFilePath, getOutputStudioFilePath, resolveDirPath, defaultBGMPath, mkdir, writingMessage, } from "../utils/file.js";
|
|
9
9
|
import { localizedText, settings2GraphAIConfig } from "../utils/utils.js";
|
|
10
10
|
import { text2hash } from "../utils/utils_node.js";
|
|
11
11
|
import { provider2TTSAgent } from "../types/provider2agent.js";
|
|
@@ -43,7 +43,9 @@ export const getBeatAudioPathOrUrl = (text, context, beat, lang) => {
|
|
|
43
43
|
].join(":");
|
|
44
44
|
GraphAILogger.log(`getBeatAudioPathOrUrl [${hash_string}]`);
|
|
45
45
|
const audioFileName = `${context.studio.filename}_${text2hash(hash_string)}`;
|
|
46
|
-
const maybeAudioFile =
|
|
46
|
+
const maybeAudioFile = context.fileDirs.grouped
|
|
47
|
+
? getGroupedAudioFilePath(audioDirPath, audioFileName, lang)
|
|
48
|
+
: getAudioFilePath(audioDirPath, context.studio.filename, audioFileName, lang);
|
|
47
49
|
return getAudioPathOrUrl(context, beat, maybeAudioFile);
|
|
48
50
|
};
|
|
49
51
|
// for lipSync
|
|
@@ -241,7 +243,7 @@ export const generateBeatAudio = async (index, context, args) => {
|
|
|
241
243
|
const fileName = MulmoStudioContextMethods.getFileName(context);
|
|
242
244
|
const audioDirPath = MulmoStudioContextMethods.getAudioDirPath(context);
|
|
243
245
|
const outDirPath = MulmoStudioContextMethods.getOutDirPath(context);
|
|
244
|
-
const audioSegmentDirPath = resolveDirPath(audioDirPath, fileName);
|
|
246
|
+
const audioSegmentDirPath = context.fileDirs.grouped ? audioDirPath : resolveDirPath(audioDirPath, fileName);
|
|
245
247
|
mkdir(outDirPath);
|
|
246
248
|
mkdir(audioSegmentDirPath);
|
|
247
249
|
const config = settings2GraphAIConfig(settings);
|
|
@@ -279,8 +281,10 @@ export const audio = async (context, args) => {
|
|
|
279
281
|
const audioDirPath = MulmoStudioContextMethods.getAudioDirPath(context);
|
|
280
282
|
const outDirPath = MulmoStudioContextMethods.getOutDirPath(context);
|
|
281
283
|
const audioArtifactFilePath = getAudioArtifactFilePath(context);
|
|
282
|
-
const audioSegmentDirPath = resolveDirPath(audioDirPath, fileName);
|
|
283
|
-
const audioCombinedFilePath =
|
|
284
|
+
const audioSegmentDirPath = context.fileDirs.grouped ? audioDirPath : resolveDirPath(audioDirPath, fileName);
|
|
285
|
+
const audioCombinedFilePath = context.fileDirs.grouped
|
|
286
|
+
? getGroupedAudioFilePath(audioDirPath, fileName, context.lang)
|
|
287
|
+
: getAudioFilePath(audioDirPath, fileName, fileName, context.lang);
|
|
284
288
|
const outputStudioFilePath = getOutputStudioFilePath(outDirPath, fileName);
|
|
285
289
|
mkdir(outDirPath);
|
|
286
290
|
mkdir(audioSegmentDirPath);
|
package/lib/actions/bundle.js
CHANGED
|
@@ -61,8 +61,8 @@ export const mulmoViewerBundle = async (context, options = {}) => {
|
|
|
61
61
|
const baseDir = context.fileDirs.baseDirPath;
|
|
62
62
|
const filename = context.studio.filename;
|
|
63
63
|
mkdir(outDir);
|
|
64
|
-
// Bundle directory: output/<script_name>/
|
|
65
|
-
const bundleDir = path.resolve(outDir, filename);
|
|
64
|
+
// Bundle directory: when grouped, outDir is already output/<script_name>/
|
|
65
|
+
const bundleDir = context.fileDirs.grouped ? outDir : path.resolve(outDir, filename);
|
|
66
66
|
mkdir(bundleDir);
|
|
67
67
|
const zipper = skipZip ? undefined : new ZipBuilder(path.resolve(bundleDir, zipFileName));
|
|
68
68
|
// text
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
2
|
import { MulmoPresentationStyleMethods, MulmoStudioContextMethods, MulmoBeatMethods, MulmoMediaSourceMethods } from "../methods/index.js";
|
|
3
|
-
import { getBeatPngImagePath, getBeatMoviePaths, getAudioFilePath } from "../utils/file.js";
|
|
3
|
+
import { getBeatPngImagePath, getBeatMoviePaths, getAudioFilePath, getGroupedAudioFilePath } from "../utils/file.js";
|
|
4
4
|
import { imagePrompt, htmlImageSystemPrompt } from "../utils/prompt.js";
|
|
5
5
|
import { renderHTMLToImage } from "../utils/html_render.js";
|
|
6
6
|
import { beatId } from "../utils/utils.js";
|
|
@@ -54,8 +54,10 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
54
54
|
returnValue.bgmFile = MulmoMediaSourceMethods.resolve(context.studio.script.audioParams.bgm, context);
|
|
55
55
|
const folderName = MulmoStudioContextMethods.getFileName(context);
|
|
56
56
|
const audioDirPath = MulmoStudioContextMethods.getAudioDirPath(context);
|
|
57
|
-
const
|
|
58
|
-
returnValue.audioFile =
|
|
57
|
+
const trimmedName = `${beatId(beat.id, index)}_trimmed`;
|
|
58
|
+
returnValue.audioFile = context.fileDirs.grouped
|
|
59
|
+
? getGroupedAudioFilePath(audioDirPath, trimmedName)
|
|
60
|
+
: getAudioFilePath(audioDirPath, folderName, trimmedName);
|
|
59
61
|
}
|
|
60
62
|
else {
|
|
61
63
|
// Audio file will be set from the beat's audio file when available
|
package/lib/cli/common.d.ts
CHANGED
package/lib/cli/common.js
CHANGED
|
@@ -25,6 +25,12 @@ export const commonOptions = (yargs) => {
|
|
|
25
25
|
describe: "Force regenerate",
|
|
26
26
|
type: "boolean",
|
|
27
27
|
default: false,
|
|
28
|
+
})
|
|
29
|
+
.option("g", {
|
|
30
|
+
alias: "grouped",
|
|
31
|
+
describe: "Output all files under output/<basename>/ directory",
|
|
32
|
+
type: "boolean",
|
|
33
|
+
default: false,
|
|
28
34
|
})
|
|
29
35
|
.option("backup", {
|
|
30
36
|
describe: "create backup media file",
|
package/lib/cli/helpers.d.ts
CHANGED
|
@@ -10,5 +10,6 @@ export declare const getFileObject: (args: {
|
|
|
10
10
|
presentationStyle?: string;
|
|
11
11
|
file: string;
|
|
12
12
|
nodeModuleRootPath?: string;
|
|
13
|
+
grouped?: boolean;
|
|
13
14
|
}) => FileObject;
|
|
14
15
|
export declare const initializeContext: (argv: CliArgs<InitOptions>, raiseError?: boolean) => Promise<MulmoStudioContext | null>;
|
package/lib/cli/helpers.js
CHANGED
|
@@ -30,9 +30,9 @@ export const setGraphAILogger = (verbose, logValues) => {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
export const getFileObject = (args) => {
|
|
33
|
-
const { basedir, outdir, imagedir, audiodir, file, presentationStyle, nodeModuleRootPath } = args;
|
|
33
|
+
const { basedir, outdir, imagedir, audiodir, file, presentationStyle, nodeModuleRootPath, grouped = false } = args;
|
|
34
34
|
const baseDirPath = getBaseDirPath(basedir);
|
|
35
|
-
const
|
|
35
|
+
const baseOutDirPath = getFullPath(baseDirPath, outdir ?? outDirName);
|
|
36
36
|
const { fileOrUrl, fileName } = (() => {
|
|
37
37
|
if (file === "__clipboard") {
|
|
38
38
|
// We generate a new unique script file from clipboard text in the output directory
|
|
@@ -40,8 +40,8 @@ export const getFileObject = (args) => {
|
|
|
40
40
|
const clipboardText = clipboardy.readSync();
|
|
41
41
|
const json = JSON.parse(clipboardText);
|
|
42
42
|
const formattedText = JSON.stringify(json, null, 2);
|
|
43
|
-
const resolvedFilePath = resolveDirPath(
|
|
44
|
-
mkdir(
|
|
43
|
+
const resolvedFilePath = resolveDirPath(baseOutDirPath, `${generatedFileName}.json`);
|
|
44
|
+
mkdir(baseOutDirPath);
|
|
45
45
|
fs.writeFileSync(resolvedFilePath, formattedText, "utf8");
|
|
46
46
|
return { fileOrUrl: resolvedFilePath, fileName: generatedFileName };
|
|
47
47
|
}
|
|
@@ -49,6 +49,7 @@ export const getFileObject = (args) => {
|
|
|
49
49
|
const parsedFileName = path.parse(resolvedFileOrUrl).name;
|
|
50
50
|
return { fileOrUrl: resolvedFileOrUrl, fileName: parsedFileName };
|
|
51
51
|
})();
|
|
52
|
+
const outDirPath = grouped ? getFullPath(baseOutDirPath, fileName) : baseOutDirPath;
|
|
52
53
|
const isHttpPath = isHttp(fileOrUrl);
|
|
53
54
|
const mulmoFilePath = isHttpPath ? "" : getFullPath(baseDirPath, fileOrUrl);
|
|
54
55
|
const mulmoFileDirPath = path.dirname(isHttpPath ? baseDirPath : mulmoFilePath);
|
|
@@ -71,6 +72,7 @@ export const getFileObject = (args) => {
|
|
|
71
72
|
presentationStylePath,
|
|
72
73
|
fileName,
|
|
73
74
|
nodeModuleRootPath,
|
|
75
|
+
grouped,
|
|
74
76
|
};
|
|
75
77
|
};
|
|
76
78
|
export const initializeContext = async (argv, raiseError = false) => {
|
|
@@ -81,6 +83,7 @@ export const initializeContext = async (argv, raiseError = false) => {
|
|
|
81
83
|
audiodir: argv.a,
|
|
82
84
|
presentationStyle: argv.p,
|
|
83
85
|
file: argv.file ?? "",
|
|
86
|
+
grouped: Boolean(argv.g),
|
|
84
87
|
});
|
|
85
88
|
setGraphAILogger(Boolean(argv.v), { files });
|
|
86
89
|
return await initializeContextFromFiles(files, raiseError, Boolean(argv.f), Boolean(argv.backup), argv.c, argv.l);
|
|
@@ -44,6 +44,9 @@ export const MulmoStudioContextMethods = {
|
|
|
44
44
|
},
|
|
45
45
|
getImageProjectDirPath(context) {
|
|
46
46
|
const imageDirPath = MulmoStudioContextMethods.getImageDirPath(context);
|
|
47
|
+
if (context.fileDirs.grouped) {
|
|
48
|
+
return imageDirPath;
|
|
49
|
+
}
|
|
47
50
|
return `${imageDirPath}/${context.studio.filename}`;
|
|
48
51
|
},
|
|
49
52
|
getOutDirPath(context) {
|
package/lib/types/type.d.ts
CHANGED
|
@@ -128,6 +128,7 @@ export interface FileObject {
|
|
|
128
128
|
outputMultilingualFilePath: string;
|
|
129
129
|
presentationStylePath: string | undefined;
|
|
130
130
|
fileName: string;
|
|
131
|
+
grouped: boolean;
|
|
131
132
|
}
|
|
132
133
|
export type InitOptions = {
|
|
133
134
|
b?: string;
|
|
@@ -138,6 +139,7 @@ export type InitOptions = {
|
|
|
138
139
|
l?: string;
|
|
139
140
|
c?: string;
|
|
140
141
|
p?: string;
|
|
142
|
+
g?: boolean;
|
|
141
143
|
};
|
|
142
144
|
export type PublicAPIArgs = {
|
|
143
145
|
settings?: Record<string, string>;
|
package/lib/utils/file.d.ts
CHANGED
|
@@ -20,7 +20,9 @@ export declare const fetchMulmoScriptFile: (url: string) => Promise<{
|
|
|
20
20
|
export declare const getOutputStudioFilePath: (outDirPath: string, fileName: string) => string;
|
|
21
21
|
export declare const getOutputMultilingualFilePath: (outDirPath: string, fileName: string) => string;
|
|
22
22
|
export declare const resolveDirPath: (dirPath: string, studioFileName: string) => string;
|
|
23
|
+
export declare const formatAudioFileName: (name: string, lang?: string) => string;
|
|
23
24
|
export declare const getAudioFilePath: (audioDirPath: string, dirName: string, fileName: string, lang?: string) => string;
|
|
25
|
+
export declare const getGroupedAudioFilePath: (audioDirPath: string, fileName: string, lang?: string) => string;
|
|
24
26
|
export declare const getAudioArtifactFilePath: (context: MulmoStudioContext) => string;
|
|
25
27
|
export declare const getOutputVideoFilePath: (outDirPath: string, fileName: string, lang?: string, caption?: string) => string;
|
|
26
28
|
export declare const imageSuffix = "p";
|
package/lib/utils/file.js
CHANGED
|
@@ -68,11 +68,15 @@ export const resolveDirPath = (dirPath, studioFileName) => {
|
|
|
68
68
|
return path.resolve(dirPath, studioFileName);
|
|
69
69
|
};
|
|
70
70
|
// audio
|
|
71
|
+
export const formatAudioFileName = (name, lang) => {
|
|
72
|
+
const suffix = lang ? `_${lang}` : "";
|
|
73
|
+
return `${name}${suffix}.mp3`;
|
|
74
|
+
};
|
|
71
75
|
export const getAudioFilePath = (audioDirPath, dirName, fileName, lang) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return path.resolve(audioDirPath,
|
|
76
|
+
return path.resolve(audioDirPath, dirName, formatAudioFileName(fileName, lang));
|
|
77
|
+
};
|
|
78
|
+
export const getGroupedAudioFilePath = (audioDirPath, fileName, lang) => {
|
|
79
|
+
return path.resolve(audioDirPath, formatAudioFileName(fileName, lang));
|
|
76
80
|
};
|
|
77
81
|
export const getAudioArtifactFilePath = (context) => {
|
|
78
82
|
const suffix = context.lang ? `_${context.lang}` : "";
|
|
@@ -4,21 +4,21 @@ import { type PartialMulmoScript } from "../tools/complete_script.js";
|
|
|
4
4
|
* Returns the first found path, or null if not found.
|
|
5
5
|
*/
|
|
6
6
|
export declare const findConfigFile: (baseDirPath: string) => string | null;
|
|
7
|
-
/**
|
|
8
|
-
* Resolve all kind:"path" references in config relative to the config file directory.
|
|
9
|
-
*/
|
|
10
|
-
export declare const resolveConfigPaths: (config: PartialMulmoScript, configDirPath: string) => PartialMulmoScript;
|
|
11
7
|
export type MulmoConfigResult = {
|
|
12
8
|
defaults: PartialMulmoScript;
|
|
13
9
|
override: PartialMulmoScript | null;
|
|
14
10
|
};
|
|
15
11
|
/**
|
|
16
12
|
* Load mulmo.config.json from baseDirPath or home directory.
|
|
17
|
-
* Resolves kind:"path" entries relative to the config file location.
|
|
18
13
|
* Returns { defaults, override } or null if no config file is found.
|
|
19
14
|
*
|
|
20
15
|
* - defaults: applied as low-priority base (script wins)
|
|
21
16
|
* - override: applied after script merge (wins over script)
|
|
17
|
+
*
|
|
18
|
+
* Note: kind:"path" entries are NOT resolved here.
|
|
19
|
+
* They remain as relative paths and are resolved at runtime
|
|
20
|
+
* relative to the script file directory (mulmoFileDirPath),
|
|
21
|
+
* consistent with all other path resolution in MulmoScript.
|
|
22
22
|
*/
|
|
23
23
|
export declare const loadMulmoConfig: (baseDirPath: string) => MulmoConfigResult | null;
|
|
24
24
|
/**
|
|
@@ -3,7 +3,6 @@ import path from "path";
|
|
|
3
3
|
import os from "os";
|
|
4
4
|
import { GraphAILogger } from "graphai";
|
|
5
5
|
import { mergeScripts } from "../tools/complete_script.js";
|
|
6
|
-
import { getFullPath } from "./file.js";
|
|
7
6
|
const CONFIG_FILE_NAME = "mulmo.config.json";
|
|
8
7
|
/**
|
|
9
8
|
* Search for mulmo.config.json in CWD → ~ order.
|
|
@@ -18,48 +17,17 @@ export const findConfigFile = (baseDirPath) => {
|
|
|
18
17
|
}
|
|
19
18
|
return null;
|
|
20
19
|
};
|
|
21
|
-
/**
|
|
22
|
-
* Resolve kind:"path" entries in config to absolute paths relative to config file location.
|
|
23
|
-
*/
|
|
24
|
-
const resolveMediaSourcePath = (source, configDirPath) => {
|
|
25
|
-
if (source.kind === "path" && typeof source.path === "string") {
|
|
26
|
-
return { ...source, path: getFullPath(configDirPath, source.path) };
|
|
27
|
-
}
|
|
28
|
-
return source;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Immutably resolve a nested kind:"path" source at the given key path.
|
|
32
|
-
* e.g. ["audioParams", "bgm"] resolves config.audioParams.bgm
|
|
33
|
-
*/
|
|
34
|
-
const resolveNestedPath = (obj, keys, configDirPath) => {
|
|
35
|
-
const [head, ...tail] = keys;
|
|
36
|
-
const child = obj[head];
|
|
37
|
-
if (!child || typeof child !== "object") {
|
|
38
|
-
return obj;
|
|
39
|
-
}
|
|
40
|
-
const childObj = child;
|
|
41
|
-
const resolved = tail.length === 0 ? resolveMediaSourcePath(childObj, configDirPath) : resolveNestedPath(childObj, tail, configDirPath);
|
|
42
|
-
return resolved === child ? obj : { ...obj, [head]: resolved };
|
|
43
|
-
};
|
|
44
|
-
/** Key paths to kind:"path" sources that need resolution */
|
|
45
|
-
const MEDIA_SOURCE_PATHS = [
|
|
46
|
-
["audioParams", "bgm"],
|
|
47
|
-
["slideParams", "branding", "logo", "source"],
|
|
48
|
-
["slideParams", "branding", "backgroundImage", "source"],
|
|
49
|
-
];
|
|
50
|
-
/**
|
|
51
|
-
* Resolve all kind:"path" references in config relative to the config file directory.
|
|
52
|
-
*/
|
|
53
|
-
export const resolveConfigPaths = (config, configDirPath) => {
|
|
54
|
-
return MEDIA_SOURCE_PATHS.reduce((acc, keys) => resolveNestedPath(acc, keys, configDirPath), config);
|
|
55
|
-
};
|
|
56
20
|
/**
|
|
57
21
|
* Load mulmo.config.json from baseDirPath or home directory.
|
|
58
|
-
* Resolves kind:"path" entries relative to the config file location.
|
|
59
22
|
* Returns { defaults, override } or null if no config file is found.
|
|
60
23
|
*
|
|
61
24
|
* - defaults: applied as low-priority base (script wins)
|
|
62
25
|
* - override: applied after script merge (wins over script)
|
|
26
|
+
*
|
|
27
|
+
* Note: kind:"path" entries are NOT resolved here.
|
|
28
|
+
* They remain as relative paths and are resolved at runtime
|
|
29
|
+
* relative to the script file directory (mulmoFileDirPath),
|
|
30
|
+
* consistent with all other path resolution in MulmoScript.
|
|
63
31
|
*/
|
|
64
32
|
export const loadMulmoConfig = (baseDirPath) => {
|
|
65
33
|
const configPath = findConfigFile(baseDirPath);
|
|
@@ -69,10 +37,9 @@ export const loadMulmoConfig = (baseDirPath) => {
|
|
|
69
37
|
try {
|
|
70
38
|
const content = readFileSync(configPath, "utf-8");
|
|
71
39
|
const raw = JSON.parse(content);
|
|
72
|
-
const configDirPath = path.dirname(configPath);
|
|
73
40
|
const { override: rawOverride, ...rest } = raw;
|
|
74
|
-
const defaults =
|
|
75
|
-
const override = rawOverride ?
|
|
41
|
+
const defaults = rest;
|
|
42
|
+
const override = rawOverride ? rawOverride : null;
|
|
76
43
|
return { defaults, override };
|
|
77
44
|
}
|
|
78
45
|
catch (error) {
|