Project plan · draft

History video portal

A local web portal that takes a YouTube episode from a topic to a finished MP4.

The channel’s niche is “You lived through a historical event”. Each episode is told in the second person, from the point of view of a fictional protagonist inside a real event. The protagonist is invented; events, dates and details must be historically accurate.

Length
About 10 minutes
Point of view
Second person, «ты»
Narration
Russian
First episode
Verdun, 1916
Title card from the Verdun script, scene s050

MVP scope

In scope

  • Script and image-prompt generation with Claude from a topic and settings.
  • Script stored as YAML with stable scene IDs.
  • Voiceover via a TTS API; static images via an image-generation API.
  • Preview at every stage.
  • Export to MP4 and a package for manual editing.

Out of scope for now

  • Scene animation (image-to-video).
  • Background music.
  • Multi-user access.
  • Authentication.

The portal runs locally on one machine and binds to 127.0.0.1 only.

Core principles

  1. Cheap before expensive. Each stage works only with what was approved in the previous stage.
  2. No paid API call without explicit confirmation. Show the estimated cost before every paid batch.
  3. Stable scene IDs. All files are named by scene ID, never by position or timecode.
  4. No timecodes in the script. Scene duration is the actual audio length plus an optional pause. The timeline is computed after audio generation.
  5. Everything about an episode lives in its YAML file: style, characters, voices, pronunciation dictionary.
  6. Edits invalidate only the dependent steps of the affected scene, not the whole episode.

Portal flow

An episode moves through five stages. Previews get richer with each one.

  1. 1 Topic Claude

    Portal

    Claude generates a full script (YAML) from the event, target duration and settings.

    User

    Sets the settings, approves the draft.

  2. 2 Script and prompts Claude

    Portal

    Shows scenes with narration text, Russian scene description and English image prompt.

    Preview

    Scene text, scene description, and estimated duration per scene and in total, from word count.

    User

    Edits lines and prompts, fact-checks, approves the final YAML.

  3. 3 Audio TTS API

    Portal

    Generates TTS per scene.

    Preview

    Real audio with pauses, and text cards instead of images: the “radio version”.

    User

    Listens, regenerates bad lines, fixes stress via the pronunciation dictionary, approves.

  4. 4 Images Image API

    Portal

    Generates images for image scenes; lets the user upload their own files for map and archive scenes.

    Preview

    Near-final video: images with camera motion plus audio, with placeholders where images are missing.

    User

    Regenerates bad images, edits prompts, approves.

  5. 5 Export ffmpeg

    Portal

    Builds the MP4 with ffmpeg and a package for manual editing.

    User

    Reviews the result.

Topic settings

Set in stage 1, before Claude writes the draft.

  • Historical event
  • Target duration, in minutes
  • Protagonist’s point of view, e.g. “French infantryman, 20 years old”
  • Tone
  • Visual style, from a list of saved style presets
  • Voice or voices
  • Language (default: Russian)

Script format

One YAML file per episode. An excerpt from the Verdun script:

projects/verdun/episode.yaml

episode:
  id: verdun
  title: "Ты прожил Верден"
  language: ru
  style: >
    1916, World War I, Verdun, French soldiers in horizon-blue greatcoats
    and Adrian helmets, 2D animated illustration, hand-painted look,
    muted desaturated palette, soft overcast lighting, paper grain,
    cinematic composition, serious dramatic tone, no gore

characters:
  lucien:
    name: Люсьен
    description: >
      a 20-year-old French infantryman, thin face, short dark hair,
      light stubble, tired brown eyes, horizon-blue greatcoat, Adrian helmet
    reference: refs/lucien.png        # optional character sheet

voices:
  narrator:
    voice_id: "VOICE_ID"
  documentary:
    voice_id: "VOICE_ID"

pronunciation:
  Дуомон: Дуомо́н
  Сувиль: Суви́ль

scenes:
  - id: s010
    part: Хук
    voice: narrator
    text: >
      Представь: февраль 1916 года. Ты лежишь в промёрзшей траншее,
      и земля под тобой дрожит.
    visual:
      type: image
      scene: >
        Крупный план лица молодого солдата в каске, иней на шинели.
        Со стенки окопа сыплются комья земли.
      characters: [lucien]
      motion: zoom_in

  - id: s050
    part: Хук
    voice: narrator
    text: >
      Солдаты назовут это место адом. Немцы — мельницей для костей.
      Сегодня ты проживёшь Верден.
    pause_after: 1.5
    visual:
      type: title
      text: "ВЕРДЕН. 1916"

  - id: s080
    part: Кто ты
    voice: documentary
    text: >
      Твоя часть стоит к северу от Вердена, старой крепости на реке Мёз,
      окружённой кольцом фортов.
    visual:
      type: map
      scene: Верден, река Мёз, кольцо фортов вокруг города.

Scene fields

FieldRequiredMeaning
idyesStable ID like s010, in steps of 10 so scenes can be inserted (s015)
partnoEpisode part, for navigation
voiceyesKey from voices
textyesNarration text
pause_afternoSilence after the line, in seconds
visual.typeyesimage, video, map, archive or title
visual.scenefor image, video, map, archiveScene description in Russian
visual.charactersnoCharacters in frame; their descriptions are injected into the prompt
visual.motionnoCamera motion at export: zoom_in, zoom_out, pan_left, pan_right, static
visual.motion_promptfor videoWhat moves (reserved for the future animation stage)
visual.promptnoEnglish image prompt; if present, it is used as is
visual.textfor titleTitle card text
notesnoNotes for manual editing

