@astro-minimax/cli 0.7.0 → 0.7.1
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/dist/commands/podcast.d.ts +2 -0
- package/dist/commands/podcast.d.ts.map +1 -0
- package/dist/commands/podcast.js +89 -0
- package/dist/commands/podcast.js.map +1 -0
- package/dist/tools/eval-ai-chat.js +34 -4
- package/dist/tools/eval-ai-chat.js.map +1 -1
- package/dist/tools/lib/audio-processor.d.ts +46 -0
- package/dist/tools/lib/audio-processor.d.ts.map +1 -0
- package/dist/tools/lib/audio-processor.js +188 -0
- package/dist/tools/lib/audio-processor.js.map +1 -0
- package/dist/tools/lib/script-generator.d.ts +61 -0
- package/dist/tools/lib/script-generator.d.ts.map +1 -0
- package/dist/tools/lib/script-generator.js +182 -0
- package/dist/tools/lib/script-generator.js.map +1 -0
- package/dist/tools/lib/tts-provider.d.ts +65 -0
- package/dist/tools/lib/tts-provider.d.ts.map +1 -0
- package/dist/tools/lib/tts-provider.js +116 -0
- package/dist/tools/lib/tts-provider.js.map +1 -0
- package/dist/tools/lib/types.d.ts +129 -0
- package/dist/tools/lib/types.d.ts.map +1 -0
- package/dist/tools/lib/types.js +64 -0
- package/dist/tools/lib/types.js.map +1 -0
- package/dist/tools/podcast-feed.d.ts +6 -0
- package/dist/tools/podcast-feed.d.ts.map +1 -0
- package/dist/tools/podcast-feed.js +121 -0
- package/dist/tools/podcast-feed.js.map +1 -0
- package/dist/tools/podcast-generate.d.ts +15 -0
- package/dist/tools/podcast-generate.d.ts.map +1 -0
- package/dist/tools/podcast-generate.js +318 -0
- package/dist/tools/podcast-generate.js.map +1 -0
- package/dist/tools/podcast-list.d.ts +6 -0
- package/dist/tools/podcast-list.d.ts.map +1 -0
- package/dist/tools/podcast-list.js +66 -0
- package/dist/tools/podcast-list.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Generate Podcast RSS Feed
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
const DATA_DIR = join(process.cwd(), "datas");
|
|
8
|
+
const PUBLIC_DIR = join(process.cwd(), "public");
|
|
9
|
+
const AUDIO_FILE = join(DATA_DIR, "podcast-audio.json");
|
|
10
|
+
const SCRIPTS_FILE = join(DATA_DIR, "podcast-scripts.json");
|
|
11
|
+
async function loadSiteConfig() {
|
|
12
|
+
try {
|
|
13
|
+
const configPath = join(process.cwd(), "src", "config.ts");
|
|
14
|
+
const raw = await readFile(configPath, "utf-8");
|
|
15
|
+
const websiteMatch = raw.match(/website:\s*["']([^"']+)["']/);
|
|
16
|
+
const titleMatch = raw.match(/title:\s*["']([^"']+)["']/);
|
|
17
|
+
const descMatch = raw.match(/desc:\s*["']([^"']+)["']/);
|
|
18
|
+
const authorMatch = raw.match(/author:\s*["']([^"']+)["']/);
|
|
19
|
+
return {
|
|
20
|
+
website: websiteMatch?.[1] || process.env.SITE_URL || "https://example.com",
|
|
21
|
+
title: titleMatch?.[1] || "Blog Podcast",
|
|
22
|
+
desc: descMatch?.[1] || "AI-generated podcast from blog posts",
|
|
23
|
+
author: authorMatch?.[1] || "Author",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {
|
|
28
|
+
website: process.env.SITE_URL || "https://example.com",
|
|
29
|
+
title: "Blog Podcast",
|
|
30
|
+
desc: "AI-generated podcast",
|
|
31
|
+
author: "Author",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function escapeXml(str) {
|
|
36
|
+
return str
|
|
37
|
+
.replace(/&/g, "&")
|
|
38
|
+
.replace(/</g, "<")
|
|
39
|
+
.replace(/>/g, ">")
|
|
40
|
+
.replace(/"/g, """)
|
|
41
|
+
.replace(/'/g, "'");
|
|
42
|
+
}
|
|
43
|
+
function generateRss(site, episodes) {
|
|
44
|
+
const items = episodes
|
|
45
|
+
.map((ep) => {
|
|
46
|
+
const audioUrl = `${site.website}/podcasts/${ep.lang}/${ep.slug.split("/")[1]}.mp3`;
|
|
47
|
+
return `
|
|
48
|
+
<item>
|
|
49
|
+
<title>${escapeXml(ep.title)}</title>
|
|
50
|
+
<description>AI-generated podcast episode from blog post: ${escapeXml(ep.title)}</description>
|
|
51
|
+
<enclosure url="${audioUrl}" length="${ep.fileSize}" type="audio/mpeg"/>
|
|
52
|
+
<guid isPermaLink="true">${audioUrl}</guid>
|
|
53
|
+
<pubDate>${new Date(ep.generatedAt).toUTCString()}</pubDate>
|
|
54
|
+
<itunes:duration>${Math.ceil(ep.duration)}</itunes:duration>
|
|
55
|
+
</item>`;
|
|
56
|
+
})
|
|
57
|
+
.join("");
|
|
58
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
+
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
60
|
+
<channel>
|
|
61
|
+
<title>${escapeXml(site.title)} Podcast</title>
|
|
62
|
+
<description>${escapeXml(site.desc)}</description>
|
|
63
|
+
<link>${site.website}</link>
|
|
64
|
+
<atom:link href="${site.website}/podcast.xml" rel="self" type="application/rss+xml"/>
|
|
65
|
+
<language>zh-cn</language>
|
|
66
|
+
<itunes:author>${escapeXml(site.author)}</itunes:author>
|
|
67
|
+
<itunes:summary>${escapeXml(site.desc)}</itunes:summary>
|
|
68
|
+
<itunes:category text="Technology"/>
|
|
69
|
+
<itunes:explicit>false</itunes:explicit>
|
|
70
|
+
${items}
|
|
71
|
+
</channel>
|
|
72
|
+
</rss>`;
|
|
73
|
+
}
|
|
74
|
+
async function main() {
|
|
75
|
+
const site = await loadSiteConfig();
|
|
76
|
+
let audioData = {
|
|
77
|
+
articles: {},
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
const raw = await readFile(AUDIO_FILE, "utf-8");
|
|
81
|
+
audioData = JSON.parse(raw);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
console.log("No audio data found. Run 'astro-minimax podcast generate' first.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let scriptsData = {
|
|
88
|
+
articles: {},
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const raw = await readFile(SCRIPTS_FILE, "utf-8");
|
|
92
|
+
scriptsData = JSON.parse(raw);
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
const episodes = [];
|
|
96
|
+
for (const [slug, meta] of Object.entries(audioData.articles)) {
|
|
97
|
+
const script = scriptsData.articles[slug];
|
|
98
|
+
episodes.push({
|
|
99
|
+
slug,
|
|
100
|
+
title: script?.title || slug.split("/")[1] || slug,
|
|
101
|
+
lang: meta.lang,
|
|
102
|
+
duration: meta.duration,
|
|
103
|
+
fileSize: meta.fileSize,
|
|
104
|
+
generatedAt: meta.generatedAt,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
episodes.sort((a, b) => new Date(b.generatedAt).getTime() - new Date(a.generatedAt).getTime());
|
|
108
|
+
if (episodes.length === 0) {
|
|
109
|
+
console.log("No episodes found.");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const rss = generateRss(site, episodes);
|
|
113
|
+
const outputPath = join(PUBLIC_DIR, "podcast.xml");
|
|
114
|
+
await writeFile(outputPath, rss, "utf-8");
|
|
115
|
+
console.log("Generated podcast RSS feed");
|
|
116
|
+
console.log(` Path: ${outputPath}`);
|
|
117
|
+
console.log(` Episodes: ${episodes.length}`);
|
|
118
|
+
console.log(` URL: ${site.website}/podcast.xml`);
|
|
119
|
+
}
|
|
120
|
+
main().catch(console.error);
|
|
121
|
+
//# sourceMappingURL=podcast-feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-feed.js","sourceRoot":"","sources":["../../src/tools/podcast-feed.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;AACxD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;AAS5D,KAAK,UAAU,cAAc;IAC3B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAEhD,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC9D,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACxD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAE5D,OAAO;YACL,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,qBAAqB;YAC3E,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,cAAc;YACxC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,sCAAsC;YAC9D,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ;SACrC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,qBAAqB;YACtD,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,sBAAsB;YAC5B,MAAM,EAAE,QAAQ;SACjB,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAWD,SAAS,WAAW,CAAC,IAAgB,EAAE,QAAmB;IACxD,MAAM,KAAK,GAAG,QAAQ;SACnB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACV,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,OAAO,aAAa,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACpF,OAAO;;eAEE,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC;kEACgC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC;wBAC7D,QAAQ,aAAa,EAAE,CAAC,QAAQ;iCACvB,QAAQ;iBACxB,IAAI,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE;yBAC9B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;YACnC,CAAC;IACT,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;aAGI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;mBACf,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3B,IAAI,CAAC,OAAO;uBACD,IAAI,CAAC,OAAO;;qBAEd,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;sBACrB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;;;EAGxC,KAAK;;OAEA,CAAC;AACR,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;IAEpC,IAAI,SAAS,GAAmD;QAC9D,QAAQ,EAAE,EAAE;KACb,CAAC;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;QAChF,OAAO;IACT,CAAC;IAED,IAAI,WAAW,GAAqD;QAClE,QAAQ,EAAE,EAAE;KACb,CAAC;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAClD,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI;YACJ,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI;YAClD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAChF,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACnD,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAE1C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,cAAc,CAAC,CAAC;AACpD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Podcast Generation Script
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm podcast:generate Generate all podcasts
|
|
7
|
+
* pnpm podcast:generate --slug=zh/xxx Single article
|
|
8
|
+
* pnpm podcast:generate --task=script Script only
|
|
9
|
+
* pnpm podcast:generate --task=audio Audio only
|
|
10
|
+
* pnpm podcast:generate --force Ignore cache
|
|
11
|
+
* pnpm podcast:generate --dry-run Preview only
|
|
12
|
+
* pnpm podcast:generate --lang=zh Specific language
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=podcast-generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-generate.d.ts","sourceRoot":"","sources":["../../src/tools/podcast-generate.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Podcast Generation Script
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm podcast:generate Generate all podcasts
|
|
7
|
+
* pnpm podcast:generate --slug=zh/xxx Single article
|
|
8
|
+
* pnpm podcast:generate --task=script Script only
|
|
9
|
+
* pnpm podcast:generate --task=audio Audio only
|
|
10
|
+
* pnpm podcast:generate --force Ignore cache
|
|
11
|
+
* pnpm podcast:generate --dry-run Preview only
|
|
12
|
+
* pnpm podcast:generate --lang=zh Specific language
|
|
13
|
+
*/
|
|
14
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import crypto from "node:crypto";
|
|
17
|
+
import { getAllPosts } from "./lib/posts.js";
|
|
18
|
+
import { extractFrontmatter } from "./lib/frontmatter.js";
|
|
19
|
+
import { hasAPIKey, getConfig } from "./lib/ai-provider.js";
|
|
20
|
+
import { hasTTSApiKey } from "./lib/tts-provider.js";
|
|
21
|
+
import { textToSpeech } from "./lib/tts-provider.js";
|
|
22
|
+
import { concatenateAudio, getAudioDuration } from "./lib/audio-processor.js";
|
|
23
|
+
import { generateScript } from "./lib/script-generator.js";
|
|
24
|
+
import { DEFAULT_SPEAKERS, isValidOpenAIVoice, } from "./lib/types.js";
|
|
25
|
+
const DATA_DIR = join(process.cwd(), "datas");
|
|
26
|
+
const PUBLIC_DIR = join(process.cwd(), "public", "podcasts");
|
|
27
|
+
const SCRIPTS_FILE = join(DATA_DIR, "podcast-scripts.json");
|
|
28
|
+
const AUDIO_FILE = join(DATA_DIR, "podcast-audio.json");
|
|
29
|
+
const SKIP_LIST_FILE = join(DATA_DIR, "ai-skip-list.json");
|
|
30
|
+
function parseArgs() {
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const flags = {
|
|
33
|
+
force: false,
|
|
34
|
+
slug: null,
|
|
35
|
+
task: null,
|
|
36
|
+
dryRun: false,
|
|
37
|
+
concurrency: 5,
|
|
38
|
+
lang: null,
|
|
39
|
+
};
|
|
40
|
+
for (const arg of args) {
|
|
41
|
+
if (arg === "--force")
|
|
42
|
+
flags.force = true;
|
|
43
|
+
else if (arg === "--dry-run")
|
|
44
|
+
flags.dryRun = true;
|
|
45
|
+
else if (arg.startsWith("--slug="))
|
|
46
|
+
flags.slug = arg.split("=")[1];
|
|
47
|
+
else if (arg.startsWith("--task="))
|
|
48
|
+
flags.task = arg.split("=")[1];
|
|
49
|
+
else if (arg.startsWith("--concurrency="))
|
|
50
|
+
flags.concurrency = parseInt(arg.split("=")[1], 10);
|
|
51
|
+
else if (arg.startsWith("--lang="))
|
|
52
|
+
flags.lang = arg.split("=")[1];
|
|
53
|
+
}
|
|
54
|
+
return flags;
|
|
55
|
+
}
|
|
56
|
+
function createCacheManager(cacheFile) {
|
|
57
|
+
let cache = null;
|
|
58
|
+
let writeQueue = Promise.resolve();
|
|
59
|
+
return {
|
|
60
|
+
async load() {
|
|
61
|
+
const cachePath = join(DATA_DIR, cacheFile);
|
|
62
|
+
try {
|
|
63
|
+
const raw = await readFile(cachePath, "utf-8");
|
|
64
|
+
cache = JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
cache = {
|
|
68
|
+
meta: { lastUpdated: null, totalProcessed: 0 },
|
|
69
|
+
articles: {},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return cache;
|
|
73
|
+
},
|
|
74
|
+
getCache() {
|
|
75
|
+
return cache;
|
|
76
|
+
},
|
|
77
|
+
async writeEntry(slug, entry, model) {
|
|
78
|
+
writeQueue = writeQueue.then(async () => {
|
|
79
|
+
if (!cache)
|
|
80
|
+
return;
|
|
81
|
+
cache.articles[slug] = entry;
|
|
82
|
+
cache.meta.lastUpdated = new Date().toISOString();
|
|
83
|
+
cache.meta.totalProcessed = Object.keys(cache.articles).length;
|
|
84
|
+
await mkdir(DATA_DIR, { recursive: true });
|
|
85
|
+
const cachePath = join(DATA_DIR, cacheFile);
|
|
86
|
+
await writeFile(cachePath, JSON.stringify(cache, null, 2), "utf-8");
|
|
87
|
+
});
|
|
88
|
+
return writeQueue;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function createAudioCacheManager() {
|
|
93
|
+
let cache = null;
|
|
94
|
+
let writeQueue = Promise.resolve();
|
|
95
|
+
return {
|
|
96
|
+
async load() {
|
|
97
|
+
try {
|
|
98
|
+
const raw = await readFile(AUDIO_FILE, "utf-8");
|
|
99
|
+
cache = JSON.parse(raw);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
cache = {
|
|
103
|
+
meta: { lastUpdated: null, totalProcessed: 0 },
|
|
104
|
+
articles: {},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return cache;
|
|
108
|
+
},
|
|
109
|
+
async writeEntry(slug, entry) {
|
|
110
|
+
writeQueue = writeQueue.then(async () => {
|
|
111
|
+
if (!cache)
|
|
112
|
+
return;
|
|
113
|
+
cache.articles[slug] = entry;
|
|
114
|
+
cache.meta.lastUpdated = new Date().toISOString();
|
|
115
|
+
cache.meta.totalProcessed = Object.keys(cache.articles).length;
|
|
116
|
+
await mkdir(DATA_DIR, { recursive: true });
|
|
117
|
+
await writeFile(AUDIO_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
118
|
+
});
|
|
119
|
+
return writeQueue;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async function loadSkipList() {
|
|
124
|
+
try {
|
|
125
|
+
const raw = await readFile(SKIP_LIST_FILE, "utf-8");
|
|
126
|
+
return JSON.parse(raw);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function saveSkipList(skipList) {
|
|
133
|
+
await mkdir(DATA_DIR, { recursive: true });
|
|
134
|
+
await writeFile(SKIP_LIST_FILE, JSON.stringify(skipList, null, 2), "utf-8");
|
|
135
|
+
}
|
|
136
|
+
function sleep(ms) {
|
|
137
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
138
|
+
}
|
|
139
|
+
async function scanArticles(flags) {
|
|
140
|
+
const allPosts = await getAllPosts({ includeDrafts: false, stripBody: false });
|
|
141
|
+
const articles = [];
|
|
142
|
+
for (const post of allPosts) {
|
|
143
|
+
if (post.draft)
|
|
144
|
+
continue;
|
|
145
|
+
if (flags.lang && post.lang !== flags.lang)
|
|
146
|
+
continue;
|
|
147
|
+
const rawContent = await readFile(post.filePath, "utf-8");
|
|
148
|
+
const fm = extractFrontmatter(rawContent);
|
|
149
|
+
const contentHash = crypto
|
|
150
|
+
.createHash("md5")
|
|
151
|
+
.update(fm.body)
|
|
152
|
+
.digest("hex")
|
|
153
|
+
.slice(0, 8);
|
|
154
|
+
const pubDatetime = fm.data.pubDatetime;
|
|
155
|
+
const pubTimestamp = pubDatetime
|
|
156
|
+
? new Date(pubDatetime).getTime()
|
|
157
|
+
: 0;
|
|
158
|
+
articles.push({
|
|
159
|
+
...post,
|
|
160
|
+
contentHash,
|
|
161
|
+
fullContent: fm.body,
|
|
162
|
+
pubTimestamp,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
articles.sort((a, b) => b.pubTimestamp - a.pubTimestamp);
|
|
166
|
+
return articles;
|
|
167
|
+
}
|
|
168
|
+
async function processArticle(article, flags, scriptCache, audioCache, skipList) {
|
|
169
|
+
const config = getConfig();
|
|
170
|
+
const lang = article.lang;
|
|
171
|
+
const speakers = DEFAULT_SPEAKERS[lang];
|
|
172
|
+
try {
|
|
173
|
+
let script = null;
|
|
174
|
+
if (flags.task !== "audio") {
|
|
175
|
+
script = await generateScript({
|
|
176
|
+
id: article.id,
|
|
177
|
+
title: article.title,
|
|
178
|
+
lang,
|
|
179
|
+
body: article.fullContent,
|
|
180
|
+
category: article.category || "",
|
|
181
|
+
tags: article.tags,
|
|
182
|
+
}, { speakers });
|
|
183
|
+
script.contentHash = article.contentHash;
|
|
184
|
+
if (!flags.dryRun) {
|
|
185
|
+
await scriptCache.writeEntry(article.id, script, config.model);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (flags.task !== "script") {
|
|
189
|
+
if (!script) {
|
|
190
|
+
const cache = scriptCache.getCache();
|
|
191
|
+
script = cache?.articles[article.id] || null;
|
|
192
|
+
}
|
|
193
|
+
if (!script) {
|
|
194
|
+
return { success: false, error: "No script available for audio generation" };
|
|
195
|
+
}
|
|
196
|
+
if (!hasTTSApiKey()) {
|
|
197
|
+
return { success: false, error: "TTS API key not configured" };
|
|
198
|
+
}
|
|
199
|
+
const audioSegments = [];
|
|
200
|
+
for (const segment of script.segments) {
|
|
201
|
+
const speaker = speakers.find((s) => s.id === segment.speaker) || speakers[0];
|
|
202
|
+
const voice = isValidOpenAIVoice(speaker.voiceId) ? speaker.voiceId : "alloy";
|
|
203
|
+
const audio = await textToSpeech(segment.text, { voice });
|
|
204
|
+
audioSegments.push(audio);
|
|
205
|
+
await sleep(200);
|
|
206
|
+
}
|
|
207
|
+
const finalAudio = await concatenateAudio(audioSegments);
|
|
208
|
+
const duration = await getAudioDuration(finalAudio);
|
|
209
|
+
const outputDir = join(PUBLIC_DIR, lang);
|
|
210
|
+
await mkdir(outputDir, { recursive: true });
|
|
211
|
+
const outputFile = join(outputDir, `${article.id.split("/")[1]}.mp3`);
|
|
212
|
+
await writeFile(outputFile, finalAudio);
|
|
213
|
+
const audioMeta = {
|
|
214
|
+
slug: article.id,
|
|
215
|
+
lang,
|
|
216
|
+
duration,
|
|
217
|
+
fileSize: finalAudio.length,
|
|
218
|
+
format: "mp3",
|
|
219
|
+
generatedAt: new Date().toISOString(),
|
|
220
|
+
contentHash: article.contentHash,
|
|
221
|
+
};
|
|
222
|
+
if (!flags.dryRun) {
|
|
223
|
+
await audioCache.writeEntry(article.id, audioMeta);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const skipKey = `podcast:${article.id}`;
|
|
227
|
+
if (skipList[skipKey]) {
|
|
228
|
+
delete skipList[skipKey];
|
|
229
|
+
await saveSkipList(skipList);
|
|
230
|
+
}
|
|
231
|
+
return { success: true };
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
const errorMsg = err.message;
|
|
235
|
+
await saveSkipList({
|
|
236
|
+
...skipList,
|
|
237
|
+
[`podcast:${article.id}`]: {
|
|
238
|
+
slug: article.id,
|
|
239
|
+
task: "podcast",
|
|
240
|
+
reason: errorMsg,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
return { success: false, error: errorMsg };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function main() {
|
|
247
|
+
const flags = parseArgs();
|
|
248
|
+
if (!hasAPIKey()) {
|
|
249
|
+
console.error("Missing AI API Key. Set AI_API_KEY environment variable.");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
const config = getConfig();
|
|
253
|
+
let skipList = await loadSkipList();
|
|
254
|
+
console.log("Podcast Generator (astro-minimax)");
|
|
255
|
+
console.log("─".repeat(50));
|
|
256
|
+
console.log(`Model: ${config.model}`);
|
|
257
|
+
console.log(`API: ${config.baseUrl}`);
|
|
258
|
+
console.log("");
|
|
259
|
+
console.log("Scanning articles...");
|
|
260
|
+
let articles = await scanArticles(flags);
|
|
261
|
+
console.log(`Found ${articles.length} articles`);
|
|
262
|
+
if (flags.slug) {
|
|
263
|
+
articles = articles.filter((a) => a.id === flags.slug);
|
|
264
|
+
if (articles.length === 0) {
|
|
265
|
+
console.error(`Article not found: ${flags.slug}`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
console.log(`Processing single article: ${flags.slug}`);
|
|
269
|
+
}
|
|
270
|
+
const scriptCache = createCacheManager("podcast-scripts.json");
|
|
271
|
+
await scriptCache.load();
|
|
272
|
+
const audioCache = createAudioCacheManager();
|
|
273
|
+
await audioCache.load();
|
|
274
|
+
const queue = [];
|
|
275
|
+
for (const article of articles) {
|
|
276
|
+
const skipKey = `podcast:${article.id}`;
|
|
277
|
+
if (!flags.force && skipList[skipKey]) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const cached = scriptCache.getCache()?.articles[article.id];
|
|
281
|
+
if (flags.force || !cached || cached.contentHash !== article.contentHash) {
|
|
282
|
+
queue.push(article);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
console.log(`Processing: ${queue.length} articles`);
|
|
286
|
+
if (flags.dryRun) {
|
|
287
|
+
console.log("\nDry run - would process:");
|
|
288
|
+
for (const a of queue) {
|
|
289
|
+
console.log(` - ${a.id}`);
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (queue.length === 0) {
|
|
294
|
+
console.log("Nothing to process");
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
let success = 0;
|
|
298
|
+
let failed = 0;
|
|
299
|
+
for (let i = 0; i < queue.length; i++) {
|
|
300
|
+
const article = queue[i];
|
|
301
|
+
console.log(`\n[${i + 1}/${queue.length}] Processing: ${article.id}`);
|
|
302
|
+
const result = await processArticle(article, flags, scriptCache, audioCache, skipList);
|
|
303
|
+
if (result.success) {
|
|
304
|
+
success++;
|
|
305
|
+
console.log(` Done`);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
failed++;
|
|
309
|
+
console.error(` Failed: ${result.error}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
console.log(`\nCompleted: ${success} success, ${failed} failed`);
|
|
313
|
+
}
|
|
314
|
+
main().catch((err) => {
|
|
315
|
+
console.error("Fatal error:", err.message);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
});
|
|
318
|
+
//# sourceMappingURL=podcast-generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-generate.js","sourceRoot":"","sources":["../../src/tools/podcast-generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-list.d.ts","sourceRoot":"","sources":["../../src/tools/podcast-list.ts"],"names":[],"mappings":";AACA;;GAEG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* List generated podcasts
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
const DATA_DIR = join(process.cwd(), "datas");
|
|
8
|
+
const SCRIPTS_FILE = join(DATA_DIR, "podcast-scripts.json");
|
|
9
|
+
const AUDIO_FILE = join(DATA_DIR, "podcast-audio.json");
|
|
10
|
+
async function main() {
|
|
11
|
+
console.log("Generated Podcasts");
|
|
12
|
+
console.log("─".repeat(60));
|
|
13
|
+
let scripts = {};
|
|
14
|
+
let audio = {};
|
|
15
|
+
try {
|
|
16
|
+
const scriptsData = await readFile(SCRIPTS_FILE, "utf-8");
|
|
17
|
+
scripts = JSON.parse(scriptsData).articles || {};
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
console.log("No scripts found. Run 'astro-minimax podcast generate' first.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const audioData = await readFile(AUDIO_FILE, "utf-8");
|
|
25
|
+
audio = JSON.parse(audioData).articles || {};
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
const slugs = Object.keys(scripts);
|
|
29
|
+
if (slugs.length === 0) {
|
|
30
|
+
console.log("No podcasts generated yet.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(`Total: ${slugs.length} podcasts\n`);
|
|
34
|
+
for (const slug of slugs.sort()) {
|
|
35
|
+
const script = scripts[slug];
|
|
36
|
+
const audioMeta = audio[slug] || {};
|
|
37
|
+
const title = script.title || slug;
|
|
38
|
+
const segments = script.segments?.length || 0;
|
|
39
|
+
const duration = audioMeta.duration
|
|
40
|
+
? formatDuration(audioMeta.duration)
|
|
41
|
+
: "N/A";
|
|
42
|
+
const size = audioMeta.fileSize
|
|
43
|
+
? formatSize(audioMeta.fileSize)
|
|
44
|
+
: "N/A";
|
|
45
|
+
console.log(`${slug}`);
|
|
46
|
+
console.log(` Title: ${title}`);
|
|
47
|
+
console.log(` Segments: ${segments}`);
|
|
48
|
+
console.log(` Duration: ${duration}`);
|
|
49
|
+
console.log(` Size: ${size}`);
|
|
50
|
+
console.log("");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function formatDuration(seconds) {
|
|
54
|
+
const mins = Math.floor(seconds / 60);
|
|
55
|
+
const secs = Math.floor(seconds % 60);
|
|
56
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
57
|
+
}
|
|
58
|
+
function formatSize(bytes) {
|
|
59
|
+
if (bytes < 1024)
|
|
60
|
+
return `${bytes} B`;
|
|
61
|
+
if (bytes < 1024 * 1024)
|
|
62
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
63
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
64
|
+
}
|
|
65
|
+
main().catch(console.error);
|
|
66
|
+
//# sourceMappingURL=podcast-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-list.js","sourceRoot":"","sources":["../../src/tools/podcast-list.ts"],"names":[],"mappings":"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"}
|