Setting the file. One moment.
Media Record · Media Use · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Resolve
audio/scripts/lib/ media-record.mjs
JavaScript · 118 lines · 4 KB
export
function
agentWritePath
(
11 hyperframesDir ,
12 rel ,
13 { anomalies , taken = new Set (), reusable = () => false },
14 ) {
15 const free = ( path ) =>
16 ! taken. has (path) &&
17 ( ! existsSync ( join (hyperframesDir, path)) ||
18 reusable (path) ||
19 AGENT_SOURCES . includes ( latestRecordFor (hyperframesDir, path)?.source));
20 if ( free (rel)) return rel;
21 const ext = extname (rel);
22 const stem = rel. slice ( 0 , rel. length - ext. length );
23 let n = 2 ;
24 while ( ! free ( `${ stem }-${ n }${ ext }` )) n ++ ;
25 const path = `${ stem }-${ n }${ ext }` ;
26 const why = taken. has (rel)
27 ? "another file of this run goes there"
28 : "the file there is yours (the media manifest does not record it as made by the engine)" ;
29 const note = `${ rel }: kept, because ${ why }; writing ${ path } instead (audio_meta.json has the path used)` ;
30 if ( ! anomalies. includes (note)) anomalies. push (note);
31 return path;
32 }
33
34 /** Each spoken line's file, picked before lines synthesize concurrently so two never land on one free name. */
35 export function voicePaths ( hyperframesDir , lines , anomalies ) {
36 const taken = new Set ();
37 const paths = new Map ();
38 for ( const line of lines. filter (( l ) => String (l.text ?? "" ). trim () || l.audio)) {
39 const rel = agentWritePath (hyperframesDir, `assets/voice/${ line . id }.wav` , { anomalies, taken });
40 taken. add (rel);
41 paths. set ( String (line.id), rel);
42 }
43 return paths;
44 }
45
46 // A cue's source as the manifest records it, and who provided it. A source missing here records as the person's
47 // file, so every rerun would write a numbered copy beside it.
48 const SFX_SOURCES = {
49 openverse: { source: "search" , provider: "openverse.audio" },
50 local: { source: "bundled" , provider: "bundled.sfx" },
51 };
52
53 // The files one engine run wrote, each with how it was made, for the project's media manifest.
54 export function writtenAssets ({ only , lines , voices , ttsProvider , bgm , bgmFields , sfx }) {
55 const textById = new Map (lines. map (( line ) => [ String (line.id), String (line.text ?? "" ). trim ()]));
56 const assets = [];
57 if (only. has ( "tts" )) {
58 for ( const voice of voices) {
59 assets. push ({
60 path: voice.path,
61 type: "voice" ,
62 source: "generated" ,
63 intent: textById. get (voice.id) || "your own recording" ,
64 duration: voice.duration_s,
65 provider: voice.source ?? ttsProvider,
66 });
67 }
68 }
69 if (only. has ( "bgm" ) && bgm && ! bgmFields.bgm_pending) {
70 assets. push ({
71 path: bgm.path,
72 type: "bgm" ,
73 source: bgmFields.bgm_mode === "retrieve" ? "search" : "generated" ,
74 intent: bgm.query,
75 duration: bgm.duration_s,
76 provider: bgmFields.bgm_provider,
77 });
78 }
79 if (only. has ( "sfx" )) {
80 for ( const cue of new Map (sfx. map (( entry ) => [entry.file, entry])). values ()) {
81 const { source , provider } = SFX_SOURCES [cue.source];
82 assets. push ({
83 path: cue.file,
84 type: "sfx" ,
85 source,
86 intent: cue.name,
87 duration: cue.duration_s,
88 provider,
89 });
90 }
91 }
92 return assets;
93 }
94
95 /** Records each asset where it lies; returns one anomaly per file left unrecorded. */
96 export function recordInManifest ( hyperframesDir , assets ) {
97 const anomalies = [];
98 for ( const { path , type , source , intent , duration , provider } of assets) {
99 try {
100 recordInPlace (hyperframesDir, {
101 type,
102 path,
103 source,
104 description: intent,
105 duration,
106 provenance: { provider: provider || "local" , ... (intent && { prompt: intent }) },
107 });
108 } catch (error) {
109 anomalies. push ( `${ path }: not recorded in the media manifest (${ error . message })` );
110 }
111 }
112 try {
113 if (anomalies. length < assets. length ) regenerateIndex (hyperframesDir);
114 } catch (error) {
115 anomalies. push ( `.media/index.md: not refreshed (${ error . message })` );
116 }
117 return anomalies;
118 }