Visual types in the MVP

image
The only type generated via API.
video
Treated as image until the animation stage exists.
map, archive
Manual tasks: the user uploads a file.
title
Rendered by the portal.

Validation rules

Run before any paid call. Errors block the pipeline, and the portal shows a list of all problems.

  • Scene IDs are unique and sorted ascending.
  • Every voice and every referenced character exists in the header.
  • Each visual type has its required fields.
  • A scene’s narration is at most about 25 words (about 10 seconds).
  • The number of video scenes does not exceed a configured limit.

Files and statuses

Suggested layout

projects/<episode_id>/
├── episode.yaml        # the script, single source of truth for content
├── state.json          # per-scene, per-stage statuses, costs, approvals
├── audio/<scene_id>.mp3
├── images/<scene_id>.png
├── uploads/<scene_id>.<ext>   # user files for map/archive scenes
├── rejected/           # rejected files are moved here, never deleted
└── exports/            # MP4, SRT, timeline, editing package

Scene status

Each scene has one of four statuses per stage:

  • pending
  • generated
  • approved
  • redo

What an edit resets

When this changesWhat happens
Narration textAudio of that scene goes back to pending.
Scene descriptionThe prompt needs regeneration, then the image.
PromptImage of that scene goes back to pending.
Episode style or a character descriptionThe portal shows how many scenes a regeneration would touch and how much it would cost, and asks for confirmation.

Export

MP4 via ffmpeg

1080p, H.264 video, AAC audio

  • Each image is shown for its audio length plus pause_after.
  • Camera motion comes from visual.motion.
  • Short crossfades between scenes.
  • Title cards are rendered from visual.text.
  • Subtitles as a separate SRT, optionally burned in.

Editing package

All media named by scene ID, a timeline with start and duration per scene, the SRT, and a project importable into DaVinci Resolve (e.g. FCPXML).

Pre-export check

Lists missing files and placeholders before the build starts.

Implementation steps

Build the pipeline as CLI scripts first, then the web UI. If an episode assembled by scripts looks bad, the UI will not fix it.

  1. Step 0: Preparation

    • Create accounts and API keys (LLM, TTS, image generation). Keys in .env, .env in .gitignore.
    • Create a private Git repository.
    • Set spending limits in API dashboards where possible.
    • Voice test: run the same script excerpt through 2–3 TTS providers, pick one.
    • Style test: 5–10 images with different style prompts, pick one.
    • Create a character sheet for the protagonist.
  2. Step 1: Pipeline without UI

    • YAML schema and validator.
    • Script generation: a Claude prompt that outputs valid YAML; on validation errors, feed the errors back to Claude and retry.
    • Image prompt generation: Russian scene description → English prompt, with character descriptions injected and the episode style appended.
    • TTS per scene, with the pronunciation dictionary applied and neighbouring lines passed as context where the API supports it.
    • Image generation per scene: 16:9, at least 1920×1080 (or upscaled).
    • MP4 assembly with ffmpeg: images with camera motion, audio, pauses, titles, SRT.
    • Run the Verdun episode end to end, from YAML to MP4, using scripts only.
  3. Step 2: Portal MVP

    • Episode list, and an episode page with stage tabs.
    • Stage “Topic”: settings form, generation, approval.
    • Stage “Script”: scene editor (text, scene description, prompt), preview with estimated duration.
    • Stage “Audio”: generation, player, per-scene regeneration, pronunciation editing, preview with audio.
    • Stage “Images”: review, regeneration, uploads for map and archive scenes, preview.
    • Scene statuses, and invalidation of dependent steps on edits.
    • Cost counter and confirmation before paid steps.
    • Keyboard shortcuts for review: approve, redo, next, previous, replay audio.
  4. Step 3: Export

    • “Build MP4” button.
    • Editing package: media, timeline, SRT, DaVinci Resolve project.
    • Pre-export check: missing files, placeholders.
  5. Step 4: First real episode

    • Produce a full episode through the portal.
    • Record friction points and fix them.
  6. Later

    Scene animation, background music, multi-user access with authentication, style presets per era.

Tech stack

Proposed, not final.

Backend
Python (FastAPI or Flask)
Frontend
Plain HTML and JS, no framework for the MVP
Storage
Files (YAML and JSON), no database
Video
ffmpeg
Providers
Configurable: LLM (Anthropic API), TTS (e.g. ElevenLabs), images (e.g. Flux via fal.ai). Provider calls sit behind a small interface so they can be swapped.

Open decisions

  • Local only at first, or remote access for a second team member?
  • Final stack choice.
  • Which accounts and providers, who pays, which cards the providers accept.
  • Budget per episode and per month.
  • Visual style and voice, after the tests in step 0.
  • Topics of the first three episodes. The first is Verdun; its script already exists.

Risks

RiskMitigation
Factual errors in generated scriptsMandatory human fact-check at the script stage
YouTube monetization rules on mass-produced or inauthentic contentCheck the current YouTube Partner Program rules before investing; invest in a recognizable authored style
Runaway API costs from a bugProvider spending limits, cost counter, confirmation before paid steps
API key leaksKeys only in .env, .env in .gitignore, private repo
Payment issues with foreign providersCheck in step 0, before choosing providers

Instructions for the coding session

Start with step 1. Do not build the web UI until the Verdun episode can be produced end to end, from YAML to MP4, with CLI scripts.

Ask before making any paid API call during development.