Initial commit: PodcastYes — AI podcast platform
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import type { EpisodeConfig, StructuredScript } from "../types";
|
||||
|
||||
const FORMAT_GUIDANCE: Record<EpisodeConfig["format"], string> = {
|
||||
SOLO: "A single host speaking directly to the listener. Use only the host speaker.",
|
||||
INTERVIEW:
|
||||
"A host interviewing a guest. Alternate naturally between host questions and guest answers.",
|
||||
MULTI_HOST:
|
||||
"A panel of co-hosts in lively conversation. Distribute lines across all speakers and let them react to each other.",
|
||||
};
|
||||
|
||||
/** Roughly 150 spoken words per minute → target word budget for the whole episode. */
|
||||
function wordBudget(minutes: number): number {
|
||||
return Math.round(minutes * 150);
|
||||
}
|
||||
|
||||
export function buildScriptMessages(config: EpisodeConfig) {
|
||||
const speakerList = config.speakers
|
||||
.map((s) => `- key "${s.speakerKey}" = ${s.displayName}`)
|
||||
.join("\n");
|
||||
|
||||
const system = [
|
||||
"You are an expert podcast scriptwriter and showrunner.",
|
||||
"You write natural, engaging, spoken-word scripts that sound great when read aloud by AI voices.",
|
||||
"Avoid stage directions, sound-effect notes, and parentheticals — output only spoken dialogue.",
|
||||
"Return STRICT JSON only, matching the requested schema. Do not include markdown fences.",
|
||||
].join(" ");
|
||||
|
||||
const user = [
|
||||
`Write a complete podcast episode script in ${config.language}.`,
|
||||
"",
|
||||
`Topic: ${config.topic}`,
|
||||
`Tone: ${config.tone}`,
|
||||
`Format: ${config.format} — ${FORMAT_GUIDANCE[config.format]}`,
|
||||
config.audience ? `Target audience: ${config.audience}` : "",
|
||||
`Approximate length: ${config.targetLengthMin} minutes (~${wordBudget(
|
||||
config.targetLengthMin
|
||||
)} words total).`,
|
||||
"",
|
||||
"Speakers (use ONLY these keys in `speakerKey`):",
|
||||
speakerList,
|
||||
"",
|
||||
"Structure the episode into 3–6 sections (e.g. intro, main segments, outro).",
|
||||
"Each section has a short title and a list of turns. Each turn is one speaker's spoken line.",
|
||||
"",
|
||||
"Return JSON with this exact shape:",
|
||||
`{
|
||||
"title": "string — a catchy episode title",
|
||||
"sections": [
|
||||
{
|
||||
"id": "kebab-case-id",
|
||||
"title": "string",
|
||||
"turns": [
|
||||
{ "speakerKey": "host", "text": "spoken line..." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
|
||||
return [
|
||||
{ role: "system" as const, content: system },
|
||||
{ role: "user" as const, content: user },
|
||||
];
|
||||
}
|
||||
|
||||
export function buildSectionMessages(
|
||||
config: EpisodeConfig,
|
||||
script: StructuredScript,
|
||||
sectionId: string
|
||||
) {
|
||||
const section = script.sections.find((s) => s.id === sectionId);
|
||||
const speakerList = config.speakers.map((s) => `"${s.speakerKey}"=${s.displayName}`).join(", ");
|
||||
|
||||
const system =
|
||||
"You are an expert podcast scriptwriter. Rewrite a single section of an existing episode, keeping the same speakers, tone, and language. Return STRICT JSON for just that one section.";
|
||||
|
||||
const user = [
|
||||
`Episode title: ${script.title}`,
|
||||
`Tone: ${config.tone}. Language: ${config.language}. Speakers: ${speakerList}.`,
|
||||
"",
|
||||
`Rewrite the section titled "${section?.title ?? sectionId}" (id "${sectionId}") to be fresh and engaging while serving the same purpose in the episode.`,
|
||||
"",
|
||||
"Return JSON with this exact shape:",
|
||||
`{ "id": "${sectionId}", "title": "string", "turns": [ { "speakerKey": "host", "text": "..." } ] }`,
|
||||
].join("\n");
|
||||
|
||||
return [
|
||||
{ role: "system" as const, content: system },
|
||||
{ role: "user" as const, content: user },
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user