Skip to content

Commit 8281cbd

Browse files
committed
Add merge-before-write to manifest updates for concurrent execution
1 parent 985684c commit 8281cbd

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-19
lines changed

scripts/generate-podcast.js

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {
18431845
async 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

scripts/generate-presentation.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,7 @@ function validateLearningObjectivesWordCount(presentation) {
14631463
/**
14641464
* Generate presentation for a file
14651465
*/
1466-
async function generatePresentation(filePath, manifest, config) {
1466+
async function generatePresentation(filePath, manifest, modifiedKeys, config) {
14671467
const relativePath = relative(DOCS_DIR, filePath);
14681468
const fileName = basename(filePath, extname(filePath));
14691469

@@ -1729,7 +1729,7 @@ async function generatePresentation(filePath, manifest, config) {
17291729
mkdirSync(dirname(staticPath), { recursive: true });
17301730
writeFileSync(staticPath, JSON.stringify(presentation, null, 2), "utf-8");
17311731

1732-
// Update manifest
1732+
// Update manifest and track modified key
17331733
const presentationUrl = `/presentations/${join(dirname(relativePath), outputFileName)}`;
17341734
manifest[relativePath] = {
17351735
presentationUrl,
@@ -1738,6 +1738,7 @@ async function generatePresentation(filePath, manifest, config) {
17381738
title: presentation.metadata.title,
17391739
generatedAt: new Date().toISOString(),
17401740
};
1741+
modifiedKeys.add(relativePath);
17411742

17421743
console.log(
17431744
` ${validationErrors.length > 0 ? "⚠️" : "✅"} Generated: ${presentationUrl}`,
@@ -1788,8 +1789,9 @@ async function main() {
17881789
`\n📚 Found ${sourceFiles.length} source file${sourceFiles.length !== 1 ? "s" : ""}`,
17891790
);
17901791

1791-
// Load existing manifest
1792+
// Load existing manifest and track modified keys for merge-on-write
17921793
let manifest = {};
1794+
const modifiedKeys = new Set();
17931795
if (existsSync(MANIFEST_PATH)) {
17941796
manifest = JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
17951797
}
@@ -1828,21 +1830,29 @@ async function main() {
18281830

18291831
for (const file of filesToProcess) {
18301832
try {
1831-
await generatePresentation(file, manifest, config);
1833+
await generatePresentation(file, manifest, modifiedKeys, config);
18321834
successCount++;
18331835
} catch (error) {
18341836
console.error(` ❌ Failed: ${error.message}`);
18351837
errorCount++;
18361838
}
18371839
}
18381840

1839-
// Save manifests
1841+
// Merge-on-write: re-read manifest and merge only our changes to avoid race conditions
1842+
let freshManifest = {};
1843+
if (existsSync(MANIFEST_PATH)) {
1844+
freshManifest = JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
1845+
}
1846+
for (const key of modifiedKeys) {
1847+
freshManifest[key] = manifest[key];
1848+
}
1849+
18401850
mkdirSync(dirname(MANIFEST_PATH), { recursive: true });
1841-
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + "\n");
1851+
writeFileSync(MANIFEST_PATH, JSON.stringify(freshManifest, null, 2) + "\n");
18421852

18431853
const staticManifestPath = join(STATIC_OUTPUT_DIR, "manifest.json");
18441854
mkdirSync(dirname(staticManifestPath), { recursive: true });
1845-
writeFileSync(staticManifestPath, JSON.stringify(manifest, null, 2) + "\n");
1855+
writeFileSync(staticManifestPath, JSON.stringify(freshManifest, null, 2) + "\n");
18461856

18471857
// Summary
18481858
console.log("\n" + "=".repeat(60));

0 commit comments

Comments
 (0)