Setting the file. One moment.
Media Record Test · Media Use · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Resolve
audio/scripts/lib/media-record.test.mjs
audio/scripts/lib/ media-record.test.mjs
JavaScript · 239 lines · 8 KB
7
import
{ agentWritePath, recordInManifest, voicePaths, writtenAssets }
from
"./media-record.mjs"
;
8
9 const noBgm = { bgm: null , bgmFields: { bgm_pending: false } };
10
11 function project ( t ) {
12 const dir = mkdtempSync ( join ( tmpdir (), "mu-record-" ));
13 t. after (() => rmSync (dir, { recursive: true , force: true }));
14 return dir;
15 }
16
17 test ( "a voice run leaves a manifest entry marked generated" , ( t ) => {
18 const dir = project (t);
19 const assets = writtenAssets ({
20 only: new Set ([ "tts" ]),
21 lines: [{ id: "01" , text: "Welcome to the launch" }],
22 voices: [{ id: "01" , path: "assets/voice/01.wav" , duration_s: 2.34 }],
23 ttsProvider: "kokoro" ,
24 sfx: [],
25 ... noBgm,
26 });
27
28 assert. deepEqual ( recordInManifest (dir, assets), []);
29
30 assert. deepEqual (
31 readManifest (dir). map (({ path , type , source , description , duration , provenance }) => ({
32 path,
33 type,
34 source,
35 description,
36 duration,
37 provenance,
38 })),
39 [
40 {
41 path: "assets/voice/01.wav" ,
42 type: "voice" ,
43 source: "generated" ,
44 description: "Welcome to the launch" ,
45 duration: 2.3 ,
46 provenance: { provider: "kokoro" , prompt: "Welcome to the launch" },
47 },
48 ],
49 );
50 });
51
52 const voiceLine = ( intent , duration ) => [
53 { path: "assets/voice/01.wav" , type: "voice" , source: "generated" , intent, duration },
54 ];
55
56 test ( "a rerun that writes the same take keeps one record, and a new length is a new take" , ( t ) => {
57 const dir = project (t);
58
59 recordInManifest (dir, voiceLine ( "Hello world" , 1.25 ));
60 recordInManifest (dir, voiceLine ( "Hello world" , 1.25 ));
61 assert. equal ( readManifest (dir). length , 1 );
62
63 recordInManifest (dir, voiceLine ( "Hello world" , 2 ));
64 assert. deepEqual (
65 readManifest (dir). map (({ duration }) => duration),
66 [ 1.3 , 2 ],
67 );
68 });
69
70 test ( "a rerun with new text records the new take, and the old text no longer finds the file" , ( t ) => {
71 const dir = project (t);
72
73 recordInManifest (dir, voiceLine ( "Hello world" , 1.25 ));
74 recordInManifest (dir, voiceLine ( "Welcome back to the show" , 3.5 ));
75
76 assert. deepEqual (
77 readManifest (dir). map (({ description , duration }) => [description, duration]),
78 [
79 [ "Hello world" , 1.3 ],
80 [ "Welcome back to the show" , 3.5 ],
81 ],
82 );
83 assert. equal ( findByPrompt (dir, "Hello world" , "voice" ), null );
84 const index = readFileSync ( join (dir, ".media/index.md" ), "utf8" );
85 assert. match (index, /Welcome back to the show/ );
86 assert. doesNotMatch (index, /Hello world/ );
87 assert. equal ( findByPrompt (dir, "Welcome back to the show" , "voice" )?.path, "assets/voice/01.wav" );
88 });
89
90 test ( "music and sound effects are marked by where they came from" , () => {
91 const assets = writtenAssets ({
92 only: new Set ([ "bgm" , "sfx" ]),
93 lines: [],
94 voices: [{ id: "01" , path: "assets/voice/01.wav" }],
95 bgm: { path: "assets/bgm/track.mp3" , query: "calm" },
96 bgmFields: { bgm_pending: false , bgm_mode: "retrieve" },
97 sfx: [
98 { file: "assets/sfx/whoosh.mp3" , name: "whoosh" , source: "local" },
99 { file: "assets/sfx/whoosh.mp3" , name: "whoosh" , source: "local" },
100 { file: "assets/sfx/glass.mp3" , name: "glass" , source: "openverse" },
101 ],
102 });
103
104 assert. deepEqual (
105 assets. map (({ path , source , provider }) => [path, source, provider]),
106 [
107 [ "assets/bgm/track.mp3" , "search" , undefined ],
108 [ "assets/sfx/whoosh.mp3" , "bundled" , "bundled.sfx" ],
109 [ "assets/sfx/glass.mp3" , "search" , "openverse.audio" ],
110 ],
111 );
112 });
113
114 test ( "a rerun of an Openverse sound effect writes over its own file, not a numbered copy" , ( t ) => {
115 const dir = project (t);
116 mkdirSync ( join (dir, "assets/sfx" ), { recursive: true });
117 const anomalies = [];
118 for ( let run = 0 ; run < 3 ; run ++ ) {
119 const file = agentWritePath (dir, "assets/sfx/door-knock.mp3" , { anomalies });
120 writeFileSync ( join (dir, file), "knock" );
121 const sfx = [{ file, name: "door knock" , source: "openverse" , duration_s: 1 }];
122 recordInManifest (
123 dir,
124 writtenAssets ({ only: new Set ([ "sfx" ]), lines: [], voices: [], sfx, ... noBgm }),
125 );
126 }
127
128 assert. deepEqual ( readdirSync ( join (dir, "assets/sfx" )), [ "door-knock.mp3" ]);
129 assert. deepEqual (anomalies, []);
130 assert. equal ( readManifest (dir). length , 1 );
131 });
132
133 test ( "music made locally is marked generated once it is ready, not while pending" , () => {
134 const written = ( bgm_pending ) =>
135 writtenAssets ({
136 only: new Set ([ "bgm" ]),
137 lines: [],
138 voices: [],
139 sfx: [],
140 bgm: { path: "assets/bgm/track.wav" },
141 bgmFields: { bgm_pending, bgm_mode: "detached-single" },
142 }). map (({ source }) => source);
143
144 assert. deepEqual ( written ( true ), []);
145 assert. deepEqual ( written ( false ), [ "generated" ]);
146 });
147
148 test ( "a file that cannot be recorded becomes an anomaly, not a failure" , ( t ) => {
149 const dir = project (t);
150 writeFileSync ( join (dir, ".media" ), "a file where the media folder should be" );
151
152 const anomalies = recordInManifest (dir, [
153 { path: "assets/voice/01.wav" , type: "voice" , source: "generated" },
154 ]);
155
156 assert. equal (anomalies. length , 1 );
157 assert. match (anomalies[ 0 ], / ^ assets \/ voice \/ 01 \. wav: not recorded in the media manifest/ );
158 });
159
160 test ( "the engine writes over only its own files, else the next free name" , ( t ) => {
161 const dir = project (t);
162 mkdirSync ( join (dir, "assets/sfx" ), { recursive: true });
163 const names = [ "mine" , "made" , "adopted" , "twice" , "twice-2" , "kept" , "kept-2" ];
164 for ( const name of names) writeFileSync ( join (dir, `assets/sfx/${ name }.mp3` ), name);
165 for ( const path of [ "assets/sfx/made.mp3" , "assets/sfx/kept-2.mp3" ])
166 recordInManifest (dir, [{ path, type: "sfx" , source: "search" }]);
167 recordInManifest (dir, [{ path: "assets/sfx/adopted.mp3" , type: "sfx" , source: "existing" }]);
168 const anomalies = [];
169 const at = ( name , reusable ) =>
170 agentWritePath (dir, `assets/sfx/${ name }.mp3` , { anomalies, reusable });
171
172 assert. equal ( at ( "new" ), "assets/sfx/new.mp3" );
173 assert. equal ( at ( "made" ), "assets/sfx/made.mp3" );
174 assert. equal (
175 at ( "mine" , ( path ) => path === "assets/sfx/mine.mp3" ),
176 "assets/sfx/mine.mp3" ,
177 );
178 assert. deepEqual (anomalies, []);
179 assert. equal ( at ( "mine" ), "assets/sfx/mine-2.mp3" );
180 assert. equal ( at ( "adopted" ), "assets/sfx/adopted-2.mp3" );
181 assert. equal ( at ( "twice" ), "assets/sfx/twice-3.mp3" );
182 assert. equal ( at ( "kept" ), "assets/sfx/kept-2.mp3" );
183 assert. equal (anomalies. length , 4 );
184 assert. match (
185 anomalies[ 0 ],
186 / ^ assets \/ sfx \/ mine \. mp3: kept, . * writing assets \/ sfx \/ mine-2 \. mp3 instead/ ,
187 );
188 });
189
190 test ( "two spoken lines never share a file when one's name is taken by the person" , ( t ) => {
191 const dir = project (t);
192 mkdirSync ( join (dir, "assets/voice" ), { recursive: true });
193 writeFileSync ( join (dir, "assets/voice/hook.wav" ), "the person's own hook" );
194 const anomalies = [];
195
196 const paths = voicePaths (
197 dir,
198 [
199 { id: "hook" , text: "First" },
200 { id: "hook-2" , text: "Second" },
201 { id: "blank" , text: " " },
202 ],
203 anomalies,
204 );
205
206 assert. deepEqual (Object. fromEntries (paths), {
207 hook: "assets/voice/hook-2.wav" ,
208 "hook-2" : "assets/voice/hook-2-2.wav" ,
209 });
210 assert. equal (anomalies. length , 2 );
211 });
212
213 test ( "a record never takes the id of a download still in flight" , ( t ) => {
214 const dir = project (t);
215 mkdirSync ( join (dir, ".media/audio/bgm" ), { recursive: true });
216 writeFileSync ( join (dir, ".media/audio/bgm/bgm_001.mp3" ), "" );
217
218 recordInManifest (dir, [{ path: "assets/bgm/track.wav" , type: "bgm" , source: "generated" }]);
219
220 assert. deepEqual (
221 readManifest (dir). map (({ id }) => id),
222 [ "bgm_002" ],
223 );
224 });
225
226 // An engine-written copy stays an agent source, or every rerun would write a numbered copy beside it
227 // (AGENT_SOURCES, manifest.mjs); what says it is the person's own voice is the provider and intent.
228 test ( "a recording line is recorded as your own recording, still an engine-written file" , () => {
229 const [ asset ] = writtenAssets ({
230 only: new Set ([ "tts" ]),
231 lines: [{ id: "01" , audio: "take.m4a" }],
232 voices: [{ id: "01" , path: "assets/voice/01.wav" , duration_s: 2 , source: "recording" }],
233 ttsProvider: "recording" ,
234 });
235
236 assert. equal (asset.provider, "recording" );
237 assert. equal (asset.intent, "your own recording" );
238 assert. ok ( AGENT_SOURCES . includes (asset.source));
239 });