@@ -1680,7 +1680,7 @@ async function promptSelectFile(files, baseDir, prompt) {
16801680/**
16811681 * Generate script for a file
16821682 */
1683- async function generateScript ( filePath , scriptManifest , config ) {
1683+ async function generateScript ( filePath , scriptManifest , modifiedScriptKeys , config ) {
16841684 const relativePath = relative ( DOCS_DIR , filePath ) ;
16851685 const fileName = basename ( filePath , extname ( filePath ) ) ;
16861686
@@ -1761,14 +1761,15 @@ async function generateScript(filePath, scriptManifest, config) {
17611761 // Save script with frontmatter
17621762 const scriptInfo = saveScript ( dialog , filePath , outputPath , fileName ) ;
17631763
1764- // Update manifest
1764+ // Update manifest and track modified key
17651765 const scriptUrl = relative ( SCRIPT_OUTPUT_DIR , outputPath ) ;
17661766 scriptManifest [ relativePath ] = {
17671767 scriptPath : scriptUrl ,
17681768 size : scriptInfo . size ,
17691769 tokenCount : scriptInfo . tokenCount ,
17701770 generatedAt : new Date ( ) . toISOString ( ) ,
17711771 } ;
1772+ modifiedScriptKeys . add ( relativePath ) ;
17721773
17731774 console . log ( ` ✅ Saved: ${ scriptUrl } ` ) ;
17741775 console . log ( ` 📊 Token count: ${ scriptInfo . tokenCount } ` ) ;
@@ -1784,7 +1785,7 @@ async function generateScript(filePath, scriptManifest, config) {
17841785/**
17851786 * Generate audio from script
17861787 */
1787- async function generateAudioFromScript ( scriptPath , audioManifest , genAI ) {
1788+ async function generateAudioFromScript ( scriptPath , audioManifest , modifiedAudioKeys , genAI ) {
17881789 const relativePath = relative ( SCRIPT_OUTPUT_DIR , scriptPath ) ;
17891790 const fileName = basename ( scriptPath , extname ( scriptPath ) ) ;
17901791
@@ -1812,7 +1813,7 @@ async function generateAudioFromScript(scriptPath, audioManifest, genAI) {
18121813 // Convert WAV to MP3
18131814 const mp3Info = await convertWavToMp3 ( wavPath , mp3Path ) ;
18141815
1815- // Update manifest using the source doc path as key
1816+ // Update manifest using the source doc path as key and track modified key
18161817 const audioUrl = `/audio/${ join ( relativeDir , mp3FileName ) } ` ;
18171818 audioManifest [ frontmatter . source ] = {
18181819 audioUrl,
@@ -1823,6 +1824,7 @@ async function generateAudioFromScript(scriptPath, audioManifest, genAI) {
18231824 generatedAt : new Date ( ) . toISOString ( ) ,
18241825 scriptSource : relativePath ,
18251826 } ;
1827+ modifiedAudioKeys . add ( frontmatter . source ) ;
18261828
18271829 console . log ( ` ✅ Generated: ${ audioUrl } ` ) ;
18281830 console . log (
@@ -1843,7 +1845,9 @@ async function generateAudioFromScript(scriptPath, audioManifest, genAI) {
18431845async function processFile (
18441846 filePath ,
18451847 scriptManifest ,
1848+ modifiedScriptKeys ,
18461849 audioManifest ,
1850+ modifiedAudioKeys ,
18471851 config ,
18481852 genAI ,
18491853) {
@@ -1854,7 +1858,7 @@ async function processFile(
18541858
18551859 // Generate script
18561860 if ( shouldGenerateScript ) {
1857- scriptPath = await generateScript ( filePath , scriptManifest , config ) ;
1861+ scriptPath = await generateScript ( filePath , scriptManifest , modifiedScriptKeys , config ) ;
18581862
18591863 if ( ! scriptPath ) {
18601864 console . log ( ` ⚠️ Skipping audio generation - no script generated` ) ;
@@ -1875,7 +1879,7 @@ async function processFile(
18751879
18761880 // Generate audio
18771881 if ( shouldGenerateAudio ) {
1878- await generateAudioFromScript ( scriptPath , audioManifest , genAI ) ;
1882+ await generateAudioFromScript ( scriptPath , audioManifest , modifiedAudioKeys , genAI ) ;
18791883 }
18801884}
18811885
@@ -1939,13 +1943,15 @@ async function main() {
19391943 `\n📚 Found ${ sourceFiles . length } source file${ sourceFiles . length !== 1 ? "s" : "" } ` ,
19401944 ) ;
19411945
1942- // Load existing manifests
1946+ // Load existing manifests and track modified keys for merge-on-write
19431947 let scriptManifest = { } ;
1948+ const modifiedScriptKeys = new Set ( ) ;
19441949 if ( existsSync ( SCRIPT_MANIFEST_PATH ) ) {
19451950 scriptManifest = JSON . parse ( readFileSync ( SCRIPT_MANIFEST_PATH , "utf-8" ) ) ;
19461951 }
19471952
19481953 let audioManifest = { } ;
1954+ const modifiedAudioKeys = new Set ( ) ;
19491955 if ( existsSync ( AUDIO_MANIFEST_PATH ) ) {
19501956 audioManifest = JSON . parse ( readFileSync ( AUDIO_MANIFEST_PATH , "utf-8" ) ) ;
19511957 }
@@ -1993,10 +1999,10 @@ async function main() {
19931999 try {
19942000 if ( config . pipeline === "audio-only" ) {
19952001 // Audio-only: file is already a script path
1996- await generateAudioFromScript ( file , audioManifest , genAI ) ;
2002+ await generateAudioFromScript ( file , audioManifest , modifiedAudioKeys , genAI ) ;
19972003 } else {
19982004 // Script or both: file is a doc path
1999- await processFile ( file , scriptManifest , audioManifest , config , genAI ) ;
2005+ await processFile ( file , scriptManifest , modifiedScriptKeys , audioManifest , modifiedAudioKeys , config , genAI ) ;
20002006 }
20012007 successCount ++ ;
20022008 } catch ( error ) {
@@ -2005,20 +2011,34 @@ async function main() {
20052011 }
20062012 }
20072013
2008- // Save manifests
2014+ // Merge-on-write: re-read manifests and merge only our changes to avoid race conditions
20092015 if ( config . pipeline !== "audio-only" ) {
2016+ let freshScriptManifest = { } ;
2017+ if ( existsSync ( SCRIPT_MANIFEST_PATH ) ) {
2018+ freshScriptManifest = JSON . parse ( readFileSync ( SCRIPT_MANIFEST_PATH , "utf-8" ) ) ;
2019+ }
2020+ for ( const key of modifiedScriptKeys ) {
2021+ freshScriptManifest [ key ] = scriptManifest [ key ] ;
2022+ }
20102023 mkdirSync ( dirname ( SCRIPT_MANIFEST_PATH ) , { recursive : true } ) ;
20112024 writeFileSync (
20122025 SCRIPT_MANIFEST_PATH ,
2013- JSON . stringify ( scriptManifest , null , 2 ) + "\n" ,
2026+ JSON . stringify ( freshScriptManifest , null , 2 ) + "\n" ,
20142027 ) ;
20152028 }
20162029
20172030 if ( config . pipeline !== "script-only" ) {
2031+ let freshAudioManifest = { } ;
2032+ if ( existsSync ( AUDIO_MANIFEST_PATH ) ) {
2033+ freshAudioManifest = JSON . parse ( readFileSync ( AUDIO_MANIFEST_PATH , "utf-8" ) ) ;
2034+ }
2035+ for ( const key of modifiedAudioKeys ) {
2036+ freshAudioManifest [ key ] = audioManifest [ key ] ;
2037+ }
20182038 mkdirSync ( dirname ( AUDIO_MANIFEST_PATH ) , { recursive : true } ) ;
20192039 writeFileSync (
20202040 AUDIO_MANIFEST_PATH ,
2021- JSON . stringify ( audioManifest , null , 2 ) + "\n" ,
2041+ JSON . stringify ( freshAudioManifest , null , 2 ) + "\n" ,
20222042 ) ;
20232043 }
20242044
0 commit comments